dolibarr  13.0.2
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@inodbox.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 <https://www.gnu.org/licenses/>.
18  */
19 
31 {
35  public $db;
36 
40  public $error = '';
41 
45  public $errors = array();
46 
47  // Context hookmanager was created for ('thirdpartycard', 'thirdpartydao', ...)
48  public $contextarray = array();
49 
50  // Array with instantiated classes
51  public $hooks = array();
52 
53  // Array result
54  public $resArray = array();
55  // Printable result
56  public $resPrint = '';
57  // Nb of qualified hook ran
58  public $resNbOfHooks = 0;
59 
65  public function __construct($db)
66  {
67  $this->db = $db;
68  }
69 
70 
82  public function initHooks($arraycontext)
83  {
84  global $conf;
85 
86  // Test if there is hooks to manage
87  if (!is_array($conf->modules_parts['hooks']) || empty($conf->modules_parts['hooks'])) return;
88 
89  // For backward compatibility
90  if (!is_array($arraycontext)) $arraycontext = array($arraycontext);
91 
92  $this->contextarray = array_unique(array_merge($arraycontext, $this->contextarray)); // All contexts are concatenated
93 
94  $arraytolog = array();
95  foreach ($conf->modules_parts['hooks'] as $module => $hooks) // Loop on each module that brings hooks
96  {
97  if (empty($conf->$module->enabled)) continue;
98 
99  //dol_syslog(get_class($this).'::initHooks module='.$module.' arraycontext='.join(',',$arraycontext));
100  foreach ($arraycontext as $context)
101  {
102  if (is_array($hooks)) $arrayhooks = $hooks; // New system
103  else $arrayhooks = explode(':', $hooks); // Old system (for backward compatibility)
104 
105  if (in_array($context, $arrayhooks) || in_array('all', $arrayhooks)) // We instantiate action class only if initialized hook is handled by module
106  {
107  // Include actions class overwriting hooks
108  if (empty($this->hooks[$context][$module]) || !is_object($this->hooks[$context][$module])) // If set to an object value, class was already loaded
109  {
110  $path = '/'.$module.'/class/';
111  $actionfile = 'actions_'.$module.'.class.php';
112 
113  $arraytolog[] = 'context='.$context.'-path='.$path.$actionfile;
114  $resaction = dol_include_once($path.$actionfile);
115  if ($resaction)
116  {
117  $controlclassname = 'Actions'.ucfirst($module);
118  $actionInstance = new $controlclassname($this->db);
119  $this->hooks[$context][$module] = $actionInstance;
120  }
121  }
122  }
123  }
124  }
125  if (count($arraytolog) > 0)
126  {
127  dol_syslog(get_class($this)."::initHooks Loading hooks: ".join(', ', $arraytolog), LOG_DEBUG);
128  }
129 
130  return 1;
131  }
132 
145  public function executeHooks($method, $parameters = array(), &$object = '', &$action = '')
146  {
147  if (!is_array($this->hooks) || empty($this->hooks)) return 0; // No hook available, do nothing.
148 
149  $parameters['context'] = join(':', $this->contextarray);
150  //dol_syslog(get_class($this).'::executeHooks method='.$method." action=".$action." context=".$parameters['context']);
151 
152  // Define type of hook ('output' or 'addreplace').
153  // TODO Remove hooks with type 'output'. All hooks must be converted into 'addreplace' hooks.
154  $hooktype = 'output';
155  if (in_array(
156  $method,
157  array(
158  'addCalendarChoice',
159  'addCalendarView',
160  'addMoreActionsButtons',
161  'addMoreMassActions',
162  'addSearchEntry',
163  'addStatisticLine',
164  'createDictionaryFieldlist',
165  'editDictionaryFieldlist',
166  'getFormMail',
167  'deleteFile',
168  'doActions',
169  'doMassActions',
170  'formatEvent',
171  'formConfirm',
172  'formCreateThirdpartyOptions',
173  'formObjectOptions',
174  'formattachOptions',
175  'formBuilddocLineOptions',
176  'formatNotificationMessage',
177  'formConfirm',
178  'getAccessForbiddenMessage',
179  'getDirList',
180  'getFormMail',
181  'getFormatedCustomerRef',
182  'getFormatedSupplierRef',
183  'getIdProfUrl',
184  'getInputIdProf',
185  'moveUploadedFile',
186  'moreHtmlStatus',
187  'pdf_build_address',
188  'pdf_writelinedesc',
189  'pdf_getlinenum',
190  'pdf_getlineref',
191  'pdf_getlineref_supplier',
192  'pdf_getlinevatrate',
193  'pdf_getlineupexcltax',
194  'pdf_getlineupwithtax',
195  'pdf_getlineqty',
196  'pdf_getlineqty_asked',
197  'pdf_getlineqty_shipped',
198  'pdf_getlineqty_keeptoship',
199  'pdf_getlineunit',
200  'pdf_getlineremisepercent',
201  'pdf_getlineprogress',
202  'pdf_getlinetotalexcltax',
203  'pdf_getlinetotalwithtax',
204  'paymentsupplierinvoices',
205  'printAddress',
206  'printEmail',
207  'printSearchForm',
208  'printTabsHead',
209  'printObjectLine',
210  'printObjectSubLine',
211  'restrictedArea',
212  'sendMail',
213  'sendMailAfter',
214  'showOptionals',
215  'showLinkToObjectBlock',
216  'setContentSecurityPolicy',
217  'setHtmlTitle',
218  'completeTabsHead'
219  )
220  )) $hooktype = 'addreplace';
221 
222  // Init return properties
223  $this->resPrint = ''; $this->resArray = array(); $this->resNbOfHooks = 0;
224 
225  // Loop on each hook to qualify modules that have declared context
226  $modulealreadyexecuted = array();
227  $resaction = 0; $error = 0;
228  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
229  {
230  if (!empty($modules))
231  {
232  foreach ($modules as $module => $actionclassinstance)
233  {
234  //print "Before hook ".get_class($actionclassinstance)." method=".$method." hooktype=".$hooktype." results=".count($actionclassinstance->results)." resprints=".count($actionclassinstance->resprints)." resaction=".$resaction."<br>\n";
235 
236  // test to avoid running twice a hook, when a module implements several active contexts
237  if (in_array($module, $modulealreadyexecuted)) continue;
238 
239  // jump to next module/class if method does not exist
240  if (!method_exists($actionclassinstance, $method)) continue;
241 
242  $this->resNbOfHooks++;
243 
244  $modulealreadyexecuted[$module] = $module; // Use the $currentcontext in method to avoid running twice
245 
246  // Clean class (an error may have been set from a previous call of another method for same module/hook)
247  $actionclassinstance->error = 0;
248  $actionclassinstance->errors = array();
249 
250  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);
251 
252  // Add current context to avoid method execution in bad context, you can add this test in your method : eg if($currentcontext != 'formfile') return;
253  $parameters['currentcontext'] = $context;
254  // Hooks that must return int (hooks with type 'addreplace')
255  if ($hooktype == 'addreplace')
256  {
257  $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)
258  if ($resaction < 0 || !empty($actionclassinstance->error) || (!empty($actionclassinstance->errors) && count($actionclassinstance->errors) > 0))
259  {
260  $error++;
261  $this->error = $actionclassinstance->error; $this->errors = array_merge($this->errors, (array) $actionclassinstance->errors);
262  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);
263  }
264 
265  if (isset($actionclassinstance->results) && is_array($actionclassinstance->results)) $this->resArray = array_merge($this->resArray, $actionclassinstance->results);
266  if (!empty($actionclassinstance->resprints)) $this->resPrint .= $actionclassinstance->resprints;
267  }
268  // Generic hooks that return a string or array (printLeftBlock, formAddObjectLine, formBuilddocOptions, ...)
269  else {
270  // TODO. this test should be done into the method of hook by returning nothing
271  if (is_array($parameters) && !empty($parameters['special_code']) && $parameters['special_code'] > 3 && $parameters['special_code'] != $actionclassinstance->module_number) continue;
272 
273  //dol_syslog("Call method ".$method." of class ".get_class($actionclassinstance).", module=".$module.", hooktype=".$hooktype, LOG_DEBUG);
274  $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)
275 
276  if (!empty($actionclassinstance->results) && is_array($actionclassinstance->results)) $this->resArray = array_merge($this->resArray, $actionclassinstance->results);
277  if (!empty($actionclassinstance->resprints)) $this->resPrint .= $actionclassinstance->resprints;
278  if (is_numeric($resaction) && $resaction < 0)
279  {
280  $error++;
281  $this->error = $actionclassinstance->error; $this->errors = array_merge($this->errors, (array) $actionclassinstance->errors);
282  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);
283  }
284  // 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
285  if (!is_array($resaction) && !is_numeric($resaction))
286  {
287  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);
288  if (empty($actionclassinstance->resprints)) { $this->resPrint .= $resaction; $resaction = 0; }
289  }
290  }
291 
292  //print "After hook ".get_class($actionclassinstance)." method=".$method." hooktype=".$hooktype." results=".count($actionclassinstance->results)." resprints=".count($actionclassinstance->resprints)." resaction=".$resaction."<br>\n";
293 
294  unset($actionclassinstance->results);
295  unset($actionclassinstance->resprints);
296  }
297  }
298  }
299 
300  return ($error ? -1 : $resaction);
301  }
302 }
if(!function_exists('dol_getprefix')) dol_include_once($relpath, $classname= '')
Make an include_once using default root and alternate root if it fails.
executeHooks($method, $parameters=array(), &$object= '', &$action= '')
Execute hooks (if they were initialized) for the given method.
initHooks($arraycontext)
Init array $this-&gt;hooks with instantiated action controlers.
$conf db
API class for accounts.
Definition: inc.php:54
Class to manage hooks.
dol_syslog($message, $level=LOG_INFO, $ident=0, $suffixinfilename= '', $restricttologhandler= '', $logcontext=null)
Write log message into outputs.
__construct($db)
Constructor.