2005-04-21 20:49:15 +02:00
< ? php
2009-01-21 15:09:42 +01:00
/* Copyright ( C ) 2005 - 2009 Laurent Destailleur < eldy @ users . sourceforge . net >
2007-10-13 01:48:20 +02:00
* Copyright ( C ) 2006 Rodolphe Quiedeville < rodolphe @ quiedeville . org >
2018-10-27 14:43:12 +02:00
* Copyright ( C ) 2010 Regis Houssin < regis . houssin @ inodbox . com >
2024-03-16 02:09:23 +01:00
* Copyright ( C ) 2024 MDW < mdeweerd @ users . noreply . github . com >
2005-04-21 20:49:15 +02:00
*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License as published by
2013-01-16 15:36:08 +01:00
* the Free Software Foundation ; either version 3 of the License , or
2005-04-21 20:49:15 +02:00
* ( at your option ) any later version .
*
* This program is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU General Public License for more details .
*
* You should have received a copy of the GNU General Public License
2019-09-23 21:55:30 +02:00
* along with this program . If not , see < https :// www . gnu . org / licenses />.
2005-04-21 20:49:15 +02:00
*/
/**
2010-11-22 10:18:53 +01:00
* \file htdocs / core / class / interfaces . class . php
* \ingroup core
2024-01-13 19:48:20 +01:00
* \brief Fichier de la class de gestion des triggers
2010-04-02 11:59:18 +02:00
*/
2005-04-21 20:49:15 +02:00
2014-10-11 18:27:30 +02:00
require_once DOL_DOCUMENT_ROOT . '/core/triggers/dolibarrtriggers.class.php' ;
2005-04-21 20:49:15 +02:00
/**
2012-01-27 16:10:44 +01:00
* Class to manage triggers
2010-04-02 11:59:18 +02:00
*/
2010-04-22 00:14:11 +02:00
class Interfaces
2005-04-21 20:49:15 +02:00
{
2020-10-31 14:32:18 +01:00
/**
* @ var DoliDB Database handler .
*/
public $db ;
public $dir ; // Directory with all core and external triggers files
2024-02-10 21:25:26 +01:00
/**
* @ var string Last module name in error
*/
public $lastmoduleerror ;
2020-10-31 14:32:18 +01:00
/**
* @ var string [] Error codes ( or messages )
*/
public $errors = array ();
/**
* Constructor
*
* @ param DoliDB $db Database handler
*/
public function __construct ( $db )
{
$this -> db = $db ;
}
// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
/**
* Function called when a Dolibarr business event occurs
* This function call all qualified triggers .
*
* @ param string $action Trigger event code
2024-01-13 19:48:20 +01:00
* @ param object $object Object concerned . Some context information may also be provided into array property object -> context .
* @ param User $user Object user
* @ param Translate $langs Object lang
* @ param Conf $conf Object conf
2020-10-31 14:32:18 +01:00
* @ return int Nb of triggers ran if no error , - Nb of triggers with errors otherwise .
*/
public function run_triggers ( $action , $object , $user , $langs , $conf )
{
// phpcs:enable
2023-09-26 13:04:03 +02:00
if ( getDolGlobalInt ( 'MAIN_TRIGGER_DEBUG' )) {
// This his too much verbose, enabled if const enabled only
dol_syslog ( get_class ( $this ) . " ::run_triggers action= " . $action . " Launch run_triggers " , LOG_DEBUG );
}
2020-10-31 14:32:18 +01:00
// Check parameters
2021-02-23 22:03:23 +01:00
if ( ! is_object ( $object ) || ! is_object ( $conf )) { // Error
2024-03-16 02:09:23 +01:00
$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 ));
2023-06-09 14:29:41 +02:00
dol_syslog ( get_class ( $this ) . '::run_triggers ' . $error , LOG_ERR );
$this -> errors [] = $error ;
2020-10-31 14:32:18 +01:00
return - 1 ;
}
2021-02-23 22:03:23 +01:00
if ( ! is_object ( $langs )) { // Warning
2024-03-16 02:09:23 +01:00
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 );
2020-10-31 14:32:18 +01:00
}
2023-10-16 20:55:56 +02:00
if ( ! is_object ( $user )) { // Warning
2024-03-16 02:09:23 +01:00
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 );
2020-10-31 14:32:18 +01:00
$user = new User ( $this -> db );
}
$nbfile = $nbtotal = $nbok = $nbko = 0 ;
2024-02-10 21:25:26 +01:00
$this -> lastmoduleerror = '' ;
2020-10-31 14:32:18 +01:00
$files = array ();
$modules = array ();
$orders = array ();
$i = 0 ;
$dirtriggers = array_merge ( array ( '/core/triggers' ), $conf -> modules_parts [ 'triggers' ]);
2021-02-23 22:03:23 +01:00
foreach ( $dirtriggers as $reldir ) {
2020-10-31 14:32:18 +01:00
$dir = dol_buildpath ( $reldir , 0 );
$newdir = dol_osencode ( $dir );
//print "xx".$dir;exit;
// Check if directory exists (we do not use dol_is_dir to avoir loading files.lib.php at each call)
2021-02-23 22:03:23 +01:00
if ( ! is_dir ( $newdir )) {
continue ;
}
2020-10-31 14:32:18 +01:00
$handle = opendir ( $newdir );
2021-02-23 22:03:23 +01:00
if ( is_resource ( $handle )) {
2020-10-31 14:32:18 +01:00
$fullpathfiles = array ();
2021-02-23 22:03:23 +01:00
while (( $file = readdir ( $handle )) !== false ) {
2020-10-31 14:32:18 +01:00
$reg = array ();
2021-02-23 22:03:23 +01:00
if ( is_readable ( $newdir . " / " . $file ) && preg_match ( '/^interface_([0-9]+)_([^_]+)_(.+)\.class\.php$/i' , $file , $reg )) {
2020-10-31 14:32:18 +01:00
$part1 = $reg [ 1 ];
$part2 = $reg [ 2 ];
$part3 = $reg [ 3 ];
$nbfile ++ ;
// Check if trigger file is disabled by name
2021-02-23 22:03:23 +01:00
if ( preg_match ( '/NORUN$/i' , $file )) {
continue ;
}
2020-10-31 14:32:18 +01:00
// Check if trigger file is for a particular module
$qualified = true ;
2021-02-23 22:03:23 +01:00
if ( strtolower ( $reg [ 2 ]) != 'all' ) {
2020-10-31 14:32:18 +01:00
$module = preg_replace ( '/^mod/i' , '' , $reg [ 2 ]);
2023-10-16 20:55:56 +02:00
if ( ! isModEnabled ( strtolower ( $module ))) {
2021-02-23 22:03:23 +01:00
$qualified = false ;
}
2020-10-31 14:32:18 +01:00
}
2021-02-23 22:03:23 +01:00
if ( ! $qualified ) {
2020-10-31 14:32:18 +01:00
//dol_syslog(get_class($this)."::run_triggers action=".$action." Triggers for file '".$file."' need module to be enabled", LOG_DEBUG);
continue ;
}
$modName = " Interface " . ucfirst ( $reg [ 3 ]);
//print "file=$file - modName=$modName\n";
2021-02-23 22:03:23 +01:00
if ( in_array ( $modName , $modules )) { // $modules = list of modName already loaded
2020-10-31 14:32:18 +01:00
$langs -> load ( " errors " );
dol_syslog ( get_class ( $this ) . " ::run_triggers action= " . $action . " " . $langs -> trans ( " ErrorDuplicateTrigger " , $newdir . " / " . $file , $fullpathfiles [ $modName ]), LOG_WARNING );
continue ;
}
try {
//print 'Todo for '.$modName." : ".$newdir.'/'.$file."\n";
include_once $newdir . '/' . $file ;
//print 'Done for '.$modName."\n";
2021-02-23 22:03:23 +01:00
} catch ( Exception $e ) {
2020-10-31 14:32:18 +01:00
dol_syslog ( 'ko for ' . $modName . " " . $e -> getMessage () . " \n " , LOG_ERR );
}
$modules [ $i ] = $modName ;
$files [ $i ] = $file ;
$fullpathfiles [ $modName ] = $newdir . '/' . $file ;
$orders [ $i ] = $part1 . '_' . $part2 . '_' . $part3 ; // Set sort criteria value
$i ++ ;
}
}
2019-10-30 14:46:46 +01:00
2019-11-12 12:05:06 +01:00
closedir ( $handle );
2020-10-31 14:32:18 +01:00
}
}
2023-11-28 22:31:36 +01:00
asort ( $orders , SORT_NATURAL );
2020-10-31 14:32:18 +01:00
// Loop on each trigger
2021-02-23 22:03:23 +01:00
foreach ( $orders as $key => $value ) {
2020-10-31 14:32:18 +01:00
$modName = $modules [ $key ];
2021-02-23 22:03:23 +01:00
if ( empty ( $modName )) {
continue ;
}
2020-10-31 14:32:18 +01:00
$objMod = new $modName ( $this -> db );
2021-02-23 22:03:23 +01:00
if ( $objMod ) {
2021-01-14 19:09:57 +01:00
$dblevelbefore = $this -> db -> transaction_opened ;
2020-10-31 14:32:18 +01:00
$result = 0 ;
2021-01-14 19:09:57 +01:00
if ( method_exists ( $objMod , 'runTrigger' )) { // New method to implement
2020-10-31 14:32:18 +01:00
//dol_syslog(get_class($this)."::run_triggers action=".$action." Launch runTrigger for file '".$files[$key]."'", LOG_DEBUG);
$result = $objMod -> runTrigger ( $action , $object , $user , $langs , $conf );
2021-01-14 19:09:57 +01:00
} elseif ( method_exists ( $objMod , 'run_trigger' )) { // Deprecated method
2020-10-31 14:32:18 +01:00
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 );
$result = $objMod -> run_trigger ( $action , $object , $user , $langs , $conf );
} else {
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 );
}
2021-01-14 19:09:57 +01:00
$dblevelafter = $this -> db -> transaction_opened ;
if ( $dblevelbefore != $dblevelafter ) {
$errormessage = " Error, the balance begin/close of db transactions has been broken into trigger " . $modName . " with action= " . $action . " before= " . $dblevelbefore . " after= " . $dblevelafter ;
$this -> errors [] = $errormessage ;
dol_syslog ( $errormessage , LOG_ERR );
$result = - 1 ;
}
2021-02-23 22:03:23 +01:00
if ( $result > 0 ) {
2020-10-31 14:32:18 +01:00
// Action OK
$nbtotal ++ ;
$nbok ++ ;
}
2021-02-23 22:03:23 +01:00
if ( $result == 0 ) {
2020-10-31 14:32:18 +01:00
// Aucune action faite
$nbtotal ++ ;
}
2021-02-23 22:03:23 +01:00
if ( $result < 0 ) {
2020-10-31 14:32:18 +01:00
// Action KO
2020-11-27 15:45:12 +01:00
//dol_syslog("Error in trigger ".$action." - result = ".$result." - Nb of error string returned = ".count($objMod->errors), LOG_ERR);
2020-10-31 14:32:18 +01:00
$nbtotal ++ ;
$nbko ++ ;
2024-02-10 21:25:26 +01:00
$this -> lastmoduleerror = $modName ;
2021-02-23 22:03:23 +01:00
if ( ! empty ( $objMod -> errors )) {
$this -> errors = array_merge ( $this -> errors , $objMod -> errors );
} elseif ( ! empty ( $objMod -> error )) {
$this -> errors [] = $objMod -> error ;
}
2020-10-31 14:32:18 +01:00
//dol_syslog("Error in trigger ".$action." - Nb of error string returned = ".count($this->errors), LOG_ERR);
}
} else {
dol_syslog ( get_class ( $this ) . " ::run_triggers action= " . $action . " Failed to instantiate trigger for file ' " . $files [ $key ] . " ' " , LOG_ERR );
}
}
2021-02-23 22:03:23 +01:00
if ( $nbko ) {
2024-02-10 21:25:26 +01:00
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 );
2020-10-31 14:32:18 +01:00
return - $nbko ;
} else {
//dol_syslog(get_class($this)."::run_triggers Files found: ".$nbfile.", Files launched: ".$nbtotal.", Done: ".$nbok.", Failed: ".$nbko, LOG_DEBUG);
return $nbok ;
}
}
/**
* Return list of triggers . Function used by admin page htdoc / admin / triggers .
* List is sorted by trigger filename so by priority to run .
*
* @ param array $forcedirtriggers null = All default directories . This parameter is used by modulebuilder module only .
* @ return array Array list of triggers
*/
public function getTriggersList ( $forcedirtriggers = null )
{
global $conf , $langs , $db ;
$files = array ();
$fullpath = array ();
$relpath = array ();
$iscoreorexternal = array ();
$modules = array ();
$orders = array ();
$i = 0 ;
$dirtriggers = array_merge ( array ( '/core/triggers/' ), $conf -> modules_parts [ 'triggers' ]);
2021-02-23 22:03:23 +01:00
if ( is_array ( $forcedirtriggers )) {
2020-10-31 14:32:18 +01:00
$dirtriggers = $forcedirtriggers ;
}
2021-02-23 22:03:23 +01:00
foreach ( $dirtriggers as $reldir ) {
2020-10-31 14:32:18 +01:00
$dir = dol_buildpath ( $reldir , 0 );
$newdir = dol_osencode ( $dir );
// Check if directory exists (we do not use dol_is_dir to avoid loading files.lib.php at each call)
2021-02-23 22:03:23 +01:00
if ( ! is_dir ( $newdir )) {
continue ;
}
2020-10-31 14:32:18 +01:00
$handle = opendir ( $newdir );
2021-02-23 22:03:23 +01:00
if ( is_resource ( $handle )) {
while (( $file = readdir ( $handle )) !== false ) {
2020-10-31 14:32:18 +01:00
$reg = array ();
2021-02-23 22:03:23 +01:00
if ( is_readable ( $newdir . '/' . $file ) && preg_match ( '/^interface_([0-9]+)_([^_]+)_(.+)\.class\.php/' , $file , $reg )) {
if ( preg_match ( '/\.back$/' , $file )) {
continue ;
}
2020-10-31 14:32:18 +01:00
$part1 = $reg [ 1 ];
$part2 = $reg [ 2 ];
$part3 = $reg [ 3 ];
$modName = 'Interface' . ucfirst ( $reg [ 3 ]);
//print "file=$file"; print "modName=$modName"; exit;
2021-02-23 22:03:23 +01:00
if ( in_array ( $modName , $modules )) {
2020-10-31 14:32:18 +01:00
$langs -> load ( " errors " );
print '<div class="error">' . $langs -> trans ( " Error " ) . ' : ' . $langs -> trans ( " ErrorDuplicateTrigger " , $modName , " /htdocs/core/triggers/ " ) . '</div>' ;
} else {
include_once $newdir . '/' . $file ;
}
$files [ $i ] = $file ;
$fullpath [ $i ] = $dir . '/' . $file ;
$relpath [ $i ] = preg_replace ( '/^\//' , '' , $reldir ) . '/' . $file ;
$iscoreorexternal [ $i ] = ( $reldir == '/core/triggers/' ? 'internal' : 'external' );
$modules [ $i ] = $modName ;
$orders [ $i ] = $part1 . '_' . $part2 . '_' . $part3 ; // Set sort criteria value
$i ++ ;
}
}
closedir ( $handle );
}
}
2023-11-28 22:31:36 +01:00
asort ( $orders , SORT_NATURAL );
2020-10-31 14:32:18 +01:00
$triggers = array ();
$j = 0 ;
// Loop on each trigger
2021-02-23 22:03:23 +01:00
foreach ( $orders as $key => $value ) {
2020-10-31 14:32:18 +01:00
$modName = $modules [ $key ];
2021-02-23 22:03:23 +01:00
if ( empty ( $modName )) {
continue ;
}
2020-10-31 14:32:18 +01:00
2021-02-23 22:03:23 +01:00
if ( ! class_exists ( $modName )) {
2020-10-31 14:32:18 +01:00
print 'Error: A trigger file was found but its class "' . $modName . '" was not found.' . " <br> \n " ;
continue ;
}
$text = '' ;
try {
$objMod = new $modName ( $db );
2021-02-23 22:03:23 +01:00
if ( is_subclass_of ( $objMod , 'DolibarrTriggers' )) {
2020-10-31 14:32:18 +01:00
// Define disabledbyname and disabledbymodule
$disabledbyname = 0 ;
$disabledbymodule = 1 ;
$module = '' ;
// Check if trigger file is disabled by name
2021-02-23 22:03:23 +01:00
if ( preg_match ( '/NORUN$/i' , $files [ $key ])) {
$disabledbyname = 1 ;
}
2020-10-31 14:32:18 +01:00
// Check if trigger file is for a particular module
2021-02-23 22:03:23 +01:00
if ( preg_match ( '/^interface_([0-9]+)_([^_]+)_(.+)\.class\.php/i' , $files [ $key ], $reg )) {
2020-10-31 14:32:18 +01:00
$module = preg_replace ( '/^mod/i' , '' , $reg [ 2 ]);
2021-02-23 22:03:23 +01:00
if ( strtolower ( $module ) == 'all' ) {
$disabledbymodule = 0 ;
2023-11-28 22:31:36 +01:00
} elseif ( ! isModEnabled ( strtolower ( $module ))) {
2021-02-23 22:03:23 +01:00
$disabledbymodule = 2 ;
}
2020-10-31 14:32:18 +01:00
$triggers [ $j ][ 'module' ] = strtolower ( $module );
}
// We set info of modules
2021-03-21 18:20:37 +01:00
$triggers [ $j ][ 'picto' ] = ( ! empty ( $objMod -> picto )) ? img_object ( '' , $objMod -> picto , 'class="valignmiddle pictomodule "' ) : img_object ( '' , 'generic' , 'class="valignmiddle pictomodule "' );
2020-10-31 14:32:18 +01:00
$triggers [ $j ][ 'file' ] = $files [ $key ];
$triggers [ $j ][ 'fullpath' ] = $fullpath [ $key ];
$triggers [ $j ][ 'relpath' ] = $relpath [ $key ];
$triggers [ $j ][ 'iscoreorexternal' ] = $iscoreorexternal [ $key ];
$triggers [ $j ][ 'version' ] = $objMod -> getVersion ();
$triggers [ $j ][ 'status' ] = img_picto ( $langs -> trans ( " Active " ), 'tick' );
2021-02-23 22:03:23 +01:00
if ( $disabledbyname > 0 || $disabledbymodule > 1 ) {
$triggers [ $j ][ 'status' ] = '' ;
}
2020-10-31 14:32:18 +01:00
$text = '<b>' . $langs -> trans ( " Description " ) . ':</b><br>' ;
$text .= $objMod -> getDesc () . '<br>' ;
$text .= '<br><b>' . $langs -> trans ( " Status " ) . ':</b><br>' ;
2021-02-23 22:03:23 +01:00
if ( $disabledbyname == 1 ) {
2020-10-31 14:32:18 +01:00
$text .= $langs -> trans ( " TriggerDisabledByName " ) . '<br>' ;
2021-02-23 22:03:23 +01:00
if ( $disabledbymodule == 2 ) {
$text .= $langs -> trans ( " TriggerDisabledAsModuleDisabled " , $module ) . '<br>' ;
}
2020-10-31 14:32:18 +01:00
} else {
2021-02-23 22:03:23 +01:00
if ( $disabledbymodule == 0 ) {
$text .= $langs -> trans ( " TriggerAlwaysActive " ) . '<br>' ;
}
if ( $disabledbymodule == 1 ) {
$text .= $langs -> trans ( " TriggerActiveAsModuleActive " , $module ) . '<br>' ;
}
if ( $disabledbymodule == 2 ) {
$text .= $langs -> trans ( " TriggerDisabledAsModuleDisabled " , $module ) . '<br>' ;
}
2020-10-31 14:32:18 +01:00
}
} else {
2021-03-21 18:20:37 +01:00
$triggers [ $j ][ 'picto' ] = ( ! empty ( $objMod -> picto )) ? img_object ( '' , $objMod -> picto , 'class="valignmiddle pictomodule "' ) : img_object ( '' , 'generic' , 'class="valignmiddle pictomodule "' );
$triggers [ $j ][ 'file' ] = $files [ $key ];
$triggers [ $j ][ 'fullpath' ] = $fullpath [ $key ];
$triggers [ $j ][ 'relpath' ] = $relpath [ $key ];
$triggers [ $j ][ 'status' ] = img_picto ( 'Error: Trigger ' . $modName . ' does not extends DolibarrTriggers' , 'warning' );
//print 'Error: Trigger '.$modName.' does not extends DolibarrTriggers<br>';
$text = 'Error: Trigger ' . $modName . ' does not extends DolibarrTriggers' ;
2020-10-31 14:32:18 +01:00
}
} catch ( Exception $e ) {
print $e -> getMessage ();
}
$triggers [ $j ][ 'info' ] = $text ;
$j ++ ;
}
return $triggers ;
}
2005-04-21 20:49:15 +02:00
}