Source of file Scenario.php
Size: 48,726 Bytes - Last Modified: 2020-10-24T02:46:31+00:00
/home/travis/build/NextDom/nextdom-core/src/Model/Entity/Scenario.php
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489 | <?php /* This file is part of NextDom Software. * * NextDom is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * NextDom Software 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 * along with NextDom Software. If not, see <http://www.gnu.org/licenses/>. */ namespace NextDom\Model\Entity; use NextDom\Enums\CacheKey; use NextDom\Enums\Common; use NextDom\Enums\DateFormat; use NextDom\Enums\LogTarget; use NextDom\Enums\NextDomObj; use NextDom\Enums\ScenarioCache; use NextDom\Enums\ScenarioConf; use NextDom\Enums\ScenarioState; use NextDom\Exceptions\CoreException; use NextDom\Helpers\AuthentificationHelper; use NextDom\Helpers\DBHelper; use NextDom\Helpers\FileSystemHelper; use NextDom\Helpers\LogHelper; use NextDom\Helpers\NextDomHelper; use NextDom\Helpers\SystemHelper; use NextDom\Helpers\TimeLineHelper; use NextDom\Helpers\Utils; use NextDom\Managers\CacheManager; use NextDom\Managers\CmdManager; use NextDom\Managers\ConfigManager; use NextDom\Managers\CronManager; use NextDom\Managers\DataStoreManager; use NextDom\Managers\EqLogicManager; use NextDom\Managers\EventManager; use NextDom\Managers\InteractDefManager; use NextDom\Managers\JeeObjectManager; use NextDom\Managers\PlanHeaderManager; use NextDom\Managers\ScenarioElementManager; use NextDom\Managers\ScenarioManager; use NextDom\Managers\UserManager; use NextDom\Managers\ViewDataManager; use NextDom\Managers\ViewManager; use NextDom\Model\Entity\Parents\BaseEntity; use NextDom\Model\Entity\Parents\ConfigurationEntity; use NextDom\Model\Entity\Parents\DisplayEntity; use NextDom\Model\Entity\Parents\IsActiveEntity; use NextDom\Model\Entity\Parents\IsVisibleEntity; use NextDom\Model\Entity\Parents\NameEntity; use NextDom\Model\Entity\Parents\OrderEntity; use NextDom\Model\Entity\Parents\RefreshEntity; /** * Scenario * * ORM\Table(name="scenario", uniqueConstraints={@ORM\UniqueConstraint(name="name", columns={"group", "object_id", "name"})}, indexes={@ORM\Index(name="group", columns={"group"}), @ORM\Index(name="fk_scenario_object1_idx", columns={"object_id"}), @ORM\Index(name="trigger", columns={"trigger"}), @ORM\Index(name="mode", columns={"mode"}), @ORM\Index(name="modeTriger", columns={"mode", "trigger"})}) * ORM\Entity */ class Scenario extends BaseEntity { const TABLE_NAME = NextDomObj::SCENARIO; use ConfigurationEntity, DisplayEntity, IsVisibleEntity, IsActiveEntity, NameEntity, OrderEntity, RefreshEntity; /** * @var array */ protected static $_templateArray; /** * @var string * * @ORM\Column(name="group", type="string", length=127, nullable=true) */ protected $group = ''; /** * @var string * * @ORM\Column(name="mode", type="string", length=127, nullable=true) */ protected $mode; /** * @var string * * @ORM\Column(name="schedule", type="text", length=65535, nullable=true) */ protected $schedule; /** * @var string * * @ORM\Column(name="scenarioElement", type="text", length=65535, nullable=true) */ protected $scenarioElement; /** * @var mixed * * @ORM\Column(name="trigger", type="string", length=255, nullable=true) */ protected $trigger; /** * @var integer * * @ORM\Column(name="timeout", type="integer", nullable=true) */ protected $timeout = 0; /** * @var string * * @ORM\Column(name="description", type="text", length=65535, nullable=true) */ protected $description; /** * @var int * * ORM\ManyToOne(targetEntity="NextDom\Model\Entity\Object") * ORM\JoinColumns({ * ORM\JoinColumn(name="object_id", referencedColumnName="id") * }) */ protected $object_id; protected $_elements = []; protected $_changeState = false; protected $_realTrigger = ''; protected $_return = true; protected $_tags = []; protected $_do = true; protected $_log; public function __construct() { if ($this->order === null) { $this->order = 9999; } } /** * * @param mixed $event * @return boolean */ public function testTrigger($event): bool { foreach ($this->getTrigger() as $triggerTotest) { $triggerTotest = str_replace(['#variable(', ')#'], ['variable(', ')'], $triggerTotest); if ($triggerTotest == $event || (strpos($triggerTotest, $event) !== false && NextDomHelper::evaluateExpression($triggerTotest))) { return true; } } return false; } /** * * @return mixed */ public function getTrigger() { return Utils::isJson($this->trigger, [$this->trigger]); } /** * * @param $_trigger * @return $this * @throws \Exception */ public function setTrigger($_trigger) { if (is_array($_trigger)) { $_trigger = json_encode($_trigger, JSON_UNESCAPED_UNICODE); } $_trigger = CmdManager::humanReadableToCmd($_trigger); $this->updateChangeState($this->trigger, $_trigger); $this->trigger = $_trigger; return $this; } /** * Lance un scénario * * @param string $trigger * @param string $message * @param bool $forceSyncMode Force synchronous mode * @return boolean * @throws \Exception */ public function launch($trigger = '', $message = '', $forceSyncMode = false) { // Test if scenarios are enabled and if this scenario is activated if (!$this->isActive() || !ScenarioManager::isEnabled()) { return false; } $state = $this->getState(); $now = strtotime('now'); if ($state == ScenarioState::STARTING) { //Scénario bloqué en starting (Exemple de cause : trop de connexions à MySql, la connexion est refusée, le scénario plante) if ($now - $this->getCache(ScenarioCache::STARTING_TIME) > 5) { LogHelper::addError(LogTarget::SCENARIO, __('La dernière exécution du scénario ne s\'est pas lancée. Vérifiez le log scenario_execution, ainsi que le log du scénario') . " \"" . $this->getName() . "\"."); $this->setLog(__('La dernière exécution du scénario ne s\'est pas lancée. Vérifiez le log scenario_execution pour l\'exécution à ') . date(DateFormat::FULL, $this->getCache(ScenarioCache::STARTING_TIME)) . "."); $this->persistLog(); } //Retarde le lancement du scénario si une autre instance est déjà en cours de démarrage if (($this->getCache(ScenarioCache::STARTING_TIME) + 2) > $now) { $i = 0; while ($state == ScenarioState::STARTING) { sleep(1); $state = $this->getState(); $i++; if ($i > 10) { break; } } if ($state == ScenarioState::STARTING) { LogHelper::addError(LogTarget::SCENARIO, __('Trop d\'appel simultané du scénario, il ne peut-être exécuté une nouvelle fois. Il est conseillé de réduire les appels au scénario') . " \"" . $this->getName() . "\"."); $this->setLog(__('Trop d\'appel simultané du scénario, il ne peut-être exécuté une nouvelle fois. Il est conseillé de réduire les appels à ce scénario') . "."); $this->persistLog(); return false; } } } if ($state == ScenarioState::IN_PROGRESS && $this->getConfiguration('allowMultiInstance', 0) == 0) { return false; } $this->setCache([ScenarioCache::STARTING_TIME => $now, ScenarioCache::STATE => ScenarioState::STARTING]); // Test execution mode if ($this->getConfiguration(ScenarioConf::SYNC_MODE) == 1 || $forceSyncMode) { $this->setLog(__('Lancement du scénario en mode synchrone')); return $this->execute($trigger, $message); } else { if (count($this->getTags()) != '') { $this->setCache(ScenarioCache::TAGS, $this->getTags()); } $cmd = NEXTDOM_ROOT . '/src/Api/start_scenario.php '; $cmd .= ' scenario_id=' . $this->getId(); $cmd .= ' trigger=' . escapeshellarg($trigger); $cmd .= ' "message=' . escapeshellarg(Utils::sanitizeAccent($message)) . '"'; $cmd .= ' >> ' . LogHelper::getPathToLog('scenario_execution') . ' 2>&1 &'; SystemHelper::php($cmd); } return true; } /** * * @return mixed * @throws \Exception */ public function getState() { return $this->getCache(ScenarioCache::STATE); } /** * Get data from cache * * Data are stored in scenarioCacheAttr + Scenario_ID * * @param string $key Key find * @param mixed $defaultValue Default value returned if key is not found * * @return mixed * * @throws \Exception */ public function getCache($key = '', $defaultValue = '') { $scenarioCacheAttr = CacheManager::byKey(CacheKey::SCENARIO_CACHE_ATTR . $this->getId())->getValue(); return Utils::getJsonAttr($scenarioCacheAttr, $key, $defaultValue); } /** * @param $name * @return $this */ public function setName($name) { if ($name != $this->getName()) { $this->_changeState = true; $this->_changed = true; } $this->name = $name; return $this; } /** * * @param mixed $_partial * @return bool|null */ public function persistLog($_partial = false) { if ($this->getConfiguration(ScenarioConf::LOG_MODE, Common::DEFAULT) == 'none') { return null; } $path = NEXTDOM_LOG . '/scenarioLog'; if (!is_dir($path)) { mkdir($path); } $path .= '/scenario' . $this->getId() . '.log'; if ($_partial) { file_put_contents($path, $this->getLog(), FILE_APPEND); } else { file_put_contents($path, "------------------------------------\n" . $this->getLog(), FILE_APPEND); } return true; } /** * * @return mixed */ public function getLog() { return $this->_log; } /** * * @param string $log */ public function setLog($log) { $this->_log .= '[' . date(DateFormat::FULL) . '][SCENARIO] ' . $log . "\n"; if ($this->getConfiguration(ScenarioConf::LOG_MODE, 'default') == 'realtime') { $this->persistLog(true); $this->_log = ''; } } /** * Store data in cache * * Data are stored in scenarioCacheAttr + Scenario_ID * * @param string|array $key Key to store * @param mixed $valueToStore Value to store * * @throws \Exception */ public function setCache($key, $valueToStore = null) { CacheManager::set(CacheKey::SCENARIO_CACHE_ATTR . $this->getId(), Utils::setJsonAttr(CacheManager::byKey(CacheKey::SCENARIO_CACHE_ATTR . $this->getId())->getValue(), $key, $valueToStore)); } /** * Execute the scenario * * @param string $trigger * @param string $message * @return mixed * @throws \Exception */ public function execute($trigger = '', $message = '') { $tags = $this->getCache(ScenarioCache::TAGS); if ($tags != '') { $this->setTags($tags); $this->setCache(ScenarioCache::TAGS, ''); } if (!$this->isActive()) { $this->setLog(__('Impossible d\'exécuter le scénario : ') . $this->getHumanName() . __(' sur : ') . $message . __(' car il est désactivé')); $this->persistLog(); return null; } if ($this->getConfiguration(ScenarioConf::TIME_DEPENDENCY, 0) == 1 && !NextDomHelper::isDateOk()) { $this->setLog(__('Lancement du scénario : ') . $this->getHumanName() . __(' annulé car il utilise une condition de type temporelle et que la date système n\'est pas OK')); $this->persistLog(); return null; } $cmd = CmdManager::byId(str_replace('#', '', $trigger)); if (is_object($cmd)) { LogHelper::addInfo(LogTarget::EVENT, __('Exécution du scénario ') . $this->getHumanName() . __(' déclenché par : ') . $cmd->getHumanName()); if ($this->getConfiguration(ScenarioConf::TIMELINE_ENABLE)) { TimeLineHelper::addTimelineEvent([Common::TYPE => NextDomObj::SCENARIO, Common::ID => $this->getId(), Common::NAME => $this->getHumanName(true), Common::DATETIME => date(DateFormat::FULL), Common::TRIGGER => $cmd->getHumanName(true)]); } } else { LogHelper::addInfo(LogTarget::EVENT, __('Exécution du scénario ') . $this->getHumanName() . __(' déclenché par : ') . $trigger); if ($this->getConfiguration(ScenarioConf::TIMELINE_ENABLE)) { TimeLineHelper::addTimelineEvent([Common::TYPE => NextDomObj::SCENARIO, Common::ID => $this->getId(), Common::NAME => $this->getHumanName(true), Common::DATETIME => date(DateFormat::FULL), Common::TRIGGER => $trigger == Common::SCHEDULE ? 'programmation' : $trigger]); } } if (count($this->getTags()) == 0) { $this->setLog('Start : ' . trim($message, "'") . '.'); } else { $this->setLog('Start : ' . trim($message, "'") . '. Tags : ' . json_encode($this->getTags())); } $this->setLastLaunch(date(DateFormat::FULL)); $this->setState(ScenarioState::IN_PROGRESS); $this->setPID(getmypid()); $this->setRealTrigger($trigger); foreach ($this->getElement() as $element) { if (!$this->getDo()) { break; } $element->execute($this); } $this->setState('stop'); $this->setPID(); $this->setLog(__('Fin correcte du scénario')); $this->persistLog(); return $this->getReturn(); } /** * * @param bool $fullInfo * @param bool $withoutGroup * @param bool $withTag * @param bool $htmlRender * @param bool $withoutScenarioName * @param bool $showObjectName * @return string * @throws \Exception */ public function getHumanName($fullInfo = false, $withoutGroup = false, $withTag = false, $htmlRender = false, $withoutScenarioName = false, $showObjectName = true) { $noneText = __('Aucun'); $result = ''; if ($showObjectName && is_numeric($this->getObject_id()) && is_object($this->getObject())) { $linkedObject = $this->getObject(); if ($withTag) { if ($linkedObject->getDisplay('tagColor') != '') { $result .= '<span class="label label-config" style="background-color:' . $linkedObject->getDisplay('tagColor') . ' !important;color:' . $linkedObject->getDisplay('tagTextColor', 'white') . ' !important">' . $linkedObject->getName() . '</span>'; } else { $result .= '<span class="label label-primary label-sticker">' . $linkedObject->getName() . '</span>'; } } else { $result .= '[' . $linkedObject->getName() . ']'; } } else { if ($fullInfo) { if ($withTag) { $result .= '<span class="label label-default label-sticker">' . __('Aucun') . '</span>'; } else { $result .= '[' . $noneText . ']'; } } } if (!$withoutGroup) { if ($this->getGroup() != '') { $result .= '[' . $this->getGroup() . ']'; } else { if ($fullInfo) { $result .= '[' . $noneText . ']'; } } } if ($htmlRender) { $result .= '<p class="title">'; } if (!$withoutScenarioName) { if ($withTag) { $result .= $this->getName(); } else { $result .= '[' . $this->getName() . ']'; } } if ($htmlRender) { $result .= '</p>'; } return $result; } /** * * @param mixed $default * @return mixed */ public function getObject_id($default = null) { if ($this->object_id == '' || !is_numeric($this->object_id)) { return $default; } return $this->object_id; } /** * * @param mixed $object_id * @return $this */ public function setObject_id($object_id = null) { if ($object_id != $this->getObject_id()) { $this->_changeState = true; $this->_changed = true; } $this->object_id = (!is_numeric($object_id)) ? null : $object_id; return $this; } /** * * @return JeeObject * @throws \Exception */ public function getObject() { return JeeObjectManager::byId($this->object_id); } /** * @return string */ public function getGroup() { return $this->group; } /** * * @param mixed $group * @return $this */ public function setGroup($group) { if ($group != $this->getGroup()) { $this->_changeState = true; $this->_changed = true; } $this->group = $group; return $this; } /** * * @return array */ public function getTags() { return $this->_tags; } /** * * @param array $_tags * @return $this */ public function setTags($_tags) { $this->_tags = $_tags; return $this; } /** * * @param mixed $lastLaunch * @throws \Exception */ public function setLastLaunch($lastLaunch) { $this->setCache(ScenarioCache::LAST_LAUNCH, $lastLaunch); } /** * * @param mixed $state * @throws \Exception */ public function setState($state) { if ($this->getCache(ScenarioCache::STATE) != $state) { $this->emptyCacheWidget(); EventManager::add('scenario::update', ['scenario_id' => $this->getId(), Common::STATE => $state, ScenarioCache::LAST_LAUNCH => $this->getLastLaunch()]); } $this->setCache(ScenarioCache::STATE, $state); } /** * */ public function emptyCacheWidget() { $mc = CacheManager::byKey('scenarioHtmldashboard' . $this->getId()); $mc->remove(); $mc = CacheManager::byKey('scenarioHtmlmobile' . $this->getId()); $mc->remove(); $mc = CacheManager::byKey('scenarioHtmlmview' . $this->getId()); $mc->remove(); $mc = CacheManager::byKey('scenarioHtmldview' . $this->getId()); $mc->remove(); } /** * * @return mixed * @throws \Exception */ public function getLastLaunch() { return $this->getCache(ScenarioCache::LAST_LAUNCH); } /** * * @param mixed $pid * @throws \Exception */ public function setPID($pid = '') { $this->setCache('pid', $pid); } /** * * @return ScenarioElement[] * @throws \Exception */ public function getElement() { if (count($this->_elements) > 0) { return $this->_elements; } $result = []; $elements = $this->getScenarioElement(); $elementId = -1; if (is_array($elements)) { foreach ($this->getScenarioElement() as $elementId) { $element = ScenarioElementManager::byId($elementId); if (is_object($element)) { $result[] = $element; } } $this->_elements = $result; } elseif ($elements != '') { $element = ScenarioElementManager::byId($elementId); if (is_object($element)) { $result[] = $element; $this->_elements = $result; } } return $result; } /** * * @return mixed */ public function getScenarioElement() { return Utils::isJson($this->scenarioElement, $this->scenarioElement); } /** * * @param $_scenarioElement * @return $this */ public function setScenarioElement($_scenarioElement) { if (is_array($_scenarioElement)) { $_scenarioElement = json_encode($_scenarioElement, JSON_UNESCAPED_UNICODE); } $this->updateChangeState($this->scenarioElement, $_scenarioElement); $this->scenarioElement = $_scenarioElement; return $this; } /** * * @return bool */ public function getDo() { return $this->_do; } /** * Set the state of the scenario. * * @param bool $_do * @return $this */ public function setDo($_do) { $this->_do = $_do; return $this; } /** * * @return string */ public function getReturn() { return $this->_return; } /** * * @param mixed $_return * @return $this */ public function setReturn($_return) { $this->_return = $_return; return $this; } /** * * @param mixed $name * @return Scenario * @throws \Exception */ public function copy($name) { $scenarioCopy = clone $this; $scenarioCopy->setName($name); $scenarioCopy->setId(''); $scenario_element_list = []; foreach ($this->getElement() as $element) { $scenario_element_list[] = $element->copy(); } $scenarioCopy->setScenarioElement($scenario_element_list); $scenarioCopy->setLog(''); $scenarioCopy->save(); if (file_exists(NEXTDOM_LOG . '/scenarioLog/scenario' . $scenarioCopy->getId() . '.log')) { unlink(NEXTDOM_LOG . '/scenarioLog/scenario' . $scenarioCopy->getId() . '.log'); } return $scenarioCopy; } /** * @throws CoreException * @throws \ReflectionException */ public function save() { if ($this->getLastLaunch() == '' && ($this->getMode() == Common::SCHEDULE || $this->getMode() == 'all')) { $calculateScheduleDate = $this->calculateScheduleDate(); $this->setLastLaunch($calculateScheduleDate['prevDate']); } DBHelper::save($this); $this->emptyCacheWidget(); if ($this->_changeState) { $this->_changeState = false; EventManager::add('scenario::update', ['scenario_id' => $this->getId(), 'isActive' => $this->getIsActive(), Common::STATE => $this->getState(), ScenarioCache::LAST_LAUNCH => $this->getLastLaunch()]); } } /** * @return string */ public function getMode() { return $this->mode; } /** * @param $_mode * @return $this */ public function setMode($_mode) { $this->updateChangeState($this->mode, $_mode); $this->mode = $_mode; return $this; } /** * @return array */ public function calculateScheduleDate() { $calculatedDate = ['prevDate' => '', 'nextDate' => '']; if (is_array($this->getSchedule())) { $calculatedDate_tmp = ['prevDate' => '', 'nextDate' => '']; foreach ($this->getSchedule() as $schedule) { try { $c = new \Cron\CronExpression($schedule, new \Cron\FieldFactory); $calculatedDate_tmp['prevDate'] = $c->getPreviousRunDate()->format(DateFormat::FULL); $calculatedDate_tmp['nextDate'] = $c->getNextRunDate()->format(DateFormat::FULL); } catch (\Exception $exc) { } if ($calculatedDate['prevDate'] == '' || strtotime($calculatedDate['prevDate']) < strtotime($calculatedDate_tmp['prevDate'])) { $calculatedDate['prevDate'] = $calculatedDate_tmp['prevDate']; } if ($calculatedDate['nextDate'] == '' || strtotime($calculatedDate['nextDate']) > strtotime($calculatedDate_tmp['nextDate'])) { $calculatedDate['nextDate'] = $calculatedDate_tmp['nextDate']; } } } else { try { $c = new \Cron\CronExpression($this->getSchedule(), new \Cron\FieldFactory); $calculatedDate['prevDate'] = $c->getPreviousRunDate()->format(DateFormat::FULL); $calculatedDate['nextDate'] = $c->getNextRunDate()->format(DateFormat::FULL); } catch (\Exception $exc) { } } return $calculatedDate; } /** * @return bool|mixed|null */ public function getSchedule() { return Utils::isJson($this->schedule, $this->schedule); } /** * @param $_schedule * @return $this */ public function setSchedule($_schedule) { if (is_array($_schedule)) { $_schedule = json_encode($_schedule, JSON_UNESCAPED_UNICODE); } $this->updateChangeState($this->schedule, $_schedule); $this->schedule = $_schedule; return $this; } /** * * @param mixed $_version * @return string * @throws \Exception */ public function toHtml($_version) { if (!$this->hasRight('r')) { return ''; } $mc = CacheManager::byKey('scenarioHtml' . $_version . $this->getId()); if ($mc->getValue() != '') { return $mc->getValue(); } $version = NextDomHelper::versionAlias($_version); $replace = [ '#id#' => $this->getId(), '#state#' => $this->getState(), '#isActive#' => $this->getIsActive(), '#name#' => ($this->getDisplay(Common::NAME) != '') ? $this->getDisplay(Common::NAME) : $this->getHumanName(), '#shortname#' => ($this->getDisplay(Common::NAME) != '') ? $this->getDisplay(Common::NAME) : $this->getName(), '#treename#' => $this->getHumanName(false, false, false, false, true), '#icon#' => $this->getIcon(), '#lastLaunch#' => $this->getLastLaunch(), '#scenarioLink#' => $this->getLinkToConfiguration(), '#version#' => $_version, '#height#' => $this->getDisplay('height', 'auto'), '#width#' => $this->getDisplay('width', 'auto') ]; if (!isset(self::$_templateArray)) { self::$_templateArray = []; } if (!isset(self::$_templateArray[$version])) { self::$_templateArray[$version] = FileSystemHelper::getCoreTemplateFileContent($version, NextDomObj::SCENARIO, ''); } $html = Utils::templateReplace($replace, self::$_templateArray[$version]); CacheManager::set('scenarioHtml' . $version . $this->getId(), $html); return $html; } /** * * @param mixed $_right * @param User|null $_user * * @return boolean */ public function hasRight($_right, $_user = null) { if ($_user !== null) { if ($_user->getProfils() == 'admin' || $_user->getProfils() == 'user') { return true; } if (strpos($_user->getRights(NextDomObj::SCENARIO . $this->getId()), $_right) !== false) { return true; } return false; } if (!AuthentificationHelper::isConnected()) { return false; } if (AuthentificationHelper::isConnectedAsAdmin() || AuthentificationHelper::isConnectedWithRights('user')) { return true; } if (strpos(UserManager::getStoredUser()->getRights(NextDomObj::SCENARIO . $this->getId()), $_right) !== false) { return true; } return false; } /** * Get scenario icon * * @param bool $onlyClass * @return string * @throws \Exception */ public function getIcon($onlyClass = false) { if ($this->isActive()) { switch ($this->getState()) { case ScenarioState::STARTING: $cssClass = 'fas fa-hourglass-start'; break; case ScenarioState::IN_PROGRESS: $cssClass = 'fas fa-spinner fa-spin'; break; case ScenarioState::ERROR: $cssClass = 'fas fa-exclamation-triangle'; break; default: // User custom icon if (strpos($this->getDisplay('icon'), '<i') === 0) { // Icon stored with HTML if ($onlyClass) { $cssClass = trim(str_replace(['<i', 'class=', '"', '/>', '></i>'], '', $this->getDisplay('icon'))); } else { return $this->getDisplay('icon'); } } else { $cssClass = 'fas fa-check'; } break; } } else { $cssClass = 'fas fa-times'; } if ($onlyClass) { return $cssClass; } else { return '<i class="' . $cssClass . '"></i>'; } } /** * * @return string */ public function getLinkToConfiguration(): string { return 'index.php?v=d&p=scenario&id=' . $this->getId(); } /** * * @throws CoreException */ public function preSave() { if ($this->getTimeout() == '' || !is_numeric($this->getTimeout())) { $this->setTimeout(0); } if ($this->getName() == '') { throw new CoreException(__('Le nom du scénario ne peut pas être vide.')); } if (($this->getMode() == Common::SCHEDULE || $this->getMode() == 'all') && $this->getSchedule() == '') { throw new CoreException(__('Le scénario est de type programmé mais la programmation est vide')); } if ($this->getConfiguration('has_return', 0) == 1) { $this->setConfiguration(ScenarioConf::SYNC_MODE, 1); } if ($this->getConfiguration(ScenarioConf::LOG_MODE) == '') { $this->setConfiguration(ScenarioConf::LOG_MODE, 'default'); } } /** * * @param mixed $_default * @return mixed */ public function getTimeout($_default = 0) { if ($this->timeout == '' || !is_numeric($this->timeout)) { return $_default; } return $this->timeout; } /** * * @param $_timeout * @return $this */ public function setTimeout($_timeout) { if ($_timeout === '' || is_nan(intval($_timeout)) || $_timeout < 1) { $_timeout = 0; } $this->updateChangeState($this->timeout, $_timeout); $this->timeout = $_timeout; return $this; } /** * */ public function postInsert() { $this->setState('stop'); $this->setPID(); } /** * * @return mixed * @throws \Exception */ public function remove() { ViewDataManager::removeByTypeLinkId(NextDomObj::SCENARIO, $this->getId()); DataStoreManager::removeByTypeLinkId(NextDomObj::SCENARIO, $this->getId()); foreach ($this->getElement() as $element) { $element->remove(); } $this->emptyCacheWidget(); if (file_exists(NEXTDOM_LOG . '/scenarioLog/scenario' . $this->getId() . '.log')) { unlink(NEXTDOM_LOG . '/scenarioLog/scenario' . $this->getId() . '.log'); } CacheManager::delete(CacheKey::SCENARIO_CACHE_ATTR . $this->getId()); return parent::remove(); } /** * * @param mixed $_key * @param mixed $_private * @return boolean * @throws \Exception */ public function removeData($_key, $_private = false) { if ($_private) { $dataStore = DataStoreManager::byTypeLinkIdKey(NextDomObj::SCENARIO, $this->getId(), $_key); } else { $dataStore = DataStoreManager::byTypeLinkIdKey(NextDomObj::SCENARIO, -1, $_key); } if (is_object($dataStore)) { return $dataStore->remove(); } return true; } /** * * @param mixed $_key * @param mixed $_value * @param bool $_private * @return boolean * @throws CoreException * @throws \ReflectionException */ public function setData($_key, $_value, $_private = false) { $dataStore = new DataStore(); $dataStore->setType(NextDomObj::SCENARIO); $dataStore->setKey($_key); $dataStore->setValue($_value); if ($_private) { $dataStore->setLink_id($this->getId()); } else { $dataStore->setLink_id(-1); } $dataStore->save(); return true; } /** * @param $key * @param bool $protected * @param string $default * @return string * @throws \Exception */ public function getData($key, $protected = false, $default = '') { if ($protected !== false) { $dataStore = DataStoreManager::byTypeLinkIdKey(NextDomObj::SCENARIO, $this->getId(), $key); } else { $dataStore = DataStoreManager::byTypeLinkIdKey(NextDomObj::SCENARIO, -1, $key); } if (is_object($dataStore)) { return $dataStore->getValue($default); } return $default; } /** * * @return boolean * @throws \Exception */ public function isDue() { $last = strtotime($this->getLastLaunch()); $now = time(); $now = ($now - $now % 60); $last = ($last - $last % 60); if ($now == $last) { return false; } if (is_array($this->getSchedule())) { foreach ($this->getSchedule() as $schedule) { try { $c = new \Cron\CronExpression($schedule, new \Cron\FieldFactory); try { if ($c->isDue()) { return true; } } catch (\Exception $e) { } try { $prev = $c->getPreviousRunDate()->getTimestamp(); } catch (\Exception $e) { continue; } $lastCheck = strtotime($this->getLastLaunch()); $diff = abs((strtotime('now') - $prev) / 60); if ($lastCheck <= $prev && $diff <= ConfigManager::byKey('maxCatchAllow') || ConfigManager::byKey('maxCatchAllow') == -1) { return true; } } catch (\Exception $e) { } } } else { try { $c = new \Cron\CronExpression($this->getSchedule(), new \Cron\FieldFactory); try { if ($c->isDue()) { return true; } } catch (\Exception $e) { } try { $prev = $c->getPreviousRunDate()->getTimestamp(); } catch (\Exception $e) { return false; } $lastCheck = strtotime($this->getLastLaunch()); $diff = abs((strtotime('now') - $prev) / 60); if ($lastCheck <= $prev && $diff <= ConfigManager::byKey('maxCatchAllow') || ConfigManager::byKey('maxCatchAllow') == -1) { return true; } } catch (\Exception $exc) { } } return false; } /** * * @return boolean * @throws \Exception */ public function stop() { $crons = CronManager::searchClassAndFunction(NextDomObj::SCENARIO, 'doIn', '"scenario_id":' . $this->getId()); if (is_array($crons)) { foreach ($crons as $cron) { if ($cron->getState() == 'run') { try { $cron->halt(); $cron->remove(); } catch (\Exception $e) { LogHelper::addInfo(LogTarget::SCENARIO, __('Can not stop subtask : ') . json_encode($cron->getOption())); } } } } if ($this->running()) { if ($this->getPID() > 0) { SystemHelper::kill($this->getPID()); $retry = 0; while ($this->running() && $retry < 10) { sleep(1); SystemHelper::kill($this->getPID()); $retry++; } } if ($this->running()) { SystemHelper::kill("scenario_id=" . $this->getId() . ' '); sleep(1); if ($this->running()) { SystemHelper::kill("scenario_id=" . $this->getId() . ' '); sleep(1); } } if ($this->running()) { throw new CoreException(__('Impossible d\'arrêter le scénario : ') . $this->getHumanName() . __('. PID : ') . $this->getPID()); } } $this->setState('stop'); return true; } /** * * @return boolean * @throws \Exception */ public function running() { if (intval($this->getPID()) > 0 && posix_getsid(intval($this->getPID())) && (!file_exists('/proc/' . $this->getPID() . '/cmdline') || strpos(file_get_contents('/proc/' . $this->getPID() . '/cmdline'), 'scenario_id=' . $this->getId()) !== false)) { return true; } if (count(SystemHelper::ps('scenario_id=' . $this->getId() . ' ', [getmypid()])) > 0) { return true; } return false; } /** * * @return string * @throws \Exception */ public function getPID() { return $this->getCache('pid'); } /** * * @return mixed * @throws \Exception */ public function toArray() { $return = Utils::o2a($this, true); $cache = $this->getCache([ScenarioCache::STATE, ScenarioCache::LAST_LAUNCH]); // TODO: Pourquoi ce test a-t-il dû être rajouté ? if (isset($cache[ScenarioCache::STATE])) { $return[ScenarioCache::STATE] = $cache[ScenarioCache::STATE]; } else { $return[ScenarioCache::STATE] = ''; } if (isset($cache[ScenarioCache::LAST_LAUNCH])) { $return[ScenarioCache::LAST_LAUNCH] = $cache[ScenarioCache::LAST_LAUNCH]; } else { $return[ScenarioCache::LAST_LAUNCH] = ''; } return $return; } /** * * @param mixed $_data * @param mixed $_level * @param mixed $_drill * @return string * @throws \Exception */ public function getLinkData(&$_data = ['node' => [], 'link' => []], $_level = 0, $_drill = null) { if ($_drill === null) { $_drill = ConfigManager::byKey('graphlink::scenario::drill'); } if (isset($_data['node'][NextDomObj::SCENARIO . $this->getId()])) { return null; } if (!$this->isActive() && $_level > 0) { return $_data; } $_level++; if ($_level > $_drill) { return $_data; } $_data['node'][NextDomObj::SCENARIO . $this->getId()] = [ Common::ID => NextDomObj::SCENARIO . $this->getId(), Common::NAME => $this->getName(), 'fontweight' => ($_level == 1) ? 'bold' : 'normal', 'shape' => 'rect', 'width' => 40, 'height' => 40, 'color' => 'green', 'image' => '/public/img/NextDom_Scenario.png', 'title' => $this->getHumanName(), 'url' => 'index.php?v=d&p=scenario&id=' . $this->getId(), ]; $use = $this->getUse(); $usedBy = $this->getUsedBy(); Utils::addGraphLink($this, NextDomObj::SCENARIO, $this->getObject(), 'object', $_data, $_level + 1, $_drill, ['dashvalue' => '1,0', 'lengthfactor' => 0.6]); Utils::addGraphLink($this, NextDomObj::SCENARIO, $use['cmd'], 'cmd', $_data, $_level, $_drill); Utils::addGraphLink($this, NextDomObj::SCENARIO, $use[NextDomObj::SCENARIO], NextDomObj::SCENARIO, $_data, $_level, $_drill); Utils::addGraphLink($this, NextDomObj::SCENARIO, $use['eqLogic'], 'eqLogic', $_data, $_level, $_drill); Utils::addGraphLink($this, NextDomObj::SCENARIO, $use['dataStore'], 'dataStore', $_data, $_level, $_drill); Utils::addGraphLink($this, NextDomObj::SCENARIO, $use['view'], 'view', $_data, $_level, $_drill); Utils::addGraphLink($this, NextDomObj::SCENARIO, $use['plan'], 'plan', $_data, $_level, $_drill); Utils::addGraphLink($this, NextDomObj::SCENARIO, $usedBy['cmd'], 'cmd', $_data, $_level, $_drill); Utils::addGraphLink($this, NextDomObj::SCENARIO, $usedBy[NextDomObj::SCENARIO], NextDomObj::SCENARIO, $_data, $_level, $_drill); Utils::addGraphLink($this, NextDomObj::SCENARIO, $usedBy['eqLogic'], 'eqLogic', $_data, $_level, $_drill); Utils::addGraphLink($this, NextDomObj::SCENARIO, $usedBy['interactDef'], 'interactDef', $_data, $_level, $_drill, ['dashvalue' => '2,6', 'lengthfactor' => 0.6]); Utils::addGraphLink($this, NextDomObj::SCENARIO, $usedBy['plan'], 'plan', $_data, $_level, $_drill, ['dashvalue' => '2,6', 'lengthfactor' => 0.6]); Utils::addGraphLink($this, NextDomObj::SCENARIO, $usedBy['view'], 'view', $_data, $_level, $_drill, ['dashvalue' => '2,6', 'lengthfactor' => 0.6]); return $_data; } /** * * @return mixed * @throws \Exception */ public function getUse() { $json = NextDomHelper::fromHumanReadable(json_encode($this->export('array'))); return NextDomHelper::getTypeUse($json); } /** * * @param mixed $_mode * @return mixed * @throws \Exception */ public function export($_mode = 'text') { $return = null; if ($_mode == 'text') { $return = ''; $return .= '- Nom du scénario : ' . $this->getName() . "\n"; if (is_numeric($this->getObject_id())) { $return .= '- Objet parent : ' . $this->getObject()->getName() . "\n"; } $return .= '- Mode du scénario : ' . $this->getMode() . "\n"; $schedules = $this->getSchedule(); if ($this->getMode() == Common::SCHEDULE || $this->getMode() == 'all') { if (is_array($schedules)) { foreach ($schedules as $schedule) { $return .= ' - Programmation : ' . $schedule . "\n"; } } else { if ($schedules != '') { $return .= ' - Programmation : ' . $schedules . "\n"; } } } if ($this->getMode() == 'provoke' || $this->getMode() == 'all') { foreach ($this->getTrigger() as $trigger) { $return .= ' - Evènement : ' . NextDomHelper::toHumanReadable($trigger) . "\n"; } } $return .= "\n"; $return .= $this->getDescription(); $return .= "\n\n"; foreach ($this->getElement() as $element) { $exports = explode("\n", $element->export()); foreach ($exports as $export) { $return .= " " . $export . "\n"; } } } if ($_mode == 'array') { $return = Utils::o2a($this); $return[Common::TRIGGER] = NextDomHelper::toHumanReadable($return[Common::TRIGGER]); $return['elements'] = []; foreach ($this->getElement() as $element) { $return['elements'][] = $element->getAjaxElement('array'); } if (isset($return['id'])) { unset($return['id']); } if (isset($return[ScenarioCache::LAST_LAUNCH])) { unset($return[ScenarioCache::LAST_LAUNCH]); } if (isset($return['log'])) { unset($return['log']); } if (isset($return['hlogs'])) { unset($return['hlogs']); } if (isset($return['object_id'])) { unset($return['object_id']); } if (isset($return['pid'])) { unset($return['pid']); } if (isset($return['scenarioElement'])) { unset($return['scenarioElement']); } if (isset($return['_templateArray'])) { unset($return['_templateArray']); } if (isset($return['_changeState'])) { unset($return['_changeState']); } if (isset($return['_realTrigger'])) { unset($return['_realTrigger']); } if (isset($return['_elements'])) { unset($return['_elements']); } } return $return; } /** * @return string */ public function getDescription() { return $this->description; } /** * @param $_description * @return $this */ public function setDescription($_description) { $this->updateChangeState($this->description, $_description); $this->description = $_description; return $this; } /** * * @param bool $_array * @return mixed * @throws \Exception */ public function getUsedBy($_array = false) { $return = [NextDomObj::CMD => [], NextDomObj::EQLOGIC => [], NextDomObj::SCENARIO => [], NextDomObj::PLAN => [], NextDomObj::VIEW => []]; $return[NextDomObj::EQLOGIC] = EqLogicManager::searchConfiguration(['#scenario' . $this->getId() . '#', '"scenario_id":"' . $this->getId()]); $return[NextDomObj::INTERACT_DEF] = InteractDefManager::searchByUse(['#scenario' . $this->getId() . '#', '"scenario_id":"' . $this->getId()]); // @TODO: scenario_id, pas de guillemet ouvrant, à vérifier $return[NextDomObj::SCENARIO] = ScenarioManager::searchByUse([ ['action' => NextDomObj::SCENARIO, 'option' => $this->getId(), 'and' => true], ['action' => '#scenario' . $this->getId() . '#'], ]); $return[NextDomObj::VIEW] = ViewManager::searchByUse(NextDomObj::SCENARIO, $this->getId()); $return[NextDomObj::PLAN] = PlanHeaderManager::searchByUse(NextDomObj::SCENARIO, $this->getId()); if ($_array) { foreach ($return as &$value) { $value = Utils::o2a($value); } } return $return; } public function clearLog() { $this->_log = ''; } public function resetRepeatIfStatus() { foreach ($this->getElement() as $element) { $element->resetRepeatIfStatus(); } } /** * * @return mixed */ public function getRealTrigger() { return $this->_realTrigger; } /** * * @param mixed $_realTrigger * @return $this */ public function setRealTrigger($_realTrigger) { $this->_realTrigger = $_realTrigger; return $this; } } |