CVE-2022-3005: Improper Neutralization of Input During Web Page Generation ('Cross-site Scripting') (CWE-79)
value: Cross-site Scripting (XSS) - Stored in GitHub repository yetiforcecompany/yetiforcecrm prior to 6.4.0.
CWE description: The product does not neutralize or incorrectly neutralizes user-controllable input before it is placed in output that is used as a web page that is served to other users.
CVSS v3 base score: 5.4
Published: 2022-09-20T11:15Z
Repository: https://github.com/yetiforcecompany/yetiforcecrm
Language: Other
Files changed: config/version.php, languages/en-US/ServiceContracts.json, layouts/basic/modules/ServiceContracts/CustomConditions.tpl, layouts/basic/modules/ServiceContracts/SlaPolicy.tpl, layouts/basic/modules/ServiceContracts/SlaPolicyCustom.tpl, layouts/basic/modules/ServiceContracts/SlaPolicyTemplate.tpl, layouts/basic/modules/Settings/SlaPolicy/EditViewBlocks.tpl, modules/ServiceContracts/views/PolicyTemplatesAjax.php, public_html/layouts/basic/modules/ServiceContracts/resources/Detail.js
Commit date: 2022-08-26 15:09:34 +0200
Commit message: Improved SLA Policy elements

Diff: diff --git a/config/version.php b/config/version.php
index 4ef034057280..b2f6d0d70078 100644
--- a/config/version.php
+++ b/config/version.php
@@ -1,7 +1,7 @@
 <?php
 
 return [
-	'appVersion' => '6.4.15',
+	'appVersion' => '6.4.16',
 	'patchVersion' => '2022.08.26',
 	'lib_roundcube' => '0.3.1',
 ];
diff --git a/languages/en-US/ServiceContracts.json b/languages/en-US/ServiceContracts.json
index a2e23e34bb81..add2c50ef37f 100644
--- a/languages/en-US/ServiceContracts.json
+++ b/languages/en-US/ServiceContracts.json
@@ -46,15 +46,7 @@
     "LBL_POLICY_MODULE": "Module",
     "LBL_ADD_ENTRY": "Add entry",
     "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"
   }
 }
diff --git a/layouts/basic/modules/ServiceContracts/CustomConditions.tpl b/layouts/basic/modules/ServiceContracts/CustomConditions.tpl
index 0c8712c631b2..f8d49d9198cf 100644
--- a/layouts/basic/modules/ServiceContracts/CustomConditions.tpl
+++ b/layouts/basic/modules/ServiceContracts/CustomConditions.tpl
@@ -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}
diff --git a/layouts/basic/modules/ServiceContracts/SlaPolicy.tpl b/layouts/basic/modules/ServiceContracts/SlaPolicy.tpl
index 6fd34c0484c9..6ebe0d72d51e 100644
--- a/layouts/basic/modules/ServiceContracts/SlaPolicy.tpl
+++ b/layouts/basic/modules/ServiceContracts/SlaPolicy.tpl
@@ -23,11 +23,13 @@
 			</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 class="js-custom-conditions" data-js="container">
+					{include file=\App\Layout::getTemplatePath('CustomConditions.tpl', $MODULE_NAME)}
+				</div>
 			</div>
 		</div>
 		<div class="row">
 			<div class="col text-center">
 				<button class="btn btn-success js-sla-policy-save-btn">
diff --git a/layouts/basic/modules/ServiceContracts/SlaPolicyCustom.tpl b/layouts/basic/modules/ServiceContracts/SlaPolicyCustom.tpl
new file mode 100644
index 000000000000..c7539f5d42fa
--- /dev/null
+++ b/layouts/basic/modules/ServiceContracts/SlaPolicyCustom.tpl
@@ -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}
diff --git a/layouts/basic/modules/ServiceContracts/SlaPolicyTemplate.tpl b/layouts/basic/modules/ServiceContracts/SlaPolicyTemplate.tpl
new file mode 100644
index 000000000000..bb99edbfcb08
--- /dev/null
+++ b/layouts/basic/modules/ServiceContracts/SlaPolicyTemplate.tpl
@@ -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}
diff --git a/layouts/basic/modules/Settings/SlaPolicy/EditViewBlocks.tpl b/layouts/basic/modules/Settings/SlaPolicy/EditViewBlocks.tpl
index acbc0f029723..dcfcbdda3619 100644
--- a/layouts/basic/modules/Settings/SlaPolicy/EditViewBlocks.tpl
+++ b/layouts/basic/modules/Settings/SlaPolicy/EditViewBlocks.tpl
@@ -19,27 +19,27 @@
 				</div>
 				<div class="card">
 					<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)}
 						{/if}
 					</div>
 					<div class="card-body">
 						<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>
 							<div class="col-12 col-md-3">
 								<label>{\App\Language::translate('LBL_OPERATIONAL_HOURS','ServiceContracts')}</label>
@@ -64,23 +64,23 @@
 						</div>
 						<div class="form-group row border-top pt-4">
 							<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>
 					</div>
 				</div>
diff --git a/modules/ServiceContracts/actions/PolicyTemplatesAjax.php b/modules/ServiceContracts/actions/PolicyTemplatesAjax.php
deleted file mode 100644
index fa70199f65f2..000000000000
--- a/modules/ServiceContracts/actions/PolicyTemplatesAjax.php
+++ /dev/null
@@ -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();
-	}
-}
diff --git a/modules/ServiceContracts/views/PolicyTemplatesAjax.php b/modules/ServiceContracts/views/PolicyTemplatesAjax.php
new file mode 100644
index 000000000000..0f098f6636cc
--- /dev/null
+++ b/modules/ServiceContracts/views/PolicyTemplatesAjax.php
@@ -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);
+	}
+}
diff --git a/public_html/layouts/basic/modules/ServiceContracts/resources/Detail.js b/public_html/layouts/basic/modules/ServiceContracts/resources/Detail.js
index 6f88c50d8d53..bbc8909d93ea 100644
--- a/public_html/layouts/basic/modules/ServiceContracts/resources/Detail.js
+++ b/public_html/layouts/basic/modules/ServiceContracts/resources/Detail.js
@@ -2,10 +2,11 @@
  * InRelation SlaPolicy
  *
  * @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';
 
 Vtiger_Detail_Js(
 	'ServiceContracts_Detail_Js',
@@ -38,87 +39,64 @@ Vtiger_Detail_Js(
 		 */
 		showCustomSettings() {
 			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'
+						});
+					});
 			});
 		},
 
 		/**
 		 * On policy type change event handler
 		 */
 		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 {
 				this.hideAll();
 			}
@@ -174,11 +152,11 @@ Vtiger_Detail_Js(
 						const rowElem = this.container.find('.js-custom-row').eq(index);
 						rowElem.data('id', row.id);
 						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);
 					});
 				}
@@ -196,70 +174,25 @@ Vtiger_Detail_Js(
 		registerAddRecordBtnClick() {
 			let addPolicyBtn = this.container.find('.js-sla-policy-add-record-btn');
 			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);
+				});
 			});
 		},
 
 		/**
 		 * Register delete button click
@@ -286,11 +219,11 @@ Vtiger_Detail_Js(
 					module: 'ServiceContracts',
 					action: 'PolicyDeleteAjax',
 					targetModule: this.targetModule,
 					record: row.data('record-id'),
 					rowId: rowId
-				}).done((data) => {
+				}).done(() => {
 					progress.progressIndicator({ mode: 'hide' });
 					$(e.target).closest('.card').remove();
 					app.showNotify({
 						text: app.vtranslate('JS_SAVE_NOTIFY_OK'),
 						type: 'success',
@@ -334,15 +267,14 @@ Vtiger_Detail_Js(
 		 */
 		initSlaPolicy() {
 			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();
 			this.registerDelBtnClick(this.container);
 			$.each(this.container.find('.js-custom-conditions .js-condition-builder'), (index, col) => {
@@ -354,11 +286,11 @@ Vtiger_Detail_Js(
 			this._super();
 			let detailViewForm = this.getForm();
 			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();
 				}
 			});
 		}

Vulnerable code: 	'appVersion' => '6.4.15',
  },
  "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"
	<!-- 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>
									<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 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 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="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>
			{/if}
		{/foreach}
	</div>
				{include file=\App\Layout::getTemplatePath('CustomConditions.tpl', $MODULE_NAME)}
							{\App\Language::translate('LBL_EDIT',$QUALIFIED_MODULE)} - {$RECORD->getName()}
								<input type="text" name="name" class="form-control" value="{$RECORD->getName()}" data-validation-engine="validate[required,maxSize[255]]">
										<option value="{$MODULE_NAME}" {if \App\Module::getModuleName($RECORD->get('tabid')) === $MODULE_NAME}selected="selected" {/if}>{\App\Language::translate($MODULE_NAME, $MODULE_NAME)}</option>
									<input type="hidden" name="reaction_time" class="c-time-period" value="{$RECORD->get('reaction_time')}">
									<input type="hidden" name="idle_time" class="c-time-period" value="{$RECORD->get('idle_time')}">
									<input type="hidden" name="resolve_time" class="c-time-period" value="{$RECORD->get('resolve_time')}">
<?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();
	}
}

		 * Get template table
		 * @param {Array} rows
		 *
		 * @returns {String} HTML
		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>`;
		loadTemplates() {
			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));
				}
				this.hideAll().showTemplateSettings().loadTemplates();
					$.each(this.container.find('.js-custom-row'), (index, rowElem) => {
				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);
				}).done((data) => {
			this.businessHours = JSON.parse(this.container.find('.js-all-business-hours').val());
			this.container.find('.js-sla-policy-type-radio').on('click', (e) => this.onPolicyTypeChange());
			app.event.on('DetailView.Tab.AfterLoad', (event, data, instance) => {
Fixed code: 	'appVersion' => '6.4.16',
	{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']|escape}">
								</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 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 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="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>
		{/if}
	{/foreach}
				<div class="js-custom-conditions" data-js="container">
					{include file=\App\Layout::getTemplatePath('CustomConditions.tpl', $MODULE_NAME)}
				</div>
{*<!-- {[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}
{*<!-- {[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}
							{\App\Language::translate('LBL_EDIT',$QUALIFIED_MODULE)} - {\App\Purifier::encodeHtml($RECORD->getName())}
								<input type="text" name="name" class="form-control" value="{\App\Purifier::encodeHtml($RECORD->getName())}" data-validation-engine="validate[required,maxSize[255]]">
										<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>
									<input type="hidden" name="reaction_time" class="c-time-period" value="{$RECORD->get('reaction_time')|escape}">
									<input type="hidden" name="idle_time" class="c-time-period" value="{$RECORD->get('idle_time')|escape}">
									<input type="hidden" name="resolve_time" class="c-time-period" value="{$RECORD->get('resolve_time')|escape}">
<?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);
	}
}
 * @author      Radosław Skrzypczak <r.skrzypczak@yetiforce.com>
		 * Get default params
		 * @returns {Object}
		getDefaultParam() {
			return {
				module: 'ServiceContracts',
				view: 'PolicyTemplatesAjax',
				targetModule: this.targetModule,
				record: Number($('#recordId').val())
			};
		 * @param {Object} param
		 * @returns
		loadTemplates(param) {
			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'
						});
					});
				this.hideAll()
					.showTemplateSettings()
					.loadTemplates({ mode: 'slaPolicyTemplate', ...this.getDefaultParam() })
					.then((data) => {
						this.container.find('.js-sla-policy-template--container').html(data);
					});
					$.each(this.container.find('.js-custom-row'), (_index, rowElem) => {
				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);
				});
				}).done(() => {
			this.container.find('.js-sla-policy-type-radio').on('click', () => this.onPolicyTypeChange());
			app.event.on('DetailView.Tab.AfterLoad', (_event, _data, instance) => {