2011-08-10 02:50:16 +02:00
< ? php
2012-02-24 08:52:30 +01:00
/* Copyright ( C ) 2010 - 2012 Laurent Destailleur < eldy @ users . sourceforge . net >
2012-12-30 15:11:07 +01:00
* Copyright ( C ) 2010 - 2012 Regis Houssin < regis . houssin @ capnetworks . 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
* along with this program . If not , see < http :// www . gnu . org / licenses />.
*/
/**
* \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
{
var $db ;
2012-06-03 20:34:09 +02:00
var $error ;
var $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', ...)
var $contextarray = array ();
2011-08-10 02:50:16 +02:00
// Array with instantiated classes
var $hooks = array ();
2012-03-15 22:37:42 +01:00
2012-03-02 10:29:11 +01:00
// Array result
var $resArray = array ();
2013-01-22 17:43:41 +01:00
// Printable result
var $resPrint = '' ;
2011-08-10 02:50:16 +02:00
/**
2011-10-14 18:51:20 +02:00
* Constructor
*
2012-03-19 17:18:11 +01:00
* @ param DoliDB $db Handler acces base de donnees
2011-08-10 02:50:16 +02:00
*/
2012-02-24 08:52:30 +01:00
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
}
/**
2011-10-14 18:51:20 +02:00
* Init array this -> hooks with instantiated action controlers .
2011-10-05 14:29:16 +02:00
* First , a hook is declared by a module by adding a constant MAIN_MODULE_MYMODULENAME_HOOKS
2011-10-16 01:38:40 +02:00
* with value 'nameofcontext1:nameofcontext2:...' into $this -> const of module descriptor file .
2011-10-14 18:51:20 +02:00
* This make conf -> hooks_modules loaded with an entry ( 'modulename' => array ( nameofcontext1 , nameofcontext2 , ... ))
2012-03-02 14:51:16 +01:00
* 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 ) .
* Then when a hook is executeHook ( 'aMethod' ... ) is called , the method aMethod found into class will be executed .
2011-10-05 14:29:16 +02:00
*
2012-02-24 11:06:05 +01:00
* @ param array $arraycontext Array list of searched hooks tab / features . For example : 'thirdpartycard' ( for hook methods into page card thirdparty ), 'thirdpartydao' ( for hook methods into Societe ), ...
* @ return int Always 1
2011-08-10 02:50:16 +02:00
*/
2012-03-02 14:51:16 +01:00
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
2012-03-15 22:37:42 +01:00
$this -> contextarray = array_unique ( array_merge ( $arraycontext , $this -> contextarray )); // All contexts are concatenated
2011-10-14 18:51:20 +02:00
2012-06-13 09:17:55 +02:00
foreach ( $conf -> modules_parts [ 'hooks' ] as $module => $hooks )
2011-08-10 02:50:16 +02:00
{
if ( $conf -> $module -> enabled )
{
2012-02-24 11:06:05 +01:00
foreach ( $arraycontext as $context )
2011-08-10 02:50:16 +02:00
{
2012-02-24 11:06:05 +01:00
if ( is_array ( $hooks )) $arrayhooks = $hooks ; // New system
else $arrayhooks = explode ( ':' , $hooks ); // Old system (for backward compatibility)
if ( in_array ( $context , $arrayhooks )) // We instantiate action class only if hook is required
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' ;
$pathroot = '' ;
// Include actions class overwriting hooks
$resaction = dol_include_once ( $path . $actionfile );
if ( $resaction )
{
$controlclassname = 'Actions' . ucfirst ( $module );
2011-08-11 00:47:33 +02:00
$actionInstance = new $controlclassname ( $this -> db );
2012-03-09 14:48:56 +01:00
$this -> hooks [ $context ][ $module ] = $actionInstance ;
2011-08-10 02:50:16 +02:00
}
}
}
}
}
return 1 ;
}
/**
2012-03-15 22:37:42 +01:00
* Execute hooks ( if they were initialized ) for the given method
2011-10-14 18:51:20 +02: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' ... )
2013-03-09 15:05:09 +01:00
* @ return mixed For doActions , formObjectOptions , pdf_xxx : Return 0 if we want to keep standard actions , > 0 if if want to stop standard actions , < 0 means KO .
2013-04-27 15:08:35 +02:00
* For printSearchForm , printLeftBlock , printTopRightMenu , formAddObjectLine , ...: Return HTML string . TODO Deprecated . Must always return an int and things to print into -> resprints .
2012-11-26 13:26:50 +01:00
* Can also return some values into an array -> results .
2011-10-14 18:51:20 +02:00
* $this -> error or this -> errors are also defined by class called by this function if error .
2011-08-10 02:50:16 +02:00
*/
2011-08-11 09:51:50 +02:00
function executeHooks ( $method , $parameters = false , & $object = '' , & $action = '' )
2011-08-10 02:50:16 +02:00
{
if ( ! is_array ( $this -> hooks ) || empty ( $this -> hooks )) return '' ;
2011-10-14 18:51:20 +02:00
$parameters [ 'context' ] = join ( ':' , $this -> contextarray );
dol_syslog ( get_class ( $this ) . '::executeHooks method=' . $method . " action= " . $action . " context= " . $parameters [ 'context' ]);
2011-08-20 19:00:34 +02:00
2013-04-12 13:18:24 +02:00
// Define type of hook ('output', 'returnvalue' or 'addreplace'). 'addreplace' should be type for all hooks. 'output' and 'returnvalue' are deprecated.
2013-03-09 15:05:09 +01:00
$hooktype = 'output' ;
if ( preg_match ( '/^pdf_/' , $method )) $hooktype = 'returnvalue' ; // pdf_xxx except pdf_writelinedesc are returnvalue hooks. When there is 2 hooks of this type, only last one win.
2013-09-10 02:09:55 +02:00
if ( in_array (
$method ,
array (
2014-01-24 11:15:25 +01:00
'addMoreActionsButtons' ,
2013-09-10 02:09:55 +02:00
'addStatisticLine' ,
'doActions' ,
2014-04-16 00:06:16 +02:00
'formCreateThirdpartyOptions' ,
2013-09-10 02:09:55 +02:00
'formObjectOptions' ,
'formattachOptions' ,
'formBuilddocLineOptions' ,
'moveUploadedFile' ,
'pdf_writelinedesc' ,
'paymentsupplierinvoices' ,
'printSearchForm'
)
)) $hooktype = 'addreplace' ;
2013-03-09 15:05:09 +01:00
2012-03-15 22:37:42 +01:00
// Loop on each hook to qualify modules that declared context
$modulealreadyexecuted = array ();
2013-07-28 14:42:50 +02:00
$resaction = 0 ; $error = 0 ; $result = '' ;
2012-11-26 13:26:50 +01:00
$this -> resPrint = '' ; $this -> resArray = array ();
2012-03-15 22:37:42 +01:00
foreach ( $this -> hooks as $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
2013-01-12 14:34:16 +01:00
//print 'class='.get_class($actionclassinstance).' method='.$method.' action='.$action;
2012-11-26 13:26:50 +01:00
// jump to next class if method does not exists
if ( ! method_exists ( $actionclassinstance , $method )) continue ;
// test to avoid to run twice a hook, when a module implements several active contexts
2012-03-15 22:37:42 +01:00
if ( in_array ( $module , $modulealreadyexecuted )) continue ;
$modulealreadyexecuted [ $module ] = $module ;
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 );
2013-07-28 14:42:50 +02:00
// TODO dead code to remove (do not enable this, but fix hook instead)
2013-07-09 21:33:45 +02:00
/* Change must be inside the method of hook if required . Only hook must decide if $action must be modified or not .
2012-11-26 13:26:50 +01:00
if ( $method == 'doActions' )
{
if ( $action == 'add' ) $action = 'create' ;
if ( $action == 'update' ) $action = 'edit' ;
2013-07-09 21:33:45 +02:00
} */
2012-11-26 13:26:50 +01:00
}
2013-04-14 01:46:33 +02:00
2013-06-05 16:24:32 +02:00
if ( is_array ( $actionclassinstance -> results )) $this -> resArray = array_merge ( $this -> resArray , $actionclassinstance -> results );
if ( ! empty ( $actionclassinstance -> resprints )) $this -> resPrint .= $actionclassinstance -> resprints ;
2011-08-10 02:50:16 +02:00
}
2013-07-28 14:42:50 +02:00
// Generic hooks that return a string or array (printSearchForm, printLeftBlock, formAddObjectLine, formBuilddocOptions, ...)
2012-11-26 13:26:50 +01:00
else
2013-03-09 15:05:09 +01:00
{
2013-03-27 14:11:15 +01:00
// TODO. this 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 ;
2013-03-09 15:05:09 +01:00
$result = $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
2013-04-19 19:45:06 +02: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 ;
2013-07-28 14:42:50 +02:00
// TODO dead code to remove (do not enable this, but fix hook instead)
2013-07-09 21:33:45 +02:00
//if (is_array($result)) $this->resArray = array_merge($this->resArray, $result);
2013-07-28 14:42:50 +02:00
// TODO dead code to remove (do not enable this, but fix hook instead): result must not be a string. we must use $actionclassinstance->resprints to return a string
if ( ! is_array ( $result ) && ! is_numeric ( $result ))
{
//print 'Error: Bug into module '.get_class($actionclassinstance).' hook must not return a string but an int and set string into ->resprints';
dol_syslog ( 'Error: Bug into module ' . get_class ( $actionclassinstance ) . ' hook must not return a string but an int and set string into ->resprints' , LOG_ERR );
if ( empty ( $actionclassinstance -> resprints )) { $this -> resPrint .= $result ; $result = 0 ; }
}
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
}
}
}
2013-03-09 15:05:09 +01:00
// TODO remove this. When there is something to print for an output hook, ->resPrint is filled.
if ( $hooktype == 'output' ) return $this -> resPrint ;
if ( $hooktype == 'returnvalue' ) return $result ;
2012-11-26 13:26:50 +01:00
return ( $error ? - 1 : $resaction );
2011-08-10 02:50:16 +02:00
}
}