I am attempting to override a module in Prestashop but the changes are just not appearing.
I have overridden a template file and a controller so I have added the following files:
\override\modules\blockwishlist\views\templates\front\mywishlist.tpl
\override\modules\blockwishlist\controllers\front\mywishlist.php
These are very simple changes where I add another column to a table that contains a button. When the button is clicked the controller generates a CSV file.
Any idea why these changes are just not being shown?
Note: I have turned on 'Force Compile' and turned of caching.
Edit:
Re overridding a controller is it:
class BlockWishListMyWishListModuleFrontController extends BlockWishListMyWishListModuleFrontControllerCore // extends ModuleFrontController
or
class BlockWishListMyWishListModuleFrontControllerOverride extends BlockWishListMyWishListModuleFrontController
okay, I did some code research (maybe thre is exists easiest way, not sure), so:
in code Dispatcher class we have
// Dispatch module controller for front office
case self::FC_MODULE :
$module_name = Validate::isModuleName(Tools::getValue('module')) ? Tools::getValue('module') : '';
$module = Module::getInstanceByName($module_name);
$controller_class = 'PageNotFoundController';
if (Validate::isLoadedObject($module) && $module->active) {
$controllers = Dispatcher::getControllers(_PS_MODULE_DIR_.$module_name.'/controllers/front/');
if (isset($controllers[strtolower($this->controller)])) {
include_once(_PS_MODULE_DIR_.$module_name.'/controllers/front/'.$this->controller.'.php');
$controller_class = $module_name.$this->controller.'ModuleFrontController';
}
}
$params_hook_action_dispatcher = array('controller_type' => self::FC_FRONT, 'controller_class' => $controller_class, 'is_module' => 1);
break;
where we see that controllers loaded without overrides, but from other side below in code we see hook execution:
// Loading controller
$controller = Controller::getController($controller_class);
// Execute hook dispatcher
if (isset($params_hook_action_dispatcher)) {
Hook::exec('actionDispatcher', $params_hook_action_dispatcher);
}
so one of possible solution (without overriding core class) :
how to override module and hope you have core version >= 1.6.0.11
in blockwishlist.php in install() method add
this->registerHook('actionDispatcher')
to condition with other hooks, so it will looks like ... !this->registerHook('actionDispatcher') || ... because this hook not registered by default and we can't just place module there.
create method (can't beautify code here)
public function hookActionDispatcher($params)
{
if ('blockwishlistmywishlistModuleFrontController' == $params['controller_class']) {
include_once(_PS_OVERRIDE_DIR_ . 'modules/' . $this->name . '/controllers/front/mywishlist.php');
$controller = Controller::getController('BlockWishListMyWishListModuleFrontControllerOverride');
}
}
you already have override/modules/blockwishlist/controllers/front/mywishlist.php file by this path
reinstall module.
it works!
more about overriding some behaviors in docs
Turns out that to override a module you dont place your files in:
~/overrides/module/MODULE_NAME
Instead you place it in:
~/themes/MY_THEME/modules/MODULE_NAME
Now the changes are exhibiting themselves.
Does anyone know if/when the module is auto-updated, will my changes get lost/overrwritten?
Related
I need to override the function
protected function getLanguageParameter()
{
$states = $this->getBackendUser()->uc['moduleData']['web_view']['States'];
$languages = $this->getPreviewLanguages();
$languageParameter = '';
if (isset($states['languageSelectorValue']) && isset($languages[$states['languageSelectorValue']])) {
$languageParameter = '&L=' . (int)$states['languageSelectorValue'];
}
$languageParameter = '&L=1';
return $languageParameter;
}
in the class TYPO3\CMS\Viewpage\Controller\ViewModuleController. It get called when you are open the View in the backend.
Lets say I would extend the class in my own extension. I already need a Hook that calls the function?
But how can I get that hook?
If the function has no hook yet you can try to insert it: make a patch and wait for it to become merged.
As 8 LTS already receives only 'priority bugfixes' it probably will not get merged.
The other way would be XClassing.
The name is quite bad, but I really don't know what else to call it.
I'm trying to make a extendable and modular plugin system for my website. I need to be able to access plugin php files that exist in a plugin directory and get access to their classes to call functions such as getting the html content that the plugin should show and more.
Below is a semi-pseudo code example of what I am trying to achieve, but how to actually arbitrarily load the plugins is where I am stuck (PluginLoader.php).
-Max
//BasePlugin.php
abstract class BasePlugin
{
public function displayContent()
{
print "<p>Base Plugin</p>";
}
};
//ExamplePlugin.php -> In specific plugin directory.
require('../BasePlugin.php');
class ExamplePlugin extends BasePlugin
{
public static function Instance()
{
static $inst = null;
if ($inst === null) {
$inst = new ExamplePlugin();
}
return $inst;
}
public function displayContent()
{
print "<p>Example Plugin</p>";
}
}
//PluginLoader.php
foreach($pluginFile : PluginFilesInDirectory) { // Iterate over plugin php files in plugin directory
$plugin = GetPlugin($pluginFile); // Somehow get instance of plugin.
echo plugin->displayContent();
}
I'm guessing here, but it seems to me that you need to:
get a list of the plugins in the desired directory.
include or require the plugin's class file.
create an instance of the class.
call the plugin's displayContent() method.
So, you probably want to do something like
$pluginDir = 'your/plugin/directory/' ;
$plugins = glob($pluginDir . '*.php') ;
foreach($plugins as $plugin) {
// include the plugin file
include_once($plugin) ;
// grab the class name from the plugin's file name
// this finds the last occurrence of a '/' and gets the file name without the .php
$className = substr($plugin,strrpos($plugin,'/') + 1, -4) ;
// create the instance and display your test
$aPlugin = $className::Instance() ;
$aPlugin->displayContent() ;
}
There's probably a cleaner way to do it, but that will ready your directory, get the plugins' code, and instantiate each one. How you manage/reference them afterwards depends on how your plugins register with your application.
I'm writing a prestashop module for prestashop 1.7.2.1.
I created a front controller for my module with the following code:
<?php
require_once (__DIR__.DIRECTORY_SEPARATOR.'..'.DIRECTORY_SEPARATOR.'..'.DIRECTORY_SEPARATOR.'php'.
DIRECTORY_SEPARATOR.'TuxInDb.php');
class TuxInModCarTypeCarTypeProductsModuleFrontController extends ModuleFrontController {
private $tuxDb;
public function initContent(){
parent::initContent();
$productIds = [];
$this->tuxDb = TuxInDb::getInstance();
$companyName = Tools::getValue('company_name');
$modelName = Tools::getValue('model_name');
$year = Tools::getValue('year');
$month = Tools::getValue('month');
$carType = Tools::getValue('car_type');
$carListCarTypeIds=$this->tuxDb->getCarListCarTypeIds($companyName,$modelName,$carType,$year,$month);
$productIds = $this->tuxDb->getProductIdsByCarListCarTypeIds($carListCarTypeIds);
$this->context->smarty->assign('product_ids',$productIds);
$this->setTemplate('module:tuxinmodcartype/views/templates/front/cartypeproducts.tpl');
}
public function setMedia() {
parent::setMedia();
$this->registerStylesheet('module-tuxinmodcartype-cartypeproducts-style','modules/'.$this->module->name.'/css/cartypeproducts.css');
$this->registerJavascript('module-tuxinmodcartype-cartypeproducts-js','modules/'.$this->module->name.'/js/cartypeproducts.js');
}
}
as you can see in setMedia() function I load a css and js files.
I even debugged it in xdebug and I noticed that those lines of code actually get executed, but when I try to browse my front controller with the following url:
http://prestashop.dev:8080/index.php?company_name=BMW&model_name=SERIA+1&year=2011&month=1&car_type=5+%D7%93%D7%9C%D7%AA%D7%95%D7%AA+%28%D7%94%D7%90%D7%A6%D7%B3%D7%91%D7%A7%29&fc=module&module=tuxinmodcartype&controller=cartypeproducts&id_lang=1
and I check the network tab of my google chrome browser I noticed that the js and css file I required do not get loaded.
any ideas?
I see no javascript errors or php errors (I also have DEV enabled in prestashop).
If an asset path is wrong then Prestashop won't even append it to the browser's <head> (or bottom depending on CCC settings) and won't throw out any errors.
Probably your path is incorrect, to get proper path use this:
$this->registerStylesheet('module-tuxinmodcartype-cartypeproducts-style', $this->module->getPathUri() . 'css/cartypeproducts.css');
$this->registerJavascript('module-tuxinmodcartype-cartypeproducts-js', $this->module->getPathUri() . 'js/cartypeproducts.js');
This works well with PrestaShop 1.7.x
Add this inside your ModuleFrontController:
public function setMedia()
{
parent::setMedia();
$this->addCSS($this->module->getPathUri().'views/css/style.css');
}
I hope this helps!
I've just started looking into Zend framework 2 .One thing that I can’t seem to figure out is how to change the behavior of the framework when its deciding what view template to use when i’m not passing it in the viewmodel.
When looking for the answer myself I found the following, which states that Zend resolves view templates using the pathing below:
{normalized-module-name}/{normalized-controller-name}/{normalized-action-name}
(Source: http://zend-framework-community.634137.n4.nabble.com/Question-regarding-template-path-stack-tp4660952p4660959.html)
Now I’m looking to edit or remove the normalized-module-name segment. All the view files stay in my module/views folder. The reason I want to change this is because I’m using sub namespaces as my module name, resulting in the first segment of the namespace as the normalized module name (which is not specific enough for me).
To give you an example, the module Foo\Bar will result in an example view being rendered from:
/modules/Foo/Bar/view/foo/test/index.phtml.
I would like to change that default behavior to:
/modules/Foo/Bar/view/bar/test/index.phtml
Starting with zf 2.3 you can use extra config parameter view_manager['controller_map'] to enable different template name resolving.
Look at this PR for more info: https://github.com/zendframework/zf2/pull/5670
'view_manager' => array(
'controller_map' => array(
'Foo\Bar' => true,
),
);
Will result in controller FQCN starting with 'Foo\Bar' to be resolved following those rules:
strip \Controller\ namespace
strip trailing Controller in classname
inflect CamelCase to dash
replace namespace separator with slash
Eg: Foo\Bar\Controller\Baz\TestController -> foo/bar/baz/test/actionname
Update:
Starting with zend-mvc v3.0 this is default behavior
I had a similar problem and here's my solution.
Default template injector is attached to an event manager of the current controller with priority -90, and it resolves a template name only if a view model is not provided with one.
Knowing this, you can create your own template injector with a required logic and attach it to the event manager with the higher priority.
Please see the code below:
public function onBootstrap(EventInterface $event)
{
$eventManager = $event->getApplication()->getEventManager();
$eventManager->getSharedManager()
->attach(
'Zend\Stdlib\DispatchableInterface',
MvcEvent::EVENT_DISPATCH,
new TemplateInjector(),
-80 // you can put here any negative number higher -90
);
}
Your template injector which resolves template paths instead of the default one.
class TemplateInjector
{
public function __invoke(MvcEvent $event)
{
$model = $event->getResult();
if (!$model instanceof ViewModel)
{
return;
}
$controller = $event->getTarget();
if ($model->getTemplate())
{
return ;
}
if (!is_object($controller))
{
return;
}
$namespace = explode('\\', ltrim(get_class($controller), '\\'));
$controllerClass = array_pop($namespace);
array_pop($namespace); //taking out the folder with controllers
array_shift($namespace); //taking out the company namespace
$moduleName = implode('/', $namespace);
$controller = substr($controllerClass, 0, strlen($controllerClass) - strlen('Controller'));
$action = $event->getRouteMatch()->getParam('action');
$model->setTemplate(strtolower($moduleName.'/'.$controller.'/'.$action));
}
}
Here's the link from my blog where I wrote about it in more details: http://blog.igorvorobiov.com/2014/10/18/creating-a-custom-template-injector-to-deal-with-sub-namespaces-in-zend-framework-2/
Right template to ViewModel is injected in MVC event 'dispatch' (defined in ViewManager) by Zend\Mvc\View\Http\InjectTemplateListener with priority -90
You'll have to create custom InjectTemplateListener and register it with higher priority to same event.
But I'd recommend to set template in every action by hand, because of performance - see http://samminds.com/2012/11/zf2-performance-quicktipp-1-viewmodels/
template name resolving is a heavy process(on system resources), and all the articles about ZF2 performance says that you should provide the template name manually to increase performance.
so don't waste time finding a way to do something that you will end up doing manually :D
In order to improve Next Developer answer, I write the following code in TemplateInjector.php:
class TemplateInjector
{
public function __invoke(MvcEvent $event)
{
$model = $event->getResult();
if (!$model instanceof ViewModel) {
return;
}
if ($model->getTemplate()) {
return;
}
$controller = $event->getTarget();
if (!is_object($controller)) {
return;
}
$splitNamespace = preg_split('/[\\\]+/', ltrim(get_class($controller), '\\'));
$moduleName = $splitNamespace[1];
$controller = $splitNamespace[0];
$action = $event->getRouteMatch()->getParam('action');
$model->setTemplate(strtolower($moduleName . '/' . $controller . '/' . $action));
}
}
I've changed the way to build the Template path. Using regexp is faster than using array methods.
I have build a CMS using Zend Framework (1.11). In the application I have two modules, one called 'cms' which contains all the CMS logic and an other 'web' which enables a user to build their own website around the CMS. This involves adding controllers/views/models etc all in that module.
The application allows you to serve multiple instances of the app with their own themes. These instances are identified by the hostname. During preDispatch(), a database lookup is done on the hostname. Based on the database field 'theme' the app then loads the required css files and calls Zend_Layout::setLayout() to change the layout file for that specific instance.
I want to extend this functionality to also allow the user to run different web modules based on the 'theme' db field. However, this is where I am stuck. As it is now, the web module serves the content for all the instances of the application.
I need the application to switch to a different web module based on the 'theme' database value (so indirectly the hostname). Any ideas?
Well, in my opinion,
You should write a front controller plugin for the web module, and do it so, that when you need another plugin, you can do so easily.
The front controller plugin should look something like this:
class My_Controller_Plugin_Web extends My_Controller_Plugin_Abstract implements My_Controller_Plugin_Interface
{
public function init()
{
// If user is not logged in - send him to login page
if(!Zend_Auth::getInstance()->hasIdentity()) {
// Do something
return false;
} else {
// You then take the domain name
$domainName = $this->_request->getParam( 'domainName', null );
// Retrieve the module name from the database
$moduleName = Module_fetcher::getModuleName( $domainName );
// And set the module name of the request
$this->_request->setModuleName( $moduleName );
if(!$this->_request->isDispatched()) {
// Good place to alter the route of the request further
// the way you want, if you want
$this->_request->setControllerName( $someController );
$this->_request->setActionName( $someAction );
$this->setLayout( $someLayout );
}
}
}
/**
* Set up layout
*/
public function setLayout( $layout )
{
$layout = Zend_Layout::getMvcInstance();
$layout->setLayout( $layout );
}
}
And the My_Controller_Plugin_Abstract, which is not an actual abstract and which your controller plugin extends,looks like this:
class My_Controller_Plugin_Abstract
{
protected $_request;
public function __construct($request)
{
$this->setRequest($request);
$this->init();
}
private function setRequest($request)
{
$this->_request = $request;
}
}
And in the end the front controller plugin itself, which decides which one of the specific front controller plugins you should execute.
require_once 'Zend/Controller/Plugin/Abstract.php';
require_once 'Zend/Locale.php';
require_once 'Zend/Translate.php';
class My_Controller_Plugin extends Zend_Controller_Plugin_Abstract
{
/**
* before dispatch set up module/controller/action
*
* #param Zend_Controller_Request_Abstract $request
*/
public function routeShutdown(Zend_Controller_Request_Abstract $request)
{
// Make sure you get something
if(is_null($this->_request->getModuleName())) $this->_request->setModuleName('web');
// You should use zend - to camelCase convertor when doing things like this
$zendFilter = new Zend_Filter_Word_SeparatorToCamelCase('-');
$pluginClass = 'My_Controller_Plugin_'
. $zendFilter->filter($this->_request->getModuleName());
// Check if it exists
if(!class_exists($pluginClass)) {
throw new Exception('The front controller plugin class "'
. $pluginClass. ' does not exist');
}
Check if it is written correctly
if(!in_array('My_Controller_Plugin_Interface', class_implements($pluginClass))) {
throw new Exception('The front controller plugin class "'
. $pluginClass.'" must implement My_Controller_Plugin_Interface');
}
// If all is well instantiate it , and you will call the construct of the
// quasi - abstract , which will then call the init method of your
// My_Plugin_Controller_Web :)
$specificController = new $pluginClass($this->_request);
}
}
If you have never done this, now is the time. :)
Also, to register your front controller plugin with the application, you should edit the frontController entry in your app configuration. I will give you a json example, i'm sure you can translate it to ini / xml / yaml if you need...
"frontController": {
"moduleDirectory": "APPLICATION_PATH/modules",
"defaultModule": "web",
"modules[]": "",
"layout": "layout",
"layoutPath": "APPLICATION_PATH/layouts/scripts",
// This is the part where you register it!
"plugins": {
"plugin": "My_Controller_Plugin"
}
This might be a tad confusing, feel free to ask for a more detailed explanation if you need it.