Prevent media deletion

I’m hooking to the media events Introduction - October CMS - 3.x to update the fields in my models when a file is renamed or moved, and it’s working ok. But I’d like to be able to also prevent the files to be deleted if they are used in a model: if the user tries to delete a file that is used in a model, I’d like to show an error message saying “This file is being used in X models and cannot be deleted”. I think that I can’t do this right now because the media.file.delete event is launched after the file has been deleted. Is there any workaround that I can use to achieve this?

Hi @Maria_UtopigStudio,

how about to disable media deletion through permissions?

There is mediaDelete permission and is checked before any other actions in MediaManager.php (in function onDeleteItem).

Hi @Maria_UtopigStudio,

This my solution to your question. Add the code below to your plugin boot method. This prohibits deleting by default.

public function boot()
    {
        \Media\Widgets\MediaManager::extend(function($manager) {
            $manager->addDynamicMethod('__onDeleteItem', function() use($manager) {
                if(!$manager->customFilter()) {
                    throw new ForbiddenException;
                }

                return $manager->onDeleteItem();
            });

            $manager->addDynamicMethod('customFilter', function() {
                // Add you logic here to check if the file is being used by your model.
                // Return true to allow deletion.
                return false;
            });            
        });

        \Media\Controllers\Index::extend(function($controller) {

            $controller->addDynamicMethod('runMediaDeleteChecks', function(string $handler) use($controller) {
                if(strpos($handler, '::')) {
                    if ($handler === 'manager::onDeleteItem') {

                        [$widgetName, $handlerName] = explode('::', $handler);

                        if ($controller->fatalError) {
                            throw new SystemException($controller->fatalError);
                        }

                        if (!isset($controller->widget->{$widgetName})) {
                            throw new SystemException(Lang::get('backend::lang.widget.not_bound', ['name'=>$widgetName]));
                        }

                        if (($widget = $controller->widget->{$widgetName}) && $widget->methodExists($handlerName)) {
                            $result = $controller->runAjaxHandlerForWidget($widget, '__'.$handlerName);
                            return $result ?: true;
                        }

                    }
                }
            });

            $controller->addDynamicMethod('runAjaxHandlerForWidget', function($widget, $handler) use($controller) {
                $controller->addViewPath($widget->getViewPaths());

                $dependancyResolver = new \System\Classes\DependencyResolver;

                $resolvedParams = $dependancyResolver->resolve($widget, $handler, $controller->params?:[]);
                $result = $widget->$handler(...$resolvedParams);

                $controller->vars = $widget->vars + $controller->vars;

                return $result;
            });
        });


        Event::listen('backend.ajax.beforeRunHandler', function($controller, string $handler) {
            if($controller instanceof \Media\Controllers\Index) {
                return $controller->runMediaDeleteChecks($handler);                
            }
        });
    }

You need to update the customFilter method to check if the is being used by your model.

$manager->addDynamicMethod('customFilter', function() {
      // Add you logic here to check if the file is being used by your model.
      // Return true to allow deletion.
      return true;
}); 

I hope this solves your issue.