403Webshell
Server IP : 80.87.202.40  /  Your IP : 216.73.216.169
Web Server : Apache
System : Linux rospirotorg.ru 5.14.0-539.el9.x86_64 #1 SMP PREEMPT_DYNAMIC Thu Dec 5 22:26:13 UTC 2024 x86_64
User : bitrix ( 600)
PHP Version : 8.2.27
Disable Function : NONE
MySQL : OFF |  cURL : ON |  WGET : ON |  Perl : ON |  Python : OFF |  Sudo : ON |  Pkexec : ON
Directory :  /home/bitrix/ext_www/cvetdv.ru/bitrix/modules/bizproc/classes/general/

Upload File :
current_dir [ Writeable] document_root [ Writeable]

 

Command :


[ Back ]     

Current File : /home/bitrix/ext_www/cvetdv.ru/bitrix/modules/bizproc/classes/general/trackingservice.php
<?php

use Bitrix\Main;
use Bitrix\Main\ArgumentException;
use Bitrix\Main\Text\Emoji;

class CBPTrackingService extends CBPRuntimeService
{
	protected const CLEAR_LOG_SELECT_LIMIT = 50000;
	protected const CLEAR_LOG_DELETE_LIMIT = 1000;
	protected $skipTypes = [];
	protected $forcedModeWorkflows = [];
	protected static $userGroupsCache = [];

	private $cutQueue = [];

	public const DEBUG_TRACK_TYPES = [
		\CBPTrackingType::Debug,
		\CBPTrackingType::DebugAutomation,
		\CBPTrackingType::DebugDesigner,
		\CBPTrackingType::DebugLink
	];

	public function start(CBPRuntime $runtime = null)
	{
		parent::Start($runtime);

		$skipTypes = \Bitrix\Main\Config\Option::get("bizproc", "log_skip_types", CBPTrackingType::ExecuteActivity.','.CBPTrackingType::CloseActivity);
		if ($skipTypes !== '')
		{
			$this->skipTypes = explode(',', $skipTypes);
		}
	}

	public function deleteAllWorkflowTracking($workflowId)
	{
		self::DeleteByWorkflow($workflowId);
	}

	public static function dumpWorkflow($workflowId)
	{
		global $DB;

		$workflowId = trim($workflowId);
		if ($workflowId == '')
			throw new Exception("workflowId");

		$dbResult = $DB->Query(
			"SELECT ID, TYPE, MODIFIED, ACTION_NAME, ACTION_TITLE, EXECUTION_STATUS, EXECUTION_RESULT, ACTION_NOTE, MODIFIED_BY ".
			"FROM b_bp_tracking ".
			"WHERE WORKFLOW_ID = '".$DB->ForSql($workflowId)."' ".
			"ORDER BY ID "
		);
		$dbResult = new CBPTrackingServiceResult($dbResult);

		$r = array();
		$level = 0;
		while ($arResult = $dbResult->GetNext())
		{
			if ($arResult["TYPE"] == CBPTrackingType::CloseActivity)
			{
				$level--;
				$arResult["PREFIX"] = str_repeat("&nbsp;&nbsp;&nbsp;", $level > 0 ? $level : 0);
				$arResult["LEVEL"] = $level;
			}
			elseif ($arResult["TYPE"] == CBPTrackingType::ExecuteActivity)
			{
				$arResult["PREFIX"] = str_repeat("&nbsp;&nbsp;&nbsp;", $level > 0 ? $level : 0);
				$arResult["LEVEL"] = $level;
				$level++;
			}
			else
			{
				$arResult["PREFIX"] = str_repeat("&nbsp;&nbsp;&nbsp;", $level > 0 ? $level : 0);
				$arResult["LEVEL"] = $level;
			}

			$r[] = $arResult;
		}

		return $r;
	}

	public function loadReport($workflowId, int $limit = 0)
	{
		$result = [];
		$navStartParams = $limit > 0 ? ['nTopCount' => $limit] : false;
		$order = ['ID' => $limit > 0 ? 'DESC' : 'ASC'];

		$dbResult = static::getList(
			$order,
			["WORKFLOW_ID" => $workflowId, "TYPE" => CBPTrackingType::Report],
			false,
			$navStartParams,
			["ID", "MODIFIED", "ACTION_NOTE"]
		);
		while ($arResult = $dbResult->GetNext())
		{
			$result[] = $arResult;
		}

		if ($limit > 0)
		{
			return array_reverse($result);
		}

		return $result;
	}

	public static function deleteByWorkflow($workflowId)
	{
		$connection = \Bitrix\Main\Application::getConnection();
		$helper = $connection->getSqlHelper();

		$workflowId = trim($workflowId);
		if (!$workflowId)
		{
			throw new Exception("workflowId");
		}

		$queryString = sprintf(
			"SELECT ID FROM b_bp_tracking t WHERE WORKFLOW_ID = '%s'",
			$helper->forSql($workflowId)
		);

		$ids = $connection->query($queryString)->fetchAll();

		while ($partIds = array_splice($ids, 0, static::CLEAR_LOG_DELETE_LIMIT))
		{
			$connection->query(
				sprintf(
					'DELETE from b_bp_tracking WHERE ID IN(%s)',
					implode(',', array_column($partIds, 'ID'))
				)
			);
		}
	}

	public function setCompletedByWorkflow($workflowId, $flag = true)
	{
		global $DB;

		$workflowId = trim($workflowId);

		if (!$workflowId)
		{
			throw new Exception("workflowId");
		}

		$value1 = $flag ? 'Y' : 'N';
		$value2 = $DB->ForSql($workflowId, 32);

		$DB->Query(
			"UPDATE b_bp_tracking SET COMPLETED = '{$value1}' WHERE WORKFLOW_ID = '{$value2}'"
		);
	}

	public static function clearOldAgent()
	{
		if (Main\ModuleManager::isModuleInstalled('bitrix24'))
		{
			static::clearB24();
		}
		else
		{
			static::ClearOld(COption::GetOptionString("bizproc", "log_cleanup_days", "90"));
		}

		return "CBPTrackingService::ClearOldAgent();";
	}

	/**
	 * @internal
	 * @deprecated for now
	 */
	public static function clearB24Agent()
	{
		return '';
	}

	/**
	 * @internal
	 * @return void
	 */
	public static function clearB24(): bool
	{
		$connection = \Bitrix\Main\Application::getConnection();
		$helper = $connection->getSqlHelper();
		$limit = static::CLEAR_LOG_SELECT_LIMIT;
		$partLimit = static::CLEAR_LOG_DELETE_LIMIT;
		$sqlInterval = $helper->addDaysToDateTime(-90);

		$strSql = "SELECT ID FROM b_bp_tracking t WHERE t.MODIFIED < {$sqlInterval} LIMIT {$limit}";
		$result = $connection->query($strSql);
		$ids = $result->fetchAll();

		if ($ids)
		{
			while ($partIds = array_splice($ids, 0, $partLimit))
			{
				$connection->query(
					sprintf(
						'DELETE from b_bp_tracking WHERE ID IN(%s)',
						implode(',', array_column($partIds, 'ID'))
					)
				);
			}
		}

		global $pPERIOD;
		if ($result->getSelectedRowsCount() === $limit)
		{
			$pPERIOD = 900; // 15 min
		}
		else
		{
			$pPERIOD = strtotime('tomorrow 01:00') - time();
		}

		return true;
	}

	public static function parseStringParameter($string, $documentType = null, $htmlSpecialChars = true)
	{
		if (!$documentType)
		{
			$documentType = ['', '', ''];
		}

		return preg_replace_callback(
			CBPActivity::ValueInlinePattern,
			static function ($matches) use ($documentType, $htmlSpecialChars)
			{
				return CBPAllTrackingService::parseStringParameterMatches(
					$matches,
					[$documentType[0], $documentType[1], $documentType[2]],
					$htmlSpecialChars
				);
			},
			$string
		);
	}

	public static function parseStringParameterMatches($matches, $documentType = null, $htmlSpecialChars = true)
	{
		$result = "";
		$documentType = is_array($documentType) ? array_filter($documentType) : null;

		if ($matches[1] === "user")
		{
			$user = $matches[2];

			$l = mb_strlen("user_");
			if (mb_strpos($user, "user_") === 0)
			{
				$result = CBPHelper::ConvertUserToPrintableForm((int)(mb_substr($user, $l)), '', $htmlSpecialChars);
			}
			elseif (mb_strpos($user, 'group_') === 0)
			{
				$result =
					$htmlSpecialChars
						? htmlspecialcharsbx(CBPHelper::getExtendedGroupName($user))
						: CBPHelper::getExtendedGroupName($user)
				;
			}
			elseif ($documentType)
			{
				$v = implode(",", $documentType);
				if (!array_key_exists($v,self::$userGroupsCache ))
				{
					self::$userGroupsCache[$v] = CBPDocument::GetAllowableUserGroups($documentType);
				}

				$result = self::$userGroupsCache[$v][$user];
			}
			else
			{
				$result = $user;
			}
		}
		elseif ($matches[1] === "group")
		{
			if (mb_strpos($matches[2], 'group_') === 0)
			{
				$result =
					$htmlSpecialChars
						? htmlspecialcharsbx(CBPHelper::getExtendedGroupName($matches[2]))
						: CBPHelper::getExtendedGroupName($matches[2])
				;
			}
			elseif ($documentType)
			{
				$v = implode(",", $documentType);
				if (!array_key_exists($v, self::$userGroupsCache))
				{
					self::$userGroupsCache[$v] = CBPDocument::GetAllowableUserGroups($documentType);
				}

				$result = self::$userGroupsCache[$v][$matches[2]];
			}
			else
			{
				$result = $matches[2];
			}
		}
		else
		{
			$result = $matches[0];
		}

		return $result;
	}

	public function setForcedMode($workflowId)
	{
		$this->forcedModeWorkflows[] = $workflowId;
		return $this;
	}

	public function isForcedMode($workflowId)
	{
		return in_array($workflowId, $this->forcedModeWorkflows);
	}

	public function canWrite($type, $workflowId)
	{
		if (in_array((int)$type, self::DEBUG_TRACK_TYPES, true))
		{
			return false;
		}

		return (!in_array($type, $this->skipTypes) || $this->isForcedMode($workflowId));
	}

	public function write(
		$workflowId,
		$type,
		$actionName,
		$executionStatus,
		$executionResult,
		$actionTitle = "",
		$actionNote = "",
		$modifiedBy = 0
	): ?int
	{
		global $DB;

		if (!$this->canWrite($type, $workflowId))
		{
			return null;
		}

		$workflowId = trim($workflowId);
		if ($workflowId == '')
			throw new Exception("workflowId");

		$actionName = trim($actionName);
		if ($actionName == '')
			throw new Exception("actionName");

		$type = intval($type);
		$executionStatus = intval($executionStatus);
		$executionResult = intval($executionResult);
		$actionNote = trim($actionNote);

		$modifiedBy = intval($modifiedBy);

		$actionTitle = is_string($actionTitle) ? $actionTitle : '';

		$DB->Query(
			"INSERT INTO b_bp_tracking(WORKFLOW_ID, TYPE, MODIFIED, ACTION_NAME, ACTION_TITLE, EXECUTION_STATUS, EXECUTION_RESULT, ACTION_NOTE, MODIFIED_BY) ".
			"VALUES('".$DB->ForSql($workflowId, 32)."', ".intval($type).", ".$DB->CurrentTimeFunction().", '".$DB->ForSql($actionName, 128)."', '".$DB->ForSql($actionTitle, 255)."', ".intval($executionStatus).", ".intval($executionResult).", ".($actionNote <> '' ? "'".$DB->ForSql($actionNote)."'" : "NULL").", ".($modifiedBy > 0 ? $modifiedBy : "NULL").")"
		);
		$id = $DB->LastID();

		if (self::getLogSizeLimit() && !$this->isForcedMode($workflowId))
		{
			$this->cutLogSizeDeferred($workflowId);
		}

		return $id;
	}

	public static function getList(
		$arOrder = ["ID" => "DESC"],
		$arFilter = [],
		$arGroupBy = false,
		$arNavStartParams = false,
		$arSelectFields = []
	)
	{
		global $DB;

		if (count($arSelectFields) <= 0)
		{
			$arSelectFields = [
				"ID",
				"WORKFLOW_ID",
				"TYPE",
				"MODIFIED",
				"ACTION_NAME",
				"ACTION_TITLE",
				"EXECUTION_STATUS",
				"EXECUTION_RESULT",
				"ACTION_NOTE",
				"MODIFIED_BY",
			];
		}

		static $arFields = [
			"ID" => ["FIELD" => "T.ID", "TYPE" => "int"],
			"WORKFLOW_ID" => ["FIELD" => "T.WORKFLOW_ID", "TYPE" => "string"],
			"TYPE" => ["FIELD" => "T.TYPE", "TYPE" => "int"],
			"ACTION_NAME" => ["FIELD" => "T.ACTION_NAME", "TYPE" => "string"],
			"ACTION_TITLE" => ["FIELD" => "T.ACTION_TITLE", "TYPE" => "string"],
			"MODIFIED" => ["FIELD" => "T.MODIFIED", "TYPE" => "datetime"],
			"EXECUTION_STATUS" => ["FIELD" => "T.EXECUTION_STATUS", "TYPE" => "int"],
			"EXECUTION_RESULT" => ["FIELD" => "T.EXECUTION_RESULT", "TYPE" => "int"],
			"ACTION_NOTE" => ["FIELD" => "T.ACTION_NOTE", "TYPE" => "string"],
			"MODIFIED_BY" => ["FIELD" => "T.MODIFIED_BY", "TYPE" => "int"],
		];

		$arSqls = CBPHelper::PrepareSql($arFields, $arOrder, $arFilter, $arGroupBy, $arSelectFields);

		$arSqls["SELECT"] = str_replace("%%_DISTINCT_%%", "", $arSqls["SELECT"]);

		if (is_array($arGroupBy) && count($arGroupBy)==0)
		{
			$strSql =
				"SELECT ".$arSqls["SELECT"]." ".
				"FROM b_bp_tracking T ".
				"	".$arSqls["FROM"]." ";
			if ($arSqls["WHERE"] <> '')
				$strSql .= "WHERE ".$arSqls["WHERE"]." ";
			if ($arSqls["GROUPBY"] <> '')
				$strSql .= "GROUP BY ".$arSqls["GROUPBY"]." ";

			$dbRes = $DB->Query($strSql);
			if ($arRes = $dbRes->Fetch())
			{
				return $arRes["CNT"];
			}

			return false;
		}

		$strSql =
			"SELECT ".$arSqls["SELECT"]." ".
			"FROM b_bp_tracking T ".
			"	".$arSqls["FROM"]." ";
		if ($arSqls["WHERE"] <> '')
			$strSql .= "WHERE ".$arSqls["WHERE"]." ";
		if ($arSqls["GROUPBY"] <> '')
			$strSql .= "GROUP BY ".$arSqls["GROUPBY"]." ";
		if ($arSqls["ORDERBY"] <> '')
			$strSql .= "ORDER BY ".$arSqls["ORDERBY"]." ";

		if (is_array($arNavStartParams) && intval($arNavStartParams["nTopCount"]) <= 0)
		{
			$strSql_tmp =
				"SELECT COUNT('x') as CNT ".
				"FROM b_bp_tracking T ".
				"	".$arSqls["FROM"]." ";
			if ($arSqls["WHERE"] <> '')
				$strSql_tmp .= "WHERE ".$arSqls["WHERE"]." ";
			if ($arSqls["GROUPBY"] <> '')
				$strSql_tmp .= "GROUP BY ".$arSqls["GROUPBY"]." ";

			$dbRes = $DB->Query($strSql_tmp);
			$cnt = 0;
			if ($arSqls["GROUPBY"] == '')
			{
				if ($arRes = $dbRes->Fetch())
				{
					$cnt = $arRes["CNT"];
				}
			}
			else
			{
				// only for MySQL
				$cnt = $dbRes->SelectedRowsCount();
			}

			$dbRes = new CDBResult();
			$dbRes->NavQuery($strSql, $cnt, $arNavStartParams);
		}
		else
		{
			if (is_array($arNavStartParams) && intval($arNavStartParams["nTopCount"]) > 0)
				$strSql .= "LIMIT ".intval($arNavStartParams["nTopCount"]);

			$dbRes = $DB->Query($strSql);
		}

		return new CBPTrackingServiceResult($dbRes);
	}

	public static function clearOld($days = 0)
	{
		$connection = \Bitrix\Main\Application::getConnection();
		$helper = $connection->getSqlHelper();
		$days = (int)$days;
		if ($days <= 0)
		{
			$days = 90;
		}

		$completed = self::shouldClearCompletedTracksOnly() ? "= 'Y'" : "IN ('N', 'Y')";
		$limit = static::CLEAR_LOG_SELECT_LIMIT;
		$partLimit = static::CLEAR_LOG_DELETE_LIMIT;
		$sqlInterval = $helper->addDaysToDateTime(-1 * $days);

		$strSql = "SELECT ID FROM b_bp_tracking t WHERE t.COMPLETED {$completed} "
			. " AND t.MODIFIED < {$sqlInterval}"
			. " AND t.TYPE IN (0,1,2,3,4,5,7,8,9) LIMIT {$limit}"
		;

		$ids = $connection->query($strSql)->fetchAll();

		while ($partIds = array_splice($ids, 0, $partLimit))
		{
			$connection->query(
				sprintf(
					'DELETE from b_bp_tracking WHERE ID IN(%s)',
					implode(',', array_column($partIds, 'ID'))
				)
			);
		}

		return true;
	}

	private function cutLogSize(string $workflowId, int $size): bool
	{
		global $DB;

		$queryResult = $DB->Query(
			sprintf(
				"SELECT ID FROM b_bp_tracking"
				. " WHERE WORKFLOW_ID = '%s' AND " . $DB->quote('TYPE') . " IN (0,1,2,3,4,5,7,8,9) ORDER BY ID DESC LIMIT %d,100",
				$DB->ForSql($workflowId),
				$size
			)
		);

		$ids = [];
		while ($row = $queryResult->fetch())
		{
			$ids[] = $row['ID'];
		}

		if ($ids)
		{
			$DB->Query(
				sprintf(
					'DELETE FROM b_bp_tracking WHERE ID IN (%s)',
					implode(',', $ids)
				),
				true
			);
		}

		return true;
	}

	private function cutLogSizeDeferred(string $workflowId)
	{
		$this->cutQueue[$workflowId] = true;
		$this->setCutJob();
	}

	private function setCutJob()
	{
		static $inserted = false;

		if (!$inserted)
		{
			Main\Application::getInstance()->addBackgroundJob(
				[$this, 'doBackgroundCut'],
				[],
				Main\Application::JOB_PRIORITY_LOW - 10
			);
			$inserted = true;
		}
	}

	public function doBackgroundCut()
	{
		$size = self::getLogSizeLimit();
		$list = array_keys($this->cutQueue);
		$this->cutQueue = [];//clear

		foreach ($list as $workflowId)
		{
			$this->cutLogSize($workflowId, $size);
		}
	}

	private static function getLogSizeLimit(): int
	{
		static $limit;
		if ($limit === null)
		{
			$limit = Main\ModuleManager::isModuleInstalled('bitrix24') ? 50 : 0;
		}

		return $limit;
	}

	public static function shouldClearCompletedTracksOnly(): bool
	{
		if (Main\ModuleManager::isModuleInstalled('bitrix24'))
		{
			//more logic later
			return false;
		}

		return true;
	}
}

class CBPTrackingServiceResult extends CDBResult
{
	public function fetch()
	{
		$result = parent::Fetch();

		if ($result && isset($result['ACTION_NOTE']) && is_string($result['ACTION_NOTE']))
		{
			$actionNote = $result['ACTION_NOTE'];

			if (isset($result['TYPE']) && in_array((int)$result['TYPE'], CBPTrackingService::DEBUG_TRACK_TYPES, true))
			{
				$decodedActionNote = [];
				try
				{
					$decodedActionNote = \Bitrix\Main\Web\Json::decode($actionNote);
				}
				catch (ArgumentException)
				{}

				if (is_array($decodedActionNote))
				{
					array_walk_recursive($decodedActionNote, static function (&$value) {
						if (is_string($value))
						{
							$value = Emoji::decode($value);
						}
					});
				}

				if (isset($decodedActionNote['propertyValue']) && is_string($decodedActionNote['propertyValue']))
				{
					$propertyValue = $decodedActionNote['propertyValue'];
					$propertyValue = \CBPTrackingService::parseStringParameter($propertyValue, null, false);
					$propertyValue = self::convertTimestampTag($propertyValue);
					$decodedActionNote['propertyValue'] = $propertyValue;
				}

				$result['ACTION_NOTE'] = \Bitrix\Main\Web\Json::encode($decodedActionNote);
			}
			else
			{
				$actionNote = \CBPTrackingService::parseStringParameter($actionNote, null, false);
				$actionNote = self::convertTimestampTag($actionNote);

				$result['ACTION_NOTE'] = $actionNote;
			}
		}

		return $result;
	}

	private static function convertTimestampTag($string): string
	{
		return preg_replace_callback(
			'/\[timestamp=(\d+)\].*\[\/timestamp\]/i',
			static function ($matches)
			{
				$timestamp = (int)$matches[1];

				$datetime = new \Bitrix\Bizproc\BaseType\Value\DateTime($timestamp, CTimeZone::GetOffset());
				if ($datetime->getTimestamp() === null)
				{
					return '';
				}

				return (string)$datetime;
			},
			$string
		);
	}
}

class CBPTrackingType
{
	public const Unknown = 0;
	public const ExecuteActivity = 1;
	public const CloseActivity = 2;
	public const CancelActivity = 3;
	public const FaultActivity = 4;
	public const Custom = 5;
	public const Report = 6;
	public const AttachedEntity = 7;
	public const Trigger = 8;
	public const Error = 9;
	public const Debug = 10;
	public const DebugAutomation = 11;
	public const DebugDesigner = 12;
	public const DebugLink = 13;
}

Youez - 2016 - github.com/yon3zu
LinuXploit