dolibarr 18.0.6
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
26require_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 $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 '.$error, LOG_ERR);
81 $this->errors[] = $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.
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.