dolibarr  7.0.0-beta
hookmanager.class.php
Go to the documentation of this file.
1 <?php
2 /* Copyright (C) 2010-2016 Laurent Destailleur <eldy@users.sourceforge.net>
3  * Copyright (C) 2010-2014 Regis Houssin <regis.houssin@capnetworks.com>
4  * Copyright (C) 2010-2011 Juanjo Menent <jmenent@2byte.es>
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 3 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program. If not, see <http://www.gnu.org/licenses/>.
18  */
19 
31 {
32  var $db;
33  var $error;
34  var $errors=array();
35 
36  // Context hookmanager was created for ('thirdpartycard', 'thirdpartydao', ...)
37  var $contextarray=array();
38 
39  // Array with instantiated classes
40  var $hooks=array();
41 
42  // Array result
43  var $resArray=array();
44  // Printable result
45  var $resPrint='';
46  // Nb of qualified hook ran
47  var $resNbOfHooks=0;
48 
54  function __construct($db)
55  {
56  $this->db = $db;
57  }
58 
59 
71  function initHooks($arraycontext)
72  {
73  global $conf;
74 
75  // Test if there is hooks to manage
76  if (! is_array($conf->modules_parts['hooks']) || empty($conf->modules_parts['hooks'])) return;
77 
78  // For backward compatibility
79  if (! is_array($arraycontext)) $arraycontext=array($arraycontext);
80 
81  $this->contextarray=array_unique(array_merge($arraycontext,$this->contextarray)); // All contexts are concatenated
82 
83  foreach($conf->modules_parts['hooks'] as $module => $hooks)
84  {
85  if ($conf->$module->enabled)
86  {
87  foreach($arraycontext as $context)
88  {
89  if (is_array($hooks)) $arrayhooks=$hooks; // New system
90  else $arrayhooks=explode(':',$hooks); // Old system (for backward compatibility)
91  if (in_array($context,$arrayhooks) || in_array('all',$arrayhooks)) // We instantiate action class only if hook is required
92  {
93  $path = '/'.$module.'/class/';
94  $actionfile = 'actions_'.$module.'.class.php';
95  $pathroot = '';
96 
97  // Include actions class overwriting hooks
98  dol_syslog('Loading hook:' . $actionfile, LOG_INFO);
99  $resaction=dol_include_once($path.$actionfile);
100  if ($resaction)
101  {
102  $controlclassname = 'Actions'.ucfirst($module);
103  $actionInstance = new $controlclassname($this->db);
104  $this->hooks[$context][$module] = $actionInstance;
105  }
106  }
107  }
108  }
109  }
110  return 1;
111  }
112 
125  function executeHooks($method, $parameters=array(), &$object='', &$action='')
126  {
127  if (! is_array($this->hooks) || empty($this->hooks)) return '';
128 
129  $parameters['context']=join(':',$this->contextarray);
130  //dol_syslog(get_class($this).'::executeHooks method='.$method." action=".$action." context=".$parameters['context']);
131 
132  // Define type of hook ('output' or 'addreplace'. 'returnvalue' is deprecated because a 'addreplace' hook can also return resPrint and resArray).
133  $hooktype='output';
134  if (in_array(
135  $method,
136  array(
137  'addCalendarChoice',
138  'addMoreActionsButtons',
139  'addMoreMassActions',
140  'addSearchEntry',
141  'addStatisticLine',
142  'deleteFile',
143  'doActions',
144  'doMassActions',
145  'formCreateThirdpartyOptions',
146  'formObjectOptions',
147  'formattachOptions',
148  'formBuilddocLineOptions',
149  'formatNotificationMessage',
150  'getFormMail',
151  'getIdProfUrl',
152  'getDirList',
153  'moveUploadedFile',
154  'pdf_build_address',
155  'pdf_writelinedesc',
156  'pdf_getlinenum',
157  'pdf_getlineref',
158  'pdf_getlineref_supplier',
159  'pdf_getlinevatrate',
160  'pdf_getlineupexcltax',
161  'pdf_getlineupwithtax',
162  'pdf_getlineqty',
163  'pdf_getlineqty_asked',
164  'pdf_getlineqty_shipped',
165  'pdf_getlineqty_keeptoship',
166  'pdf_getlineunit',
167  'pdf_getlineremisepercent',
168  'pdf_getlineprogress',
169  'pdf_getlinetotalexcltax',
170  'pdf_getlinetotalwithtax',
171  'paymentsupplierinvoices',
172  'printAddress',
173  'printSearchForm',
174  'printTabsHead',
175  'formatEvent',
176  'printObjectLine',
177  'printObjectSubLine',
178  'createDictionaryFieldList',
179  'editDictionaryFieldlist',
180  'getFormMail',
181  'showLinkToObjectBlock'
182  )
183  )) $hooktype='addreplace';
184 
185  if ($method == 'insertExtraFields')
186  {
187  $hooktype='returnvalue'; // @deprecated. TODO Remove all code with "executeHooks('insertExtraFields'" as soon as there is a trigger available.
188  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);
189  }
190 
191  // Init return properties
192  $this->resPrint=''; $this->resArray=array(); $this->resNbOfHooks=0;
193 
194  // Loop on each hook to qualify modules that have declared context
195  $modulealreadyexecuted=array();
196  $resaction=0; $error=0; $result='';
197  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
198  {
199  if (! empty($modules))
200  {
201  foreach($modules as $module => $actionclassinstance)
202  {
203  //print "Before hook ".get_class($actionclassinstance)." method=".$method." hooktype=".$hooktype." results=".count($actionclassinstance->results)." resprints=".count($actionclassinstance->resprints)." resaction=".$resaction." result=".$result."<br>\n";
204 
205  // test to avoid running twice a hook, when a module implements several active contexts
206  if (in_array($module,$modulealreadyexecuted)) continue;
207 
208  // jump to next module/class if method does not exist
209  if (! method_exists($actionclassinstance,$method)) continue;
210 
211  $this->resNbOfHooks++;
212 
213  dol_syslog(get_class($this).'::executeHooks a qualified hook was found for method='.$method.' module='.$module." action=".$action." context=".$context);
214 
215  $modulealreadyexecuted[$module]=$module; // Use the $currentcontext in method to avoid running twice
216 
217  // Clean class (an error may have been set from a previous call of another method for same module/hook)
218  $actionclassinstance->error=0;
219  $actionclassinstance->errors=array();
220 
221  // Add current context to avoid method execution in bad context, you can add this test in your method : eg if($currentcontext != 'formfile') return;
222  $parameters['currentcontext'] = $context;
223  // Hooks that must return int (hooks with type 'addreplace')
224  if ($hooktype == 'addreplace')
225  {
226  dol_syslog("Call method ".$method." of class ".get_class($actionclassinstance).", module=".$module.", hooktype=".$hooktype, LOG_DEBUG);
227  $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)
228  if ($resaction < 0 || ! empty($actionclassinstance->error) || (! empty($actionclassinstance->errors) && count($actionclassinstance->errors) > 0))
229  {
230  $error++;
231  $this->error=$actionclassinstance->error; $this->errors=array_merge($this->errors, (array) $actionclassinstance->errors);
232  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);
233  }
234 
235  if (isset($actionclassinstance->results) && is_array($actionclassinstance->results)) $this->resArray =array_merge($this->resArray, $actionclassinstance->results);
236  if (! empty($actionclassinstance->resprints)) $this->resPrint.=$actionclassinstance->resprints;
237  }
238  // Generic hooks that return a string or array (printLeftBlock, formAddObjectLine, formBuilddocOptions, ...)
239  else
240  {
241  // TODO. this test should be done into the method of hook by returning nothing
242  if (is_array($parameters) && ! empty($parameters['special_code']) && $parameters['special_code'] > 3 && $parameters['special_code'] != $actionclassinstance->module_number) continue;
243 
244  //dol_syslog("Call method ".$method." of class ".get_class($actionclassinstance).", module=".$module.", hooktype=".$hooktype, LOG_DEBUG);
245  $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)
246 
247  if (! empty($actionclassinstance->results) && is_array($actionclassinstance->results)) $this->resArray =array_merge($this->resArray, $actionclassinstance->results);
248  if (! empty($actionclassinstance->resprints)) $this->resPrint.=$actionclassinstance->resprints;
249  // 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
250  if (! is_array($resaction) && ! is_numeric($resaction))
251  {
252  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);
253  if (empty($actionclassinstance->resprints)) { $this->resPrint.=$resaction; $resaction=0; }
254  }
255  }
256 
257  //print "After hook ".get_class($actionclassinstance)." method=".$method." hooktype=".$hooktype." results=".count($actionclassinstance->results)." resprints=".count($actionclassinstance->resprints)." resaction=".$resaction." result=".$result."<br>\n";
258 
259  unset($actionclassinstance->results);
260  unset($actionclassinstance->resprints);
261  }
262  }
263  }
264 
265  return ($error?-1:$resaction);
266  }
267 
268 }
initHooks($arraycontext)
Init array $this->hooks with instantiated action controlers.
executeHooks($method, $parameters=array(), &$object='', &$action='')
Execute hooks (if they were initialized) for the given method.
Class to manage hooks.
dol_syslog($message, $level=LOG_INFO, $ident=0, $suffixinfilename='', $restricttologhandler='')
Write log message into outputs.
if(!function_exists('dol_getprefix')) dol_include_once($relpath, $classname='')
Return a prefix to use for this Dolibarr instance, for session/cookie names or email id...
__construct($db)
Constructor.