VYPR
Moderate severityNVD Advisory· Published Sep 20, 2022· Updated May 27, 2025

Cross-site Scripting (XSS) - Stored in yetiforcecompany/yetiforcecrm

CVE-2022-3005

Description

Cross-site Scripting (XSS) - Stored in GitHub repository yetiforcecompany/yetiforcecrm prior to 6.4.0.

Affected packages

Versions sourced from the GitHub Security Advisory.

PackageAffected versionsPatched versions
yetiforce/yetiforce-crmPackagist
<= 6.4.0

Affected products

1

Patches

1
e55886781509

Improved SLA Policy elements

https://github.com/yetiforcecompany/yetiforcecrmRadosław SkrzypczakAug 26, 2022via ghsa
10 files changed · +251 235
  • config/version.php+1 1 modified
    @@ -1,7 +1,7 @@
     <?php
     
     return [
    -	'appVersion' => '6.4.15',
    +	'appVersion' => '6.4.16',
     	'patchVersion' => '2022.08.26',
     	'lib_roundcube' => '0.3.1',
     ];
    
  • languages/en-US/ServiceContracts.json+0 8 modified
    @@ -48,13 +48,5 @@
         "LBL_STATUS": "Status",
         "LBL_CONDITIONS": "Conditions",
         "LBL_TIMES": "Times"
    -  },
    -  "js": {
    -    "JS_POLICY_NAME": "Policy name",
    -    "JS_OPERATIONAL_HOURS": "Operational hours",
    -    "JS_REACTION_TIME": "Reaction time",
    -    "JS_IDLE_TIME": "Idle time",
    -    "JS_RESOLVE_TIME": "Resolve time",
    -    "JS_BUSINESS_HOURS": "Business hours"
       }
     }
    
  • layouts/basic/modules/ServiceContracts/CustomConditions.tpl+44 51 modified
    @@ -1,67 +1,60 @@
     {*<!-- {[The file is published on the basis of YetiForce Public License 5.0 that can be found in the following directory: licenses/LicenseEN.txt or yetiforce.com]} -->*}
     {strip}
    -	<!-- tpl-ServiceContracts-CustomConditions -->
    -	<input type="hidden" class="js-all-business-hours" value="{\App\Purifier::encodeHtml(\App\Json::encode($ALL_BUSINESS_HOURS))}">
    -	<div class="d-none js-conditions-template" data-js="container">
    -		{include file=\App\Layout::getTemplatePath('ConditionBuilder.tpl', $MODULE_NAME) ADVANCE_CRITERIA=[]}
    -	</div>
    -	<div class="js-custom-conditions" data-js="container">
    -		{foreach item=ROW from=$SLA_POLICY_ROWS key=$ROW_INDEX}
    -			{if $ROW['policy_type']===2}
    -				<div class="card js-custom-row shadow-sm mb-2" data-id="{$ROW['id']}" data-record-id="{$RECORD->getId()}" data-js="container">
    -					<div class="card-body">
    -						<div class="d-flex">
    -							<div class="d-block" style="flex-grow:1">
    -								<div class="row no-gutters">
    -									<div class="col-5 pr-2">
    -										{assign var=ROW_HOURS value=explode(',', $ROW['business_hours'])}
    -										<label>{\App\Language::translate('LBL_BUSINESS_HOURS', 'ServiceContracts')}</label>
    -										<div>
    -											<select class="select2 js-business-hours" name="business_hours[{$ROW_INDEX}][]" multiple data-validation-engine="validate[required,funcCall[Vtiger_Base_Validator_Js.invokeValidation]]">
    -												{foreach item=BUSINESS_HOURS from=$ALL_BUSINESS_HOURS}
    -													<option value="{$BUSINESS_HOURS['id']}" {if in_array($BUSINESS_HOURS['id'], $ROW_HOURS)}selected="selected" {/if}>{$BUSINESS_HOURS['name']}</option>
    -												{/foreach}
    -											</select>
    -										</div>
    +	{foreach item=ROW from=$SLA_POLICY_ROWS key=$ROW_INDEX}
    +		{if $ROW['policy_type']===2}
    +			<div class="card js-custom-row shadow-sm mb-2" data-id="{$ROW['id']}" data-record-id="{$RECORD->getId()}" data-js="container">
    +				<div class="card-body">
    +					<div class="d-flex">
    +						<div class="d-block" style="flex-grow:1">
    +							<div class="row no-gutters">
    +								<div class="col-5 pr-2">
    +									{assign var=ROW_HOURS value=explode(',', $ROW['business_hours'])}
    +									<label>{\App\Language::translate('LBL_BUSINESS_HOURS', 'ServiceContracts')}</label>
    +									<div>
    +										<select class="select2 js-business-hours" name="business_hours[{$ROW_INDEX}][]" multiple data-validation-engine="validate[required,funcCall[Vtiger_Base_Validator_Js.invokeValidation]]">
    +											{foreach item=BUSINESS_HOURS from=$ALL_BUSINESS_HOURS}
    +												<option value="{$BUSINESS_HOURS['id']}" {if in_array($BUSINESS_HOURS['id'], $ROW_HOURS)}selected="selected" {/if}>{$BUSINESS_HOURS['name']}</option>
    +											{/foreach}
    +										</select>
     									</div>
    -									<div class="col-2 pr-2">
    -										<label>{\App\Language::translate('LBL_REACTION_TIME','ServiceContracts')}</label>
    -										<div class="input-group time">
    -											<input type="hidden" name="reaction_time[{$ROW_INDEX}]" class="c-time-period" value="{$ROW['reaction_time']}">
    -										</div>
    -									</div>
    -									<div class="col-2 pr-2">
    -										<label>{\App\Language::translate('LBL_IDLE_TIME','ServiceContracts')}</label>
    -										<div class="input-group time">
    -											<input type="hidden" name="idle_time[{$ROW_INDEX}]" class="c-time-period" value="{$ROW['idle_time']}">
    -										</div>
    +								</div>
    +								<div class="col-2 pr-2">
    +									<label>{\App\Language::translate('LBL_REACTION_TIME','ServiceContracts')}</label>
    +									<div class="input-group time">
    +										<input type="hidden" name="reaction_time[{$ROW_INDEX}]" class="c-time-period" value="{$ROW['reaction_time']|escape}">
     									</div>
    -									<div class="col-2 pr-2">
    -										<label>{\App\Language::translate('LBL_RESOLVE_TIME','ServiceContracts')}</label>
    -										<div class="input-group time">
    -											<input type="hidden" name="resolve_time[{$ROW_INDEX}]" class="c-time-period" value="{$ROW['resolve_time']}">
    -										</div>
    +								</div>
    +								<div class="col-2 pr-2">
    +									<label>{\App\Language::translate('LBL_IDLE_TIME','ServiceContracts')}</label>
    +									<div class="input-group time">
    +										<input type="hidden" name="idle_time[{$ROW_INDEX}]" class="c-time-period" value="{$ROW['idle_time']|escape}">
     									</div>
     								</div>
    -								<div class="row mt-2">
    -									<div class="js-conditions-col col">
    -										<label>{\App\Language::translate('LBL_CONDITIONS', $MODULE_NAME)}</label>
    -										<input type="hidden" name="rowid[{$ROW_INDEX}]" value="{$ROW['id']}" class="js-custom-row-id" />
    -										<input type="hidden" name="conditions[{$ROW_INDEX}]" class="js-conditions-value" value="{\App\Purifier::encodeHtml($ROW['conditions'])}" data-js="container">
    -										{include file=\App\Layout::getTemplatePath('ConditionBuilder.tpl', $MODULE_NAME) ADVANCE_CRITERIA=\App\Json::decode($ROW['conditions'])}
    +								<div class="col-2 pr-2">
    +									<label>{\App\Language::translate('LBL_RESOLVE_TIME','ServiceContracts')}</label>
    +									<div class="input-group time">
    +										<input type="hidden" name="resolve_time[{$ROW_INDEX}]" class="c-time-period" value="{$ROW['resolve_time']|escape}">
     									</div>
     								</div>
     							</div>
    -							<div class="d-inline-flex text-right border-left" style="flex-grow:0">
    -								<div class="d-inline-block align-center" style="margin:auto 0;">
    -									<a href class="btn btn-danger ml-4 js-delete-row-action"><span class="fas fa-trash-alt"></span></a>
    +							<div class="row mt-2">
    +								<div class="js-conditions-col col">
    +									<label>{\App\Language::translate('LBL_CONDITIONS', $MODULE_NAME)}</label>
    +									<input type="hidden" name="rowid[{$ROW_INDEX}]" value="{$ROW['id']}" class="js-custom-row-id" />
    +									<input type="hidden" name="conditions[{$ROW_INDEX}]" class="js-conditions-value" value="{\App\Purifier::encodeHtml($ROW['conditions'])}" data-js="container">
    +									{include file=\App\Layout::getTemplatePath('ConditionBuilder.tpl', $MODULE_NAME) ADVANCE_CRITERIA=\App\Json::decode($ROW['conditions'])}
     								</div>
     							</div>
     						</div>
    +						<div class="d-inline-flex text-right border-left" style="flex-grow:0">
    +							<div class="d-inline-block align-center" style="margin:auto 0;">
    +								<a href class="btn btn-danger ml-4 js-delete-row-action"><span class="fas fa-trash-alt"></span></a>
    +							</div>
    +						</div>
     					</div>
     				</div>
    -			{/if}
    -		{/foreach}
    -	</div>
    +			</div>
    +		{/if}
    +	{/foreach}
     	<!-- /tpl-ServiceContracts-CustomConditions -->
     {/strip}
    
  • layouts/basic/modules/ServiceContracts/SlaPolicyCustom.tpl+41 0 added
    @@ -0,0 +1,41 @@
    +{*<!-- {[The file is published on the basis of YetiForce Public License 5.0 that can be found in the following directory: licenses/LicenseEN.txt or yetiforce.com]} -->*}
    +{strip}
    +	<!-- tpl-ServiceContracts-SlaPolicyCustom -->
    +	<div class="js-sla-policy relatedContainer" data-js="container">
    +		<input type="hidden" name="target" value="{$TARGET_MODULE}">
    +		<div class="form-group row text-center">
    +			<div class="col-12 flex">
    +				<label class="d-inline-block mr-2">
    +					{\App\Language::translate('LBL_POLICY_TYPE', $MODULE_NAME)}:
    +				</label>
    +				<label class="d-inline-block mr-2">
    +					<input type="radio" name="policy_type" class="form-control d-inline-block mr-1 js-sla-policy-type-radio" value="0" {if $POLICY_TYPE===0} checked="checked" {/if} />{\App\Language::translate('LBL_POLICY_TYPE_GLOBAL', $MODULE_NAME)}
    +				</label>
    +				<label class="d-inline-block mr-2">
    +					<input type="radio" name="policy_type" class="form-control d-inline-block mr-1 js-sla-policy-type-radio" value="1" {if $POLICY_TYPE===1} checked="checked" {/if} />{\App\Language::translate('LBL_POLICY_TYPE_TEMPLATE', $MODULE_NAME)}
    +				</label>
    +				<label class="d-inline-block mr-2">
    +					<input type="radio" name="policy_type" class="form-control d-inline-block mr-1 js-sla-policy-type-radio" value="2" {if $POLICY_TYPE===2} checked="checked" {/if} /> {\App\Language::translate('LBL_POLICY_TYPE_CUSTOM', $MODULE_NAME)}
    +				</label>
    +				<button class="js-sla-policy-custom js-sla-policy-add-record-btn btn btn-success float-right d-none" data-record-id="{$RECORD->getId()}">
    +					<span class="fas fa-plus"></span> {\App\Language::translate('LBL_ADD_ENTRY', $MODULE_NAME)}
    +				</button>
    +			</div>
    +		</div>
    +		<div class="js-sla-policy-template js-sla-policy-template--container form-group row d-none" data-js="container"></div>
    +		<div class="js-sla-policy-custom form-group row d-none" data-js="container">
    +			<div class="col-12">
    +				{include file=\App\Layout::getTemplatePath('CustomConditions.tpl', $MODULE_NAME)}
    +			</div>
    +		</div>
    +		<div class="row">
    +			<div class="col text-center">
    +				<button class="btn btn-success js-sla-policy-save-btn">
    +					<span class="fas fa-check mr-2"></span>
    +					{\App\Language::translate('LBL_SAVE')}
    +				</button>
    +			</div>
    +		</div>
    +	</div>
    +	<!-- /tpl-ServiceContracts-SlaPolicyCustom -->
    +{/strip}
    
  • layouts/basic/modules/ServiceContracts/SlaPolicyTemplate.tpl+31 0 added
    @@ -0,0 +1,31 @@
    +{*<!-- {[The file is published on the basis of YetiForce Public License 5.0 that can be found in the following directory: licenses/LicenseEN.txt or yetiforce.com]} -->*}
    +{strip}
    +	<!-- tpl-ServiceContracts-SlaPolicyTemplate -->
    +	<div class="col-12">
    +		<table class="table js-sla-policy-template-table">
    +			<thead>
    +				<tr>
    +					<th></th>
    +					<th>{\App\Language::translate('LBL_POLICY_NAME', $MODULE_NAME)}</th>
    +					<th>{\App\Language::translate('LBL_OPERATIONAL_HOURS', $MODULE_NAME)}</th>
    +					<th>{\App\Language::translate('LBL_REACTION_TIME', $MODULE_NAME)}</th>
    +					<th>{\App\Language::translate('LBL_IDLE_TIME', $MODULE_NAME)}</th>
    +					<th>{\App\Language::translate('LBL_RESOLVE_TIME', $MODULE_NAME)}</th>
    +				</tr>
    +			</thead>
    +			<tbody>
    +				{foreach from=\App\Utils\ServiceContracts::getSlaPolicyByModule($TARGET_MODULE_ID) item=ROW key=key name=slaTemplates}
    +					<tr>
    +						<td><input type="radio" name="policy_id" value="{$ROW['id']}" {if $SELECTED_TEMPLATE === $ROW['id'] || (!$SELECTED_TEMPLATE && $smarty.foreach.slaTemplates.first)} checked="checked" {/if}></td>
    +						<td>{\App\Purifier::encodeHtml($ROW.name)}</td>
    +						<td>{\App\Purifier::encodeHtml($ROW.operational_hours)}</td>
    +						<td>{\App\Purifier::encodeHtml($ROW.reaction_time)}</td>
    +						<td>{\App\Purifier::encodeHtml($ROW.idle_time)}</td>
    +						<td>{\App\Purifier::encodeHtml($ROW.resolve_time)}</td>
    +					</tr>
    +				{/foreach}
    +			</tbody>
    +		</table>
    +	</div>
    +	<!-- /tpl-ServiceContracts-SlaPolicyTemplate -->
    +{/strip}
    
  • layouts/basic/modules/ServiceContracts/SlaPolicy.tpl+3 1 modified
    @@ -25,7 +25,9 @@
     		<div class="js-sla-policy-template js-sla-policy-template--container form-group row d-none" data-js="container"></div>
     		<div class="js-sla-policy-custom form-group row d-none" data-js="container">
     			<div class="col-12">
    -				{include file=\App\Layout::getTemplatePath('CustomConditions.tpl', $MODULE_NAME)}
    +				<div class="js-custom-conditions" data-js="container">
    +					{include file=\App\Layout::getTemplatePath('CustomConditions.tpl', $MODULE_NAME)}
    +				</div>
     			</div>
     		</div>
     		<div class="row">
    
  • layouts/basic/modules/Settings/SlaPolicy/EditViewBlocks.tpl+6 6 modified
    @@ -21,7 +21,7 @@
     					<div class="card-header">
     						{if !empty($RECORD->getId())}
     							<span class="yfi yfi-full-editing-view mr-2"></span>
    -							{\App\Language::translate('LBL_EDIT',$QUALIFIED_MODULE)} - {$RECORD->getName()}
    +							{\App\Language::translate('LBL_EDIT',$QUALIFIED_MODULE)} - {\App\Purifier::encodeHtml($RECORD->getName())}
     						{else}
     							<span class="fas fa-plus mr-2"></span>
     							{\App\Language::translate('LBL_CREATE',$QUALIFIED_MODULE)}
    @@ -31,13 +31,13 @@
     						<div class="form-group row">
     							<div class="col-12 col-md-3">
     								<label>{\App\Language::translate('LBL_NAME',$QUALIFIED_MODULE)}</label>
    -								<input type="text" name="name" class="form-control" value="{$RECORD->getName()}" data-validation-engine="validate[required,maxSize[255]]">
    +								<input type="text" name="name" class="form-control" value="{\App\Purifier::encodeHtml($RECORD->getName())}" data-validation-engine="validate[required,maxSize[255]]">
     							</div>
     							<div class="col-12 col-md-3">
     								<label>{\App\Language::translate('LBL_SOURCE_MODULE',$QUALIFIED_MODULE)}</label>
     								<select name="source_module" class="select2" data-validation-engine="validate[required]">
     									{foreach item=MODULE_NAME from=$MODULES}
    -										<option value="{$MODULE_NAME}" {if \App\Module::getModuleName($RECORD->get('tabid')) === $MODULE_NAME}selected="selected" {/if}>{\App\Language::translate($MODULE_NAME, $MODULE_NAME)}</option>
    +										<option value="{$MODULE_NAME|escape}" {if \App\Module::getModuleName($RECORD->get('tabid')) === $MODULE_NAME}selected="selected" {/if}>{\App\Language::translate($MODULE_NAME, $MODULE_NAME)}</option>
     									{/foreach}
     								</select>
     							</div>
    @@ -66,19 +66,19 @@
     							<div class="col-12 col-md-4">
     								<label>{\App\Language::translate('LBL_REACTION_TIME','ServiceContracts')}</label>
     								<div class="input-group time">
    -									<input type="hidden" name="reaction_time" class="c-time-period" value="{$RECORD->get('reaction_time')}">
    +									<input type="hidden" name="reaction_time" class="c-time-period" value="{$RECORD->get('reaction_time')|escape}">
     								</div>
     							</div>
     							<div class="col-12 col-md-4">
     								<label>{\App\Language::translate('LBL_IDLE_TIME','ServiceContracts')}</label>
     								<div class="input-group time">
    -									<input type="hidden" name="idle_time" class="c-time-period" value="{$RECORD->get('idle_time')}">
    +									<input type="hidden" name="idle_time" class="c-time-period" value="{$RECORD->get('idle_time')|escape}">
     								</div>
     							</div>
     							<div class="col-12 col-md-4">
     								<label>{\App\Language::translate('LBL_RESOLVE_TIME','ServiceContracts')}</label>
     								<div class="input-group time">
    -									<input type="hidden" name="resolve_time" class="c-time-period" value="{$RECORD->get('resolve_time')}">
    +									<input type="hidden" name="resolve_time" class="c-time-period" value="{$RECORD->get('resolve_time')|escape}">
     								</div>
     							</div>
     						</div>
    
  • modules/ServiceContracts/actions/PolicyTemplatesAjax.php+0 48 removed
    @@ -1,48 +0,0 @@
    -<?php
    -/**
    - * ServiceContracts PolicyTemplatesAjax Action class.
    - *
    - * @package   Action
    - *
    - * @copyright YetiForce S.A.
    - * @license   YetiForce Public License 5.0 (licenses/LicenseEN.txt or yetiforce.com)
    - * @author    Rafal Pospiech <r.pospiech@yetiforce.com>
    - * @author    Mariusz Krzaczkowski <m.krzaczkowski@yetiforce.com>
    - */
    -class ServiceContracts_PolicyTemplatesAjax_Action extends \App\Controller\Action
    -{
    -	/** {@inheritdoc} */
    -	public function checkPermission(App\Request $request)
    -	{
    -		$record = Vtiger_DetailView_Model::getInstance($request->getModule(), $request->getInteger('record'));
    -		if (!$record->getRecord()->isViewable()) {
    -			throw new \App\Exceptions\NoPermittedToRecord('ERR_NO_PERMISSIONS_FOR_THE_RECORD', 406);
    -		}
    -	}
    -
    -	/** {@inheritdoc} */
    -	public function process(App\Request $request)
    -	{
    -		$sla = \App\Utils\ServiceContracts::getSlaPolicyForServiceContracts($request->getInteger('record'));
    -		$slaId = $sla ? $sla[0]['sla_policy_id'] : false;
    -		$rows = [];
    -		foreach (\App\Utils\ServiceContracts::getSlaPolicyByModule(\App\Module::getModuleId($request->getByType('targetModule', 'Alnum'))) as $row) {
    -			$moduleName = \App\Module::getModuleName($row['tabid']);
    -			$row['tabid'] = \App\Language::translate($moduleName, $moduleName);
    -			$row['operational_hours'] = $row['operational_hours'] ? \App\Language::translate('LBL_CALENDAR_HOURS', 'ServiceContracts') : \App\Language::translate('LBL_BUSINESS_HOURS', 'ServiceContracts');
    -			$row['business_hours'] = implode(', ', array_column(\App\Utils\ServiceContracts::getBusinessHoursByIds(explode(',', $row['business_hours'])), 'name'));
    -			$row['reaction_time'] = \App\Fields\TimePeriod::getLabel($row['reaction_time']);
    -			$row['idle_time'] = \App\Fields\TimePeriod::getLabel($row['idle_time']);
    -			$row['resolve_time'] = \App\Fields\TimePeriod::getLabel($row['resolve_time']);
    -			if ($row['id'] === $slaId) {
    -				$row['checked'] = true;
    -			} else {
    -				$row['checked'] = false;
    -			}
    -			$rows[] = $row;
    -		}
    -		$response = new Vtiger_Response();
    -		$response->setResult($rows);
    -		$response->emit();
    -	}
    -}
    
  • modules/ServiceContracts/views/PolicyTemplatesAjax.php+73 0 added
    @@ -0,0 +1,73 @@
    +<?php
    +/**
    + * ServiceContracts PolicyTemplatesAjax View class.
    + *
    + * @package   View
    + *
    + * @copyright YetiForce S.A.
    + * @license   YetiForce Public License 5.0 (licenses/LicenseEN.txt or yetiforce.com)
    + * @author    Radosław Skrzypczak <r.skrzypczak@yetiforce.com>
    + */
    +class ServiceContracts_PolicyTemplatesAjax_View extends Vtiger_Index_View
    +{
    +	use \App\Controller\ExposeMethod;
    +
    +	/** @var Vtiger_Record_Model Record model instance. */
    +	public $record;
    +
    +	/**
    +	 * Construct.
    +	 */
    +	public function __construct()
    +	{
    +		parent::__construct();
    +		$this->exposeMethod('slaPolicyTemplate');
    +		$this->exposeMethod('slaPolicyCustom');
    +	}
    +
    +	/** {@inheritdoc} */
    +	public function checkPermission(App\Request $request)
    +	{
    +		$this->record = Vtiger_Record_Model::getInstanceById($request->getInteger('record'), $request->getModule());
    +		if (!$this->record->isViewable() || !$this->record->isPermitted('ServiceContractsSla') || !App\Privilege::isPermitted($request->getByType('targetModule', \App\Purifier::ALNUM))) {
    +			throw new \App\Exceptions\NoPermittedToRecord('ERR_NO_PERMISSIONS_FOR_THE_RECORD', 406);
    +		}
    +	}
    +
    +	/** {@inheritdoc} */
    +	public function slaPolicyTemplate(App\Request $request)
    +	{
    +		$moduleName = $request->getModule();
    +		$sla = \App\Utils\ServiceContracts::getSlaPolicyForServiceContracts($request->getInteger('record'));
    +		$slaId = $sla[0]['sla_policy_id'] ?? 0;
    +
    +		$viewer = $this->getViewer($request);
    +		$viewer->assign('TARGET_MODULE_ID', \App\Module::getModuleId($request->getByType('targetModule', \App\Purifier::ALNUM)));
    +		$viewer->assign('SELECTED_TEMPLATE', $slaId);
    +
    +		return $viewer->view('SlaPolicyTemplate.tpl', $moduleName);
    +	}
    +
    +	/** {@inheritdoc} */
    +	public function slaPolicyCustom(App\Request $request)
    +	{
    +		$moduleName = $request->getModule();
    +		$index = $request->getInteger('index', time());
    +		$defaultEmptyData = [
    +			'id' => 0,
    +			'policy_type' => 2,
    +			'conditions' => '[]',
    +			'reaction_time' => '1:d',
    +			'idle_time' => '1:d',
    +			'resolve_time' => '1:d',
    +			'business_hours' => '',
    +		];
    +
    +		$viewer = $this->getViewer($request);
    +		$viewer->assign('RECORD', $this->record);
    +		$viewer->assign('ALL_BUSINESS_HOURS', \App\Utils\ServiceContracts::getAllBusinessHours());
    +		$viewer->assign('SLA_POLICY_ROWS', [$index => $defaultEmptyData]);
    +
    +		return $viewer->view('CustomConditions.tpl', $moduleName);
    +	}
    +}
    
  • public_html/layouts/basic/modules/ServiceContracts/resources/Detail.js+52 120 modified
    @@ -4,6 +4,7 @@
      * @description InRelation scripts for SlaPolicy module
      * @license     YetiForce Public License 5.0
      * @author      Mariusz Krzaczkowski <m.krzaczkowski@yetiforce.com>
    + * @author      Radosław Skrzypczak <r.skrzypczak@yetiforce.com>
      */
     'use strict';
     
    @@ -40,73 +41,45 @@ Vtiger_Detail_Js(
     			this.container.find('.js-sla-policy-custom').removeClass('d-none');
     			return this;
     		},
    -
     		/**
    -		 * Get template table
    -		 * @param {Array} rows
    -		 *
    -		 * @returns {String} HTML
    +		 * Get default params
    +		 * @returns {Object}
     		 */
    -		getTemplateTableHtml(rows) {
    -			let somethingChecked = false;
    -			rows.forEach((row) => {
    -				if (row.checked) {
    -					somethingChecked = true;
    -					return false;
    -				}
    -			});
    -			if (!somethingChecked && typeof rows[0] !== 'undefined') {
    -				rows[0].checked = true;
    -			}
    -			return `<div class="col-12"><table class="table js-sla-policy-template-table">
    -		<thead>
    -			<tr>
    -				<th></th>
    -				<th>${app.vtranslate('JS_POLICY_NAME')}</th>
    -				<th>${app.vtranslate('JS_OPERATIONAL_HOURS')}</th>
    -				<th>${app.vtranslate('JS_REACTION_TIME')}</th>
    -				<th>${app.vtranslate('JS_IDLE_TIME')}</th>
    -				<th>${app.vtranslate('JS_RESOLVE_TIME')}</th>
    -			</tr>
    -		</thead>
    -		<tbody>
    -		${rows
    -			.map((row) => {
    -				return `<tr>
    -				<td><input type="radio" name="policy_id" value="${row.id}"${row.checked ? 'checked="checked"' : ''}></td>
    -				<td>${row.name}</td>
    -				<td>${row.operational_hours}</td>
    -				<td>${row.reaction_time}</td>
    -				<td>${row.idle_time}</td>
    -				<td>${row.resolve_time}</td>
    -			</tr>`;
    -			})
    -			.join('')}
    -		</tbody>
    -		</table>
    -		</div>`;
    +		getDefaultParam() {
    +			return {
    +				module: 'ServiceContracts',
    +				view: 'PolicyTemplatesAjax',
    +				targetModule: this.targetModule,
    +				record: Number($('#recordId').val())
    +			};
     		},
     
     		/**
     		 * Load predefined sla policy templates
    +		 * @param {Object} param
    +		 * @returns
     		 */
    -		loadTemplates() {
    +		loadTemplates(param) {
     			const progress = jQuery.progressIndicator({
     				position: 'html',
     				blockInfo: {
     					enabled: true
     				}
     			});
    -			AppConnector.request({
    -				module: 'ServiceContracts',
    -				action: 'PolicyTemplatesAjax',
    -				targetModule: this.targetModule,
    -				record: Number($('#recordId').val())
    -			}).done((data) => {
    -				progress.progressIndicator({ mode: 'hide' });
    -				if (data.success) {
    -					this.container.find('.js-sla-policy-template--container').html(this.getTemplateTableHtml(data.result));
    -				}
    +			return new Promise((resolve, _reject) => {
    +				AppConnector.request(param)
    +					.done((data) => {
    +						progress.progressIndicator({ mode: 'hide' });
    +						resolve(data);
    +					})
    +					.fail((e, t) => {
    +						progress.progressIndicator({ mode: 'hide' });
    +						app.errorLog(e, t);
    +						app.showNotify({
    +							text: app.vtranslate('JS_ERROR'),
    +							type: 'error'
    +						});
    +					});
     			});
     		},
     
    @@ -116,7 +89,12 @@ Vtiger_Detail_Js(
     		onPolicyTypeChange() {
     			this.policyType = Number(this.container.find('[name="policy_type"]:checked').val());
     			if (this.policyType === 1) {
    -				this.hideAll().showTemplateSettings().loadTemplates();
    +				this.hideAll()
    +					.showTemplateSettings()
    +					.loadTemplates({ mode: 'slaPolicyTemplate', ...this.getDefaultParam() })
    +					.then((data) => {
    +						this.container.find('.js-sla-policy-template--container').html(data);
    +					});
     			} else if (this.policyType === 2) {
     				this.hideAll().showCustomSettings();
     			} else {
    @@ -176,7 +154,7 @@ Vtiger_Detail_Js(
     						rowElem.find('.js-custom-row-id').val(row.id);
     					});
     				} else {
    -					$.each(this.container.find('.js-custom-row'), (index, rowElem) => {
    +					$.each(this.container.find('.js-custom-row'), (_index, rowElem) => {
     						rowElem = $(rowElem);
     						rowElem.data('id', 0);
     						rowElem.find('.js-custom-row-id').val(0);
    @@ -198,66 +176,21 @@ Vtiger_Detail_Js(
     			addPolicyBtn.on('click', (e) => {
     				e.preventDefault();
     				e.stopPropagation();
    -				const index = this.container.find('.js-custom-row').length;
    -				const row = $(`<div class="card js-custom-row shadow-sm mb-2" data-id="0" data-record-id="` + addPolicyBtn.data('record-id') + `" data-js="container">
    -			<div class="card-body">
    -				<div class="d-flex">
    -					<div class="d-block" style="flex-grow:1">
    -						<div class="row no-gutters">
    -							<div class="col-5 pr-2">
    -								<label>${app.vtranslate('JS_BUSINESS_HOURS')}</label>
    -								<select class="select2" name="business_hours[${index}][]" multiple data-validation-engine="validate[required,funcCall[Vtiger_Base_Validator_Js.invokeValidation]]">
    -								${this.businessHours
    -									.map((businessHours) => {
    -										return `<option value="${businessHours.id}">${businessHours.name}</option>`;
    -									})
    -									.join('')}
    -								</select>
    -							</div>
    -							<div class="col-2 pr-2">
    -								<label>${app.vtranslate('JS_REACTION_TIME')}</label>
    -								<div class="input-group time">
    -									<input type="hidden" name="reaction_time[${index}]" class="c-time-period" value="1:d">
    -								</div>
    -							</div>
    -							<div class="col-2 pr-2">
    -								<label>${app.vtranslate('JS_IDLE_TIME')}</label>
    -								<div class="input-group time">
    -									<input type="hidden" name="idle_time[${index}]" class="c-time-period" value="1:d">
    -								</div>
    -							</div>
    -							<div class="col-2 pr-2">
    -								<label>${app.vtranslate('JS_RESOLVE_TIME')}</label>
    -								<div class="input-group time">
    -									<input type="hidden" name="resolve_time[${index}]" class="c-time-period" value="1:d">
    -								</div>
    -							</div>
    -						</div>
    -						<div class="row mt-2">
    -							<div class="js-conditions-col col">
    -								<input type="hidden" name="rowid[${index}]" value="0" class="js-custom-row-id" />
    -								<input type="hidden" name="conditions[${index}]" class="js-conditions-value" value="{}" data-js="container">
    -								${this.container.find('.js-conditions-template').html()}
    -							</div>
    -						</div>
    -					</div>
    -					<div class="d-inline-flex text-right border-left" style="flex-grow:0">
    -						<div class="d-inline-block align-center" style="margin:auto 0;">
    -							<a href class="btn btn-danger ml-4 js-delete-row-action"><span class="fas fa-trash-alt"></span></a>
    -						</div>
    -					</div>
    -				</div>
    -			</div>
    -		</div>`
    -				);
    -				App.Fields.TimePeriod.register(row);
    -				this.registerDelBtnClick(row);
    -				App.Fields.Picklist.showSelect2ElementView(row.find('.select2'));
    -				this.registerConditionBuilder(
    -					row.find('.js-condition-builder').eq(0),
    -					this.container.find('.js-conditions-col').length
    -				);
    -				this.container.find('.js-custom-conditions').append(row);
    +				this.loadTemplates({
    +					mode: 'slaPolicyCustom',
    +					index: this.container.find('.js-custom-row').length,
    +					...this.getDefaultParam()
    +				}).then((data) => {
    +					let html = $(data);
    +					App.Fields.TimePeriod.register(html);
    +					this.registerDelBtnClick(html);
    +					App.Fields.Picklist.showSelect2ElementView(html.find('.select2'));
    +					this.registerConditionBuilder(
    +						html.find('.js-condition-builder').eq(0),
    +						this.container.find('.js-conditions-col').length
    +					);
    +					this.container.find('.js-custom-conditions').append(html);
    +				});
     			});
     		},
     
    @@ -288,7 +221,7 @@ Vtiger_Detail_Js(
     					targetModule: this.targetModule,
     					record: row.data('record-id'),
     					rowId: rowId
    -				}).done((data) => {
    +				}).done(() => {
     					progress.progressIndicator({ mode: 'hide' });
     					$(e.target).closest('.card').remove();
     					app.showNotify({
    @@ -336,11 +269,10 @@ Vtiger_Detail_Js(
     			this.container = this.getForm();
     			this.policyType = Number(this.container.find('[name="policy_type"]:checked').val());
     			this.targetModule = this.container.find('[name="target"]').val();
    -			this.businessHours = JSON.parse(this.container.find('.js-all-business-hours').val());
     			this.conditionBuilders = [];
     			this.conditionsBuildersContainers = [];
     			this.container.off('submit').on('submit', this.onSubmit.bind(this));
    -			this.container.find('.js-sla-policy-type-radio').on('click', (e) => this.onPolicyTypeChange());
    +			this.container.find('.js-sla-policy-type-radio').on('click', () => this.onPolicyTypeChange());
     			this.onPolicyTypeChange();
     			App.Fields.TimePeriod.register(this.container);
     			this.registerAddRecordBtnClick();
    @@ -356,7 +288,7 @@ Vtiger_Detail_Js(
     			if (detailViewForm.find('.js-sla-policy').length) {
     				this.initSlaPolicy();
     			}
    -			app.event.on('DetailView.Tab.AfterLoad', (event, data, instance) => {
    +			app.event.on('DetailView.Tab.AfterLoad', (_event, _data, instance) => {
     				if (instance.getForm().find('.js-sla-policy').length) {
     					instance.initSlaPolicy();
     				}
    

Vulnerability mechanics

Generated by null/stub on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.

References

4

News mentions

0

No linked articles in our index yet.