dolibarr  13.0.2
interfaces.class.php
Go to the documentation of this file.
1 <?php
2 /* Copyright (C) 2005-2009 Laurent Destailleur <eldy@users.sourceforge.net>
3  * Copyright (C) 2006 Rodolphe Quiedeville <rodolphe@quiedeville.org>
4  * Copyright (C) 2010 Regis Houssin <regis.houssin@inodbox.com>
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 
26 require_once DOL_DOCUMENT_ROOT.'/core/triggers/dolibarrtriggers.class.php';
27 
28 
33 {
37  public $db;
38 
39  public $dir; // Directory with all core and external triggers files
40 
44  public $errors = array();
45 
51  public function __construct($db)
52  {
53  $this->db = $db;
54  }
55 
56  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
68  public function run_triggers($action, $object, $user, $langs, $conf)
69  {
70  // phpcs:enable
71  // Check parameters
72  if (!is_object($object) || !is_object($conf)) // Error
73  {
74  $this->error = 'function run_triggers called with wrong parameters action='.$action.' object='.is_object($object).' user='.is_object($user).' langs='.is_object($langs).' conf='.is_object($conf);
75  dol_syslog(get_class($this).'::run_triggers '.$this->error, LOG_ERR);
76  $this->errors[] = $this->error;
77  return -1;
78  }
79  if (!is_object($langs)) // Warning
80  {
81  dol_syslog(get_class($this).'::run_triggers was called with wrong parameters action='.$action.' object='.is_object($object).' user='.is_object($user).' langs='.is_object($langs).' conf='.is_object($conf), LOG_WARNING);
82  }
83  if (!is_object($user)) // Warning
84  {
85  dol_syslog(get_class($this).'::run_triggers was called with wrong parameters action='.$action.' object='.is_object($object).' user='.is_object($user).' langs='.is_object($langs).' conf='.is_object($conf), LOG_WARNING);
86  $user = new User($this->db);
87  }
88  //dol_syslog(get_class($this)."::run_triggers action=".$action." Launch run_triggers", LOG_DEBUG);
89 
90  $nbfile = $nbtotal = $nbok = $nbko = 0;
91 
92  $files = array();
93  $modules = array();
94  $orders = array();
95  $i = 0;
96 
97 
98  $dirtriggers = array_merge(array('/core/triggers'), $conf->modules_parts['triggers']);
99  foreach ($dirtriggers as $reldir)
100  {
101  $dir = dol_buildpath($reldir, 0);
102  $newdir = dol_osencode($dir);
103  //print "xx".$dir;exit;
104 
105  // Check if directory exists (we do not use dol_is_dir to avoir loading files.lib.php at each call)
106  if (!is_dir($newdir)) continue;
107 
108  $handle = opendir($newdir);
109  if (is_resource($handle))
110  {
111  $fullpathfiles = array();
112  while (($file = readdir($handle)) !== false)
113  {
114  $reg = array();
115  if (is_readable($newdir."/".$file) && preg_match('/^interface_([0-9]+)_([^_]+)_(.+)\.class\.php$/i', $file, $reg))
116  {
117  $part1 = $reg[1];
118  $part2 = $reg[2];
119  $part3 = $reg[3];
120 
121  $nbfile++;
122 
123  // Check if trigger file is disabled by name
124  if (preg_match('/NORUN$/i', $file)) continue;
125  // Check if trigger file is for a particular module
126  $qualified = true;
127  if (strtolower($reg[2]) != 'all')
128  {
129  $module = preg_replace('/^mod/i', '', $reg[2]);
130  $constparam = 'MAIN_MODULE_'.strtoupper($module);
131  if (empty($conf->global->$constparam)) $qualified = false;
132  }
133 
134  if (!$qualified)
135  {
136  //dol_syslog(get_class($this)."::run_triggers action=".$action." Triggers for file '".$file."' need module to be enabled", LOG_DEBUG);
137  continue;
138  }
139 
140  $modName = "Interface".ucfirst($reg[3]);
141  //print "file=$file - modName=$modName\n";
142  if (in_array($modName, $modules)) // $modules = list of modName already loaded
143  {
144  $langs->load("errors");
145  dol_syslog(get_class($this)."::run_triggers action=".$action." ".$langs->trans("ErrorDuplicateTrigger", $newdir."/".$file, $fullpathfiles[$modName]), LOG_WARNING);
146  continue;
147  }
148 
149  try {
150  //print 'Todo for '.$modName." : ".$newdir.'/'.$file."\n";
151  include_once $newdir.'/'.$file;
152  //print 'Done for '.$modName."\n";
153  } catch (Exception $e)
154  {
155  dol_syslog('ko for '.$modName." ".$e->getMessage()."\n", LOG_ERR);
156  }
157 
158  $modules[$i] = $modName;
159  $files[$i] = $file;
160  $fullpathfiles[$modName] = $newdir.'/'.$file;
161  $orders[$i] = $part1.'_'.$part2.'_'.$part3; // Set sort criteria value
162 
163  $i++;
164  }
165  }
166 
167  closedir($handle);
168  }
169  }
170 
171  asort($orders);
172 
173  // Loop on each trigger
174  foreach ($orders as $key => $value)
175  {
176  $modName = $modules[$key];
177  if (empty($modName)) continue;
178 
179  $objMod = new $modName($this->db);
180  if ($objMod)
181  {
182  $dblevelbefore = $this->db->transaction_opened;
183 
184  $result = 0;
185 
186  if (method_exists($objMod, 'runTrigger')) { // New method to implement
187  //dol_syslog(get_class($this)."::run_triggers action=".$action." Launch runTrigger for file '".$files[$key]."'", LOG_DEBUG);
188  $result = $objMod->runTrigger($action, $object, $user, $langs, $conf);
189  } elseif (method_exists($objMod, 'run_trigger')) { // Deprecated method
190  dol_syslog(get_class($this)."::run_triggers action=".$action." Launch old method run_trigger (rename your trigger into runTrigger) for file '".$files[$key]."'", LOG_WARNING);
191  $result = $objMod->run_trigger($action, $object, $user, $langs, $conf);
192  } else {
193  dol_syslog(get_class($this)."::run_triggers action=".$action." A trigger was declared for class ".get_class($objMod)." but method runTrigger was not found", LOG_ERR);
194  }
195 
196  $dblevelafter = $this->db->transaction_opened;
197 
198  if ($dblevelbefore != $dblevelafter) {
199  $errormessage = "Error, the balance begin/close of db transactions has been broken into trigger ".$modName." with action=".$action." before=".$dblevelbefore." after=".$dblevelafter;
200  $this->errors[] = $errormessage;
201  dol_syslog($errormessage, LOG_ERR);
202  $result = -1;
203  }
204 
205  if ($result > 0)
206  {
207  // Action OK
208  $nbtotal++;
209  $nbok++;
210  }
211  if ($result == 0)
212  {
213  // Aucune action faite
214  $nbtotal++;
215  }
216  if ($result < 0)
217  {
218  // Action KO
219  //dol_syslog("Error in trigger ".$action." - result = ".$result." - Nb of error string returned = ".count($objMod->errors), LOG_ERR);
220  $nbtotal++;
221  $nbko++;
222  if (!empty($objMod->errors)) $this->errors = array_merge($this->errors, $objMod->errors);
223  elseif (!empty($objMod->error)) $this->errors[] = $objMod->error;
224  //dol_syslog("Error in trigger ".$action." - Nb of error string returned = ".count($this->errors), LOG_ERR);
225  }
226  } else {
227  dol_syslog(get_class($this)."::run_triggers action=".$action." Failed to instantiate trigger for file '".$files[$key]."'", LOG_ERR);
228  }
229  }
230 
231  if ($nbko)
232  {
233  dol_syslog(get_class($this)."::run_triggers action=".$action." Files found: ".$nbfile.", Files launched: ".$nbtotal.", Done: ".$nbok.", Failed: ".$nbko." - Nb of error string returned in this->errors = ".count($this->errors), LOG_ERR);
234  return -$nbko;
235  } else {
236  //dol_syslog(get_class($this)."::run_triggers Files found: ".$nbfile.", Files launched: ".$nbtotal.", Done: ".$nbok.", Failed: ".$nbko, LOG_DEBUG);
237  return $nbok;
238  }
239  }
240 
248  public function getTriggersList($forcedirtriggers = null)
249  {
250  global $conf, $langs, $db;
251 
252  $files = array();
253  $fullpath = array();
254  $relpath = array();
255  $iscoreorexternal = array();
256  $modules = array();
257  $orders = array();
258  $i = 0;
259 
260  $dirtriggers = array_merge(array('/core/triggers/'), $conf->modules_parts['triggers']);
261  if (is_array($forcedirtriggers))
262  {
263  $dirtriggers = $forcedirtriggers;
264  }
265 
266  foreach ($dirtriggers as $reldir)
267  {
268  $dir = dol_buildpath($reldir, 0);
269  $newdir = dol_osencode($dir);
270 
271  // Check if directory exists (we do not use dol_is_dir to avoid loading files.lib.php at each call)
272  if (!is_dir($newdir)) continue;
273 
274  $handle = opendir($newdir);
275  if (is_resource($handle))
276  {
277  while (($file = readdir($handle)) !== false)
278  {
279  $reg = array();
280  if (is_readable($newdir.'/'.$file) && preg_match('/^interface_([0-9]+)_([^_]+)_(.+)\.class\.php/', $file, $reg))
281  {
282  if (preg_match('/\.back$/', $file)) continue;
283 
284  $part1 = $reg[1];
285  $part2 = $reg[2];
286  $part3 = $reg[3];
287 
288  $modName = 'Interface'.ucfirst($reg[3]);
289  //print "file=$file"; print "modName=$modName"; exit;
290  if (in_array($modName, $modules))
291  {
292  $langs->load("errors");
293  print '<div class="error">'.$langs->trans("Error").' : '.$langs->trans("ErrorDuplicateTrigger", $modName, "/htdocs/core/triggers/").'</div>';
294  } else {
295  include_once $newdir.'/'.$file;
296  }
297 
298  $files[$i] = $file;
299  $fullpath[$i] = $dir.'/'.$file;
300  $relpath[$i] = preg_replace('/^\//', '', $reldir).'/'.$file;
301  $iscoreorexternal[$i] = ($reldir == '/core/triggers/' ? 'internal' : 'external');
302  $modules[$i] = $modName;
303  $orders[$i] = $part1.'_'.$part2.'_'.$part3; // Set sort criteria value
304 
305  $i++;
306  }
307  }
308  closedir($handle);
309  }
310  }
311 
312  asort($orders);
313 
314  $triggers = array();
315  $j = 0;
316 
317  // Loop on each trigger
318  foreach ($orders as $key => $value)
319  {
320  $modName = $modules[$key];
321  if (empty($modName)) continue;
322 
323  if (!class_exists($modName))
324  {
325  print 'Error: A trigger file was found but its class "'.$modName.'" was not found.'."<br>\n";
326  continue;
327  }
328 
329  $text = '';
330 
331  try {
332  $objMod = new $modName($db);
333 
334  if (is_subclass_of($objMod, 'DolibarrTriggers'))
335  {
336  // Define disabledbyname and disabledbymodule
337  $disabledbyname = 0;
338  $disabledbymodule = 1;
339  $module = '';
340 
341  // Check if trigger file is disabled by name
342  if (preg_match('/NORUN$/i', $files[$key])) $disabledbyname = 1;
343  // Check if trigger file is for a particular module
344  if (preg_match('/^interface_([0-9]+)_([^_]+)_(.+)\.class\.php/i', $files[$key], $reg))
345  {
346  $module = preg_replace('/^mod/i', '', $reg[2]);
347  $constparam = 'MAIN_MODULE_'.strtoupper($module);
348  if (strtolower($module) == 'all') $disabledbymodule = 0;
349  elseif (empty($conf->global->$constparam)) $disabledbymodule = 2;
350  $triggers[$j]['module'] = strtolower($module);
351  }
352 
353  // We set info of modules
354  $triggers[$j]['picto'] = $objMod->picto ?img_object('', $objMod->picto, 'class="valignmiddle pictomodule "') : img_object('', 'generic', 'class="valignmiddle pictomodule "');
355  $triggers[$j]['file'] = $files[$key];
356  $triggers[$j]['fullpath'] = $fullpath[$key];
357  $triggers[$j]['relpath'] = $relpath[$key];
358  $triggers[$j]['iscoreorexternal'] = $iscoreorexternal[$key];
359  $triggers[$j]['version'] = $objMod->getVersion();
360  $triggers[$j]['status'] = img_picto($langs->trans("Active"), 'tick');
361  if ($disabledbyname > 0 || $disabledbymodule > 1) $triggers[$j]['status'] = '';
362 
363  $text = '<b>'.$langs->trans("Description").':</b><br>';
364  $text .= $objMod->getDesc().'<br>';
365  $text .= '<br><b>'.$langs->trans("Status").':</b><br>';
366  if ($disabledbyname == 1)
367  {
368  $text .= $langs->trans("TriggerDisabledByName").'<br>';
369  if ($disabledbymodule == 2) $text .= $langs->trans("TriggerDisabledAsModuleDisabled", $module).'<br>';
370  } else {
371  if ($disabledbymodule == 0) $text .= $langs->trans("TriggerAlwaysActive").'<br>';
372  if ($disabledbymodule == 1) $text .= $langs->trans("TriggerActiveAsModuleActive", $module).'<br>';
373  if ($disabledbymodule == 2) $text .= $langs->trans("TriggerDisabledAsModuleDisabled", $module).'<br>';
374  }
375  } else {
376  print 'Error: Trigger '.$modName.' does not extends DolibarrTriggers<br>';
377  }
378  } catch (Exception $e) {
379  print $e->getMessage();
380  }
381 
382  $triggers[$j]['info'] = $text;
383  $j++;
384  }
385  return $triggers;
386  }
387 }
dol_osencode($str)
Return a string encoded into OS filesystem encoding.
Class to manage Dolibarr users.
Definition: user.class.php:44
dol_buildpath($path, $type=0, $returnemptyifnotfound=0)
Return path of url or filesystem.
$conf db
API class for accounts.
Definition: inc.php:54
__construct($db)
Constructor.
img_picto($titlealt, $picto, $moreatt= '', $pictoisfullpath=false, $srconly=0, $notitle=0, $alt= '', $morecss= '', $marginleftonlyshort=2)
Show picto whatever it&#39;s its name (generic function)
dol_syslog($message, $level=LOG_INFO, $ident=0, $suffixinfilename= '', $restricttologhandler= '', $logcontext=null)
Write log message into outputs.
img_object($titlealt, $picto, $moreatt= '', $pictoisfullpath=false, $srconly=0, $notitle=0)
Show a picto called object_picto (generic function)
run_triggers($action, $object, $user, $langs, $conf)
Function called when a Dolibarr business event occurs This function call all qualified triggers...
print
Draft customers invoices.
Definition: index.php:89
getTriggersList($forcedirtriggers=null)
Return list of triggers.
Class to manage triggers.