2011-08-10 02:50:16 +02:00
< ? php
2016-07-06 11:14:17 +02:00
/* Copyright ( C ) 2010 - 2016 Laurent Destailleur < eldy @ users . sourceforge . net >
2018-10-27 14:43:12 +02:00
* Copyright ( C ) 2010 - 2014 Regis Houssin < regis . houssin @ inodbox . com >
2011-08-10 02:50:16 +02:00
* Copyright ( C ) 2010 - 2011 Juanjo Menent < jmenent @ 2 byte . es >
*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License as published by
2013-01-16 15:36:08 +01:00
* the Free Software Foundation ; either version 3 of the License , or
2011-08-10 02:50:16 +02:00
* ( at your option ) any later version .
*
* This program is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU General Public License for more details .
*
* You should have received a copy of the GNU General Public License
2019-09-23 21:55:30 +02:00
* along with this program . If not , see < https :// www . gnu . org / licenses />.
2011-08-10 02:50:16 +02:00
*/
/**
* \file htdocs / core / class / hookmanager . class . php
* \ingroup core
* \brief File of class to manage hooks
*/
/**
2012-05-06 00:45:15 +02:00
* Class to manage hooks
2011-08-10 02:50:16 +02:00
*/
class HookManager
{
2018-08-22 11:06:34 +02:00
/**
* @ var DoliDB Database handler .
*/
public $db ;
2019-01-21 15:22:35 +01:00
2018-08-22 10:37:16 +02:00
/**
* @ var string Error code ( or message )
*/
public $error = '' ;
2018-08-21 19:08:11 +02:00
2018-08-17 19:31:22 +02:00
/**
* @ var string [] Error codes ( or messages )
*/
2018-08-21 19:08:11 +02:00
public $errors = array ();
2011-08-10 02:50:16 +02:00
2011-10-14 18:51:20 +02:00
// Context hookmanager was created for ('thirdpartycard', 'thirdpartydao', ...)
2019-02-26 23:15:28 +01:00
public $contextarray = array ();
2011-08-10 02:50:16 +02:00
// Array with instantiated classes
2019-02-26 23:15:28 +01:00
public $hooks = array ();
2012-03-15 22:37:42 +01:00
2012-03-02 10:29:11 +01:00
// Array result
2019-02-26 23:15:28 +01:00
public $resArray = array ();
2013-01-22 17:43:41 +01:00
// Printable result
2019-02-26 23:15:28 +01:00
public $resPrint = '' ;
2017-06-06 01:34:25 +02:00
// Nb of qualified hook ran
2019-02-26 23:15:28 +01:00
public $resNbOfHooks = 0 ;
2011-08-10 02:50:16 +02:00
/**
2011-10-14 18:51:20 +02:00
* Constructor
*
2015-01-01 11:24:00 +01:00
* @ param DoliDB $db Database handler
2011-08-10 02:50:16 +02:00
*/
2019-02-26 23:15:28 +01:00
public function __construct ( $db )
2011-08-10 02:50:16 +02:00
{
2012-02-24 08:52:30 +01:00
$this -> db = $db ;
2011-08-10 02:50:16 +02:00
}
/**
2015-01-01 11:24:00 +01:00
* Init array $this -> hooks with instantiated action controlers .
2017-06-08 11:18:41 +02:00
* First , a hook is declared by a module by adding a constant MAIN_MODULE_MYMODULENAME_HOOKS with value 'nameofcontext1:nameofcontext2:...' into $this -> const of module descriptor file .
2015-01-01 11:24:00 +01:00
* This makes $conf -> hooks_modules loaded with an entry ( 'modulename' => array ( nameofcontext1 , nameofcontext2 , ... ))
* When initHooks function is called , with initHooks ( list_of_contexts ), an array $this -> hooks is defined with instance of controler
2011-10-14 18:51:20 +02:00
* class found into file / mymodule / class / actions_mymodule . class . php ( if module has declared the context as a managed context ) .
2015-01-01 11:24:00 +01:00
* Then when a hook executeHooks ( 'aMethod' ... ) is called , the method aMethod found into class will be executed .
2011-10-05 14:29:16 +02:00
*
2016-04-09 19:32:05 +02:00
* @ param string [] $arraycontext Array list of searched hooks tab / features . For example : 'thirdpartycard' ( for hook methods into page card thirdparty ), 'thirdpartydao' ( for hook methods into Societe ), ...
2017-06-08 11:18:41 +02:00
* @ return int Always 1
2011-08-10 02:50:16 +02:00
*/
2019-02-26 23:15:28 +01:00
public function initHooks ( $arraycontext )
2011-08-10 02:50:16 +02:00
{
global $conf ;
2011-10-14 18:51:20 +02:00
// Test if there is hooks to manage
2012-06-13 09:17:55 +02:00
if ( ! is_array ( $conf -> modules_parts [ 'hooks' ]) || empty ( $conf -> modules_parts [ 'hooks' ])) return ;
2011-08-10 02:50:16 +02:00
// For backward compatibility
2012-02-24 11:06:05 +01:00
if ( ! is_array ( $arraycontext )) $arraycontext = array ( $arraycontext );
2011-08-10 02:50:16 +02:00
2019-01-27 11:55:16 +01:00
$this -> contextarray = array_unique ( array_merge ( $arraycontext , $this -> contextarray )); // All contexts are concatenated
2011-10-14 18:51:20 +02:00
2019-11-10 17:48:50 +01:00
$arraytolog = array ();
2018-04-21 11:24:45 +02:00
foreach ( $conf -> modules_parts [ 'hooks' ] as $module => $hooks ) // Loop on each module that brings hooks
2011-08-10 02:50:16 +02:00
{
2018-04-21 11:24:45 +02:00
if ( empty ( $conf -> $module -> enabled )) continue ;
//dol_syslog(get_class($this).'::initHooks module='.$module.' arraycontext='.join(',',$arraycontext));
foreach ( $arraycontext as $context )
2011-08-10 02:50:16 +02:00
{
2018-04-21 11:24:45 +02:00
if ( is_array ( $hooks )) $arrayhooks = $hooks ; // New system
2019-01-27 11:55:16 +01:00
else $arrayhooks = explode ( ':' , $hooks ); // Old system (for backward compatibility)
2018-04-21 11:24:45 +02:00
if ( in_array ( $context , $arrayhooks ) || in_array ( 'all' , $arrayhooks )) // We instantiate action class only if initialized hook is handled by module
2011-08-10 02:50:16 +02:00
{
2018-04-21 11:24:45 +02:00
// Include actions class overwriting hooks
if ( ! is_object ( $this -> hooks [ $context ][ $module ])) // If set, class was already loaded
2011-08-10 02:50:16 +02:00
{
2011-08-21 20:34:49 +02:00
$path = '/' . $module . '/class/' ;
2011-08-10 02:50:16 +02:00
$actionfile = 'actions_' . $module . '.class.php' ;
2019-11-10 17:48:50 +01:00
$arraytolog [] = 'context=' . $context . '-path=' . $path . $actionfile ;
2011-08-10 02:50:16 +02:00
$resaction = dol_include_once ( $path . $actionfile );
if ( $resaction )
{
2018-04-21 11:24:45 +02:00
$controlclassname = 'Actions' . ucfirst ( $module );
$actionInstance = new $controlclassname ( $this -> db );
$this -> hooks [ $context ][ $module ] = $actionInstance ;
2011-08-10 02:50:16 +02:00
}
}
}
}
}
2019-11-10 17:48:50 +01:00
if ( count ( $arraytolog ) > 0 )
{
dol_syslog ( get_class ( $this ) . " ::initHooks Loading hooks: " . join ( ', ' , $arraytolog ), LOG_DEBUG );
}
2011-08-10 02:50:16 +02:00
return 1 ;
}
/**
2019-02-26 23:15:28 +01:00
* Execute hooks ( if they were initialized ) for the given method
2011-10-14 18:51:20 +02:00
*
2019-02-26 23:15:28 +01:00
* @ param string $method Name of method hooked ( 'doActions' , 'printSearchForm' , 'showInputField' , ... )
* @ param array $parameters Array of parameters
* @ param Object $object Object to use hooks on
* @ param string $action Action code on calling page ( 'create' , 'edit' , 'view' , 'add' , 'update' , 'delete' ... )
* @ return mixed For 'addreplace' hooks ( doActions , formObjectOptions , pdf_xxx , ... ) : Return 0 if we want to keep standard actions , > 0 if we want to stop / replace standard actions , < 0 if KO . Things to print are returned into -> resprints and set into -> resPrint . Things to return are returned into -> results by hook and set into -> resArray for caller .
* For 'output' hooks ( printLeftBlock , formAddObjectLine , formBuilddocOptions , ... ) : Return 0 , < 0 if KO . Things to print are returned into -> resprints and set into -> resPrint . Things to return are returned into -> results by hook and set into -> resArray for caller .
* All types can also return some values into an array -> results that will be finaly merged into this -> resArray for caller .
* $this -> error or this -> errors are also defined by class called by this function if error .
2011-08-10 02:50:16 +02:00
*/
2019-02-26 23:15:28 +01:00
public function executeHooks ( $method , $parameters = array (), & $object = '' , & $action = '' )
2011-08-10 02:50:16 +02:00
{
if ( ! is_array ( $this -> hooks ) || empty ( $this -> hooks )) return '' ;
2019-01-27 11:55:16 +01:00
$parameters [ 'context' ] = join ( ':' , $this -> contextarray );
2017-04-03 10:43:04 +02:00
//dol_syslog(get_class($this).'::executeHooks method='.$method." action=".$action." context=".$parameters['context']);
2011-08-20 19:00:34 +02:00
2015-06-30 17:09:52 +02:00
// Define type of hook ('output' or 'addreplace'. 'returnvalue' is deprecated because a 'addreplace' hook can also return resPrint and resArray).
2013-03-09 15:05:09 +01:00
$hooktype = 'output' ;
2019-02-26 23:15:28 +01:00
if ( in_array (
2015-06-25 11:10:28 +02:00
$method ,
array (
2018-04-05 13:29:27 +02:00
'addCalendarChoice' ,
'addMoreActionsButtons' ,
'addMoreMassActions' ,
'addSearchEntry' ,
2015-06-25 11:10:28 +02:00
'addStatisticLine' ,
2018-04-05 13:29:27 +02:00
'createDictionaryFieldlist' ,
2018-02-03 17:32:38 +01:00
'editDictionaryFieldlist' ,
'getFormMail' ,
2018-04-05 13:29:27 +02:00
'deleteFile' ,
2015-06-25 11:10:28 +02:00
'doActions' ,
2018-04-05 13:29:27 +02:00
'doMassActions' ,
'formatEvent' ,
2015-06-25 11:10:28 +02:00
'formCreateThirdpartyOptions' ,
'formObjectOptions' ,
'formattachOptions' ,
'formBuilddocLineOptions' ,
2018-04-05 13:29:27 +02:00
'formatNotificationMessage' ,
2019-06-04 12:48:06 +02:00
'getAccessForbiddenMessage' ,
2018-04-05 13:29:27 +02:00
'getDirList' ,
2019-06-04 12:48:06 +02:00
'getFormMail' ,
'getFormatedCustomerRef' ,
2019-01-21 15:22:35 +01:00
'getFormatedSupplierRef' ,
2019-06-04 12:48:06 +02:00
'getIdProfUrl' ,
2015-06-25 11:10:28 +02:00
'moveUploadedFile' ,
2018-02-27 22:06:21 +01:00
'moreHtmlStatus' ,
2018-04-05 13:29:27 +02:00
'pdf_build_address' ,
2015-06-25 11:10:28 +02:00
'pdf_writelinedesc' ,
2015-10-22 17:51:05 +02:00
'pdf_getlinenum' ,
'pdf_getlineref' ,
'pdf_getlineref_supplier' ,
'pdf_getlinevatrate' ,
'pdf_getlineupexcltax' ,
'pdf_getlineupwithtax' ,
'pdf_getlineqty' ,
'pdf_getlineqty_asked' ,
'pdf_getlineqty_shipped' ,
'pdf_getlineqty_keeptoship' ,
'pdf_getlineunit' ,
'pdf_getlineremisepercent' ,
'pdf_getlineprogress' ,
'pdf_getlinetotalexcltax' ,
'pdf_getlinetotalwithtax' ,
2015-06-25 11:10:28 +02:00
'paymentsupplierinvoices' ,
'printAddress' ,
'printSearchForm' ,
2016-11-12 14:36:52 +01:00
'printTabsHead' ,
2018-04-05 13:29:27 +02:00
'printObjectLine' ,
'printObjectSubLine' ,
2018-05-10 11:39:29 +02:00
'restrictedArea' ,
2018-02-03 17:32:38 +01:00
'sendMail' ,
2018-04-05 13:29:27 +02:00
'sendMailAfter' ,
2018-04-30 14:39:12 +02:00
'showLinkToObjectBlock' ,
2018-06-28 22:59:53 +02:00
'setContentSecurityPolicy' ,
'setHtmlTitle'
2015-06-25 11:10:28 +02:00
)
)) $hooktype = 'addreplace' ;
2017-01-21 17:30:35 +01:00
2019-02-26 23:15:28 +01:00
if ( $method == 'insertExtraFields' ) {
$hooktype = 'returnvalue' ; // @deprecated. TODO Remove all code with "executeHooks('insertExtraFields'" as soon as there is a trigger available.
dol_syslog ( " Warning: The hook 'insertExtraFields' is deprecated and must not be used. Use instead trigger on CRUD event (ask it to dev team if not implemented) " , LOG_WARNING );
2015-03-13 20:27:54 +01:00
}
2013-03-09 15:05:09 +01:00
2017-06-02 23:02:48 +02:00
// Init return properties
2017-06-06 10:29:34 +02:00
$this -> resPrint = '' ; $this -> resArray = array (); $this -> resNbOfHooks = 0 ;
2017-06-02 23:02:48 +02:00
2015-01-01 11:24:00 +01:00
// Loop on each hook to qualify modules that have declared context
2012-03-15 22:37:42 +01:00
$modulealreadyexecuted = array ();
2013-07-28 14:42:50 +02:00
$resaction = 0 ; $error = 0 ; $result = '' ;
2015-01-01 11:24:00 +01:00
foreach ( $this -> hooks as $context => $modules ) // $this->hooks is an array with context as key and value is an array of modules that handle this context
2011-08-10 02:50:16 +02:00
{
2012-03-09 14:48:56 +01:00
if ( ! empty ( $modules ))
2011-08-10 02:50:16 +02:00
{
2012-03-15 22:37:42 +01:00
foreach ( $modules as $module => $actionclassinstance )
2011-08-10 02:50:16 +02:00
{
2013-07-28 14:42:50 +02:00
//print "Before hook ".get_class($actionclassinstance)." method=".$method." hooktype=".$hooktype." results=".count($actionclassinstance->results)." resprints=".count($actionclassinstance->resprints)." resaction=".$resaction." result=".$result."<br>\n";
2013-06-12 22:17:15 +02:00
2017-06-06 01:34:25 +02:00
// test to avoid running twice a hook, when a module implements several active contexts
2019-01-27 11:55:16 +01:00
if ( in_array ( $module , $modulealreadyexecuted )) continue ;
2017-06-06 01:34:25 +02:00
2015-01-01 11:24:00 +01:00
// jump to next module/class if method does not exist
2019-01-27 11:55:16 +01:00
if ( ! method_exists ( $actionclassinstance , $method )) continue ;
2014-10-18 00:33:30 +02:00
2017-06-06 10:29:34 +02:00
$this -> resNbOfHooks ++ ;
2017-06-02 23:02:48 +02:00
2015-01-01 11:24:00 +01:00
$modulealreadyexecuted [ $module ] = $module ; // Use the $currentcontext in method to avoid running twice
2014-10-18 00:33:30 +02:00
2015-01-01 11:24:00 +01:00
// Clean class (an error may have been set from a previous call of another method for same module/hook)
2014-10-18 00:33:30 +02:00
$actionclassinstance -> error = 0 ;
$actionclassinstance -> errors = array ();
2018-04-21 11:24:45 +02:00
dol_syslog ( get_class ( $this ) . " ::executeHooks Qualified hook found (hooktype= " . $hooktype . " ). We call method " . $method . " of class " . get_class ( $actionclassinstance ) . " , module= " . $module . " , action= " . $action . " context= " . $context , LOG_DEBUG );
2015-01-01 11:24:00 +01:00
// Add current context to avoid method execution in bad context, you can add this test in your method : eg if($currentcontext != 'formfile') return;
2014-09-29 14:40:02 +02:00
$parameters [ 'currentcontext' ] = $context ;
2014-01-24 11:15:25 +01:00
// Hooks that must return int (hooks with type 'addreplace')
2013-03-09 15:05:09 +01:00
if ( $hooktype == 'addreplace' )
2011-08-10 02:50:16 +02:00
{
2013-03-09 15:05:09 +01:00
$resaction += $actionclassinstance -> $method ( $parameters , $object , $action , $this ); // $object and $action can be changed by method ($object->id during creation for example or $action to go back to other action for example)
2012-11-26 13:26:50 +01:00
if ( $resaction < 0 || ! empty ( $actionclassinstance -> error ) || ( ! empty ( $actionclassinstance -> errors ) && count ( $actionclassinstance -> errors ) > 0 ))
{
$error ++ ;
$this -> error = $actionclassinstance -> error ; $this -> errors = array_merge ( $this -> errors , ( array ) $actionclassinstance -> errors );
2019-01-27 11:55:16 +01:00
dol_syslog ( " Error on hook module= " . $module . " , method " . $method . " , class " . get_class ( $actionclassinstance ) . " , hooktype= " . $hooktype . ( empty ( $this -> error ) ? '' : " " . $this -> error ) . ( empty ( $this -> errors ) ? '' : " " . join ( " , " , $this -> errors )), LOG_ERR );
2012-11-26 13:26:50 +01:00
}
2013-04-14 01:46:33 +02:00
2015-03-29 17:53:05 +02:00
if ( isset ( $actionclassinstance -> results ) && is_array ( $actionclassinstance -> results )) $this -> resArray = array_merge ( $this -> resArray , $actionclassinstance -> results );
2013-06-05 16:24:32 +02:00
if ( ! empty ( $actionclassinstance -> resprints )) $this -> resPrint .= $actionclassinstance -> resprints ;
2011-08-10 02:50:16 +02:00
}
2015-05-29 17:11:42 +02:00
// Generic hooks that return a string or array (printLeftBlock, formAddObjectLine, formBuilddocOptions, ...)
2012-11-26 13:26:50 +01:00
else
2013-03-09 15:05:09 +01:00
{
2017-06-09 10:12:01 +02:00
// TODO. this test should be done into the method of hook by returning nothing
2012-11-26 13:26:50 +01:00
if ( is_array ( $parameters ) && ! empty ( $parameters [ 'special_code' ]) && $parameters [ 'special_code' ] > 3 && $parameters [ 'special_code' ] != $actionclassinstance -> module_number ) continue ;
2014-10-18 00:33:30 +02:00
//dol_syslog("Call method ".$method." of class ".get_class($actionclassinstance).", module=".$module.", hooktype=".$hooktype, LOG_DEBUG);
2017-02-03 12:14:29 +01:00
$resaction = $actionclassinstance -> $method ( $parameters , $object , $action , $this ); // $object and $action can be changed by method ($object->id during creation for example or $action to go back to other action for example)
2012-11-26 13:26:50 +01:00
2019-02-08 19:38:52 +01:00
if ( ! empty ( $actionclassinstance -> results ) && is_array ( $actionclassinstance -> results )) $this -> resArray = array_merge ( $this -> resArray , $actionclassinstance -> results );
2012-11-26 13:26:50 +01:00
if ( ! empty ( $actionclassinstance -> resprints )) $this -> resPrint .= $actionclassinstance -> resprints ;
2019-02-08 19:38:52 +01:00
if ( is_numeric ( $resaction ) && $resaction < 0 )
{
$error ++ ;
$this -> error = $actionclassinstance -> error ; $this -> errors = array_merge ( $this -> errors , ( array ) $actionclassinstance -> errors );
2019-02-08 23:53:40 +01:00
dol_syslog ( " Error on hook module= " . $module . " , method " . $method . " , class " . get_class ( $actionclassinstance ) . " , hooktype= " . $hooktype . ( empty ( $this -> error ) ? '' : " " . $this -> error ) . ( empty ( $this -> errors ) ? '' : " " . join ( " , " , $this -> errors )), LOG_ERR );
2019-02-08 19:38:52 +01:00
}
2017-06-09 10:12:01 +02:00
// TODO dead code to remove (do not enable this, but fix hook instead): result must not be a string but an int. you must use $actionclassinstance->resprints to return a string
2017-02-03 12:14:29 +01:00
if ( ! is_array ( $resaction ) && ! is_numeric ( $resaction ))
2013-07-28 14:42:50 +02:00
{
2015-05-07 10:45:35 +02:00
dol_syslog ( 'Error: Bug into hook ' . $method . ' of module class ' . get_class ( $actionclassinstance ) . '. Method must not return a string but an int (0=OK, 1=Replace, -1=KO) and set string into ->resprints' , LOG_ERR );
2017-02-03 12:14:29 +01:00
if ( empty ( $actionclassinstance -> resprints )) { $this -> resPrint .= $resaction ; $resaction = 0 ; }
2013-07-28 14:42:50 +02:00
}
2011-08-10 12:55:34 +02:00
}
2013-01-23 00:24:20 +01:00
2013-07-28 14:42:50 +02:00
//print "After hook ".get_class($actionclassinstance)." method=".$method." hooktype=".$hooktype." results=".count($actionclassinstance->results)." resprints=".count($actionclassinstance->resprints)." resaction=".$resaction." result=".$result."<br>\n";
2013-06-11 12:10:02 +02:00
unset ( $actionclassinstance -> results );
unset ( $actionclassinstance -> resprints );
2011-08-10 02:50:16 +02:00
}
}
}
2012-11-26 13:26:50 +01:00
return ( $error ? - 1 : $resaction );
2011-08-10 02:50:16 +02:00
}
}