dolibarr 21.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 * 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
27require_once DOL_DOCUMENT_ROOT.'/core/triggers/dolibarrtriggers.class.php';
28
29
34{
38 public $db;
39
43 public $dir; // Directory with all core and external triggers files
44
48 public $lastmoduleerror;
49
53 public $errors = array();
54
60 public function __construct($db)
61 {
62 $this->db = $db;
63 }
64
65 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
77 public function run_triggers($action, $object, $user, $langs, $conf)
78 {
79 // phpcs:enable
80
81 if (getDolGlobalInt('MAIN_TRIGGER_DEBUG')) {
82 // This his too much verbose, enabled if const enabled only
83 dol_syslog(get_class($this)."::run_triggers action=".$action." Launch run_triggers", LOG_DEBUG);
84 }
85
86 // Check parameters
87 if (!is_object($object) || !is_object($conf)) { // Error
88 $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));
89 dol_syslog(get_class($this).'::run_triggers '.$error, LOG_ERR);
90 $this->errors[] = $error;
91 return -1;
92 }
93 if (!is_object($langs)) { // 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 }
96 if (!is_object($user)) { // Warning
97 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);
98 $user = new User($this->db);
99 }
100
101 $nbfile = $nbtotal = $nbok = $nbko = 0;
102 $this->lastmoduleerror = '';
103
104 $files = array();
105 $modules = array();
106 $orders = array();
107 $i = 0;
108
109 $dirtriggers = array_merge(array('/core/triggers'), $conf->modules_parts['triggers']);
110 foreach ($dirtriggers as $reldir) {
111 $dir = dol_buildpath($reldir, 0);
112 $newdir = dol_osencode($dir);
113 //print "xx".$dir;exit;
114
115 // Check if directory exists (we do not use dol_is_dir to avoir loading files.lib.php at each call)
116 if (!is_dir($newdir)) {
117 continue;
118 }
119
120 $handle = opendir($newdir);
121 if (is_resource($handle)) {
122 $fullpathfiles = array();
123 '@phan-var-force array<string,string> $fullpathfiles';
124 while (($file = readdir($handle)) !== false) {
125 $reg = array();
126 if (is_readable($newdir."/".$file) && preg_match('/^interface_([0-9]+)_([^_]+)_(.+)\.class\.php$/i', $file, $reg)) {
127 $part1 = $reg[1];
128 $part2 = $reg[2];
129 $part3 = $reg[3];
130
131 $nbfile++;
132
133 // Check if trigger file is disabled by name
134 if (preg_match('/NORUN$/i', $file)) {
135 continue;
136 }
137 // Check if trigger file is for a particular module
138 $qualified = true;
139 if (strtolower($reg[2]) != 'all') {
140 $module = preg_replace('/^mod/i', '', $reg[2]);
141 if (!isModEnabled(strtolower($module))) {
142 $qualified = false;
143 }
144 }
145
146 if (!$qualified) {
147 //dol_syslog(get_class($this)."::run_triggers action=".$action." Triggers for file '".$file."' need module to be enabled", LOG_DEBUG);
148 continue;
149 }
150
151 $modName = "Interface".ucfirst($reg[3]);
152 //print "file=$file - modName=$modName\n";
153 if (array_key_exists($modName, $fullpathfiles)) { // $modules = list of modName already loaded, fullpathfiles[$modName] is alsoset
154 $langs->load("errors");
155 dol_syslog(get_class($this)."::run_triggers action=".$action." ".$langs->trans("ErrorDuplicateTrigger", $newdir."/".$file, $fullpathfiles[$modName]), LOG_WARNING);
156 continue;
157 }
158
159 try {
160 //print 'Todo for '.$modName." : ".$newdir.'/'.$file."\n";
161 include_once $newdir.'/'.$file;
162 //print 'Done for '.$modName."\n";
163 } catch (Exception $e) {
164 dol_syslog('ko for '.$modName." ".$e->getMessage()."\n", LOG_ERR);
165 }
166
167 $modules[$i] = $modName;
168 $files[$i] = $file;
169 $fullpathfiles[$modName] = $newdir.'/'.$file;
170 $orders[$i] = $part1.'_'.$part2.'_'.$part3; // Set sort criteria value
171
172 $i++;
173 }
174 }
175
176 closedir($handle);
177 }
178 }
179
180 asort($orders, SORT_NATURAL);
181
182 // Loop on each trigger
183 foreach ($orders as $key => $value) {
184 $modName = $modules[$key];
185 if (empty($modName)) {
186 continue;
187 }
188 if (!class_exists($modName)) {
189 dol_syslog(get_class($this)."::run_triggers action=".$action." A trigger file was found with a name interfaces_*_*_".preg_replace('/^interface/', '', strtolower($modName)).".class.php, but the class ".$modName." seems to not exists even after the include of this interface file. Surely a bug in the trigger file or in its name.", LOG_ERR);
190 continue;
191 }
192
193 $objMod = new $modName($this->db);
194 '@phan-var-force DolibarrTriggers $objMod';
195 if ($objMod) {
196 $dblevelbefore = $this->db->transaction_opened;
197
198 $result = 0;
199
200 if (method_exists($objMod, 'runTrigger')) { // New method to implement
201 //dol_syslog(get_class($this)."::run_triggers action=".$action." Launch runTrigger for file '".$files[$key]."'", LOG_DEBUG);
202 $result = $objMod->runTrigger($action, $object, $user, $langs, $conf);
203 } elseif (method_exists($objMod, 'run_trigger')) { // Deprecated method
204 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);
205 // @phan-suppress-next-line PhanUndeclaredMethod
206 $result = $objMod->run_trigger($action, $object, $user, $langs, $conf);
207 } else {
208 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);
209 }
210
211 $dblevelafter = $this->db->transaction_opened;
212
213 if ($dblevelbefore != $dblevelafter) {
214 $errormessage = "Error, the balance begin/close of db transactions has been broken into trigger ".$modName." with action=".$action." before=".$dblevelbefore." after=".$dblevelafter;
215 $this->errors[] = $errormessage;
216 dol_syslog($errormessage, LOG_ERR);
217 $result = -1;
218 }
219
220 if ($result > 0) {
221 // Action OK
222 $nbtotal++;
223 $nbok++;
224 }
225 if ($result == 0) {
226 // Aucune action faite
227 $nbtotal++;
228 }
229 if ($result < 0) {
230 // Action KO
231 //dol_syslog("Error in trigger ".$action." - result = ".$result." - Nb of error string returned = ".count($objMod->errors), LOG_ERR);
232 $nbtotal++;
233 $nbko++;
234 $this->lastmoduleerror = $modName;
235 if (!empty($objMod->errors)) {
236 $this->errors = array_merge($this->errors, $objMod->errors);
237 } elseif (!empty($objMod->error)) {
238 $this->errors[] = $objMod->error;
239 }
240 //dol_syslog("Error in trigger ".$action." - Nb of error string returned = ".count($this->errors), LOG_ERR);
241 }
242 } else {
243 dol_syslog(get_class($this)."::run_triggers action=".$action." Failed to instantiate trigger for file '".$files[$key]."'", LOG_ERR);
244 }
245 }
246
247 if ($nbko) {
248 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);
249 return -$nbko;
250 } else {
251 //dol_syslog(get_class($this)."::run_triggers Files found: ".$nbfile.", Files launched: ".$nbtotal.", Done: ".$nbok.", Failed: ".$nbko, LOG_DEBUG);
252 return $nbok;
253 }
254 }
255
263 public function getTriggersList($forcedirtriggers = null)
264 {
265 global $conf, $langs, $db;
266
267 $files = array();
268 $fullpath = array();
269 $relpath = array();
270 $iscoreorexternal = array();
271 $modules = array();
272 $orders = array();
273 $i = 0;
274
275 $dirtriggers = array_merge(array('/core/triggers/'), $conf->modules_parts['triggers']);
276 if (is_array($forcedirtriggers)) {
277 $dirtriggers = $forcedirtriggers;
278 }
279
280 foreach ($dirtriggers as $reldir) {
281 $dir = dol_buildpath($reldir, 0);
282 $newdir = dol_osencode($dir);
283
284 // Check if directory exists (we do not use dol_is_dir to avoid loading files.lib.php at each call)
285 if (!is_dir($newdir)) {
286 continue;
287 }
288
289 $handle = opendir($newdir);
290 if (is_resource($handle)) {
291 while (($file = readdir($handle)) !== false) {
292 $reg = array();
293 if (is_readable($newdir.'/'.$file) && preg_match('/^interface_([0-9]+)_([^_]+)_(.+)\.class\.php/', $file, $reg)) {
294 if (preg_match('/\.back$/', $file)) {
295 continue;
296 }
297
298 $part1 = $reg[1];
299 $part2 = $reg[2];
300 $part3 = $reg[3];
301
302 $modName = 'Interface'.ucfirst($reg[3]);
303 //print "file=$file"; print "modName=$modName"; exit;
304 if (in_array($modName, $modules)) {
305 $langs->load("errors");
306 print '<div class="error">'.$langs->trans("Error").' : '.$langs->trans("ErrorDuplicateTrigger", $modName, "/htdocs/core/triggers/").'</div>';
307 } else {
308 include_once $newdir.'/'.$file;
309 }
310
311 $files[$i] = $file;
312 $fullpath[$i] = $dir.'/'.$file;
313 $relpath[$i] = preg_replace('/^\//', '', $reldir).'/'.$file;
314 $iscoreorexternal[$i] = ($reldir == '/core/triggers/' ? 'internal' : 'external');
315 $modules[$i] = $modName;
316 $orders[$i] = $part1.'_'.$part2.'_'.$part3; // Set sort criteria value
317
318 $i++;
319 }
320 }
321 closedir($handle);
322 }
323 }
324
325 asort($orders, SORT_NATURAL);
326
327 $triggers = array();
328 $j = 0;
329
330 // Loop on each trigger
331 foreach ($orders as $key => $value) {
332 $modName = $modules[$key];
333 if (empty($modName)) {
334 continue;
335 }
336
337 if (!class_exists($modName)) {
338 print 'Error: A trigger file was found but its class "'.$modName.'" was not found.'."<br>\n";
339 continue;
340 }
341
342 $text = '';
343
344 try {
345 $objMod = new $modName($db);
346 '@phan-var-force DolibarrTriggers $objMod';
347
348 if (is_subclass_of($objMod, 'DolibarrTriggers')) {
349 // Define disabledbyname and disabledbymodule
350 $disabledbyname = 0;
351 $disabledbymodule = 1;
352 $module = '';
353
354 // Check if trigger file is disabled by name
355 if (preg_match('/NORUN$/i', $files[$key])) {
356 $disabledbyname = 1;
357 }
358 // Check if trigger file is for a particular module
359 if (preg_match('/^interface_([0-9]+)_([^_]+)_(.+)\.class\.php/i', $files[$key], $reg)) {
360 $module = preg_replace('/^mod/i', '', $reg[2]);
361 if (strtolower($module) == 'all') {
362 $disabledbymodule = 0;
363 } elseif (!isModEnabled(strtolower($module))) {
364 $disabledbymodule = 2;
365 }
366 $triggers[$j]['module'] = strtolower($module);
367 }
368
369 // We set info of modules
370 $triggers[$j]['picto'] = (!empty($objMod->picto)) ? img_object('', $objMod->picto, 'class="valignmiddle pictomodule "') : img_object('', 'generic', 'class="valignmiddle pictomodule "');
371 $triggers[$j]['file'] = $files[$key];
372 $triggers[$j]['fullpath'] = $fullpath[$key];
373 $triggers[$j]['relpath'] = $relpath[$key];
374 $triggers[$j]['iscoreorexternal'] = $iscoreorexternal[$key];
375 $triggers[$j]['version'] = $objMod->getVersion();
376 $triggers[$j]['status'] = img_picto($langs->trans("Active"), 'tick');
377 if ($disabledbyname > 0 || $disabledbymodule > 1) {
378 $triggers[$j]['status'] = '';
379 }
380
381 $text = '<b>'.$langs->trans("Description").':</b><br>';
382 $text .= $objMod->getDesc().'<br>';
383 $text .= '<br><b>'.$langs->trans("Status").':</b><br>';
384 if ($disabledbyname == 1) {
385 $text .= $langs->trans("TriggerDisabledByName").'<br>';
386 if ($disabledbymodule == 2) {
387 $text .= $langs->trans("TriggerDisabledAsModuleDisabled", $module).'<br>';
388 }
389 } else {
390 if ($disabledbymodule == 0) {
391 $text .= $langs->trans("TriggerAlwaysActive").'<br>';
392 }
393 if ($disabledbymodule == 1) {
394 $text .= $langs->trans("TriggerActiveAsModuleActive", $module).'<br>';
395 }
396 if ($disabledbymodule == 2) {
397 $text .= $langs->trans("TriggerDisabledAsModuleDisabled", $module).'<br>';
398 }
399 }
400 } else {
401 $triggers[$j]['picto'] = (!empty($objMod->picto)) ? img_object('', $objMod->picto, 'class="valignmiddle pictomodule "') : img_object('', 'generic', 'class="valignmiddle pictomodule "');
402 $triggers[$j]['file'] = $files[$key];
403 $triggers[$j]['fullpath'] = $fullpath[$key];
404 $triggers[$j]['relpath'] = $relpath[$key];
405 $triggers[$j]['status'] = img_picto('Error: Trigger '.$modName.' does not extends DolibarrTriggers', 'warning');
406
407 //print 'Error: Trigger '.$modName.' does not extends DolibarrTriggers<br>';
408 $text = 'Error: Trigger '.$modName.' does not extend DolibarrTriggers';
409 }
410 } catch (Exception $e) {
411 print $e->getMessage();
412 }
413
414 $triggers[$j]['info'] = $text;
415 $j++;
416 }
417 return $triggers;
418 }
419}
if( $user->socid > 0) if(! $user->hasRight('accounting', 'chartofaccount')) $object
Definition card.php:66
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.
img_picto($titlealt, $picto, $moreatt='', $pictoisfullpath=0, $srconly=0, $notitle=0, $alt='', $morecss='', $marginleftonlyshort=2, $allowothertags=array())
Show picto whatever it's its name (generic function)
dol_osencode($str)
Return a string encoded into OS filesystem encoding.
img_object($titlealt, $picto, $moreatt='', $pictoisfullpath=0, $srconly=0, $notitle=0, $allowothertags=array())
Show a picto called object_picto (generic function)
getDolGlobalInt($key, $default=0)
Return a Dolibarr global constant int value.
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.
global $conf
The following vars must be defined: $type2label $form $conf, $lang, The following vars may also be de...
Definition member.php:79