dolibarr  20.0.0-alpha
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  * Copyright (C) 2024 MDW <mdeweerd@users.noreply.github.com>
6  *
7  * This program is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation; either version 3 of the License, or
10  * (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program. If not, see <https://www.gnu.org/licenses/>.
19  */
20 
27 require_once DOL_DOCUMENT_ROOT.'/core/triggers/dolibarrtriggers.class.php';
28 
29 
34 {
38  public $db;
39 
40  public $dir; // Directory with all core and external triggers files
41 
45  public $lastmoduleerror;
46 
50  public $errors = array();
51 
57  public function __construct($db)
58  {
59  $this->db = $db;
60  }
61 
62  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
74  public function run_triggers($action, $object, $user, $langs, $conf)
75  {
76  // phpcs:enable
77 
78  if (getDolGlobalInt('MAIN_TRIGGER_DEBUG')) {
79  // This his too much verbose, enabled if const enabled only
80  dol_syslog(get_class($this)."::run_triggers action=".$action." Launch run_triggers", LOG_DEBUG);
81  }
82 
83  // Check parameters
84  if (!is_object($object) || !is_object($conf)) { // Error
85  $error = 'function run_triggers called with wrong parameters action='.$action.' object='.((string) (int) is_object($object)).' user='.((string) (int) is_object($user)).' langs='.((string) (int) is_object($langs)).' conf='.((string) (int) is_object($conf));
86  dol_syslog(get_class($this).'::run_triggers '.$error, LOG_ERR);
87  $this->errors[] = $error;
88  return -1;
89  }
90  if (!is_object($langs)) { // Warning
91  dol_syslog(get_class($this).'::run_triggers was called with wrong parameters action='.$action.' object='.((string) (int) is_object($object)).' user='.((string) (int) is_object($user)).' langs='.((string) (int) is_object($langs)).' conf='.((string) (int) is_object($conf)), LOG_WARNING);
92  }
93  if (!is_object($user)) { // Warning
94  dol_syslog(get_class($this).'::run_triggers was called with wrong parameters action='.$action.' object='.((string) (int) is_object($object)).' user='.((string) (int) is_object($user)).' langs='.((string) (int) is_object($langs)).' conf='.((string) (int) is_object($conf)), LOG_WARNING);
95  $user = new User($this->db);
96  }
97 
98  $nbfile = $nbtotal = $nbok = $nbko = 0;
99  $this->lastmoduleerror = '';
100 
101  $files = array();
102  $modules = array();
103  $orders = array();
104  $i = 0;
105 
106  $dirtriggers = array_merge(array('/core/triggers'), $conf->modules_parts['triggers']);
107  foreach ($dirtriggers as $reldir) {
108  $dir = dol_buildpath($reldir, 0);
109  $newdir = dol_osencode($dir);
110  //print "xx".$dir;exit;
111 
112  // Check if directory exists (we do not use dol_is_dir to avoir loading files.lib.php at each call)
113  if (!is_dir($newdir)) {
114  continue;
115  }
116 
117  $handle = opendir($newdir);
118  if (is_resource($handle)) {
119  $fullpathfiles = array();
120  while (($file = readdir($handle)) !== false) {
121  $reg = array();
122  if (is_readable($newdir."/".$file) && preg_match('/^interface_([0-9]+)_([^_]+)_(.+)\.class\.php$/i', $file, $reg)) {
123  $part1 = $reg[1];
124  $part2 = $reg[2];
125  $part3 = $reg[3];
126 
127  $nbfile++;
128 
129  // Check if trigger file is disabled by name
130  if (preg_match('/NORUN$/i', $file)) {
131  continue;
132  }
133  // Check if trigger file is for a particular module
134  $qualified = true;
135  if (strtolower($reg[2]) != 'all') {
136  $module = preg_replace('/^mod/i', '', $reg[2]);
137  if (!isModEnabled(strtolower($module))) {
138  $qualified = false;
139  }
140  }
141 
142  if (!$qualified) {
143  //dol_syslog(get_class($this)."::run_triggers action=".$action." Triggers for file '".$file."' need module to be enabled", LOG_DEBUG);
144  continue;
145  }
146 
147  $modName = "Interface".ucfirst($reg[3]);
148  //print "file=$file - modName=$modName\n";
149  if (in_array($modName, $modules)) { // $modules = list of modName already loaded
150  $langs->load("errors");
151  dol_syslog(get_class($this)."::run_triggers action=".$action." ".$langs->trans("ErrorDuplicateTrigger", $newdir."/".$file, $fullpathfiles[$modName]), LOG_WARNING);
152  continue;
153  }
154 
155  try {
156  //print 'Todo for '.$modName." : ".$newdir.'/'.$file."\n";
157  include_once $newdir.'/'.$file;
158  //print 'Done for '.$modName."\n";
159  } catch (Exception $e) {
160  dol_syslog('ko for '.$modName." ".$e->getMessage()."\n", LOG_ERR);
161  }
162 
163  $modules[$i] = $modName;
164  $files[$i] = $file;
165  $fullpathfiles[$modName] = $newdir.'/'.$file;
166  $orders[$i] = $part1.'_'.$part2.'_'.$part3; // Set sort criteria value
167 
168  $i++;
169  }
170  }
171 
172  closedir($handle);
173  }
174  }
175 
176  asort($orders, SORT_NATURAL);
177 
178  // Loop on each trigger
179  foreach ($orders as $key => $value) {
180  $modName = $modules[$key];
181  if (empty($modName)) {
182  continue;
183  }
184 
185  $objMod = new $modName($this->db);
186  if ($objMod) {
187  $dblevelbefore = $this->db->transaction_opened;
188 
189  $result = 0;
190 
191  if (method_exists($objMod, 'runTrigger')) { // New method to implement
192  //dol_syslog(get_class($this)."::run_triggers action=".$action." Launch runTrigger for file '".$files[$key]."'", LOG_DEBUG);
193  $result = $objMod->runTrigger($action, $object, $user, $langs, $conf);
194  } elseif (method_exists($objMod, 'run_trigger')) { // Deprecated method
195  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);
196  $result = $objMod->run_trigger($action, $object, $user, $langs, $conf);
197  } else {
198  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);
199  }
200 
201  $dblevelafter = $this->db->transaction_opened;
202 
203  if ($dblevelbefore != $dblevelafter) {
204  $errormessage = "Error, the balance begin/close of db transactions has been broken into trigger ".$modName." with action=".$action." before=".$dblevelbefore." after=".$dblevelafter;
205  $this->errors[] = $errormessage;
206  dol_syslog($errormessage, LOG_ERR);
207  $result = -1;
208  }
209 
210  if ($result > 0) {
211  // Action OK
212  $nbtotal++;
213  $nbok++;
214  }
215  if ($result == 0) {
216  // Aucune action faite
217  $nbtotal++;
218  }
219  if ($result < 0) {
220  // Action KO
221  //dol_syslog("Error in trigger ".$action." - result = ".$result." - Nb of error string returned = ".count($objMod->errors), LOG_ERR);
222  $nbtotal++;
223  $nbko++;
224  $this->lastmoduleerror = $modName;
225  if (!empty($objMod->errors)) {
226  $this->errors = array_merge($this->errors, $objMod->errors);
227  } elseif (!empty($objMod->error)) {
228  $this->errors[] = $objMod->error;
229  }
230  //dol_syslog("Error in trigger ".$action." - Nb of error string returned = ".count($this->errors), LOG_ERR);
231  }
232  } else {
233  dol_syslog(get_class($this)."::run_triggers action=".$action." Failed to instantiate trigger for file '".$files[$key]."'", LOG_ERR);
234  }
235  }
236 
237  if ($nbko) {
238  dol_syslog(get_class($this)."::run_triggers action=".$action." Files found: ".$nbfile.", Files launched: ".$nbtotal.", Done: ".$nbok.", Failed: ".$nbko.($this->lastmoduleerror ? " - Last module in error: ".$this->lastmoduleerror : "")." - Nb of error string returned in this->errors = ".count($this->errors), LOG_ERR);
239  return -$nbko;
240  } else {
241  //dol_syslog(get_class($this)."::run_triggers Files found: ".$nbfile.", Files launched: ".$nbtotal.", Done: ".$nbok.", Failed: ".$nbko, LOG_DEBUG);
242  return $nbok;
243  }
244  }
245 
253  public function getTriggersList($forcedirtriggers = null)
254  {
255  global $conf, $langs, $db;
256 
257  $files = array();
258  $fullpath = array();
259  $relpath = array();
260  $iscoreorexternal = array();
261  $modules = array();
262  $orders = array();
263  $i = 0;
264 
265  $dirtriggers = array_merge(array('/core/triggers/'), $conf->modules_parts['triggers']);
266  if (is_array($forcedirtriggers)) {
267  $dirtriggers = $forcedirtriggers;
268  }
269 
270  foreach ($dirtriggers as $reldir) {
271  $dir = dol_buildpath($reldir, 0);
272  $newdir = dol_osencode($dir);
273 
274  // Check if directory exists (we do not use dol_is_dir to avoid loading files.lib.php at each call)
275  if (!is_dir($newdir)) {
276  continue;
277  }
278 
279  $handle = opendir($newdir);
280  if (is_resource($handle)) {
281  while (($file = readdir($handle)) !== false) {
282  $reg = array();
283  if (is_readable($newdir.'/'.$file) && preg_match('/^interface_([0-9]+)_([^_]+)_(.+)\.class\.php/', $file, $reg)) {
284  if (preg_match('/\.back$/', $file)) {
285  continue;
286  }
287 
288  $part1 = $reg[1];
289  $part2 = $reg[2];
290  $part3 = $reg[3];
291 
292  $modName = 'Interface'.ucfirst($reg[3]);
293  //print "file=$file"; print "modName=$modName"; exit;
294  if (in_array($modName, $modules)) {
295  $langs->load("errors");
296  print '<div class="error">'.$langs->trans("Error").' : '.$langs->trans("ErrorDuplicateTrigger", $modName, "/htdocs/core/triggers/").'</div>';
297  } else {
298  include_once $newdir.'/'.$file;
299  }
300 
301  $files[$i] = $file;
302  $fullpath[$i] = $dir.'/'.$file;
303  $relpath[$i] = preg_replace('/^\//', '', $reldir).'/'.$file;
304  $iscoreorexternal[$i] = ($reldir == '/core/triggers/' ? 'internal' : 'external');
305  $modules[$i] = $modName;
306  $orders[$i] = $part1.'_'.$part2.'_'.$part3; // Set sort criteria value
307 
308  $i++;
309  }
310  }
311  closedir($handle);
312  }
313  }
314 
315  asort($orders, SORT_NATURAL);
316 
317  $triggers = array();
318  $j = 0;
319 
320  // Loop on each trigger
321  foreach ($orders as $key => $value) {
322  $modName = $modules[$key];
323  if (empty($modName)) {
324  continue;
325  }
326 
327  if (!class_exists($modName)) {
328  print 'Error: A trigger file was found but its class "'.$modName.'" was not found.'."<br>\n";
329  continue;
330  }
331 
332  $text = '';
333 
334  try {
335  $objMod = new $modName($db);
336 
337  if (is_subclass_of($objMod, 'DolibarrTriggers')) {
338  // Define disabledbyname and disabledbymodule
339  $disabledbyname = 0;
340  $disabledbymodule = 1;
341  $module = '';
342 
343  // Check if trigger file is disabled by name
344  if (preg_match('/NORUN$/i', $files[$key])) {
345  $disabledbyname = 1;
346  }
347  // Check if trigger file is for a particular module
348  if (preg_match('/^interface_([0-9]+)_([^_]+)_(.+)\.class\.php/i', $files[$key], $reg)) {
349  $module = preg_replace('/^mod/i', '', $reg[2]);
350  if (strtolower($module) == 'all') {
351  $disabledbymodule = 0;
352  } elseif (!isModEnabled(strtolower($module))) {
353  $disabledbymodule = 2;
354  }
355  $triggers[$j]['module'] = strtolower($module);
356  }
357 
358  // We set info of modules
359  $triggers[$j]['picto'] = (!empty($objMod->picto)) ? img_object('', $objMod->picto, 'class="valignmiddle pictomodule "') : img_object('', 'generic', 'class="valignmiddle pictomodule "');
360  $triggers[$j]['file'] = $files[$key];
361  $triggers[$j]['fullpath'] = $fullpath[$key];
362  $triggers[$j]['relpath'] = $relpath[$key];
363  $triggers[$j]['iscoreorexternal'] = $iscoreorexternal[$key];
364  $triggers[$j]['version'] = $objMod->getVersion();
365  $triggers[$j]['status'] = img_picto($langs->trans("Active"), 'tick');
366  if ($disabledbyname > 0 || $disabledbymodule > 1) {
367  $triggers[$j]['status'] = '';
368  }
369 
370  $text = '<b>'.$langs->trans("Description").':</b><br>';
371  $text .= $objMod->getDesc().'<br>';
372  $text .= '<br><b>'.$langs->trans("Status").':</b><br>';
373  if ($disabledbyname == 1) {
374  $text .= $langs->trans("TriggerDisabledByName").'<br>';
375  if ($disabledbymodule == 2) {
376  $text .= $langs->trans("TriggerDisabledAsModuleDisabled", $module).'<br>';
377  }
378  } else {
379  if ($disabledbymodule == 0) {
380  $text .= $langs->trans("TriggerAlwaysActive").'<br>';
381  }
382  if ($disabledbymodule == 1) {
383  $text .= $langs->trans("TriggerActiveAsModuleActive", $module).'<br>';
384  }
385  if ($disabledbymodule == 2) {
386  $text .= $langs->trans("TriggerDisabledAsModuleDisabled", $module).'<br>';
387  }
388  }
389  } else {
390  $triggers[$j]['picto'] = (!empty($objMod->picto)) ? img_object('', $objMod->picto, 'class="valignmiddle pictomodule "') : img_object('', 'generic', 'class="valignmiddle pictomodule "');
391  $triggers[$j]['file'] = $files[$key];
392  $triggers[$j]['fullpath'] = $fullpath[$key];
393  $triggers[$j]['relpath'] = $relpath[$key];
394  $triggers[$j]['status'] = img_picto('Error: Trigger '.$modName.' does not extends DolibarrTriggers', 'warning');
395 
396  //print 'Error: Trigger '.$modName.' does not extends DolibarrTriggers<br>';
397  $text = 'Error: Trigger '.$modName.' does not extends DolibarrTriggers';
398  }
399  } catch (Exception $e) {
400  print $e->getMessage();
401  }
402 
403  $triggers[$j]['info'] = $text;
404  $j++;
405  }
406  return $triggers;
407  }
408 }
if($user->socid > 0) if(! $user->hasRight('accounting', 'chartofaccount')) $object
Definition: card.php:58
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:50
img_object($titlealt, $picto, $moreatt='', $pictoisfullpath=0, $srconly=0, $notitle=0)
Show a picto called object_picto (generic function)
img_picto($titlealt, $picto, $moreatt='', $pictoisfullpath=0, $srconly=0, $notitle=0, $alt='', $morecss='', $marginleftonlyshort=2)
Show picto whatever it's its name (generic function)
dol_osencode($str)
Return a string encoded into OS filesystem encoding.
getDolGlobalInt($key, $default=0)
Return a Dolibarr global constant int value.
dol_buildpath($path, $type=0, $returnemptyifnotfound=0)
Return path of url or filesystem.
isModEnabled($module)
Is Dolibarr module enabled.
dol_syslog($message, $level=LOG_INFO, $ident=0, $suffixinfilename='', $restricttologhandler='', $logcontext=null)
Write log message into outputs.