Drupal: Media, files and how to control their visibility
Misunderstandings in the relationship between media and files
Since the Media module and the Media Library arrived in the Drupal core (i.e. some time ago), it has made sense to work with media instead of image fields in order to be able to use all the advantages of media entities (own publication status of the entity, any fields for description, caption.., revisions and much more).
However, two phenomena occasionally cause irritation:
- If a media object is deleted, the corresponding file is still there (and publicly accessible via the file URL)!
- If a media object is unpublished (and it is no longer displayed as such anywhere), the file used by it is also still publicly accessible via the direct file URL!
Both are, of course, completely counterintuitive for users: anyone who deletes or unpublishes a media object, naturally expects that the corresponding file (jpg, pdf,..) will also be deleted or no longer accessible.
The problem is that media and files are different entities and the file used by a media object is independent of it. The web server does not know about a connection between the media object, publication status and file. If the file exists in a public directory, the web server delivers it - without Drupal being involved in this process.
So it can happen, for example, that someone opens a document or image via a search hit using the direct file URL that has long since been unpublished or even deleted from an editorial point of view and should no longer be available at all (and which is actually no longer visible in the actual, editorial sense as a Drupal Media embedded object).
Remedy for deleting media
To delete files when a media object is deleted, there is the Media File Delete module. The module adds a button to the form when deleting media with the option to delete the file of the media object at the same time - problem 1) solved!

Media delection Dialog extended by Media File Delete
Unpublishing - more complicated than simply deleting...
In the case of unpublishing, it is of course more complicated because here, the file must be made inaccessible instead of being deleted. It should only be temporarily inaccessible - as long as the corresponding media object is unpublished.
There is the Drupal module File Visibility, which works together with Track Usage and can be configured for various use cases. The module then moves files that are used in (as yet) unpublished content or media to Drupal's private file system and retrieves them back to the public file system as soon as the parent content (node, paragraph or media) is published.
The configuration is, however, relatively extensive and not directly self-explanatory, so you need to familiarize yourself a little with the interaction of the two modules. On the other hand, it also seemed to me that this would create a lot of overhead in the background. Furthermore, the original use case of the File Visibility module is different, it is mainly about avoiding spamdexing.
So you might be tempted to use the private file system directly as the default in order to exclude the two problematic cases. However, in most cases this would lead to an absurdly high server load, because Drupal would then have to check the output of every single file - as Claudiu Cristea explains on the project page of File Visibility:
The site builder might be tempted to set the private file system as the default scheme for all uploaded files. But this is not recommended, as Drupal might crash in heavy traffic. Actually, for each file the webserver, PHP, and Drupal will have to do heavy processing. While a public file is served directly by the webserver, and 90% of the time is retrieved either from the frontend cache or even from the browser's cache.
But actually the file used by the respective unpublished media object should just no longer be accessible by its URL. I was looking for a simpler approach and was able to find a remedy with the all-in-one tool ECA without having to write my own module. We have also described the Drupal module ECA (Event, Condition, Action) in general here.
How files can be hidden
The idea behind our more light-footed approach is simple: if a media object is depublished, we temporarily rename the file so that the web server will simply no longer output it. The simple prefix .ht
does this, just like with the well-known files .htaccess
or .htpasswd
.
The original URL to the file then returns the status 404 Not found
, because a file with this name no longer exists. If, hypothetically, someone tries to access the renamed file, the web server will respond with 403 Forbidden
.
If the media object receives the status "published" again, the prefix of the file must be removed again: Problem 2) solved.
Check media when creating / updating
The first part of our ECA model reacts to insert
and update
events from media in the system. This first step is simply to check whether the media object has a relevant file field (here only for Image, Document, Video - but it can of course be extended for any custom media).

ECA flow to check whether a relevant file is contained in the media object.
Checking the status and renaming
In the second step, the custom event Adjust Filename
is triggered. It then checks whether the media object is published or not and acts accordingly:
- If it is not published and the file name does not yet begin with
.ht.
, the file is renamed and given this prefix - If it is published and the file name still begins with
.ht.
, the prefix is removed again

The second ECA flow renames files depending on the publication status of the parent media object