dolibarr  17.0.4
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 
72  if (getDolGlobalInt('MAIN_TRIGGER_DEBUG')) {
73  // This his too much verbose, enabled if const enabled only
74  dol_syslog(get_class($this)."::run_triggers action=".$action." Launch run_triggers", LOG_DEBUG);
75  }
76 
77  // Check parameters
78  if (!is_object($object) || !is_object($conf)) { // Error
79  $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);
80  dol_syslog(get_class($this).'::run_triggers '.$this->error, LOG_ERR);
81  $this->errors[] = $this->error;
82  return -1;
83  }
84  if (!is_object($langs)) { // Warning
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  }
87  if (!is_object($user)) { // Warning
88  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);
89  $user = new User($this->db);
90  }
91 
92  $nbfile = $nbtotal = $nbok = $nbko = 0;
93 
94  $files = array();
95  $modules = array();
96  $orders = array();
97  $i = 0;
98 
99 
100  $dirtriggers = array_merge(array('/core/triggers'), $conf->modules_parts['triggers']);
101  foreach ($dirtriggers as $reldir) {
102  $dir = dol_buildpath($reldir, 0);
103  $newdir = dol_osencode($dir);
104  //print "xx".$dir;exit;
105 
106  // Check if directory exists (we do not use dol_is_dir to avoir loading files.lib.php at each call)
107  if (!is_dir($newdir)) {
108  continue;
109  }
110 
111  $handle = opendir($newdir);
112  if (is_resource($handle)) {
113  $fullpathfiles = array();
114  while (($file = readdir($handle)) !== false) {
115  $reg = array();
116  if (is_readable($newdir."/".$file) && preg_match('/^interface_([0-9]+)_([^_]+)_(.+)\.class\.php$/i', $file, $reg)) {
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)) {
125  continue;
126  }
127  // Check if trigger file is for a particular module
128  $qualified = true;
129  if (strtolower($reg[2]) != 'all') {
130  $module = preg_replace('/^mod/i', '', $reg[2]);
131  $constparam = 'MAIN_MODULE_'.strtoupper($module);
132  if (empty($conf->global->$constparam)) {
133  $qualified = false;
134  }
135  }
136 
137  if (!$qualified) {
138  //dol_syslog(get_class($this)."::run_triggers action=".$action." Triggers for file '".$file."' need module to be enabled", LOG_DEBUG);
139  continue;
140  }
141 
142  $modName = "Interface".ucfirst($reg[3]);
143  //print "file=$file - modName=$modName\n";
144  if (in_array($modName, $modules)) { // $modules = list of modName already loaded
145  $langs->load("errors");
146  dol_syslog(get_class($this)."::run_triggers action=".$action." ".$langs->trans("ErrorDuplicateTrigger", $newdir."/".$file, $fullpathfiles[$modName]), LOG_WARNING);
147  continue;
148  }
149 
150  try {
151  //print 'Todo for '.$modName." : ".$newdir.'/'.$file."\n";
152  include_once $newdir.'/'.$file;
153  //print 'Done for '.$modName."\n";
154  } catch (Exception $e) {
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  $modName = $modules[$key];
176  if (empty($modName)) {
177  continue;
178  }
179 
180  $objMod = new $modName($this->db);
181  if ($objMod) {
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  // Action OK
207  $nbtotal++;
208  $nbok++;
209  }
210  if ($result == 0) {
211  // Aucune action faite
212  $nbtotal++;
213  }
214  if ($result < 0) {
215  // Action KO
216  //dol_syslog("Error in trigger ".$action." - result = ".$result." - Nb of error string returned = ".count($objMod->errors), LOG_ERR);
217  $nbtotal++;
218  $nbko++;
219  if (!empty($objMod->errors)) {
220  $this->errors = array_merge($this->errors, $objMod->errors);
221  } elseif (!empty($objMod->error)) {
222  $this->errors[] = $objMod->error;
223  }
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  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);
233  return -$nbko;
234  } else {
235  //dol_syslog(get_class($this)."::run_triggers Files found: ".$nbfile.", Files launched: ".$nbtotal.", Done: ".$nbok.", Failed: ".$nbko, LOG_DEBUG);
236  return $nbok;
237  }
238  }
239 
247  public function getTriggersList($forcedirtriggers = null)
248  {
249  global $conf, $langs, $db;
250 
251  $files = array();
252  $fullpath = array();
253  $relpath = array();
254  $iscoreorexternal = array();
255  $modules = array();
256  $orders = array();
257  $i = 0;
258 
259  $dirtriggers = array_merge(array('/core/triggers/'), $conf->modules_parts['triggers']);
260  if (is_array($forcedirtriggers)) {
261  $dirtriggers = $forcedirtriggers;
262  }
263 
264  foreach ($dirtriggers as $reldir) {
265  $dir = dol_buildpath($reldir, 0);
266  $newdir = dol_osencode($dir);
267 
268  // Check if directory exists (we do not use dol_is_dir to avoid loading files.lib.php at each call)
269  if (!is_dir($newdir)) {
270  continue;
271  }
272 
273  $handle = opendir($newdir);
274  if (is_resource($handle)) {
275  while (($file = readdir($handle)) !== false) {
276  $reg = array();
277  if (is_readable($newdir.'/'.$file) && preg_match('/^interface_([0-9]+)_([^_]+)_(.+)\.class\.php/', $file, $reg)) {
278  if (preg_match('/\.back$/', $file)) {
279  continue;
280  }
281 
282  $part1 = $reg[1];
283  $part2 = $reg[2];
284  $part3 = $reg[3];
285 
286  $modName = 'Interface'.ucfirst($reg[3]);
287  //print "file=$file"; print "modName=$modName"; exit;
288  if (in_array($modName, $modules)) {
289  $langs->load("errors");
290  print '<div class="error">'.$langs->trans("Error").' : '.$langs->trans("ErrorDuplicateTrigger", $modName, "/htdocs/core/triggers/").'</div>';
291  } else {
292  include_once $newdir.'/'.$file;
293  }
294 
295  $files[$i] = $file;
296  $fullpath[$i] = $dir.'/'.$file;
297  $relpath[$i] = preg_replace('/^\//', '', $reldir).'/'.$file;
298  $iscoreorexternal[$i] = ($reldir == '/core/triggers/' ? 'internal' : 'external');
299  $modules[$i] = $modName;
300  $orders[$i] = $part1.'_'.$part2.'_'.$part3; // Set sort criteria value
301 
302  $i++;
303  }
304  }
305  closedir($handle);
306  }
307  }
308 
309  asort($orders);
310 
311  $triggers = array();
312  $j = 0;
313 
314  // Loop on each trigger
315  foreach ($orders as $key => $value) {
316  $modName = $modules[$key];
317  if (empty($modName)) {
318  continue;
319  }
320 
321  if (!class_exists($modName)) {
322  print 'Error: A trigger file was found but its class "'.$modName.'" was not found.'."<br>\n";
323  continue;
324  }
325 
326  $text = '';
327 
328  try {
329  $objMod = new $modName($db);
330 
331  if (is_subclass_of($objMod, 'DolibarrTriggers')) {
332  // Define disabledbyname and disabledbymodule
333  $disabledbyname = 0;
334  $disabledbymodule = 1;
335  $module = '';
336 
337  // Check if trigger file is disabled by name
338  if (preg_match('/NORUN$/i', $files[$key])) {
339  $disabledbyname = 1;
340  }
341  // Check if trigger file is for a particular module
342  if (preg_match('/^interface_([0-9]+)_([^_]+)_(.+)\.class\.php/i', $files[$key], $reg)) {
343  $module = preg_replace('/^mod/i', '', $reg[2]);
344  $constparam = 'MAIN_MODULE_'.strtoupper($module);
345  if (strtolower($module) == 'all') {
346  $disabledbymodule = 0;
347  } elseif (empty($conf->global->$constparam)) {
348  $disabledbymodule = 2;
349  }
350  $triggers[$j]['module'] = strtolower($module);
351  }
352 
353  // We set info of modules
354  $triggers[$j]['picto'] = (!empty($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) {
362  $triggers[$j]['status'] = '';
363  }
364 
365  $text = '<b>'.$langs->trans("Description").':</b><br>';
366  $text .= $objMod->getDesc().'<br>';
367  $text .= '<br><b>'.$langs->trans("Status").':</b><br>';
368  if ($disabledbyname == 1) {
369  $text .= $langs->trans("TriggerDisabledByName").'<br>';
370  if ($disabledbymodule == 2) {
371  $text .= $langs->trans("TriggerDisabledAsModuleDisabled", $module).'<br>';
372  }
373  } else {
374  if ($disabledbymodule == 0) {
375  $text .= $langs->trans("TriggerAlwaysActive").'<br>';
376  }
377  if ($disabledbymodule == 1) {
378  $text .= $langs->trans("TriggerActiveAsModuleActive", $module).'<br>';
379  }
380  if ($disabledbymodule == 2) {
381  $text .= $langs->trans("TriggerDisabledAsModuleDisabled", $module).'<br>';
382  }
383  }
384  } else {
385  $triggers[$j]['picto'] = (!empty($objMod->picto)) ? img_object('', $objMod->picto, 'class="valignmiddle pictomodule "') : img_object('', 'generic', 'class="valignmiddle pictomodule "');
386  $triggers[$j]['file'] = $files[$key];
387  $triggers[$j]['fullpath'] = $fullpath[$key];
388  $triggers[$j]['relpath'] = $relpath[$key];
389  $triggers[$j]['status'] = img_picto('Error: Trigger '.$modName.' does not extends DolibarrTriggers', 'warning');
390 
391  //print 'Error: Trigger '.$modName.' does not extends DolibarrTriggers<br>';
392  $text = 'Error: Trigger '.$modName.' does not extends DolibarrTriggers';
393  }
394  } catch (Exception $e) {
395  print $e->getMessage();
396  }
397 
398  $triggers[$j]['info'] = $text;
399  $j++;
400  }
401  return $triggers;
402  }
403 }
Class to manage triggers.
__construct($db)
Constructor.
run_triggers($action, $object, $user, $langs, $conf)
Function called when a Dolibarr business event occurs This function call all qualified triggers.
getTriggersList($forcedirtriggers=null)
Return list of triggers.
Class to manage Dolibarr users.
Definition: user.class.php:47
dol_osencode($str)
Return a string encoded into OS filesystem encoding.
img_object($titlealt, $picto, $moreatt='', $pictoisfullpath=false, $srconly=0, $notitle=0)
Show a picto called object_picto (generic function)
getDolGlobalInt($key, $default=0)
Return dolibarr global constant int value.
img_picto($titlealt, $picto, $moreatt='', $pictoisfullpath=false, $srconly=0, $notitle=0, $alt='', $morecss='', $marginleftonlyshort=2)
Show picto whatever it's its name (generic function)
dol_buildpath($path, $type=0, $returnemptyifnotfound=0)
Return path of url or filesystem.
dol_syslog($message, $level=LOG_INFO, $ident=0, $suffixinfilename='', $restricttologhandler='', $logcontext=null)
Write log message into outputs.
$conf db
API class for accounts.
Definition: inc.php:41