I'd like to include action hooks similar to those found in Wordpress. I've read the chapter on writing plugins, but I'd to be able to maintain them without altering the code in the parent app.
Is there any baked-in support for this?
If not, is there a good way to do it? I have some ideas but I'm worried I'm going to be reinventing the wheel.
Yes there is.
CakePHP Event System
It works a lot like WordPress's hooks, but only better.
You can register callbacks in different places, but easy way is to do this in the bootstrap.php of the plugin.
When the application loads the plugin it can tell CakePHP to bootstrap it. This is done with this command.
CakePlugin::loadAll(array(array('bootstrap'=>true,'routes'=>true)));
I'm not sure what you're trying to acomplish, but you could do something like this:
Your controller(s) beforeFilter() method is a good place to create certains hooks:
public function beforeFilter() {
parent::beforeFilter(); // don't forget to call parent code
$myHandler->doSomethingInteresting( $this->name, $this->action );
}
where $this->name will give you the name of the controller being called, and $this->action will give you the name of the current action.
I hope it helps a little.
Related
I am using SS 3.02 and have made a lots of modification in the core files. I am facing the issue that I am trying to set the color of the navigation background dynamically. This works fine for pages other than security/login page. Suppose I am getting the value in $navbgcolor, this shows up well on home page or about us page or any other page. But this does not show up on the Security/login page. Any help would be greatly appreciated. Thanks.
Firstly, it is never a good idea to alter the core files as this prevents you from easily updating your version of SilverStripe. You could miss out on bug fixes and important security updates.
The reason this isn't working on the login page is because the login page works from the Security controller which directly extends Controller. Your code (presumably in Page_Controller) will be completely bypassed.
Here is a way you could apply your code to all controllers, without touching the core:
<?php
class MyControllerExtension extends Extension {
public function onAfterInit() {
//... Your code here...
}
}
In your config file you would apply your new controller extension to Controller.
If you're using _config.php
Object::add_extension("MyControllerExtension", "MyControllerExtension")
If you're using YAML (recommended)
Controller:
extensions:
- 'MyControllerExtension'
You can learn more about extensions here: http://doc.silverstripe.org/framework/en/reference/dataextension
Also to let you know, you can create specific template file for the Security login pages by creating action sub-templates. Example is if you created a file in your theme called "Security_login.ss" you can call in variable, change the mark up etc.
Note the convention here is the filename is called the name of the class in this case "Security" then "_" followed by the name of the action to be rendered by your controller ("login" in this case).
As mentioned by micmania1, the golden rule for developing in SilverStripe is...
"Don't hack the core or modules!"
Instead as pointed out use extensions to decorate classes, or use subclasses if you have to.
I'm using Zend Framework to build a website and I'm having some trouble with the dispatch loop.
Normally, Zend Framework URLs are built this way: http://www.domain.com/module/controller/action.
On my website, I'm using customized dynamic URLs which are parsed on the dispatch loop by a custom method. So, each one of these URLs, after being parsed, will execute a specific action of a specific controller and module.
I need to perform some tasks which depend on the module, controller and action that were parsed. The problem is that I'm only being able to know the parsed module, controller and action when dispatchLoopShutdown occurs. The tasks that I need to execute will set some cookies which will make changes on the output that will be sent to the browser.
But, at this point, the view has already been rendered, and the cookies that were set when dispatchLoopShutdown occurs won't change the output accordingly.
So, my question is... is there a way to force the view to be rendered again? Or a way to know what module, controller and action will be executed, before the dispatchLoopShutdown? I've also tried to accomplish this on the postDispatch but the results are the same!
Hope I could explain my problem right.
Thank you for your help.
Here is a good schema about the Zend Framework sequence.
You can know the module, controller and action before the dispatch by using a controller plugin:
<?php
class Custom_Controller_Plugin_CheckRoute extends Zend_Controller_Plugin_Abstract
{
public function preDispatch($request)
{
//Get Request
$controller = $request->controller;
$action = $request->action;
$module = $request->module;
//=> perform actions before dispatch
//Update the Request
$request->setModuleName('default')
->setControllerName('index')
->setActionName('action2');
}
}
?>
I had the same problem. It was solved by Zend_Controller_Plugin_ActionStack. I added some action where implemented logic from dispatchLoopShutdown. This link can be useful http://framework.zend.com/manual/1.12/en/zend.controller.plugins.html#zend.controller.plugins.standard.actionstack
I searched a lot around the web but I couldn't find any specific sollution to this.
In CakePHP 1.3, different from 1.2, if you had a controller inside a plugin, and both had the same name, you could access through "<plugin>/<action>", and it would call the 'default' controller. But in 1.3, according to this:
http://cakeqs.org/eng/questions/view/setting_up_magic_routes_for_plugins_in_cakephp_1_3
It was removed, and only the 'index' action in the default plugin controller can be accessed this way.
I thought about adding extra code in my routes.php file, and loop through all the plugins in my app, making such routes for every action in the controllers named after the plugin, but it doesn't seem like it's the right thing to do...
any other suggestions to make this work in 1.3? or at least some very specific code documentation of this particular change? I've already read something in the 1.3.0-RC4 annoucement, but it was not clear enough..
thanks
Assuming a plugin named "test", you could do something like this in app/plugins/test/controller/test_controller.php:
<?php
class TestController
extends AppController
{
public function index()
{
// Is there any additional args passed to us?
if(count($this->passedArgs) > 0)
{
// Is this a request for one of our actions?
$actualAction = $this->passedArgs[0];
if(is_callable(array($this, $actualAction)))
{
// Yup. Do it.
return call_user_func_array(array($this, $actualAction), array_slice($this->passedArgs, 1));
}
}
// Default functionality here.
die("Index of plugin requested.");
}
public function another($param1, $param2)
{
die("{$param1}, {$param2}");
}
}
You'll also have to add the following to app/config/routes.php:
Router::connect("/test/*", array("plugin" => "test", "controller" => "test"));
With this done, a request to /test/another/one/two will correctly render "one, two" in the browser, and a request to /test will display "Index of plugin requested."
I think this isn't a bad way to go, minimal fuss on the plugin consumer side, only a little bit of fluff in the plugin code.
How could I prevent mentioned plugin's login form from using default layout? I am aware of this question, but that answer doesnt work for me. For starters, there's no signin module in modules dir, probably plugins handle it in different way, I dont know. Just learning symfony. Thanks in advance :)
For now its not possible to set custom layout for some sfGuardAuth action via custom view.yml.
This is how I did it.
This is my apps/backend/modules/sfGuardAuth/actions/actions.class.php:
<?php
require_once(sfConfig::get('sf_plugins_dir').'/sfDoctrineGuardPlugin/modules/sfGuardAuth/lib/BasesfGuardAuthActions.class.php');
class sfGuardAuthActions extends BasesfGuardAuthActions
{
public function preExecute()
{
$layout = $this->getActionName() == sfConfig::get('sf_login_action') ? 'sfGuardLayout' : $this->getLayout();
$this->setLayout($layout);
}
}
If you just want to set a different layout, you need to add a module (just create it manually) called "sfGuardAuth". Inside the /config/ directory for that, change the layout in the view.yml like for any other module. This is explained in:
http://www.symfony-project.org/plugins/sfDoctrineGuardPlugin/4_0_0
... under section "Customize sfGuardAuth module actions".
However, if you want to "embed" your login form on another existing page, you could turn the login into a component - which means it uses the existing layout of the page it occurs in.
Component action in a custom module:
public function executeSigninLightbox(sfWebRequest $request)
{
$class = sfConfig::get('app_sf_guard_plugin_signin_form', 'sfGuardFormSignin');
$this->form = new $class();
}
... which like all components uses a partial as its view. The partial now has access to $form like a standard login page. The partial for this would be called "_signinLightbox".
Hope that helps.
I want to add some new functions to the core string helper, which is found in system/helpers folder. I think there was a 'right' way to do this using MY_String_helper, or something of that sort. I can't remember exactly how it was done though. Any thoughts on this issue?
I found it. Make a file with a name such as this, in the application/helpers directory:
MY_xx_helper.php
E.g:
MY_string_helper.php
Then you can call
$this->load->helper('string');
And it should load all the existing helper functions as well as the new ones you add.
Doing that you can not only add new functions but replace exising helper functions.
For a primary source, in case things change in the future, the CodeIgniter User Guide's Helpers page has a section describing how to extend helpers.