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