* * UPGRADE NOTE: * Do not edit or add to this file if you wish to upgrade AbanteCart to newer * versions in the future. If you wish to customize AbanteCart for your * needs please refer to http://www.AbanteCart.com for more information. */ if (!defined('DIR_CORE')) { header('Location: static_pages/'); } /** * Class AHtml * @method SelectboxHtmlElement buildSelectBox(array $data) * @method MultiSelectboxHtmlElement buildMultiSelectBox(array $data) * @method HiddenHtmlElement buildHidden(array $data) * @method MultiValueHtmlElement buildMultiValue(array $data) * @method MultiValueListHtmlElement buildMultiValueList(array $data) * @method SubmitHtmlElement buildSubmit(array $data) * @method InputHtmlElement buildInput(array $data) * @method PasswordHtmlElement buildPassword(array $data) * @method TextareaHtmlElement buildTextarea(array $data) * @method TextEditorHtmlElement buildTextEditor(array $data) * @method CheckboxHtmlElement buildCheckbox(array $data) * @method CheckboxGroupHtmlElement buildCheckboxGroup(array $data) * @method FileHtmlElement buildFile(array $data) * @method RadioHtmlElement buildRadio(array $data) * @method ButtonHtmlElement buildButton(array $data) * @method RatingHtmlElement buildRating(array $data) * @method CaptchaHtmlElement buildCaptcha(array $data) * @method ReCaptchaHtmlElement buildReCaptcha(array $data) * @method PasswordSetHtmlElement buildPasswordSet(array $data) * @method ResourceHtmlElement buildResource(array $data) * @method ResourceImageHtmlElement buildResourceImage(array $data) * @method DateHtmlElement buildDate(array $data) * @method EmailHtmlElement buildEmail(array $data) * @method NumberHtmlElement buildNumber(array $data) * @method PhoneHtmlElement buildPhone(array $data) * @method IPAddressHtmlElement buildIPAddress(array $data) * @method CountriesHtmlElement buildCountries(array $data) * @method ZonesHtmlElement buildZones(array $data) * @method PaginationHtmlElement buildPagination(array $data) * @method ModalHtmlElement buildModal(array $data) * @method LabelHtmlElement buildLabel(array $data) */ class AHtml extends AController { /** * @var Registry */ protected $registry; /** * @var array */ protected $args = []; /** * @var ARequest */ protected $request; /** * @param Registry $registry */ public function __construct($registry) { $this->registry = $registry; $this->request = $this->registry->get('request'); } /** * Magic method for html-element classes calls * * @param string $function_name * @param $args * * @return null|string * @throws AException */ public function __call(string $function_name, $args) { $class_name = ltrim($function_name, 'build') . 'HtmlElement'; if (class_exists($class_name)) { /** * @var SelectboxHtmlElement|HiddenHtmlElement| $item */ $item = new $class_name($args[0]); if (method_exists($item, 'getHtml')) { return $item->getHtml(); } else { unset($item); } } return null; } /** * PR Build sub URL * * @param string $rt * @param string $params * * @return string * @throws AException */ private function buildURL($rt, $params = '') { $subUrl = ''; //#PR Add admin path if we are in admin if (IS_ADMIN) { $subUrl .= '&s=' . ADMIN_PATH; } //add template if present if (!empty($this->request->get['sf'])) { $subUrl .= '&sf=' . $this->request->get['sf']; } //if in embed mode add response prefix if ($this->registry->get('config')->get('embed_mode')) { $subUrl .= '&embed_mode=1'; if (substr($rt, 0, 2) != 'r/') { $rt = 'r/' . $rt; } } return '?' . ($rt ? 'rt=' . $rt : '') . $params . $subUrl; } /** * Get non-secure home URL. * * @return string * * Note: Non-secure URL is base on store_url setting. If this setting is using https URL, all URLs will be secure * @throws AException */ public function getHomeURL() { $seo_prefix = $this->registry->get('config')->get('seo_prefix'); //for embed mode get home link with getURL if ($this->registry->get('config')->get('embed_mode')) { return $this->getURL('index/home'); } else { //get config_url first $home_url = $this->registry->get('config')->get('config_url') . $seo_prefix; if (!$home_url) { $home_url = defined('HTTP_SERVER') ? HTTP_SERVER . $seo_prefix : 'http://' . REAL_HOST . get_url_path($_SERVER['PHP_SELF']); } return $home_url; } } /** * Get non-secure URL. Read note below. * * @param string $rt * @param string $params * @param string $encode * * @return string * * Note: Non-secure URL is base on store_url setting. If this setting is using https URL, all URLs will be secure * @throws AException */ public function getNonSecureURL($rt, $params = '', $encode = '') { return $this->getURL($rt, $params, $encode, true); } /** * Get URL with auto-detection for protocol * * @param string $rt * @param string $params * @param string $encode * @param bool $nonsecure - force to be non-secure * * @return string * @throws AException */ public function getURL($rt, $params = '', $encode = '', $nonsecure = false) { $seo_prefix = $this->registry->get('config')->get('seo_prefix'); //detect if request is using HTTPS if ($nonsecure === false && HTTPS === true) { $server = defined('HTTPS_SERVER') ? HTTPS_SERVER . $seo_prefix : 'https://' . REAL_HOST . get_url_path($_SERVER['PHP_SELF']); } else { //to prevent garbage session need to check constant HTTP_SERVER $server = defined('HTTP_SERVER') ? HTTP_SERVER . $seo_prefix : 'http://' . REAL_HOST . get_url_path($_SERVER['PHP_SELF']); } if ($this->registry->get('config')->get('storefront_template_debug') && isset($this->request->get['tmpl_debug']) ) { $params .= '&tmpl_debug=' . $this->request->get['tmpl_debug']; } // add session id for cross-domain transition in secure mode if ($this->registry->get('config')->get('config_shared_session') && HTTPS === true) { $params .= '&session_id=' . session_id(); } //add token for embed mode with forbidden 3d-party cookies if ($this->registry->get('session')->data['session_mode'] == 'embed_token') { $params .= '&' . EMBED_TOKEN_NAME . '=' . session_id(); } return $server . INDEX_FILE . $this->url_encode($this->buildURL($rt, $params), $encode); } /** * Build secure URL with session token * * @param string $rt * @param string $params * @param string $encode * * @return string * @throws AException */ public function getSecureURL($rt, $params = '', $encode = '') { $session = $this->registry->get('session'); $config = $this->registry->get('config'); $seo_prefix = $config->get('seo_prefix'); // add session id for cross-domain transition in non-secure mode if ($config->get('config_shared_session') && HTTPS !== true) { $params .= '&session_id=' . session_id(); } $subUrl = $this->buildURL($rt, $params); if (IS_ADMIN === true || (defined('IS_API') && IS_API === true)) { //Add session token for admin and API if (isset($session->data['token']) && $session->data['token']) { $subUrl .= '&token=' . $this->registry->get('session')->data['token']; } } //add token for embed mode with forbidden 3d-party cookies if ($session->data['session_mode'] == 'embed_token') { $subUrl .= '&' . EMBED_TOKEN_NAME . '=' . session_id(); } if ($config->get('storefront_template_debug') && isset($this->request->get['tmpl_debug'])) { $subUrl .= '&tmpl_debug=' . $this->request->get['tmpl_debug']; } return HTTPS_SERVER . $seo_prefix . INDEX_FILE . $this->url_encode($subUrl, $encode); } /** * Build non-secure SEO URL * * @param string $rt * @param string $params * @param string $encode * * @return string * @throws AException */ public function getSEOURL($rt, $params = '', $encode = '') { //skip SEO for embed mode if ($this->registry->get('config')->get('embed_mode')) { return $this->getURL($rt, $params); } $this->loadModel('tool/seo_url'); //#PR Generate SEO URL based on standard URL //NOTE: SEO URL is non-secure url return $this->url_encode($this->model_tool_seo_url->rewrite($this->getNonSecureURL($rt, $params)), $encode); } /** * Build secure SEO URL * * @param string $rt * @param string $params * @param string $encode * * @return string * @throws AException */ public function getSecureSEOURL($rt, $params = '', $encode = '') { //add token for embed mode with forbidden 3d-party cookies if ($this->registry->get('session')->data['session_mode'] == 'embed_token') { $params .= '&' . EMBED_TOKEN_NAME . '=' . session_id(); } //#PR Generate SEO URL based on standard URL $this->loadModel('tool/seo_url'); return $this->url_encode($this->model_tool_seo_url->rewrite($this->getSecureURL($rt, $params)), $encode); } /** * This builds URL to the catalog to be used in admin * * @param string $rt * @param string $params * @param string $encode * @param bool $ssl * * @return string * @throws AException */ public function getCatalogURL($rt, $params = '', $encode = '', $ssl = false) { $seo_prefix = $this->registry->get('config')->get('seo_prefix'); //add token for embed mode with forbidden 3d-party cookies if ($this->registry->get('session')->data['session_mode'] == 'embed_token') { $params .= '&' . EMBED_TOKEN_NAME . '=' . session_id(); } $subUrl = '?' . ($rt ? 'rt=' . $rt : '') . $params; if ($this->registry->get('config')->get('config_ssl') == 2) { $ssl = true; } if ($ssl && parse_url($this->registry->get('config')->get('config_ssl_url'), PHP_URL_SCHEME) == 'https') { $HTTPS_SERVER = $this->registry->get('config')->get('config_ssl_url') . $seo_prefix; } else { $HTTPS_SERVER = HTTPS_SERVER . $seo_prefix; } $http = $ssl ? $HTTPS_SERVER : HTTP_SERVER . $seo_prefix; return $http . INDEX_FILE . $this->url_encode($subUrl, $encode); } /** * encode URL for & to be & * * @param string $url * @param bool $encode * * @return string */ public function url_encode($url, $encode = false) { if ($encode) { return str_replace('&', '&', (string)$url); } else { return $url; } } /** * Current URL built based on get params with ability to exclude params * * @param $filter_params array - array of vars to filter * * @return string - url without unwanted filter parameters * @throws AException */ public function currentURL($filter_params = []) { $params_arr = $this->request->get; //detect if there is RT in the params. $rt = 'index/home'; if (has_value($params_arr['rt'])) { $rt = $params_arr['rt']; $filter_params[] = 'rt'; } if (has_value($params_arr['s'])) { $filter_params[] = 's'; } $URI = '&' . $this->buildURI($params_arr, $filter_params); return $this->getURL($rt, $URI); } /** * URI encrypt parameters in URI * * @param $uri * * @return string - url without unwanted filter parameters * @internal param array $filter_params - array of vars to filter */ public function encryptURI($uri) { $encrypted = base64_encode($uri); if (strlen($encrypted) <= 250) { return '__e=' . $encrypted; } else { return $uri; } } /** * Build URI from array provided * * @param $params_arr array - data array to process * @param $filter_params array - array of vars to filter * * @return string - url without unwanted filter parameters */ public function buildURI($params_arr, $filter_params = []) { foreach ($filter_params as $rv) { unset($params_arr[$rv]); } return urldecode(http_build_query($params_arr, '', '&')); } /** * Filter query parameters from url. * * @param $url string - url to process * @param $filter_params string|array - single var or array of vars * * @return string - url without unwanted filter query parameters */ public function filterQueryParams($url, $filter_params = []) { list($url_part, $q_part) = explode('?', $url); parse_str($q_part, $q_vars); //build array if passed as string if (!is_array($filter_params)) { $filter_params = [$filter_params]; } foreach ($filter_params as $rv) { unset($q_vars[$rv]); } foreach ($q_vars as $key => $val) { $q_vars[$key] = $this->request->clean($val); } $new_qs = urldecode(http_build_query($q_vars, '', '&')); return $url_part . '?' . $new_qs; } /** * function returns text error or empty * * @param string $query * @param string $keyword * * @return string * @throws AException */ public function isSEOKeywordExists($query = '', $keyword = '') { if (!$keyword) { return ''; } $seo_key = SEOEncode($keyword); $db = $this->registry->get('db'); $sql = "SELECT * FROM " . $db->table('url_aliases') . " WHERE query<>'" . $db->escape($query) . "' AND keyword='" . $db->escape($seo_key) . "'"; $result = $db->query($sql); $kList = array_merge( array_column($result->rows, 'keyword'), array_map('basename', glob(DIR_ROOT . '/*', GLOB_ONLYDIR)) ); if (in_array($seo_key, $kList)) { $url = HTTP_CATALOG . $seo_key; return $this->language->getAndReplace('error_seo_keyword', '', [$url, $seo_key]); } return ''; } /** * create html code based on passed data * * @param $data - array with element data * sample * $data = array( * 'type' => 'input' //(hidden, textarea, selectbox, file...) * 'name' => 'input name' * 'value' => 'input value' // can be an array for select * 'style' => 'my-form' * 'form' => 'form id' // needed for unique element ID * * ); * * @return object * @throws AException */ public function buildElement($data) { return HtmlElementFactory::create($data); } /** * @return string * @throws AException */ public function getStoreSwitcher() { $registry = $this->registry; $view = new AView($this->registry, 0); //check if store_id is passed or in the session $store_id = $registry->get('config')->get('current_store_id'); //set store selector $stores = []; $hidden = []; $stores[0] = ['name' => $registry->get('language')->get('text_default')]; $registry->get('load')->model('setting/store'); /** * @var ModelSettingStore $model */ $model = $registry->get('model_setting_store'); //if loaded not default store - hide store switcher $default_store_settings = $model->getStore(0); if ($this->registry->get('config')->get('config_url') != $default_store_settings['config_url']) { return ''; } $result_stores = $model->getStores(); if (sizeof($result_stores) <= 1) { //hide store switcher if only one store return ''; } else { foreach ($result_stores as $rs) { $stores[$rs['store_id']] = [ 'name' => $rs['alias'] ?: $rs['name'], 'store_id' => $rs['store_id'], ]; } foreach ($registry->get('request')->get as $name => $value) { if ($name == 'store_id') { continue; } $hidden[$name] = $value; } $view->assign('all_stores', $stores); $view->assign('current_store', $stores[$store_id]['name']); $view->assign('hiddens', $hidden); $view->assign('text_select_store', $registry->get('language')->get('text_select_store')); return $view->fetch('form/store_switcher.tpl'); } } /** * @return string * @throws AException */ public function getContentLanguageSwitcher() { $registry = $this->registry; $view = new AView($this->registry, 0); $registry->get('load')->model('localisation/language'); $results = $registry->get('model_localisation_language')->getLanguages(); $template['languages'] = []; foreach ($results as $result) { if ($result['status']) { $template['languages'][] = [ 'name' => $result['name'], 'code' => $result['code'], 'image' => $result['image'], ]; } } if (sizeof($template['languages']) > 1) { //selected in selectbox $template['language_code'] = $registry->get('language')->getContentLanguageCode(); foreach ($registry->get('request')->get as $name => $value) { if ($name == 'content_language_code') { continue; } $template['hiddens'][$name] = $value; } } else { $template['languages'] = []; } $view->batchAssign($template); return $view->fetch('form/language_switcher.tpl'); } /** * @return string * @throws AException */ public function getContentLanguageFlags() { $registry = $this->registry; $view = new AView($this->registry, 0); $registry->get('load')->model('localisation/language'); $results = $registry->get('model_localisation_language')->getLanguages(); $template['languages'] = []; foreach ($results as $result) { if ($result['status']) { $template['languages'][] = [ 'name' => $result['name'], 'code' => $result['code'], 'image' => $result['image'], ]; } } if (sizeof($template['languages']) > 1) { //selected in selectbox $template['language_code'] = $registry->get('language')->getContentLanguageCode(); foreach ($registry->get('request')->get as $name => $value) { if ($name == 'content_language_code') { continue; } $template['hiddens'][$name] = $value; } } else { $template['languages'] = []; } $view->batchAssign($template); return $view->fetch('form/language_flags.tpl'); } /** * @param string $html - text that might contain internal links #admin# or #storefront# * $mode - 'href' create complete a tag or default just replace URL * @param string $type - can be 'message' to convert url into tag or empty * @param bool $for_admin - force mode for converting links to admin side from storefront scope (see AIM-class etc.) * * @return string - html code with parsed internal URLs * @throws AException */ public function convertLinks($html, $type = '', $for_admin = false) { $is_admin = (IS_ADMIN === true || $for_admin); $route_sections = $is_admin ? ["admin", "storefront"] : ["storefront"]; foreach ($route_sections as $rt_type) { preg_match_all( '/(#' . $rt_type . '#rt=)[A-z0-9\/_\-?&=%#]{1,255}(\b|")/', $html, $matches, PREG_OFFSET_CAPTURE ); if ($matches) { foreach ($matches[0] as $match) { $href = str_replace('?', '&', (string)$match[0]); if ($rt_type == 'admin') { if ($for_admin && IS_ADMIN !== true) { $href .= '&s=' . ADMIN_PATH; } $new_href = str_replace('#admin#', $this->getSecureURL('') . '&', $href); } else { $new_href = str_replace('#storefront#', $this->getCatalogURL('') . '&', $href); } $new_href = str_replace(['&', '&&', '&?'], '&', $new_href); $new_href = str_replace('?&', '?', $new_href); $new_href = str_replace('&', '&', $new_href); switch ($type) { case 'message': $new_href = '#link-text#'; break; default: break; } $html = str_replace($match[0], $new_href, $html); } } } return $html; } } /** * Class HtmlElementFactory */ class HtmlElementFactory { static private $available_elements = [ 'I' => [ 'type' => 'input', 'method' => 'buildInput', 'class' => 'InputHtmlElement', ], 'T' => [ 'type' => 'textarea', 'method' => 'buildTextarea', 'class' => 'TextareaHtmlElement', ], 'S' => [ 'type' => 'selectbox', 'method' => 'buildSelectbox', 'class' => 'SelectboxHtmlElement', ], 'M' => [ 'type' => 'multiselectbox', 'method' => 'buildMultiselectbox', 'class' => 'MultiSelectboxHtmlElement', ], 'R' => [ 'type' => 'radio', 'method' => 'buildRadio', 'class' => 'RadioHtmlElement', ], 'C' => [ 'type' => 'checkbox', 'method' => 'buildCheckbox', 'class' => 'CheckboxHtmlElement', ], 'G' => [ 'type' => 'checkboxgroup', 'method' => 'buildCheckboxgroup', 'class' => 'CheckboxgroupHtmlElement', ], 'U' => [ 'type' => 'file', 'method' => 'buildFile', 'class' => 'FileHtmlElement', ], 'K' => [ 'type' => 'captcha', 'method' => 'buildCaptcha', 'class' => 'CaptchaHtmlElement', ], 'J' => [ 'type' => 'recaptcha', 'method' => 'buildReCaptcha', 'class' => 'ReCaptchaHtmlElement', ], 'H' => [ 'type' => 'hidden', 'method' => 'buildHidden', 'class' => 'HiddenHtmlElement', ], 'P' => [ 'type' => 'multivalue', 'method' => 'buildMultivalue', 'class' => 'MultiValueHtmlElement', ], 'L' => [ 'type' => 'multivaluelist', 'method' => 'buildMultivalueList', 'class' => 'MultivalueListHtmlElement', ], 'D' => [ 'type' => 'date', 'method' => 'buildDateInput', 'class' => 'DateInputHtmlElement', ], 'E' => [ 'type' => 'email', 'method' => 'buildEmail', 'class' => 'EmailHtmlElement', ], 'N' => [ 'type' => 'number', 'method' => 'buildNumber', 'class' => 'NumberHtmlElement', ], 'F' => [ 'type' => 'phone', 'method' => 'buildPhone', 'class' => 'PhoneHtmlElement', ], 'A' => [ 'type' => 'IPaddress', 'method' => 'buildIPaddress', 'class' => 'IPaddressHtmlElement', ], 'O' => [ 'type' => 'countries', 'method' => 'buildCountries', 'class' => 'CountriesHtmlElement', ], 'Z' => [ 'type' => 'zones', 'method' => 'buildZones', 'class' => 'ZonesHtmlElement', ], 'B' => [ 'type' => 'label', 'method' => 'buildLabel', 'class' => 'LabelHtmlElement', ], ]; static private $elements_with_options = [ 'S', 'M', 'R', 'G', 'O', 'Z', ]; static private $multivalue_elements = [ 'M', 'R', 'G', ]; static private $elements_with_placeholder = [ 'S', 'I', 'M', 'O', 'Z', 'F', 'N', 'E', 'D', 'U', 'T', ]; /** * return array of HTML elements supported * array key - code of element * [ * type - element type * method - method in html class to get element html * class - element class * ] * * @static * @return array */ static function getAvailableElements() { return self::$available_elements; } /** * return array of elements indexes for elements which has options * * @static * @return array */ static function getElementsWithOptions() { return self::$elements_with_options; } /** * return array of elements indexes for elements which has options * * @static * @return array */ static function getElementsWithPlaceholder() { return self::$elements_with_placeholder; } /** * return array of elements indexes for elements which has options * * @static * @return array */ static function getMultivalueElements() { return self::$multivalue_elements; } /** * return element type * * @static * * @param $code - element code ( from $available_elements ) * * @return null | string */ static function getElementType($code) { if (!array_key_exists($code, self::$available_elements)) { return null; } return self::$available_elements[$code]['type']; } /** * @param $data * * @return HiddenHtmlElement | MultivalueListHtmlElement | MultivalueHtmlElement | SubmitHtmlElement * @return InputHtmlElement | PasswordHtmlElement | PaginationHtmlElement | TextareaHtmlElement * @return SelectboxHtmlElement | MultiSelectboxHtmlElement | CheckboxHtmlElement * @return CheckboxGroupHtmlElement | FileHtmlElement | RadioHtmlElement | ButtonHtmlElement * @return FormHtmlElement | RatingHtmlElement | CaptchaHtmlElement | ReCaptchaHtmlElement * @return PasswordSetHtmlElement | ResourceHtmlElement | ResourceImageHtmlElement | DateHtmlElement * @return EmailHtmlElement | NumberHtmlElement | PhoneHtmlElement | IPaddressHtmlElement * @return CountriesHtmlElement | ZonesHtmlElement | ModalHtmlElement * * @throws AException */ static function create($data) { $class = ucfirst($data['type'] . 'HtmlElement'); if (!class_exists($class)) { throw new AException(AC_ERR_LOAD, 'Error: Could not load HTML element ' . $data['type'] . '!'); } return new $class($data); } } /** * @abstract * Class HtmlElement * @property mixed $value * @property array $options * @property array $disabled_options * @property bool $required * @property bool $multilingual * @property string $template * @property array $history * @property AMessage $messages * * @property ARequest $request * @property AConfig $config * @property ALoader $load * @property ALanguageManager|ALanguage $language * @property AHtml $html * @property ADocument $document * @property CSRFToken $csrftoken */ abstract class HtmlElement { /** @var array */ protected $data = []; /** @var string */ public $element_id; /** @var AView */ protected $view; /** * @param array $data * * @throws AException */ function __construct($data) { if (!isset($data['value'])) { $data['value'] = ''; } if (isset($data['required']) && $data['required'] == 1) { $data['required'] = 'Y'; } $this->data = $data; $this->element_id = ($data['id'] ?? '') ? preformatTextID($data['id']) : preformatTextID($data['name'] ?? ''); if ($data['form'] ?? '') { $this->element_id = $data['form'] . '_' . $this->element_id; } $this->view = new AView(Registry::getInstance(), 0); } /** * @param string $name * * @return null|string */ public function __get($name) { if (array_key_exists($name, $this->data)) { return $this->data[$name]; } return Registry::getInstance()->get($name); } /** * @param string $name * @param mixed $value * * @return void */ public function __set(string $name, $value) { $this->data[$name] = $value; } /** * @param string $name * * @return bool */ public function __isset($name) { return isset($this->data[$name]); } /** * @return string */ public function __toString() { $javascript = ''; $this->data['javascript'] = $this->data['javascript'] ?? ''; if ($this->data['javascript']) { $javascript = $this->data['javascript']; } return $javascript . $this->getHtml(); } public function __serialize(): array { $viewData = $this->view->getData(); $this->data['view_data'] = $viewData; $this->view = null; return (array)$this; } public function __unserialize($data) { $this->view = new AView(Registry::getInstance(), 0); $this->view->batchAssign((array)$this->data['view_data']); unset($this->data['view_data']); if ($data) { foreach ($data as $key => $val) { $this->{$key} = $val; } } } public function addHookVars(array $vars) { if (!$vars) { return; } //add hook variables into tpl. can be used in *.post.tpl or *.pre.tpl foreach ($vars as $hkvName => $hkvValue) { $this->view->addHookVar($hkvName, $hkvValue); } } protected function extendAndBatchAssign(array $array) { $this->view->batchAssign( array_merge($this->data, $array) ); } protected function getMultiLingual() { if (is_object($this->language) && sizeof($this->language->getActiveLanguages()) > 1) { $multilingual = $this->multilingual; } else { $multilingual = false; } return $multilingual; } /** * @return null */ public function getHtml() { return null; } protected function _validate_options() { $this->disabled_options = (array)$this->disabled_options; //check case when all options are disabled $all_disabled = true; foreach ((array)$this->options as $id => $text) { if (!in_array($id, $this->disabled_options)) { $all_disabled = false; break; } } //if all disabled and options presents (for select-chosen element or empty) if ($all_disabled && $this->options) { if (in_array($this->data['type'], ['selectbox', 'multiselectbox'])) { $this->options = ['' => '------'] + $this->options; } $this->value = [0]; if ($this->required) { $seo_prefix = $this->config->get('seo_prefix'); $url = HTTPS_SERVER . $seo_prefix; $query_string = $this->request->server['QUERY_STRING']; if (!str_contains((string)$query_string, '_route_=')) { $url .= '?'; } else { $query_string = str_replace('_route_=', '', $query_string); } $url .= $query_string; $this->messages->saveWarning( 'Form Field #' . $this->element_id . ' Issue', 'Abnormal situation. All options of required field "' . $this->data['name'] . '" are disabled. URL: ' . $url . "" ); } } } } /** * Class HiddenHtmlElement * * @property string $element_id * @property string $name * @property string $value * @property string $attr */ class HiddenHtmlElement extends HtmlElement { /** * @return string * @throws AException */ public function getHtml() { $this->extendAndBatchAssign( [ 'id' => $this->element_id, 'name' => $this->name, 'value' => $this->value, 'attr' => $this->attr, ] ); return $this->view->fetch($this->template ?: 'form/hidden.tpl'); } } /** * Class MultiValueListHtmlElement * * @property string $element_id * @property string $name * @property array $values * @property array $postvars * @property string $content_url * @property string $edit_url * @property string $form * @property string $multivalue_hidden_id * @property string $return_to * @property string $with_sorting * @property array $text */ class MultiValueListHtmlElement extends HtmlElement { /** * @return string * @throws AException */ public function getHtml() { $data = [ 'id' => $this->element_id, 'name' => $this->name, 'values' => $this->values, 'content_url' => $this->content_url, 'edit_url' => $this->edit_url, 'postvars' => $this->postvars, 'form_name' => $this->form, 'multivalue_hidden_id' => $this->multivalue_hidden_id, 'return_to' => ($this->return_to ?: $this->form . '_' . $this->multivalue_hidden_id . '_item_count'), 'with_sorting' => $this->with_sorting, ]; $data['text']['delete'] = $this->text['delete'] ?: 'delete'; $data['text']['delete_confirm'] = $this->text['delete_confirm'] ?: 'Confirm to delete?'; $data['text']['column_action'] = $this->language->get('column_action'); $data['text']['column_sort_order'] = $this->language->get('text_sort_order'); $this->extendAndBatchAssign($data); return $this->view->fetch($this->template ?: 'form/multivalue_list.tpl'); } } /** * Class MultiValueHtmlElement * * @property string $element_id * @property string $name * @property string $selected_name * @property string $title * @property string $selected * @property string $content_url * @property string $form * @property string $return_to * @property string $no_save * @property array $text * @property int $popup_height * @property int $popup_width * @property array $js * @property array $postvars */ class MultiValueHtmlElement extends HtmlElement { /** * @return string * @throws AException */ public function getHtml() { $data = [ 'id' => $this->element_id, 'name' => $this->name, 'selected_name' => ($this->selected_name ?: 'selected[]'), 'title' => $this->title, 'selected' => $this->selected, 'content_url' => $this->content_url, 'postvars' => ($this->postvars ? json_encode($this->postvars) : ''), 'form_name' => $this->form, 'return_to' => ($this->return_to ?: $this->element_id . '_item_count'), 'no_save' => (isset($this->no_save) && $this->no_save), 'popup_height' => ((int)$this->popup_height ?: 620), 'popup_width' => ((int)$this->popup_width ?: 800), // custom triggers for dialog events (custom functions calls) 'js' => [ 'apply' => $this->js['apply'], 'cancel' => $this->js['cancel'], ], 'text_selected' => $this->text['selected'], 'text_edit' => $this->text['edit'] ?: 'Add / Edit', 'text_apply' => $this->text['apply'] ?: 'apply', 'text_save' => $this->text['save'] ?: 'save', 'text_reset' => $this->text['reset'] ?: 'reset', ]; $this->extendAndBatchAssign($data); return $this->view->fetch($this->template ?: 'form/multivalue_hidden.tpl'); } } /** * Class SubmitHtmlElement * * @property string $form, * @property string $name * @property string $value * @property string $attr * @property string $style * @property string $icon */ class SubmitHtmlElement extends HtmlElement { /** * @return string * @throws AException */ public function getHtml() { $this->extendAndBatchAssign( [ 'form' => $this->form, 'name' => $this->name, 'value' => $this->value, 'attr' => $this->attr, 'style' => $this->style, 'icon' => $this->icon, ] ); return $this->view->fetch($this->template ?: 'form/submit.tpl'); } } /** * Class InputHtmlElement * * @property string $element_id * @property string $default * @property string $value * @property string $name * @property string $attr * @property string $required * @property string $style * @property string $placeholder * @property string $regexp_pattern * @property string $error_text * @property string $help_url * @property bool $multilingual * @property array $list */ class InputHtmlElement extends HtmlElement { /** * @return string * @throws AException */ public function getHtml() { if (!isset($this->default)) { $this->default = ''; } if ($this->value == '' && !empty($this->default)) { $this->value = $this->default; } $history_url = ''; if ($this->history) { // Check if a field name contains square brackets or block_ prefix if (preg_match('/.*\[(.*?)\]$/', $this->name, $matches)) { $fieldName = $matches[1]; } else if (preg_match('/block_(.*?)$/', $this->name, $matches)) { $fieldName = $matches[1]; } else { $fieldName = $this->name; } $history_url = $this->html->getSecureURL( 'r/common/common/getDescriptionHistory', '&' . http_build_query( [ 'field' => $fieldName, 'table_name' => $this->history['table'], 'record_id' => $this->history['record_id'], //element ID (need to paste selected value back to the form field) 'elm_id' => $this->element_id ] ) ); if (is_object($this->language)) { $button_field_history = $this->language->get('button_field_history'); } } $this->extendAndBatchAssign( [ 'name' => $this->name, 'id' => $this->element_id, 'type' => 'text', 'value' => str_replace('"', '"', (string)$this->value), 'default' => $this->default, 'attr' => $this->attr, 'required' => $this->required, 'style' => $this->style, 'placeholder' => $this->placeholder, 'regexp_pattern' => trim((string)$this->regexp_pattern, '/'), 'error_text' => $this->error_text, 'multilingual' => $this->getMultiLingual(), 'help_url' => $this->help_url, 'list' => $this->list, 'history_url' => $history_url, 'button_field_history' => $button_field_history ?? '', ] ); return $this->view->fetch($this->template ?: 'form/input.tpl'); } } /** * Class InputHtmlElement * * @property string $element_id * @property string $default * @property string $value * @property string $name * @property string $attr * @property string $required * @property string $style * @property string $placeholder * @property string $regexp_pattern * @property string $error_text * @property string $help_url * @property bool $multilingual */ class ColorHtmlElement extends HtmlElement { /** * @return string * @throws AException */ public function getHtml() { if (!isset($this->default)) { $this->default = ''; } if ($this->value == '' && !empty($this->default)) { $this->value = $this->default; } $this->extendAndBatchAssign( [ 'name' => $this->name, 'id' => $this->element_id, 'type' => 'color', 'value' => str_replace('"', '"', (string)$this->value), 'default' => $this->default, 'attr' => $this->attr, 'required' => $this->required, 'style' => $this->style, 'error_text' => $this->error_text, 'multilingual' => $this->getMultiLingual(), 'help_url' => $this->help_url, ] ); return $this->view->fetch($this->template ?: 'form/input.tpl'); } } /** * Class PasswordHtmlElement * * @property string $element_id * @property string $default * @property string $value * @property string $name * @property string $attr * @property string $required * @property string $style * @property string $placeholder * @property string $regexp_pattern * @property string $error_text */ class PasswordHtmlElement extends HtmlElement { static $starPassword = '**********'; /** * @return string * @throws AException */ public function getHtml() { if (!isset($this->default)) { $this->default = ''; } if ($this->value == '' && !empty($this->default)) { $this->value = $this->default; } if (!$this->required && $this->value) { $value = self::$starPassword; } else { $value = ''; } $this->extendAndBatchAssign( [ 'name' => $this->name, 'id' => $this->element_id, 'type' => 'password', 'value' => $value, 'has_value' => ($this->value) ? 'Y' : 'N', 'attr' => $this->attr, 'required' => $this->required, 'style' => $this->style, 'placeholder' => $this->placeholder, 'regexp_pattern' => trim((string)$this->regexp_pattern, '/'), 'error_text' => $this->error_text, ] ); return $this->view->fetch($this->template ?: 'form/input.tpl'); } } /** * Class TextareaHtmlElement * * @property string $element_id * @property string $value * @property string $name * @property string $attr * @property string $required * @property string $style * @property string $placeholder * @property string $label_text * @property bool $multilingual * @property string $help_url */ class TextareaHtmlElement extends HtmlElement { /** * @return string * @throws AException */ public function getHtml() { $history_url = ''; if ($this->history) { // Check if a field name contains square brackets or block_ prefix if (preg_match('/.*\[(.*?)\]$/', $this->name, $matches)) { $fieldName = $matches[1]; } else if (preg_match('/block_(.*?)$/', $this->name, $matches)) { $fieldName = $matches[1]; } else { $fieldName = $this->name; } $history_url = $this->html->getSecureURL( 'r/common/common/getDescriptionHistory', '&' . http_build_query( [ 'field' => $fieldName, 'table_name' => $this->history['table'], 'record_id' => $this->history['record_id'], //element ID (need to paste selected value back to the form field) 'elm_id' => $this->element_id ] ) ); if (is_object($this->language)) { $button_field_history = $this->language->get('button_field_history'); } } $this->extendAndBatchAssign( [ 'name' => $this->name, 'id' => $this->element_id, 'value' => $this->value, 'ovalue' => htmlentities($this->value, ENT_QUOTES, 'UTF-8'), 'attr' => $this->attr, 'required' => $this->required, 'style' => $this->style, 'placeholder' => $this->placeholder, 'label_text' => $this->label_text, 'multilingual' => $this->getMultiLingual(), 'help_url' => $this->help_url, 'history_url' => $history_url, 'button_field_history' => $button_field_history ?? '', ] ); return $this->view->fetch($this->template ?: 'form/textarea.tpl'); } } /** * Class TextEditorHtmlElement * * @property ALanguage $language * @property string $element_id * @property string $value * @property string $name * @property string $attr * @property string $required * @property string $style * @property string $placeholder * @property string $base_url - need for inserting pictures into html for emails * @property bool $preview - enable/disable visual mode of tinymce. default true * @property string $preview_url - custom preview url * @property string $js_onload - custom js-code will be run on doc ready * @property bool $multilingual * @property array $history - array with table, record_id */ class TextEditorHtmlElement extends HtmlElement { /** * @return string * @throws AException */ public function getHtml() { $this->multilingual = $this->multilingual ?? ''; $data = [ 'name' => $this->name, 'id' => $this->element_id, 'value' => $this->value, 'ovalue' => htmlentities($this->value, ENT_QUOTES, 'UTF-8'), 'attr' => $this->attr ?? '', 'required' => $this->required ?? false, 'style' => $this->style ?? '', 'placeholder' => $this->placeholder ?? '', 'base_url' => $this->base_url ?? '', 'preview' => $this->preview ?? true, 'preview_url' => $this->preview_url ?? '', 'js_onload' => $this->js_onload ?? '' ]; if ($this->history) { // Check if a field name contains square brackets or if (preg_match('/.*\[(.*?)\]$/', $this->name, $matches)) { $fieldName = $matches[1]; } else if (preg_match('/block_(.*?)$/', $this->name, $matches)) { $fieldName = $matches[1]; } else { $fieldName = $this->name; } $data['history_url'] = $this->html->getSecureURL( 'r/common/common/getDescriptionHistory', '&' . http_build_query( [ 'field' => $fieldName, 'table_name' => $this->history['table'], 'record_id' => $this->history['record_id'], //element ID (need to paste selected value back to the form field) 'elm_id' => $this->element_id ] ) ); } if (is_object($this->language)) { if (sizeof($this->language->getActiveLanguages()) > 1) { $data['multilingual'] = $this->multilingual; } $data['language_code'] = $this->language->getContentLanguageCode(); $data['tab_text'] = $this->language->get('tab_text'); $data['tab_visual'] = $this->language->get('tab_visual'); $data['button_add_media'] = $this->language->get('button_add_media'); $data['button_preview'] = $this->language->get('button_preview'); $data['button_field_history'] = $this->language->get('button_field_history'); } $this->extendAndBatchAssign($data); return $this->view->fetch($this->template ?: 'form/text_editor.tpl'); } } /** * Class SelectboxHtmlElement * * @property string $element_id * @property string $value * @property string $name * @property string $attr * @property string $required * @property string $style * @property string $placeholder * @property array $options * @property array $disabled_options * @property bool $disabled * @property string $ajax_url * @property string $search_mode * @property string $help_url */ class SelectboxHtmlElement extends HtmlElement { /** * @return string * @throws AException */ public function getHtml() { if (!is_array($this->value)) { $this->value = [$this->value => (string)$this->value]; } $this->options = !$this->options ? [] : (array)$this->options; foreach ($this->options as &$opt) { $opt = (string)$opt; } unset($opt); $text_continue_typing = $text_looking_for = ''; if (is_object($this->language)) { $text_continue_typing = $this->language->get('text_continue_typing', '', true); $text_looking_for = $this->language->get('text_looking_for', '', true); } $text_continue_typing = !$text_continue_typing || $text_continue_typing == 'text_continue_typing' ? 'Continue typing ...' : $text_continue_typing; $text_looking_for = !$text_looking_for || $text_looking_for == 'text_looking_for' ? 'Looking for' : $text_looking_for; $this->_validate_options(); $data = [ 'name' => $this->name, 'id' => $this->element_id, 'value' => $this->value, 'ovalue' => $this->value, 'options' => $this->options, 'disabled' => $this->disabled, 'disabled_options' => $this->disabled_options, 'attr' => $this->attr, 'required' => $this->required, 'style' => $this->style, 'placeholder' => $this->placeholder, 'ajax_url' => $this->ajax_url, //if mode of data load is ajax based 'search_mode' => $this->search_mode, 'text_continue_typing' => $text_continue_typing, 'text_looking_for' => $text_looking_for, 'help_url' => $this->help_url, ]; if ($this->template) { $template = $this->template; } elseif (str_contains((string)$this->style, 'chosen')) { $data['ajax_url'] = $this->ajax_url; //if mode of data load is ajax based $data['text_continue_typing'] = $text_continue_typing; $data['text_looking_for'] = $text_looking_for; $template = 'form/chosen_select.tpl'; } else { $template = 'form/selectbox.tpl'; } $this->extendAndBatchAssign($data); return $this->view->fetch($template); } } /** * Class MultiSelectBoxHtmlElement * * @property string $element_id * @property array|mixed $value * @property string $name * @property string $attr * @property string $required * @property string $style * @property string $placeholder * @property array $options * @property array $disabled_options * @property string $filter_params - some additional parameters * @property string $ajax_url * @property string|array $option_attr * @property string $help_url * @property bool $disabled */ class MultiSelectBoxHtmlElement extends HtmlElement { /** * @return string * @throws AException */ public function getHtml() { if (!is_array($this->value)) { $this->value = [$this->value => $this->value]; } $this->_validate_options(); $data = [ 'name' => $this->name, 'id' => $this->element_id, 'value' => $this->value, 'options' => $this->options, 'disabled_options' => $this->disabled_options, 'disabled' => $this->disabled, 'attr' => $this->attr . ' multiple="multiple" ', 'required' => $this->required, 'style' => $this->style, 'placeholder' => $this->placeholder, 'filter_params' => $this->filter_params, 'help_url' => $this->help_url, ]; if ($this->template) { $template = $this->template; } elseif (str_contains((string)$this->style, 'chosen')) { $option_attr = $this->option_attr && !is_array($this->option_attr) ? [$this->option_attr] : $this->option_attr; $option_attr = $option_attr ?: []; $data['ajax_url'] = $this->ajax_url; //if mode of data load is ajax based $data['option_attr'] = $option_attr; //list of custom html5 attributes for options of selectbox $data['text_continue_typing'] = $this->language->get('text_continue_typing', '', true); $data['text_looking_for'] = $this->language->get('text_looking_for', '', true); $template = 'form/chosen_select.tpl'; } else { $template = 'form/selectbox.tpl'; } $this->extendAndBatchAssign($data); return $this->view->fetch($template); } } /** * Class CheckboxHtmlElement * * @property string|int $value * @property bool $checked * @property string $element_id * @property string $name * @property string $attr * @property string $required * @property string $style * @property string $label_text * @property string $help_url */ class CheckboxHtmlElement extends HtmlElement { /** * @return string * @throws AException */ public function getHtml() { $checked = false; if ($this->template) { $tpl = $this->template; } elseif (str_contains((string)$this->style, 'btn_switch')) { //for switch button NOTE: value is binary (1 or 0)!!! $checked = is_null($this->checked) && $this->value || (bool)$this->checked; if ($checked) { $this->value = 1; } else { $this->value = 0; } $tpl = 'form/switch.tpl'; } else {//for generic checkbox NOTE: in this case value must be any and goes to tpl as-is $checked = !is_null($this->checked) ? $this->checked : false; $tpl = 'form/checkbox.tpl'; } $text_on = $text_off = ''; if (is_object($this->language)) { $text_on = $this->language->get('text_on', '', true); $text_off = $this->language->get('text_off', '', true); } $text_on = !$text_on || $text_on == 'text_on' ? 'ON' : $text_on; $text_off = !$text_off || $text_off == 'text_off' ? 'OFF' : $text_off; $this->extendAndBatchAssign( [ 'name' => $this->name, 'id' => $this->element_id, 'value' => $this->value, 'attr' => $this->attr, 'required' => $this->required, 'label_text' => $this->label_text, 'checked' => $checked, 'style' => $this->style, 'text_on' => $text_on, 'text_off' => $text_off, 'help_url' => $this->help_url, ] ); return $this->view->fetch($tpl); } } /** * Class CheckboxGroupHtmlElement * * @property string|int $value * @property array $options * @property array $disabled_options * @property string $element_id * @property string $name * @property string $attr * @property string $required * @property string $style * @property bool $scrollbox * @property string $help_url * @property string $placeholder */ class CheckboxGroupHtmlElement extends HtmlElement { public function getHtml() { $this->value = !is_array($this->value) ? [$this->value => $this->value] : $this->value; $this->_validate_options(); if ($this->options && is_array($this->options)) { $option_keys = array_keys($this->options); foreach ($this->value as $value) { if ($value && !in_array($value, $option_keys)) { $this->options += [$value => 'unknown']; } } } $this->extendAndBatchAssign( [ 'name' => $this->name, 'id' => $this->element_id, 'value' => $this->value, 'options' => $this->options, 'disabled_options' => $this->disabled_options, 'attr' => $this->attr . ' multiple="multiple" ', 'required' => $this->required, 'scrollbox' => $this->scrollbox, 'style' => $this->style, 'placeholder' => $this->placeholder, 'help_url' => $this->help_url, ] ); if ($this->template) { $template = $this->template; } elseif (str_contains((string)$this->style, 'chosen')) { $template = 'form/chosen_select.tpl'; } else { $template = 'form/checkboxgroup.tpl'; } return $this->view->fetch($template); } } /** * Class FileHtmlElement * * @property string $element_id * @property string $name * @property string $attr * @property string $required * @property string $style * @property string $help_url * @property string $placeholder */ class FileHtmlElement extends HtmlElement { /** * @return string * @throws AException */ public function getHtml() { $this->extendAndBatchAssign( [ 'name' => $this->name, 'id' => $this->element_id, 'attr' => $this->attr, 'required' => $this->required, 'style' => $this->style, 'default_text' => $this->language->get('text_click_browse_file'), 'text_browse' => $this->language->get('text_browse'), 'placeholder' => $this->placeholder, 'help_url' => $this->help_url, ] ); return $this->view->fetch($this->template ?: 'form/file.tpl'); } } /** * Class RadioHtmlElement * * @property string $value * @property string $element_id * @property string $name * @property string $attr * @property string $required * @property string $style * @property array $options * @property array $disabled_options * @property array $disabled * @property string $help_url */ class RadioHtmlElement extends HtmlElement { public function getHtml() { //if no option provided, default to value if (empty($this->options) && has_value($this->value)) { $this->options = [$this->value => $this->value]; } $this->_validate_options(); $this->extendAndBatchAssign( [ 'name' => $this->name, 'id' => $this->element_id, 'value' => $this->value, 'options' => $this->options, 'disabled_options' => $this->disabled_options, 'disabled' => $this->disabled, 'attr' => $this->attr, 'required' => $this->required, 'style' => $this->style, 'help_url' => $this->help_url, ] ); return $this->view->fetch($this->template ?: 'form/radio.tpl'); } } /** * Class ButtonHtmlElement * * @property string $element_id * @property string $text * @property string $title * @property string $attr * @property string $href * @property string $style * @property string $href_class * @property string $icon * @property string $target */ class ButtonHtmlElement extends HtmlElement { public function getHtml() { $this->extendAndBatchAssign( [ 'text' => $this->text, 'title' => $this->title, 'id' => $this->element_id, 'attr' => $this->attr, 'style' => $this->style, 'href' => $this->href, 'href_class' => $this->href_class, 'icon' => $this->icon, 'target' => $this->target, ] ); return $this->view->fetch($this->template ?: 'form/button.tpl'); } } /** * Class FormHtmlElement * * @property string $name * @property string $action * @property string $attr * @property string $method * @property string $style * @property string $enctype * @property bool $csrf */ class FormHtmlElement extends HtmlElement { public function getHtml() { $this->method = $this->method ?: 'post'; $this->enctype = $this->enctype ?: 'application/x-www-form-urlencoded'; $data = [ 'id' => $this->name, 'name' => $this->name, 'action' => $this->action, 'method' => $this->method, 'attr' => $this->attr, 'style' => $this->style, 'enctype' => $this->enctype, ]; //add CSRF token if ($this->csrf === true) { $csrftoken = $this->csrftoken; $data['csrfinstance'] = $csrftoken->setInstance(); $data['csrftoken'] = $csrftoken->setToken(); } $this->extendAndBatchAssign($data); return $this->view->fetch($this->template ?: 'form/form_open.tpl') . $this->view->fetch('form/form_csrf.tpl'); } } /** * Class RatingHtmlElement * * @property string $element_id * @property string $name * @property string $value * @property array $options * @property bool $required */ class RatingHtmlElement extends HtmlElement { public function getHtml() { $this->extendAndBatchAssign( [ 'name' => $this->name, 'id' => $this->element_id, 'value' => $this->value, 'options' => $this->options, 'style' => 'star', 'required' => $this->required, ] ); return $this->view->fetch($this->template ?: 'form/rating.tpl'); } } /** * Class CaptchaHtmlElement * * @property string $element_id * @property string $name * @property string $style * @property string $attr * @property bool $required * @property string $placeholder * @property Registry $registry */ class CaptchaHtmlElement extends HtmlElement { public function getHtml() { $this->extendAndBatchAssign( [ 'name' => $this->name, 'id' => $this->element_id, //TODO: remove deprecated attribute aform_field_type 'attr' => 'aform_field_type="captcha" ' . $this->attr . ' data-aform-field-type="captcha"', 'style' => $this->style, 'required' => $this->required, 'captcha_url' => $this->html->getURL('common/captcha'), 'placeholder' => $this->placeholder, ] ); return $this->view->fetch($this->template ?: 'form/captcha.tpl'); } } /** * Class ReCaptchaHtmlElement * * @property string $element_id * @property string $name * @property string $attr * @property string $language_code * @property string $recaptcha_site_key */ class ReCaptchaHtmlElement extends HtmlElement { public function getHtml() { $this->extendAndBatchAssign( [ 'name' => $this->name ?: 'g-recaptcha-response', 'id' => $this->element_id, 'attr' => $this->attr . ' data-aform-field-type="captcha"', 'language_code' => $this->language_code, 'recaptcha_site_key' => trim((string)$this->recaptcha_site_key), 'recaptcha_v3' => $this->config->get('account_recaptcha_v3') ?: 0, ] ); return $this->view->fetch($this->template ?: 'form/recaptcha.tpl'); } } /** * Class PasswordsetHtmlElement * * @property string $element_id * @property string $name * @property string $value * @property string $style * @property string $attr * @property bool $required * @property string $placeholder */ class PasswordsetHtmlElement extends HtmlElement { public function getHtml() { $this->extendAndBatchAssign( [ 'name' => $this->name, 'id' => $this->element_id, 'value' => $this->value, 'attr' => $this->attr, 'style' => $this->style, 'required' => $this->required, 'text_confirm_password' => $this->language->get('text_confirm_password'), 'placeholder' => $this->placeholder, ] ); return $this->view->fetch($this->template ?: 'form/passwordset.tpl'); } } /** * Class ResourceHtmlElement * * @property string $rl_type - image or audio or pdf etc * @property string $element_id * @property string $name * @property string $resource_path * @property int $resource_id * @property string $object_name * @property string $object_id * @property bool $hide - sign to hide image preview * @property string $placeholder */ class ResourceHtmlElement extends HtmlElement { function __construct($data) { parent::__construct($data); } public function getHtml() { if (empty($this->rl_type)) { throw new AException( AC_ERR_LOAD, 'Error: Could not load HTML element of resource library. Resource type not given!' ); } $data = [ 'id' => $this->element_id, 'wrapper_id' => $this->element_id . '_wrapper', 'name' => $this->name, 'resource_path' => $this->resource_path, 'resource_id' => $this->resource_id, 'object_name' => $this->object_name, 'object_id' => $this->object_id, 'rl_type' => $this->rl_type, 'hide' => (bool)$this->hide, ]; if (!$data['resource_id'] && $data['resource_path']) { $path = ltrim($data['resource_path'], $data['rl_type'] . '/'); $r = new AResource($data['rl_type']); $data['resource_id'] = $r->getIdFromHexPath($path); } if ($data['resource_id'] && !$data['resource_path']) { $r = new AResource($data['rl_type']); $info = $r->getResource($data['resource_id']); if ($info['resource_path']) { $data['resource_path'] = $data['rl_type'] . '/' . $info['resource_path']; } else { //for code-resources $data['resource_path'] = $data['resource_id']; } } $this->extendAndBatchAssign($data); return $this->view->fetch($this->template ?: 'form/resource.tpl'); } } /** * Class ResourceImageHtmlElement * * @property int $width * @property int $height * @property string $attr * @property string $url */ class ResourceImageHtmlElement extends HtmlElement { function __construct($data) { parent::__construct($data); } public function getHtml() { $this->extendAndBatchAssign( [ 'url' => $this->url, 'width' => $this->width, 'height' => $this->height, 'attr' => $this->attr, ] ); return $this->view->fetch($this->template ?: 'common/resource_image.tpl'); } } /** * Class DateHtmlElement * * @property string $element_id * @property string $name * @property string $end_date_name * @property string $start_date_name * @property string $value * @property string $default * @property string $style * @property string $attr * @property array $js_custom_config * @property string $mode * @property bool $required * @property string $dateformat * @property string $highlight * @property string $help_url */ class DateHtmlElement extends HtmlElement { /** * @return string * @throws AException */ public function getHtml() { if (!isset($this->default)) { $this->default = ''; } if ($this->value == '' && !empty($this->default)) { $this->value = $this->default; } $this->element_id = preg_replace('/[\[+\]]/', '_', $this->element_id); if ($this->end_date_name || $this->start_date_name) { $this->mode = 'range'; } $this->extendAndBatchAssign( [ 'name' => $this->name, 'id' => $this->element_id, 'type' => 'text', 'value' => $this->value, 'default' => $this->default, 'required' => $this->required, 'attr' => $this->attr, //js config of initialization of date field 'js_custom_config' => (object)$this->js_custom_config, 'mode' => in_array($this->mode, ["single", "multiple", "range"]) ? $this->mode : "single", 'style' => $this->style, 'dateformat' => $this->dateformat ?: format4Datepicker($this->language->get('date_format_short')), 'help_url' => $this->help_url, 'end_date_name' => $this->end_date_name, 'start_date_name' => $this->start_date_name, 'text_reset' => $this->language->get('button_reset'), ] ); /** @see public_html/admin/view/default/template/form/date.tpl */ return $this->view->fetch($this->template ?: 'form/date.tpl'); } } /** * Class EmailHtmlElement * * @property string $element_id * @property string $name * @property string $value * @property string $default * @property string $style * @property string $attr * @property bool $required * @property string $placeholder * @property string $regexp_pattern * @property string $error_text * @property string $help_url */ class EmailHtmlElement extends HtmlElement { /** * @return string * @throws AException */ public function getHtml() { if (!isset($this->default)) { $this->default = ''; } if ($this->value == '' && !empty($this->default)) { $this->value = $this->default; } $this->extendAndBatchAssign( [ 'name' => $this->name, 'id' => $this->element_id, 'type' => 'email', 'value' => str_replace('"', '"', (string)$this->value), 'default' => $this->default, //TODO: remove deprecated attribute aform_field_type 'attr' => 'aform_field_type="email" ' . $this->attr . ' data-aform-field-type="captcha"', 'required' => $this->required, 'style' => $this->style, 'placeholder' => $this->placeholder, 'regexp_pattern' => trim((string)$this->regexp_pattern, '/'), 'error_text' => $this->error_text, 'help_url' => $this->help_url, ] ); return $this->view->fetch($this->template ?: 'form/input.tpl'); } } /** * Class NumberHtmlElement * * @property string $element_id * @property string $name * @property string $value * @property int $min * @property int $max * @property string $default * @property string $style * @property string $attr * @property bool $required * @property string $placeholder * @property string $regexp_pattern * @property string $error_text * @property string $help_url */ class NumberHtmlElement extends HtmlElement { /** * @return string * @throws AException */ public function getHtml() { if (!isset($this->default)) { $this->default = ''; } if ($this->value == '' && !empty($this->default)) { $this->value = $this->default; } $this->extendAndBatchAssign( [ 'name' => $this->name, 'id' => $this->element_id, 'type' => 'number', 'value' => str_replace('"', '"', (string)$this->value), 'min' => $this->min, 'max' => $this->max, 'default' => $this->default, //TODO: remove deprecated attribute aform_field_type 'attr' => 'aform_field_type="number" ' . $this->attr . ' data-aform-field-type="captcha"', 'required' => $this->required, 'style' => $this->style, 'placeholder' => $this->placeholder, 'regexp_pattern' => trim((string)$this->regexp_pattern, '/'), 'error_text' => $this->error_text, 'help_url' => $this->help_url, ] ); return $this->view->fetch($this->template ?: 'form/input.tpl'); } } /** * Class PhoneHtmlElement * * @property string $element_id * @property string $name * @property string $value * @property string $default * @property string $style * @property string $attr * @property bool $required * @property string $placeholder * @property string $regexp_pattern * @property string $error_text * @property string $help_url */ class PhoneHtmlElement extends HtmlElement { /** * @return string * @throws AException */ public function getHtml() { if (!isset($this->default)) { $this->default = ''; } if ($this->value == '' && !empty($this->default)) { $this->value = $this->default; } $telJs = '/javascript/intl-tel-input/js/intlTelInput.min.js'; if (is_file($this->view->templateResource($telJs, 'file'))) { $this->document->addScript($this->view->templateResource($telJs)); $this->document->addStyle( [ 'href' => $this->view->templateResource('/javascript/intl-tel-input/css/intlTelInput.css'), 'rel' => 'stylesheet', ] ); } $this->extendAndBatchAssign( [ 'name' => $this->name, 'id' => $this->element_id, 'type' => 'tel', 'value' => str_replace('"', '"', (string)$this->value), 'default' => $this->default, 'attr' => $this->attr, 'required' => $this->required, 'style' => $this->style, 'placeholder' => $this->placeholder, 'regexp_pattern' => trim((string)$this->regexp_pattern, '/'), 'error_text' => $this->error_text, 'help_url' => $this->help_url, ] ); return $this->view->fetch($this->template ?: 'form/phone.tpl'); } } /** * Class IPaddressHtmlElement * * @property string $element_id * @property string $name * @property string $attr */ class IPAddressHtmlElement extends HtmlElement { public function getHtml() { $this->extendAndBatchAssign( [ 'id' => $this->element_id, 'name' => $this->name, 'value' => $this->request->getRemoteIP(), //TODO: remove deprecated attribute aform_field_type 'attr' => 'aform_field_type="ipaddress" ' . $this->attr . ' data-aform-field-type="captcha"', ] ); return $this->view->fetch($this->template ?: 'form/hidden.tpl'); } } /** * Class CountriesHtmlElement * * @property string $element_id * @property string $name * @property string $value * @property array $options * @property string $style * @property string $attr * @property bool $required * @property string $placeholder * @property string $help_url */ class CountriesHtmlElement extends HtmlElement { public function __construct($data) { parent::__construct($data); /** @var ModelLocalisationCountry $mdl */ $mdl = $this->load->model('localisation/country'); $results = $mdl->getCountries(); $this->options = array_column($results, 'name', 'name'); } public function getHtml() { if (!is_array($this->value)) { $this->value = [$this->value => (string)$this->value]; } $this->options = !$this->options ? [] : $this->options; $this->extendAndBatchAssign( [ 'name' => $this->name, 'id' => $this->element_id, 'value' => $this->value, 'options' => $this->options, 'attr' => $this->attr, 'required' => $this->required, 'style' => $this->style, 'placeholder' => $this->placeholder, 'help_url' => $this->help_url, ] ); return $this->view->fetch($this->template ?: 'form/selectbox.tpl'); } } /** * Class ZonesHtmlElement * * @property string $element_id * @property string $name * @property string $value * @property string $submit_mode * @property int|array $default_value * @property string $zone_field_name * @property string $default_zone_field_name * @property string $default_zone_name * @property array $default_zone_value * @property string $zone_name * @property array $options * @property array $zone_options * @property array|string|int $zone_value * @property string $style * @property string $attr * @property bool $required * @property string $placeholder * @property string $help_url */ class ZonesHtmlElement extends HtmlElement { public function __construct($data) { parent::__construct($data); /** @var ModelLocalisationCountry $mdl */ $mdl = $this->load?->model('localisation/country'); $results = $mdl->getCountries(); $this->zone_options = []; $this->default_zone_field_name = 'zone_id'; $config_country_id = $this->config->get('config_country_id'); $options = []; foreach ($results as $c) { if ($c['country_id'] == $config_country_id) { $this->default_value = $this->submit_mode == 'id' ? [$config_country_id] : [$c['name'] => $c['name']]; } if ($this->submit_mode == 'id') { $options[$c['country_id']] = $c['name']; } else { $options[$c['name']] = $c['name']; } } $this->options = $options; } public function getHtml() { if (!is_array($this->value)) { if (!$this->value) { $this->value = []; } else { $this->value = [$this->value => (string)$this->value]; } } $this->zone_name = !$this->zone_name ? '' : urlencode($this->zone_name); $this->default_zone_value = []; $this->options = !$this->options ? [] : $this->options; $this->element_id = preg_replace('/[\[+\]+]/', '_', $this->element_id); $html = new AHtml(Registry::getInstance()); if ($this->submit_mode == 'id') { $url = $html->getSecureURL('common/zone'); } else { $url = $html->getSecureURL('common/zone/names'); } /** @var ModelLocalisationZone $model_zone */ $model_zone = $this->load->model('localisation/zone'); $config_country_id = $this->config->get('config_country_id'); if ($this->submit_mode == 'id') { $id = $this->value ? key($this->value) : $config_country_id; $results = $model_zone->getZonesByCountryId($id); } else { if ($this->value) { $name = current($this->value); } else { /** @var ModelLocalisationCountry $mdl */ $mdl = $this->load->model('localisation/country'); $temp = $mdl->getCountry($config_country_id); $name = $temp['name']; } $results = $model_zone->getZonesByCountryName($name); } if (!is_array($this->zone_value)) { $this->zone_value = $this->zone_value ? [(string)$this->zone_value => (string)$this->zone_value] : []; } $config_zone_id = $this->config->get('config_zone_id'); $zone_options = []; foreach ($results as $result) { // default zone_id is zone of shop if ($result['zone_id'] == $config_zone_id) { $this->default_zone_value = $this->submit_mode == 'id' ? [$config_zone_id] : [$result['name'] => $result['name']]; $this->default_zone_name = $result['name']; } if ($this->submit_mode == 'id') { $zone_options[$result['zone_id']] = $result['name']; } else { $zone_options[$result['name']] = $result['name']; } $this->zone_options = $zone_options; } $this->extendAndBatchAssign( [ 'name' => $this->name, 'id' => $this->element_id, 'value' => $this->value ?: $this->default_value, 'options' => $this->options, 'attr' => $this->attr, 'required' => $this->required, 'style' => $this->style, 'url' => $url, 'zone_field_name' => $this->zone_field_name ?: $this->default_zone_field_name, 'zone_name' => $this->zone_name ?: $this->default_zone_name, 'zone_value' => (array)($this->zone_value ?: $this->default_zone_value), 'zone_options' => $this->zone_options, 'submit_mode' => $this->submit_mode, 'placeholder' => $this->placeholder, 'help_url' => $this->help_url, ] ); return $this->view->fetch($this->template ?: 'form/countries_zones.tpl'); } } /* * Build pagination HTML element based on the template. * @since 1.1.5 */ class PaginationHtmlElement extends HtmlElement { public $sts = []; /** * @param array $data * * @throws AException */ public function __construct($data) { parent::__construct($data); //default settings $this->sts['total'] = 0; $this->sts['page'] = 1; $this->sts['limit'] = (int)$this->config?->get('config_catalog_limit') ?: 20; $this->sts['split'] = $data['split'] ?: 10; $this->sts['limits'] = []; //max pages to show in pagination $this->sts['num_links'] = $data['num_links'] ?: 10; $this->sts['url'] = ''; $this->sts['text'] = $this->language->get('text_pagination_pages_info', '', true); $this->sts['text'] = $this->sts['text'] == 'text_pagination_pages_info' ? 'Showing {start} to {end} of {total} ({pages} Pages)' : $this->sts['text']; $this->sts['text_limit'] = $this->language->get('text_per_page', '', true); $this->sts['text_limit'] = $this->sts['text_limit'] == 'text_per_page' ? 'Per Page' : $this->sts['text_limit']; $this->sts['text_first'] = $this->language->get('text_first', '', true); $this->sts['text_first'] = $this->sts['text_first'] == 'text_first' ? '|<<' : $this->sts['text_first']; $this->sts['text_last'] = $this->language->get('text_last', '', true); $this->sts['text_last'] = $this->sts['text_last'] == 'text_last' ? '>>|' : $this->sts['text_last']; $this->sts['text_next'] = $this->language->get('text_next', '', true); $this->sts['text_next'] = $this->sts['text_next'] == 'text_next' ? '>' : $this->sts['text_next']; $this->sts['text_prev'] = $this->language->get('text_previous', '', true); $this->sts['text_prev'] = $this->sts['text_prev'] == 'text_previous' ? '<' : $this->sts['text_prev']; $this->sts['style_links'] = 'links'; $this->sts['style_results'] = 'results'; $this->sts['style_limits'] = 'limits'; //override default foreach ($this->data as $key => $val) { if (isset($val)) { $this->sts[$key] = $val; } } } /** * @return string * @throws AException */ public function getHtml() { //Build pagination data and display $registry = Registry::getInstance(); $html = new AHtml($registry); $s = $this->sts; $s['no_perpage'] = $s['no_perpage'] ?? 0; //some more defaults if ($s['page'] < 1 || !is_numeric($s['page'])) { $s['page'] = 1; } if (!$s['limit'] || !is_numeric($s['limit'])) { $s['limit'] = 10; } //count limits if needed if (!$s['no_perpage'] && !$s['limits']) { $s['limits'][0] = $x = ($s['split'] ?: $registry->get('config')->get('config_catalog_limit')); while ($x <= 50) { $s['limits'][] = $x; $x += $s['limits'][0]; } } $s['url'] = str_replace('--limit--', $s['limit'], (string)$s['url']); $s['direct_url'] = str_replace('--limit--', $s['limit'], (string)$s['direct_url']); $s['total_pages'] = ceil($s['total'] / $s['limit']); if ($s['page'] > 1) { //not first page $this->view->assign('first_url', str_replace('--page--', 1, (string)$s['url'])); $this->view->assign('prev_url', str_replace('--page--', $s['page'] - 1, (string)$s['url'])); } if ($s['total_pages'] > 1) { if ($s['total_pages'] <= $s['num_links']) { $s['start'] = 1; $s['end'] = $s['total_pages']; } else { $s['start'] = $s['page'] - floor($s['num_links'] / 2); $s['end'] = $s['page'] + floor($s['num_links'] / 2); if ($s['start'] < 1) { $s['end'] += abs($s['start']) + 1; $s['start'] = 1; } if ($s['end'] > $s['total_pages']) { $s['start'] -= ($s['end'] - $s['total_pages']); $s['end'] = $s['total_pages']; } } } else { $s['start'] = $s['end'] = 1; } if ($s['page'] < $s['total_pages']) { $this->view->assign('next_url', str_replace('--page--', $s['page'] + 1, (string)$s['url'])); $this->view->assign('last_url', str_replace('--page--', $s['total_pages'], (string)$s['url'])); } $replace = [ ($s['total']) ? (($s['page'] - 1) * $s['limit']) + 1 : 0, ((($s['page'] - 1) * $s['limit']) > ($s['total'] - $s['limit'])) ? $s['total'] : ((($s['page'] - 1) * $s['limit']) + $s['limit']), $s['total'], $s['total_pages'], ]; if (!$s['no_perpage']) { if (!in_array($s['limit'], $s['limits'])) { $s['limits'][] = $s['limit']; sort($s['limits']); } $options = []; foreach ($s['limits'] as $item) { $options[$item] = $item; } $limit_url = str_replace('--page--', 1, (string)$s['url']); $limit_url = str_replace('&limit=' . $s['limit'], '', $limit_url); $limit_select = $html->buildSelectbox( [ 'name' => 'limit', 'value' => $s['limit'], 'options' => $options, 'style' => 'input-mini', 'attr' => ' onchange="location=\'' . $limit_url . '&limit=\'+this.value;"', ] ); $limit_select = str_replace('&', '&', (string)$limit_select); $this->view->assign('limit_select', $limit_select); } $find = [ '{start}', '{end}', '{total}', '{pages}', '--limit--', ]; $s['text'] = str_replace($find, $replace, (string)$s['text']); $this->extendAndBatchAssign($s); return $this->view->fetch($this->template ?: 'form/pagination.tpl'); } } /** * Class ModalHtmlElement * * @property string $id * @property string $title * @property string $modal_type * @property string $content * @property string $footer * @property string $data_source * @property string $js_onshow * @property string $js_onload * @property string $js_onclose */ class ModalHtmlElement extends HtmlElement { /** * @param array $data * * @throws AException */ public function __construct($data) { parent::__construct($data); } /** * @return string * @throws AException */ public function getHtml() { $modal_type = $this->modal_type ?: 'lg'; $this->extendAndBatchAssign( [ 'id' => $this->id, 'title' => $this->title, 'content' => $this->content, 'footer' => $this->footer, 'modal_type' => $modal_type, // if 'ajax' we clean up modal content after it close 'data_source' => (string)$this->data_source, // js-triggers for modal events 'js_onshow' => (string)$this->js_onshow, 'js_onload' => ($this->data_source == 'ajax' ? (string)$this->js_onload : ';'), //if content 'js_onclose' => (string)$this->js_onclose, ] ); /** @see public_html/admin/view/default/template/form/modal.tpl */ return $this->view->fetch($this->template ?: 'form/modal.tpl'); } } /** * Class LabelHtmlElement * NOTE: only for storefront * * @property string $element_id * @property string $name * @property string $text * @property string $value * @property string $style * @property string $help_url * @property string $attr */ class LabelHtmlElement extends HtmlElement { /** * @return string * @throws AException */ public function getHtml() { if (IS_ADMIN === true) { ADebug::error('labelHtmlElement', E_USER_ERROR, 'You cannot to build Label-field from Admin-side!'); return null; } $this->extendAndBatchAssign( [ 'name' => $this->name, 'id' => $this->element_id, 'text' => str_replace('"', '"', ($this->text ?: $this->value)), 'attr' => $this->attr, 'style' => $this->style, 'help_url' => $this->help_url, ] ); return $this->view->fetch($this->template ?: 'form/label.tpl'); } } /** * @property string $name * @property float|int $min * @property float|int $max * @property float|int $step * @property float|int $from * @property float|int $to * @property boolean $disabled */ class RangeHtmlElement extends HtmlElement { public function getHtml() { $data = [ 'id' => $this->element_id, 'name' => $this->name, 'min' => $this->min, 'max' => $this->max, 'step' => $this->step, 'from' => $this->from, 'to' => $this->to, 'disabled' => $this->disabled, ]; $this->view->batchAssign($data); return $this->view->fetch($this->template ?: 'form/range.tpl'); } }