2011-08-10 00:50:16 +00:00
< ? php
2016-07-06 09:14:17 +00:00
/* Copyright ( C ) 2010 - 2016 Laurent Destailleur < eldy @ users . sourceforge . net >
2018-10-27 12:43:12 +00:00
* Copyright ( C ) 2010 - 2014 Regis Houssin < regis . houssin @ inodbox . com >
2011-08-10 00:50:16 +00: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 14:36:08 +00:00
* the Free Software Foundation ; either version 3 of the License , or
2011-08-10 00:50:16 +00: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 19:55:30 +00:00
* along with this program . If not , see < https :// www . gnu . org / licenses />.
2011-08-10 00:50:16 +00:00
*/
/**
* \file htdocs / core / class / hookmanager . class . php
* \ingroup core
* \brief File of class to manage hooks
*/
/**
2012-05-05 22:45:15 +00:00
* Class to manage hooks
2011-08-10 00:50:16 +00:00
*/
class HookManager
{
2018-08-22 09:06:34 +00:00
/**
* @ var DoliDB Database handler .
*/
public $db ;
2019-01-21 14:22:35 +00:00
2018-08-22 08:37:16 +00:00
/**
* @ var string Error code ( or message )
*/
2020-04-10 08:59:32 +00:00
public $error = '' ;
2018-08-21 17:08:11 +00:00
2018-08-17 17:31:22 +00:00
/**
* @ var string [] Error codes ( or messages )
*/
2018-08-21 17:08:11 +00:00
public $errors = array ();
2011-08-10 00:50:16 +00:00
2011-10-14 16:51:20 +00:00
// Context hookmanager was created for ('thirdpartycard', 'thirdpartydao', ...)
2020-04-10 08:59:32 +00:00
public $contextarray = array ();
2011-08-10 00:50:16 +00:00
// Array with instantiated classes
2020-04-10 08:59:32 +00:00
public $hooks = array ();
2012-03-15 21:37:42 +00:00
2012-03-02 09:29:11 +00:00
// Array result
2020-04-10 08:59:32 +00:00
public $resArray = array ();
2013-01-22 16:43:41 +00:00
// Printable result
2020-04-10 08:59:32 +00:00
public $resPrint = '' ;
2017-06-05 23:34:25 +00:00
// Nb of qualified hook ran
2020-04-10 08:59:32 +00:00
public $resNbOfHooks = 0 ;
2011-08-10 00:50:16 +00:00
/**
2011-10-14 16:51:20 +00:00
* Constructor
*
2015-01-01 10:24:00 +00:00
* @ param DoliDB $db Database handler
2011-08-10 00:50:16 +00:00
*/
2019-02-26 22:15:28 +00:00
public function __construct ( $db )
2011-08-10 00:50:16 +00:00
{
2012-02-24 07:52:30 +00:00
$this -> db = $db ;
2011-08-10 00:50:16 +00:00
}
/**
2015-01-01 10:24:00 +00:00
* Init array $this -> hooks with instantiated action controlers .
2017-06-08 09:18:41 +00: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 10:24:00 +00: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 16:51:20 +00:00
* class found into file / mymodule / class / actions_mymodule . class . php ( if module has declared the context as a managed context ) .
2015-01-01 10:24:00 +00:00
* Then when a hook executeHooks ( 'aMethod' ... ) is called , the method aMethod found into class will be executed .
2011-10-05 12:29:16 +00:00
*
2016-04-09 17:32:05 +00: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 09:18:41 +00:00
* @ return int Always 1
2011-08-10 00:50:16 +00:00
*/
2019-02-26 22:15:28 +00:00
public function initHooks ( $arraycontext )
2011-08-10 00:50:16 +00:00
{
global $conf ;
2011-10-14 16:51:20 +00:00
// Test if there is hooks to manage
2020-04-10 08:59:32 +00:00
if ( ! is_array ( $conf -> modules_parts [ 'hooks' ]) || empty ( $conf -> modules_parts [ 'hooks' ])) return ;
2011-08-10 00:50:16 +00:00
// For backward compatibility
2020-04-10 08:59:32 +00:00
if ( ! is_array ( $arraycontext )) $arraycontext = array ( $arraycontext );
2011-08-10 00:50:16 +00:00
2020-04-10 08:59:32 +00:00
$this -> contextarray = array_unique ( array_merge ( $arraycontext , $this -> contextarray )); // All contexts are concatenated
2011-10-14 16:51:20 +00:00
2019-11-10 16:48:50 +00:00
$arraytolog = array ();
2020-04-10 08:59:32 +00:00
foreach ( $conf -> modules_parts [ 'hooks' ] as $module => $hooks ) // Loop on each module that brings hooks
2011-08-10 00:50:16 +00:00
{
2018-04-21 09:24:45 +00:00
if ( empty ( $conf -> $module -> enabled )) continue ;
//dol_syslog(get_class($this).'::initHooks module='.$module.' arraycontext='.join(',',$arraycontext));
2020-04-10 08:59:32 +00:00
foreach ( $arraycontext as $context )
2011-08-10 00:50:16 +00:00
{
2020-04-10 08:59:32 +00:00
if ( is_array ( $hooks )) $arrayhooks = $hooks ; // New system
else $arrayhooks = explode ( ':' , $hooks ); // Old system (for backward compatibility)
2018-04-21 09:24:45 +00: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 00:50:16 +00:00
{
2018-04-21 09:24:45 +00:00
// Include actions class overwriting hooks
2020-10-30 03:13:31 +00:00
if ( empty ( $this -> hooks [ $context ][ $module ]) || ! is_object ( $this -> hooks [ $context ][ $module ])) // If set to an object value, class was already loaded
2011-08-10 00:50:16 +00:00
{
2020-04-10 08:59:32 +00:00
$path = '/' . $module . '/class/' ;
2011-08-10 00:50:16 +00:00
$actionfile = 'actions_' . $module . '.class.php' ;
2019-11-10 16:48:50 +00:00
$arraytolog [] = 'context=' . $context . '-path=' . $path . $actionfile ;
2020-04-10 08:59:32 +00:00
$resaction = dol_include_once ( $path . $actionfile );
2011-08-10 00:50:16 +00:00
if ( $resaction )
{
2018-04-21 09:24:45 +00:00
$controlclassname = 'Actions' . ucfirst ( $module );
$actionInstance = new $controlclassname ( $this -> db );
$this -> hooks [ $context ][ $module ] = $actionInstance ;
2011-08-10 00:50:16 +00:00
}
}
}
}
}
2019-11-10 16:48:50 +00:00
if ( count ( $arraytolog ) > 0 )
{
dol_syslog ( get_class ( $this ) . " ::initHooks Loading hooks: " . join ( ', ' , $arraytolog ), LOG_DEBUG );
}
2011-08-10 00:50:16 +00:00
return 1 ;
}
/**
2019-02-26 22:15:28 +00:00
* Execute hooks ( if they were initialized ) for the given method
2011-10-14 16:51:20 +00:00
*
2019-02-26 22:15:28 +00: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 00:50:16 +00:00
*/
2019-02-26 22:15:28 +00:00
public function executeHooks ( $method , $parameters = array (), & $object = '' , & $action = '' )
2011-08-10 00:50:16 +00:00
{
2020-04-10 08:59:32 +00:00
if ( ! is_array ( $this -> hooks ) || empty ( $this -> hooks )) return '' ;
2011-08-10 00:50:16 +00:00
2020-04-10 08:59:32 +00:00
$parameters [ 'context' ] = join ( ':' , $this -> contextarray );
2017-04-03 08:43:04 +00:00
//dol_syslog(get_class($this).'::executeHooks method='.$method." action=".$action." context=".$parameters['context']);
2011-08-20 17:00:34 +00:00
2020-04-12 14:36:25 +00:00
// Define type of hook ('output' or 'addreplace'). Type 'returnvalue' is deprecated because a 'addreplace' hook can also return resPrint and resArray).
2020-04-10 08:59:32 +00:00
$hooktype = 'output' ;
2019-02-26 22:15:28 +00:00
if ( in_array (
2015-06-25 09:10:28 +00:00
$method ,
array (
2018-04-05 11:29:27 +00:00
'addCalendarChoice' ,
'addMoreActionsButtons' ,
'addMoreMassActions' ,
'addSearchEntry' ,
2015-06-25 09:10:28 +00:00
'addStatisticLine' ,
2018-04-05 11:29:27 +00:00
'createDictionaryFieldlist' ,
2018-02-03 16:32:38 +00:00
'editDictionaryFieldlist' ,
'getFormMail' ,
2018-04-05 11:29:27 +00:00
'deleteFile' ,
2015-06-25 09:10:28 +00:00
'doActions' ,
2018-04-05 11:29:27 +00:00
'doMassActions' ,
'formatEvent' ,
2020-10-28 16:08:09 +00:00
'formConfirm' ,
2015-06-25 09:10:28 +00:00
'formCreateThirdpartyOptions' ,
'formObjectOptions' ,
'formattachOptions' ,
'formBuilddocLineOptions' ,
2018-04-05 11:29:27 +00:00
'formatNotificationMessage' ,
2019-06-04 10:48:06 +00:00
'getAccessForbiddenMessage' ,
2018-04-05 11:29:27 +00:00
'getDirList' ,
2019-06-04 10:48:06 +00:00
'getFormMail' ,
'getFormatedCustomerRef' ,
2019-01-21 14:22:35 +00:00
'getFormatedSupplierRef' ,
2019-06-04 10:48:06 +00:00
'getIdProfUrl' ,
2020-02-29 11:32:27 +00:00
'getInputIdProf' ,
2015-06-25 09:10:28 +00:00
'moveUploadedFile' ,
2018-02-27 21:06:21 +00:00
'moreHtmlStatus' ,
2018-04-05 11:29:27 +00:00
'pdf_build_address' ,
2015-06-25 09:10:28 +00:00
'pdf_writelinedesc' ,
2015-10-22 15:51:05 +00: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 09:10:28 +00:00
'paymentsupplierinvoices' ,
'printAddress' ,
2020-04-12 14:36:25 +00:00
'printEmail' ,
2015-06-25 09:10:28 +00:00
'printSearchForm' ,
2016-11-12 13:36:52 +00:00
'printTabsHead' ,
2018-04-05 11:29:27 +00:00
'printObjectLine' ,
'printObjectSubLine' ,
2018-05-10 09:39:29 +00:00
'restrictedArea' ,
2018-02-03 16:32:38 +00:00
'sendMail' ,
2018-04-05 11:29:27 +00:00
'sendMailAfter' ,
2018-04-30 12:39:12 +00:00
'showLinkToObjectBlock' ,
2018-06-28 20:59:53 +00:00
'setContentSecurityPolicy' ,
2020-04-09 12:54:47 +00:00
'setHtmlTitle' ,
'completeTabsHead'
2015-06-25 09:10:28 +00:00
)
2020-04-10 08:59:32 +00:00
)) $hooktype = 'addreplace' ;
2017-01-21 16:30:35 +00:00
2019-02-26 22:15:28 +00:00
if ( $method == 'insertExtraFields' ) {
2020-04-10 08:59:32 +00:00
$hooktype = 'returnvalue' ; // @deprecated. TODO Remove all code with "executeHooks('insertExtraFields'" as soon as there is a trigger available.
2019-02-26 22:15:28 +00:00
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 19:27:54 +00:00
}
2013-03-09 14:05:09 +00:00
2017-06-02 21:02:48 +00:00
// Init return properties
2020-04-10 08:59:32 +00:00
$this -> resPrint = '' ; $this -> resArray = array (); $this -> resNbOfHooks = 0 ;
2017-06-02 21:02:48 +00:00
2015-01-01 10:24:00 +00:00
// Loop on each hook to qualify modules that have declared context
2020-04-10 08:59:32 +00:00
$modulealreadyexecuted = array ();
$resaction = 0 ; $error = 0 ; $result = '' ;
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 00:50:16 +00:00
{
2020-04-10 08:59:32 +00:00
if ( ! empty ( $modules ))
2011-08-10 00:50:16 +00:00
{
2020-04-10 08:59:32 +00:00
foreach ( $modules as $module => $actionclassinstance )
2011-08-10 00:50:16 +00:00
{
2013-07-28 12:42:50 +00: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 20:17:15 +00:00
2017-06-05 23:34:25 +00:00
// test to avoid running twice a hook, when a module implements several active contexts
2019-01-27 10:55:16 +00:00
if ( in_array ( $module , $modulealreadyexecuted )) continue ;
2017-06-05 23:34:25 +00:00
2015-01-01 10:24:00 +00:00
// jump to next module/class if method does not exist
2020-04-10 08:59:32 +00:00
if ( ! method_exists ( $actionclassinstance , $method )) continue ;
2014-10-17 22:33:30 +00:00
2017-06-06 08:29:34 +00:00
$this -> resNbOfHooks ++ ;
2017-06-02 21:02:48 +00:00
2020-04-10 08:59:32 +00:00
$modulealreadyexecuted [ $module ] = $module ; // Use the $currentcontext in method to avoid running twice
2014-10-17 22:33:30 +00:00
2015-01-01 10:24:00 +00:00
// Clean class (an error may have been set from a previous call of another method for same module/hook)
2020-04-10 08:59:32 +00:00
$actionclassinstance -> error = 0 ;
$actionclassinstance -> errors = array ();
2014-10-17 22:33:30 +00:00
2018-04-21 09:24:45 +00: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 10:24:00 +00: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 12:40:02 +00:00
$parameters [ 'currentcontext' ] = $context ;
2014-01-24 10:15:25 +00:00
// Hooks that must return int (hooks with type 'addreplace')
2013-03-09 14:05:09 +00:00
if ( $hooktype == 'addreplace' )
2011-08-10 00:50:16 +00:00
{
2013-03-09 14:05:09 +00: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)
2020-04-10 08:59:32 +00:00
if ( $resaction < 0 || ! empty ( $actionclassinstance -> error ) || ( ! empty ( $actionclassinstance -> errors ) && count ( $actionclassinstance -> errors ) > 0 ))
2012-11-26 12:26:50 +00:00
{
$error ++ ;
2020-04-10 08:59:32 +00:00
$this -> error = $actionclassinstance -> error ; $this -> errors = array_merge ( $this -> errors , ( array ) $actionclassinstance -> errors );
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 12:26:50 +00:00
}
2013-04-13 23:46:33 +00:00
2020-04-10 08:59:32 +00:00
if ( isset ( $actionclassinstance -> results ) && is_array ( $actionclassinstance -> results )) $this -> resArray = array_merge ( $this -> resArray , $actionclassinstance -> results );
if ( ! empty ( $actionclassinstance -> resprints )) $this -> resPrint .= $actionclassinstance -> resprints ;
2011-08-10 00:50:16 +00:00
}
2015-05-29 15:11:42 +00:00
// Generic hooks that return a string or array (printLeftBlock, formAddObjectLine, formBuilddocOptions, ...)
2012-11-26 12:26:50 +00:00
else
2013-03-09 14:05:09 +00:00
{
2017-06-09 08:12:01 +00:00
// TODO. this test should be done into the method of hook by returning nothing
2020-04-10 08:59:32 +00:00
if ( is_array ( $parameters ) && ! empty ( $parameters [ 'special_code' ]) && $parameters [ 'special_code' ] > 3 && $parameters [ 'special_code' ] != $actionclassinstance -> module_number ) continue ;
2012-11-26 12:26:50 +00:00
2014-10-17 22:33:30 +00:00
//dol_syslog("Call method ".$method." of class ".get_class($actionclassinstance).", module=".$module.", hooktype=".$hooktype, LOG_DEBUG);
2017-02-03 11:14:29 +00: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 12:26:50 +00:00
2020-04-10 08:59:32 +00:00
if ( ! empty ( $actionclassinstance -> results ) && is_array ( $actionclassinstance -> results )) $this -> resArray = array_merge ( $this -> resArray , $actionclassinstance -> results );
if ( ! empty ( $actionclassinstance -> resprints )) $this -> resPrint .= $actionclassinstance -> resprints ;
2019-02-08 18:38:52 +00:00
if ( is_numeric ( $resaction ) && $resaction < 0 )
{
$error ++ ;
2020-04-10 08:59:32 +00:00
$this -> error = $actionclassinstance -> error ; $this -> errors = array_merge ( $this -> errors , ( array ) $actionclassinstance -> errors );
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 18:38:52 +00:00
}
2017-06-09 08:12:01 +00: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
2020-04-10 08:59:32 +00:00
if ( ! is_array ( $resaction ) && ! is_numeric ( $resaction ))
2013-07-28 12:42:50 +00:00
{
2015-05-07 08:45:35 +00: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 );
2020-04-10 08:59:32 +00:00
if ( empty ( $actionclassinstance -> resprints )) { $this -> resPrint .= $resaction ; $resaction = 0 ; }
2013-07-28 12:42:50 +00:00
}
2011-08-10 10:55:34 +00:00
}
2013-01-22 23:24:20 +00:00
2013-07-28 12:42:50 +00: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 10:10:02 +00:00
unset ( $actionclassinstance -> results );
unset ( $actionclassinstance -> resprints );
2011-08-10 00:50:16 +00:00
}
}
}
2020-04-10 08:59:32 +00:00
return ( $error ? - 1 : $resaction );
2011-08-10 00:50:16 +00:00
}
}