Including Methods to a Controller from a Plugin

Including methods to a controller from a plugin

A common pattern is to use Dispatcher.to_prepare inside your plugin's init.rb. This is required because in development mode (or generally if config.cache_classes = false) Rails reloads all classes right before each request to pick up changes without the need to completely restart the application server each time.

This however means the you have to apply your patch again after the class got reloaded as Rails can't know which modules got injected later on. Using Dispatcher.to_prepare you can achieve exactly that. The code defined in the block is executed once in production mode and before each request in development mode which makes it the premier place to monkey patch core classes.

The upside of this approach is that you can have your plugins self-contained and do not need to change anything in the surrounding application.

Put this inside your init.rb, e.g. vendor/plugins/my_plugin/init.rb

require 'redmine'

# Patches to the Redmine core.
require 'dispatcher'

Dispatcher.to_prepare do
ApplicationController.send(:include, MyPlugin::ApplicationControllerPatch) unless ApplicationController.include?(RedmineSpentTimeColumn::Patches::IssuePatch)
end

Redmine::Plugin.register :my_plugin do
name 'My Plugin'
[...]
end

Your patch should always be namespaced inside a module named after your plugin to not run into issues with multiple plugins defining the same module names. Then put the patch into lib/my_plugin/application_controller_patch.rb. That way, it will be picked up automatically by the Rails Autoloader.

Put this into vendor/plugins/my_plugin/lib/my_plugin/application_controller_patch.rb

module MyPlugin
module ApplicationControllerPatch
def self.included(base) # :nodoc:
base.class_eval do
rescue_from AnException, :with => :rescue_method

def rescue_method(exception)
[...]
end
end
end
end
end

CakePHP: Use function from Plugin Controller in Main Controller

Short answer: Use OOP and extend the other controller. Also get an understanding of MVC. You are not supposed to use a method of a controller inside another controller, in CakePHP this should be done as a component. They can be shared between controllers. Check the CakePHP Book.

Also the name of the plugin and the method name indicate that this is a bad plugin. This sounds like somebody did not know about the Auth component of CakePHP. Again, check the book for the AuthComponent. You want a custom authentication adapter.

If the user is logged in you can get its id by calling $this->Auth->user('id'). Read the chapter about Auth. If you want a properly done user plugin check out: CakeDC Users

cakephp call a plugin controller

Just to build on Thorpe Obazee’s answer and my comment, it’s probably best to build something like permissions management as a controller component as opposed to a controller unto itself. As aforementioned, you really shouldn’t be calling controllers from controllers—this goes against the principles of MVC (and in turn CakePHP).

A component can be packaged within a plugin. If your plugin is called Permissions and your component is called Permissions too, then your directory structure would look like this:

  • app
    • Plugin
      • Permissions
        • Controller
          • Component
            • PermissionsComponent.php

When you load your plugin, you can then use this component in your core app’s controller as any other component, just with the plugin syntax:

<?php
class UsersController extends AppController {

public $components = array(
'Permissions.Permissions'
);
}

And your actual component within your plugin would look like this:

<?php
App::uses('Component', 'Controller');

class PermissionsComponent extends Component {

public function checkPermission() {
// code...
}
}

You would then call any component methods as you would a core component method:

<?php
class UsersController extends AppController {

public $components = array(
'Permissions.Permissions'
);

public function beforeFilter() {
$this->Permissions->checkPermission();
}
}

Hope this helps.

How to use a plugin method in the onDispatch event?

The problem isn't that "the plugin hasn't been loaded yet", but that you are not inside a controller, and you are trying to call a controller plugin.

What you must do here is to create a service, retrive an instance from the service manager, and call the method on that instance.

Example

module.config.php

'service_manager' => [
'factories' => [
// Avoid anonymous functions
\User\Service\AccessControl::class => \User\Service\AccessControlFactory::class
]
]

AccessControlFactory

namespace User\Service;

use Zend\ServiceManager\Factory\FactoryInterface;
use User\Service\AccessControl;

class AccessControlFactory implements FactoryInterface {

/**
* Factory called
*
* @param \Interop\Container\ContainerInterface $container
* @param string $requestedName
* @param array|null $options
* @return TranslatorPlugin
*/
public function __invoke(\Interop\Container\ContainerInterface $container, $requestedName, array $options = null) {
$userTable = $container->get(\User\Model\UserTable::class);
$grantsTable = $container->get(\User\Model\GrantsTable::class);
$authentication = $container->get(\User\Model\Authentication::class);
return new AccessControl($userTable, $grantsTable, $authentication);

}

}

AccessControl

namespace User\Service;

use User\Model\UserTable;
use User\Model\GrantsTable;
use User\Model\Authentication;

class AccessControl {

private $userTable;
private $grantsTable;
private $authentication;

function __construct(UserTable $userTable, GrantsTable $grantsTable, Authentication $authentication) {
$this->userTable = $userTable;
$this->grantsTable = $grantsTable;
$this->authentication = $authentication;

}

public function access(){
// Do your stuff
}

}

And finally, you can create it in your Module class and use it:

User\Module

public function onDispatch(MvcEvent $event) {
$controller = $event->getTarget();
$controllerName = $event->getRouteMatch()->getParam('controller', null);
$actionName = $event->getRouteMatch()->getParam('action', null);
$actionName = str_replace('-', '', lcfirst(ucwords($actionName, '-')));
$container = $event->getApplication()->getServiceManager();
$accessService = $container->get(\User\Service\AccessControl::class);
$accessService->access()->checkAccess($controllerName, $actionName);

}

Side note

From your snippets, it is quite obvious what you are trying to achieve. I'd suggest you to take a look at this question/answer, and at the comment done by rkeet, because that's the proper way to do access controls. ;)

Rails. How to extend controller class from plugin without any modification in controller file?

Sure, if you know the name of your controller, do

NameController.send(:include, Plug)


Related Topics



Leave a reply



Submit