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/rospirotorg.ru/bitrix/components/bitrix/bizproc.user.processes.start/

Upload File :
current_dir [ Writeable] document_root [ Writeable]

 

Command :


[ Back ]     

Current File : /home/bitrix/ext_www/rospirotorg.ru/bitrix/components/bitrix/bizproc.user.processes.start/class.php
<?php

if (!defined('B_PROLOG_INCLUDED') || B_PROLOG_INCLUDED !== true)
{
	die();
}

use Bitrix\Bizproc\Api\Data\UserService\UsersToGet;
use Bitrix\Bizproc\Api\Service\UserService;
use Bitrix\Bizproc\DocumentType\DocumentTypeUserOptionTable;
use Bitrix\Bizproc\UI\Helpers\DurationFormatter;
use Bitrix\Bizproc\Workflow\Entity\WorkflowStateTable;
use Bitrix\Bizproc\WorkflowTemplateTable;
use Bitrix\Lists\Api\Data\IBlockService\IBlockListFilter;
use Bitrix\Lists\Api\Request\ServiceFactory\GetAverageIBlockTemplateDurationRequest;
use Bitrix\Lists\Api\Service\ServiceFactory\ProcessService;
use Bitrix\Lists\Api\Service\ServiceFactory\ServiceFactory;
use Bitrix\Main\Engine\CurrentUser;
use Bitrix\Main\Localization\Loc;
use Bitrix\Main\Error;
use Bitrix\Main\ErrorCollection;
use Bitrix\Main\Web\Uri;

class BizprocUserProcessesStart
	extends CBitrixComponent
	implements \Bitrix\Main\Errorable, \Bitrix\Main\Engine\Contract\Controllerable
{
	private string $gridId = 'bizproc_user_processes_start';
	private string $filterId = 'bizproc_user_processes_start_filter';
	protected const NAVIGATION_ID = 'page';
	private const TITLE_WIDTH = 360;

	protected array $listIBlockBpTemplates = [];
	protected array $filterPresets = [];

	private ErrorCollection $errorCollection;
	private static array $usersData = [];

	public function __construct($component = null)
	{
		parent::__construct($component);

		$this->errorCollection = new ErrorCollection();
	}

	public static function pinAction(int $iblockId, CurrentUser $user): bool
	{
		if (!$iblockId)
		{
			return false;
		}

		$userId = (int)$user->getId();
		if (!$userId)
		{
			return false;
		}

		$documentType = self::getDocumentType($iblockId);
		if ($documentType === null)
		{
			return false;
		}

		$result = DocumentTypeUserOptionTable::addOption($documentType, $userId, DocumentTypeUserOptionTable::PINNED);

		return $result->isSuccess();
	}

	public static function unpinAction(int $iblockId, CurrentUser $user): bool
	{
		if (!$iblockId)
		{
			return false;
		}

		$userId = (int)$user->getId();
		if (!$userId)
		{
			return false;
		}

		$documentType = self::getDocumentType($iblockId);
		if ($documentType === null)
		{
			return false;
		}

		$result = DocumentTypeUserOptionTable::deleteOption($documentType, $userId, DocumentTypeUserOptionTable::PINNED);

		return $result->isSuccess();
	}

	protected static function getDocumentType(int $iblockId): ?array
	{
		if (!\Bitrix\Main\Loader::includeModule('lists'))
		{
			return null;
		}
		$iBlockTypeId = ProcessService::getIBlockTypeId();

		return BizprocDocument::generateDocumentComplexType($iBlockTypeId, $iblockId);
	}

	public function configureActions(): array
	{
		return [];
	}

	public function addErrors(array $errors): static
	{
		$this->errorCollection->add($errors);

		return $this;
	}

	public function getErrorByCode($code): ?Error
	{
		return $this->errorCollection->getErrorByCode($code);
	}

	public function getErrors(): array
	{
		return $this->errorCollection->toArray();
	}

	public function setError(Error $error): static
	{
		$this->errorCollection->setError($error);

		return $this;
	}

	public function hasErrors(): bool
	{
		return !$this->errorCollection->isEmpty();
	}

	public function onPrepareComponentParams($params): array
	{
		$params['isShowLiveFeed'] = $this->request->get('LIVEFEED_PRESET') ? 'Y' : 'N';

		return $params;
	}

	public function executeComponent(): void
	{
		global $APPLICATION;
		$APPLICATION->SetTitle(Loc::getMessage('BIZPROC_USER_PROCESSES_START_TITLE'));

		$this->init();
		$this->fillGridInfo();

		if (!$this->hasErrors())
		{
			$this->addToolbar();
			$this->fillGridData();

			$this->includeComponentTemplate();

			return;
		}

		$this->includeComponentTemplate('error');
	}

	private function init(): void
	{
		$this->gridId .= $this->arParams['isShowLiveFeed'] === 'Y' ? '_livefeed' : '';
		$this->filterId .= $this->arParams['isShowLiveFeed'] === 'Y' ? '_livefeed' : '';
		$this->checkModules();
		$this->arResult['gridData'] = [];
	}

	private function checkModules(): void
	{
		if (!\Bitrix\Main\Loader::includeModule('bizproc'))
		{
			$errorMessage = Loc::getMessage('BIZPROC_USER_PROCESSES_MODULE_ERROR', ['#MODULE#' => 'bizproc']);
			$this->setError(new Error($errorMessage));
		}
		if (!\Bitrix\Main\Loader::includeModule('lists'))
		{
			$errorMessage = Loc::getMessage('BIZPROC_USER_PROCESSES_MODULE_ERROR', ['#MODULE#' => 'lists']);
			$this->setError(new Error($errorMessage));
		}
	}

	private function fillGridInfo(): void
	{
		$this->arResult['gridId'] = $this->gridId;
		$this->arResult['filterId'] = $this->filterId;
		$this->arResult['navigationId'] = static::NAVIGATION_ID;
		$this->arResult['gridColumns'] = $this->getGridColumns();
		$this->arResult['pageNavigation'] = $this->getPageNavigation();
	}

	private function getGridColumns(): array
	{
		return [
			[
				'id' => 'PIN',
				'name' => '',
				'default' => true,
				'class' => 'bizproc-user-processes-start-grid-header-pin',
				'resizeable' => false,
			],
			[
				'id' => 'NAME',
				'name' => Loc::getMessage('BIZPROC_USER_PROCESSES_START_GRID_COLUMN_DOCUMENT_NAME'),
				'default' => true,
				'width' => 400,
			],
			[
				'id' => 'START',
				'name' => Loc::getMessage('BIZPROC_USER_PROCESSES_START_GRID_COLUMN_ACTION'),
				'default' => true,
			],
			[
				'id' => 'IN_PROGRESS',
				'name' => Loc::getMessage('BIZPROC_USER_PROCESSES_START_GRID_COLUMN_IN_PROGRESS'),
				'default' => true,
			],
			[
				'id' => 'LAST_ACTION',
				'name' => Loc::getMessage('BIZPROC_USER_PROCESSES_START_GRID_COLUMN_LAST_ACTION'),
				'default' => true,
			],
			[
				'id' => 'MODIFIED_BY',
				'name' => Loc::getMessage('BIZPROC_USER_PROCESSES_START_GRID_COLUMN_MODIFIED_BY'),
				'default' => true,
			],
		];
	}

	private function getPageNavigation(): \Bitrix\Main\UI\PageNavigation
	{
		$options = new \Bitrix\Main\Grid\Options($this->gridId);
		$navParams = $options->GetNavParams();

		$pageNavigation = new \Bitrix\Main\UI\PageNavigation(static::NAVIGATION_ID);
		$pageNavigation->setPageSize($navParams['nPageSize'])->initFromUri();

		$currentPage = $this->request->getQuery(static::NAVIGATION_ID);
		if (is_numeric($currentPage))
		{
			$pageNavigation->setCurrentPage((int)$currentPage);
		}

		return $pageNavigation;
	}

	private function fillGridData()
	{
		$currentUserId = $this->getCurrentUserId();
		$iBlockTypeId = ProcessService::getIBlockTypeId();

		$service = ServiceFactory::getServiceByIBlockTypeId($iBlockTypeId, $currentUserId);
		if (!$service)
		{
			return;
		}

		$checkPermissionResult = $service->checkIBlockTypePermission();
		$lists_perm = $checkPermissionResult->getPermission();

		if (!$checkPermissionResult->isSuccess())
		{
			return;
		}

		if ($lists_perm <= CListPermissions::ACCESS_DENIED)
		{
			return;
		}

		$gridData = [];
		$filter = $this->getUserFilter();

		$getCatalogResult = $service->getFilteredCatalog($filter);
		$jsHandlerStartTemplate = "BX.Bizproc.Component.UserProcessesStart.Instance.startWorkflow(event, '%s', %d, '%s');";

		if ($getCatalogResult->isSuccess())
		{

			$iblocks = $this->getOrderedCatalog($this->addPins($getCatalogResult->getCatalog()));
			$this->warmUpCaches($iblocks);

			foreach ($iblocks as $iBlock)
			{
				$jsHandlerStart = sprintf($jsHandlerStartTemplate, $iBlockTypeId, $iBlock['ID'], CUtil::JSEscape($iBlock['NAME']));
				if (isset($this->filterPresets['LIVEFEED_PRESET']) && !\CLists::getLiveFeed($iBlock['ID']))
				{
					continue;
				}

				$data = [
					'id' => $iBlock['ID'],
					'columns' => [
						'PIN' => '',
						'NAME' => $this->renderIBlockName($service, $iBlock, $jsHandlerStart),
						'START' => $this->renderStartButton($jsHandlerStart, $iBlock['ID']),
						'IN_PROGRESS' => $this->renderInProgress($iBlock['ID']),
						'LAST_ACTION' => \CBPViewHelper::formatDateTime($this->getLastActivity((int)$iBlock['ID'])),
						'MODIFIED_BY' => $this->renderModifiedBy($iBlock['ID']),
					],
					'data' => [
						'NAME' => $iBlock['NAME'],
					],
					'actions' => [
						[
							'TEXT' => Loc::getMessage('BIZPROC_USER_PROCESSES_START_GRID_ROW_ACTION_EDIT'),
							'MENU' => $this->getEditActions($iBlock['ID']),
						],
					],
					'cellActions' => [
						'PIN' => $this->getPinAction($iBlock['ID'], $iBlock['PIN'] ?? false),
					],
				];

				$constantActions = $this->getEditConstantsActions($iBlock['ID']);
				if ($constantActions)
				{
					$data['actions'][] =[
						'TEXT' => Loc::getMessage('BIZPROC_USER_PROCESSES_START_GRID_ROW_ACTION_EDIT_CONSTANTS'),
						'MENU' => $constantActions,
					];
				}


				$gridData[] = $data;
			}
		}

		$this->arResult['gridData'] = $gridData;
	}

	private function renderModifiedBy(int $iblockId): string
	{
		$templates = $this->getIBlockBpTemplates($iblockId);
		if (empty($templates))
		{
			return '';
		}

		$template = array_shift($templates);
		$workflowTemplate = WorkflowTemplateTable::getById($template['id'])->fetchObject();
		if (!$workflowTemplate)
		{
			return '';
		}

		if (isset(self::$usersData[$workflowTemplate->getUserId()]))
		{
			$userData = self::$usersData[$workflowTemplate->getUserId()];

			$emptyAvatar = (empty($userData['avatar']) ? 'empty' : '');
			$avatarStyle = (empty($userData['avatar']) ? '' : ' style="background-image: url(\''. Uri::urnEncode($userData['avatar']).'\')"');
			$fullName = htmlspecialcharsbx($userData['full_name']);

			return
				'<div class="bizproc-user-processes-start-grid-username-wrapper">'
				. '<a class="bizproc-user-processes-start-grid-username">'
				. "<span class=\"ui-icon ui-icon-common-user bizproc-user-processes-start-grid-avatar {$emptyAvatar}\"><i $avatarStyle></i></span>"
				. "<span bx-tooltip-user-id=\"{$userData['id']}\" class=\"bizproc-user-processes-start-grid-username-inner\">$fullName</span>"
				. '</a>'
				. '</div>'
				;
		}

		return '';
	}

	private function warmUpCaches(array $iblocks): void
	{
		$userIds = [];
		foreach ($iblocks as $iblock)
		{
			$templates = $this->getIBlockBpTemplates($iblock["ID"]);
			if (!empty($templates))
			{
				$template = array_shift($templates);
				$workflowTemplate = WorkflowTemplateTable::getById($template['id'])->fetchObject();
				if ($workflowTemplate)
				{
					$userIds[] = $workflowTemplate->getUserId();
				}
			}
		}

		$userService = new UserService();
		$response = $userService->getUsersView(new UsersToGet($userIds));

		if ($response->isSuccess())
		{
			$userViews = $response->getUserViews();
			foreach ($userViews as $userView)
			{
				$userData = [
					'id' => $userView->getUserId(),
					'full_name' => $userView->getFullName(),
					'avatar' => $userView->getUserAvatar(),
				];
				if (!isset(self::$usersData[$userView->getUserId()]))
				{
					self::$usersData[$userView->getUserId()] = $userData;
				}
			}
		}
	}

	private function renderInProgress(int $iblockId): string
	{
		$templates = $this->getIBlockBpTemplates($iblockId);
		if (empty($templates))
		{
			return '';
		}

		$template = array_shift($templates);

		$instancesView = new \Bitrix\Bizproc\UI\WorkflowTemplateInstancesView($template['id'], true);
		$viewParam = htmlspecialcharsbx(\Bitrix\Main\Web\Json::encode($instancesView));

		return <<<HTML
			<div data-role="wt-progress-{$instancesView->getTplId()}" data-widget="{$viewParam}">
				<script>
					BX.ready(() => {
						BX.Bizproc.Workflow.Instances.Widget.renderTo(
							document.querySelector('[data-role="wt-progress-{$instancesView->getTplId()}"]')
						)
					})
				</script>
			</div>
		HTML;
	}

	private function getLastActivity(int $iblockId): ?\Bitrix\Main\Type\DateTime
	{
		$templates = $this->getIBlockBpTemplates($iblockId);
		if (empty($templates))
		{
			return null;
		}

		$lastWorkflow = WorkflowStateTable::getList([
			'filter' => [
				'@WORKFLOW_TEMPLATE_ID' => array_column($templates, 'id'),
			],
			'select' => ['STARTED'],
			'limit' => 1,
			'order' => ['STARTED' => 'DESC'],
		])->fetch();

		if ($lastWorkflow)
		{
			return $lastWorkflow['STARTED'];
		}

		return null;
	}

	private function addPins(array $iBlocks): array
	{
		$iBlockTypeId = ProcessService::getIBlockTypeId();

		foreach ($iBlocks as &$iBlock)
		{
			$iBlock['PIN'] = $this->checkOption(
				BizprocDocument::generateDocumentComplexType($iBlockTypeId, $iBlock['ID']),
				DocumentTypeUserOptionTable::PINNED,
			);
		}

		return $iBlocks;
	}

	private function checkOption(array $documentComplexType, int $optionCode): bool
	{
		return (bool)DocumentTypeUserOptionTable::getList([
			'select' => ['ID'],
			'filter' => [
				'=MODULE_ID' => $documentComplexType[0],
				'=ENTITY' => $documentComplexType[1],
				'=DOCUMENT_TYPE' => $documentComplexType[2],
				'=USER_ID' => $this->getCurrentUserId(),
				'=OPTION_CODE' => $optionCode,
			],
			'limit' => 1,
		])->fetch();
	}

	private function getOrderedCatalog(array $catalog): array
	{
		usort($catalog,
			fn($a, $b) => $a['SORT'] <=> $b['SORT']
		);

		usort($catalog,
			fn($a, $b) => $b['PIN'] <=> $a['PIN']
		);

		return $catalog;
	}

	private function getUserFilter(): IBlockListFilter
	{
		$filterOptions = new \Bitrix\Main\UI\Filter\Options($this->filterId);
		$this->filterPresets = $filterOptions->getFilter($this->getFilterFields());
		$filter = new IBlockListFilter();

		if (isset($this->filterPresets['NAME']))
		{
			$filter->setField('NAME', $this->filterPresets['NAME'], '%');
		}

		if ($filterOptions->getSearchString())
		{
			$filter->setField('NAME', $filterOptions->getSearchString(), '%');
		}

		return $filter;
	}

	private function getFilterFields(): array
	{
		$filterFields = [
			'NAME' => [
				'id' => 'NAME',
				'name' => Loc::getMessage('BIZPROC_USER_PROCESSES_START_GRID_COLUMN_DOCUMENT_NAME'),
				'type' => 'string',
				'default' => true,
			],
		];

		if ($this->arParams['isShowLiveFeed'] === 'Y')
		{
			$filterFields['LIVEFEED_PRESET'] = [
				'id' => 'LIVEFEED_PRESET',
				'name' => Loc::getMessage('BIZPROC_USER_PROCESSES_START_LIVEFEED_PRESET_NAME'),
				'type' => 'list',
				'items' => [
					'' => Loc::getMessage('BIZPROC_USER_PROCESSES_START_LIVEFEED_PRESET_ALL'),
					'show_livefeed' => Loc::getMessage('BIZPROC_USER_PROCESSES_START_LIVEFEED_PRESET_YES'),
				],
			];
		}

		return $filterFields;
	}

	private function getFilterPresets(): array
	{
		$filterPresets = [
			'filter_default' => [
				'name' => Loc::getMessage('BIZPROC_USER_PROCESSES_START_SYSTEM_PRESET_NAME'),
				'fields' => [
					'NAME' => '',
				],
				'default' => false,
			],
		];

		if ($this->arParams['isShowLiveFeed'] === 'Y')
		{
			$filterPresets['show_livefeed'] = [
				'name' => Loc::getMessage('BIZPROC_USER_PROCESSES_START_LIVEFEED_PRESET_NAME'),
				'fields' => ['LIVEFEED_PRESET' => 'show_livefeed'],
				'default' => false,
			];
		}

		return $filterPresets;
	}

	private function getPinAction(int $id, bool $pinned): array
	{
		$actionClass = [
			\Bitrix\Main\Grid\CellActions::PIN,
		];
		if ($pinned)
		{
			$actionClass[] = \Bitrix\Main\Grid\CellActionState::ACTIVE;
		}

		return [
			[
				'class' => $actionClass,
				'events' => [
					'click' => "BX.Bizproc.Component.UserProcessesStart.changePin.bind(BX.Bizproc.Component.UserProcessesStart, {$id}, '$this->gridId')",
				],
			],
		];
	}

	private function renderIBlockName(ServiceFactory $service, array $iBlock, string $handler): string
	{
		$description = $iBlock['DESCRIPTION'] ?? '';
		if ($description === '')
		{
			$description = Loc::getMessage('BIZPROC_USER_PROCESSES_START_EMPTY_DESCRIPTION');
		}

		$description = \CBPViewHelper::prepareTaskDescription(
			\CBPHelper::convertBBtoText(
				preg_replace('|\n+|', "\n", trim($description)),
			)
		);

		$templateDescriptionElement = sprintf(
			'<span data-hint="%s" data-hint-html class="ui-hint"></span>',
			htmlspecialcharsbx($description)
		);

		$templateNameElement = sprintf(
			'<a class="ui-btn-link ui-typography-text-lg" onclick="%s" href="#">%s</a>',
			htmlspecialcharsbx($handler),
			htmlspecialcharsbx($iBlock['NAME']) . $templateDescriptionElement
		);

		$durationResponse = $service->getAverageIBlockTemplateDuration(
			new GetAverageIBlockTemplateDurationRequest(
				$iBlock['ID'],
				CBPDocumentEventType::Create,
				false,
				false,
			)
		);
		$duration = null;
		if ($durationResponse->isSuccess())
		{
			$duration = $durationResponse->getAverageDuration();
		}

		$durationText = $duration !== null
			? DurationFormatter::format($duration)
			: Loc::getMessage('BIZPROC_USER_PROCESSES_START_NO_DATA')
		;

		$averageTimeElement = sprintf(
			'<div class="%s">%s</div>',
			'bizproc-workflow-start-list-grid-template-average-time',
			Loc::getMessage(
				'BIZPROC_USER_PROCESSES_START_AVERAGE_WAITING_TIME',
				[
					'#TIME#' => '<b>' . $durationText . '</b>',
				]
			),
		);

		return sprintf(
			'<div class="bizproc-user-processes-start-grid-document-name-wrapper">%s</div>',
			$templateNameElement . $averageTimeElement,
		);
	}

	private function renderStartButton(string $handler, int $iblockId): string
	{
		return sprintf(
			'
				<div class="bizproc-user-processes-start-column-start">
					<button
						class="ui-btn ui-btn-success ui-btn-round ui-btn-xs ui-btn ui-btn-no-caps"
						onclick="%s"
					>%s</button>
					<div
						class="bizproc-user-processes-start-column-start-counter-wrapper"
						data-role="iblock-%s-counter"
					></div>
				</div>
			',
			htmlspecialcharsbx($handler),
			Loc::getMessage('BIZPROC_USER_PROCESSES_START_GRID_COLUMN_START_BUTTON'),
			$iblockId,
		);
	}


	private function getCurrentUserId(): int
	{
		return (int)CurrentUser::get()->getId();
	}

	private function addToolbar(): void
	{
		$filterParams = [
			'FILTER_ID' => $this->filterId,
			'GRID_ID' => $this->gridId,
			'FILTER' => $this->getFilterFields(),
			'FILTER_PRESETS' => $this->getFilterPresets(),
			'ENABLE_LABEL' => true,
			'RESET_TO_DEFAULT_MODE' => true,
			'THEME' => \Bitrix\Main\UI\Filter\Theme::LIGHT,
		];

		\Bitrix\UI\Toolbar\Facade\Toolbar::addFilter($filterParams);
	}

	protected function getEditActions(int $iblockId): array
	{
		$editActions = [];
		$templates = $this->getIBlockBpTemplates($iblockId);
		foreach ($templates as $template)
		{
			$bizprocEditorUrl = str_replace('#ID#', $template['id'], $template['bizprocEditorUrl']);
			$canEdit = $template['canEdit'] ?? false;
			$editActions[] = [
				'TITLE' => $template['description'],
				'TEXT' => $template['name'],
				'ONCLICK' => "BX.Bizproc.Component.UserProcessesStart.Instance.editTemplate(event, '$bizprocEditorUrl', $canEdit);",
			];
		}

		return $editActions;
	}

	private function getEditConstantsActions(int $iblockId): array
	{
		$documentType = self::getDocumentType($iblockId);
		if (
			!CBPDocument::canUserOperateDocumentType(
				CBPCanUserOperateOperation::CreateWorkflow,
				$this->getCurrentUserId(),
				$documentType,
			)
		)
		{
			return [];
		}

		$editConstantsActions = [];
		$templates = $this->getIBlockBpTemplates($iblockId);
		foreach ($templates as $template)
		{
			$templateId = $template['id'];
			$constants = CBPWorkflowTemplateLoader::getTemplateConstants($templateId);
			if ($constants)
			{
				$signedDocumentType = CBPDocument::signDocumentType($documentType);

				$editConstantsActions[] = [
					'TEXT' => $template['name'],
					'ONCLICK' => "BX.Bizproc.Component.UserProcessesStart.Instance.editTemplateConstants('$templateId', '$signedDocumentType');"
				];
			}

		}

		return $editConstantsActions;
	}

	protected function getIBlockBpTemplates(int $iblockId): array
	{
		if (!isset($this->listIBlockBpTemplates[$iblockId]))
		{
			$this->listIBlockBpTemplates[$iblockId] = [];
			$iBlockTypeId = ProcessService::getIBlockTypeId();

			if (CLists::isBpFeatureEnabled($iBlockTypeId))
			{
				$documentType = BizprocDocument::generateDocumentComplexType($iBlockTypeId, $iblockId);
				$this->listIBlockBpTemplates[$iblockId] = CBPDocument::getTemplatesForStart(
					$this->getCurrentUserId(),
					$documentType,
				);

				foreach ($this->listIBlockBpTemplates[$iblockId] as &$template)
				{
					$template['bizprocEditorUrl'] = CBPDocumentService::getBizprocEditorUrl($documentType);
					$template['canEdit'] = CBPDocument::CanUserOperateDocumentType(
						CBPCanUserOperateOperation::CreateWorkflow,
						$this->getCurrentUserId(),
						$documentType,
					);
				}
			}
		}

		return $this->listIBlockBpTemplates[$iblockId];
	}

}

Youez - 2016 - github.com/yon3zu
LinuXploit