<?php

#[AllowDynamicProperties]
class formsViewCfs extends viewCfs
{
  protected $_twig;
  private $_renderFormIter = 0;
  private $_lastForm = null;
  private $_saveLastForm = false;

  public function getTabContent()
  {
    frameCfs::_()->getModule('templates')->loadJqGrid();
    frameCfs::_()->addScript('admin.forms', $this->getModule()->getModPath() . 'js/admin.forms.js');
    frameCfs::_()->addScript('admin.forms.list', $this->getModule()->getModPath() . 'js/admin.forms.list.js');
    frameCfs::_()->addJSVar('admin.forms.list', 'cfsTblDataUrl', uriCfs::mod('forms', 'getListForTbl', ['reqType' => 'ajax']));

    $this->assign('addNewLink', frameCfs::_()->getModule('options')->getTabUrl('forms_add_new'));
    return parent::getContent('formsAdmin');
  }
  public function getAddNewTabContent()
  {
    frameCfs::_()->getModule('templates')->loadJqueryUi();
    frameCfs::_()->addStyle('admin.forms', $this->getModule()->getModPath() . 'css/admin.forms.css');
    frameCfs::_()->addScript('admin.forms', $this->getModule()->getModPath() . 'js/admin.forms.js');
    frameCfs::_()->getModule('templates')->loadMagicAnims();

    $changeFor = (int) reqCfs::getVar('change_for', 'get');
    //frameCfs::_()->addJSVar('admin.forms', 'cfsChangeFor', array($changeFor));
    if ($changeFor) {
      $originalForm = $this->getModel()->supGetById($changeFor);
      $editLink = $this->getModule()->getEditLink($changeFor);
      $this->assign('originalForm', $originalForm);
      $this->assign('editLink', $editLink);
      frameCfs::_()->addJSVar('admin.forms', 'cfsOriginalForm', $originalForm);
      dispatcherCfs::addFilter('mainBreadcrumbs', [$this, 'modifyBreadcrumbsForChangeTpl']);
    }

    $this->assign('list', dispatcherCfs::applyFilters('showTplsList', $this->getModel()->getFormsTplList()));
    $this->assign('changeFor', $changeFor);

    return parent::getContent('formsAddNewAdmin');
  }
  public function modifyBreadcrumbsForChangeTpl($crumbs)
  {
    $crumbs[count($crumbs) - 1]['label'] = __('Modify Form Template', CFS_LANG_CODE);
    return $crumbs;
  }
  public function adminBreadcrumbsClassAdd()
  {
    echo ' supsystic-sticky';
  }
  public function getEditTabContent($id)
  {
    $form = $this->getModel()->supGetById($id);
    if (empty($form)) {
      return __('Cannot find required Form', CFS_LANG_CODE);
    }
    dispatcherCfs::doAction('beforeFormEdit', $form);

    dispatcherCfs::addAction('afterAdminBreadcrumbs', [$this, 'showEditFormFormControls']);
    dispatcherCfs::addAction('adminBreadcrumbsClassAdd', [$this, 'adminBreadcrumbsClassAdd']);
    if (empty($form['ab_id'])) {
      dispatcherCfs::addFilter('mainBreadcrumbs', [$this, 'changeMainBreadCrumbsClb']);
    }

    // !remove this!!!!
    //$form['params']['opts_attrs']['bg_number'] = 2;
    /*$form['params']['opts_attrs'] = array(
        'bg_number' => 4,
        'txt_block_number' => 1,
    );*/
    /*$form['params']['opts_attrs']['txt_block_number'] = 0;
    $form['params']['opts_attrs']['video_width_as_forms'] = 1;
    $form['params']['opts_attrs']['video_height_as_forms'] = 1;*/
    // !remove this!!!!
    if (!is_array($form['params'])) {
      $form['params'] = [];
    }

    frameCfs::_()->getModule('templates')->loadJqueryUi();
    frameCfs::_()->getModule('templates')->loadSortable();
    frameCfs::_()->getModule('templates')->loadCodemirror();
    frameCfs::_()->getModule('templates')->loadSupTablesUi();
    //frameCfs::_()->getModule('templates')->loadSerializeJson();
    if (!class_exists('_WP_Editors', false)) {
      require ABSPATH . WPINC . '/class-wp-editor.php';
    }

    $fieldTypes = $this->getModule()->getFieldTypes();
    $cfsAddNewUrl = frameCfs::_()->getModule('options')->getTabUrl('forms_add_new');
    frameCfs::_()->addStyle('admin.forms', $this->getModule()->getModPath() . 'css/admin.forms.css');
    frameCfs::_()->addScript('admin.forms.fields', $this->getModule()->getModPath() . 'js/admin.forms.fields.js');
    frameCfs::_()->addJSVar('admin.forms.fields', 'cfsFormTypes', $fieldTypes);
    frameCfs::_()->addJSVar('admin.forms.fields', 'cfsAllCountries', fieldAdapterCfs::getCountries());

    frameCfs::_()->addScript('admin.forms.submit', $this->getModule()->getModPath() . 'js/admin.forms.submit.js');
    frameCfs::_()->addScript('admin.forms', $this->getModule()->getModPath() . 'js/admin.forms.js');
    frameCfs::_()->addScript('admin.forms.edit', $this->getModule()->getModPath() . 'js/admin.forms.edit.js');
    /*var_dump($form);
     exit();*/
    frameCfs::_()->addJSVar('admin.forms.edit', 'cfsForm', $form);
    frameCfs::_()->addJSVar('admin.forms.edit', 'cfsAddNewUrl', $cfsAddNewUrl);

    $frontendStyles = $this->getFrontendStyles();
    frameCfs::_()->addJSVar('admin.forms.edit', 'frontendStyles', $frontendStyles);

    frameCfs::_()->addScript('wp.tabs', CFS_JS_PATH . 'wp.tabs.js');

    $bgType = [
      'none' => __('None', CFS_LANG_CODE),
      'img' => __('Image', CFS_LANG_CODE),
      'color' => __('Color', CFS_LANG_CODE),
    ];

    $this->assign('csvExportUrl', uriCfs::mod('forms', 'exportCsv', ['id' => $id]));

    $this->assign('adminEmail', get_bloginfo('admin_email'));
    $this->assign('isPro', frameCfs::_()->getModule('supsystic_promo')->isPro());
    $this->assign('mainLink', frameCfs::_()->getModule('supsystic_promo')->getMainLink());
    $this->assign('promoModPath', frameCfs::_()->getModule('supsystic_promo')->getAssetsUrl());

    $this->assign('cfsAddNewUrl', $cfsAddNewUrl);
    $this->assign('bgTypes', $bgType);
    $this->assign('previewUrl', uriCfs::mod('forms', 'getPreviewHtml', ['id' => $id]));
    $this->assign('form', $form);
    $this->assign('fieldTypes', $fieldTypes);

    $this->assign('bgNames', $this->getModel()->getBgNamesForForm($form['unique_id']));

    $tabs = [
      'cfsFormTpl' => [
        'title' => __('Design', CFS_LANG_CODE),
        'content' => $this->getMainFormTplTab(),
        'fa_icon' => 'fa-picture-o',
        'sort_order' => 0,
      ],
      'cfsFormFields' => [
        'title' => __('Fields', CFS_LANG_CODE),
        'content' => $this->getMainFormFieldsTab(),
        'fa_icon' => 'fa-list',
        'sort_order' => 10,
      ],
      'cfsSubmitOpts' => [
        'title' => __('Submit Options', CFS_LANG_CODE),
        'content' => $this->getMainFormSubmitOptsTab(),
        'fa_icon' => 'fa-envelope-o',
        'sort_order' => 20,
      ],
      'cfsFormStatistics' => [
        'title' => __('Statistics', CFS_LANG_CODE),
        'content' => $this->getMainFormStatisticsOptsTab(),
        'fa_icon' => 'fa-line-chart',
        'sort_order' => 100,
      ],
      'cfsFormEditors' => [
        'title' => __('CSS / HTML Code', CFS_LANG_CODE),
        'content' => $this->getMainFormCodeTab(),
        'fa_icon' => 'fa-code',
        'sort_order' => 999,
      ],
    ];
    $tabs = dispatcherCfs::applyFilters('formsEditTabs', $tabs, $form);
    uasort($tabs, [$this, 'sortEditFormTabsClb']);
    $this->assign('tabs', $tabs);
    dispatcherCfs::doAction('beforeFormEditRender', $form);
    return parent::getContent('formsEditAdmin');
  }
  public function changeMainBreadCrumbsClb($crumbs)
  {
    return [$crumbs[count($crumbs) - 1]]; // Get rid of all other breadcrumbs - leave space on this page for other important things (buttons, etc.)
  }
  public function showEditFormFormControls()
  {
    $popupSupported = false;
    if (class_exists('framePps')) {
      //PopUp is supported
      $this->assign('popupSelectUrl', framePps::_()->getModule('options')->getTabUrl('popup'));
      $popupSupported = true;
    }

    $membershipPluginActive = false;
    $membershipModel = frameCfs::_()->getModule('membership')->getModel();
    if ($membershipModel) {
      $membershipPluginActive = $membershipModel->isPluginActive();
    }

    $this->assign('pluginInstallUrl', add_query_arg(['s' => 'Membership by Supsystic', 'tab' => 'search', 'type' => 'term'], admin_url('plugin-install.php')));
    $this->assign('membershipPluginActive', $membershipPluginActive);
    $this->assign('popupSupported', $popupSupported);
    parent::display('formsEditFormControls');
  }
  public function sortEditFormTabsClb($a, $b)
  {
    if ($a['sort_order'] > $b['sort_order']) {
      return 1;
    }
    if ($a['sort_order'] < $b['sort_order']) {
      return -1;
    }
    return 0;
  }
  public function getMainFormSubmitOptsTab()
  {
    global $wpdb;
    $postTypes = get_post_types('', 'objects');
    $postTypesForSelect = [];
    foreach ($postTypes as $key => $value) {
      if (!in_array($key, ['attachment', 'revision', 'nav_menu_item'])) {
        $postTypesForSelect[$key] = $value->labels->name;
      }
    }
    // We are not using wp methods here - as list can be very large - and it can take too much memory
    $postTypesForCategoriesList = $this->getModule()->getListAvailableTerms();

    $allCategories = [];
    global $wpdb;
    foreach ($postTypesForCategoriesList as $categoryId) {
      $allCategories[] = $wpdb->get_row($wpdb->prepare("SELECT t.term_id, t.name FROM {$wpdb->terms} t INNER JOIN {$wpdb->term_taxonomy} tt ON t.term_id = tt.term_id WHERE tt.taxonomy = %s ORDER BY t.name ASC", $categoryId), ARRAY_A);
    }

    // $allCategories = dbCfs::get("SELECT t.term_id, t.name FROM $wpdb->terms t "
    // 	. "INNER JOIN $wpdb->term_taxonomy tt ON t.term_id = tt.term_id "
    // 	. "WHERE tt.taxonomy IN ('". implode("','", $postTypesForCategoriesList). "') ORDER BY t.name");

    $allCategoriesForSelect = [];
    if (!empty($allCategories)) {
      foreach ($allCategories as $c) {
        if (!empty($c['term_id']) && !empty($c['name'])) {
          $allCategoriesForSelect[$c['term_id']] = $c['name'];
        }
      }
    }
    $this->assign('postTypesForSelect', $postTypesForSelect);
    $this->assign('postStatusesForSelect', get_post_statuses());
    $this->assign('allCategoriesForSelect', $allCategoriesForSelect);
    $this->assign(
      'allContactsListUrl',
      frameCfs::_()
        ->getModule('options')
        ->getTabUrl($this->getCode() . '_contacts', ['id' => $this->form['id']]),
    );
    $this->assign('regRolesForSelect', $this->getAvailableUserRolesForSelect());
    $this->assign('submitOptsAddTabs', [
      'cfsFormSubmitEditTabPublish' => [
        'title' => __('Publish Content', CFS_LANG_CODE),
        'content' => $this->_getMainFormPublishContentTab(),
        'fa_icon' => 'fa-thumb-tack',
        'sort_order' => 0,
      ],

      'cfsFormSubmitEditTabRegistration' => [
        'title' => __('Registration', CFS_LANG_CODE),
        'content' => $this->_getMainFormRegistrationTab(),
        'fa_icon' => 'fa-user-plus',
        'sort_order' => 10,
      ],
    ]);
    return parent::getContent('formsEditFormSubmitOpts');
  }
  public function getAvailableUserRolesForSelect()
  {
    global $wp_roles;
    $res = [];
    $allRoles = $wp_roles->roles;
    $editableRoles = apply_filters('editable_roles', $allRoles);

    if (!empty($editableRoles)) {
      foreach ($editableRoles as $role => $data) {
        if (in_array($role, ['administrator', 'editor'])) {
          continue;
        }
        if ($role == 'subscriber') {
          // Subscriber - at the begining of array
          $res = [$role => $data['name']] + $res;
        } else {
          $res[$role] = $data['name'];
        }
      }
    }
    return $res;
  }
  private function _getMainFormPublishContentTab()
  {
    return parent::getContent('formsEditAdminPublishContentOpts');
  }
  private function _getMainFormRegistrationTab()
  {
    return parent::getContent('formsEditAdminRegistrationOpts');
  }
  public function getMainFormTplTab()
  {
    return parent::getContent('formsEditAdminTplOpts');
  }
  public function getMainFormFieldsTab()
  {
    frameCfs::_()->getModule('templates')->loadBxSlider();
    $isGoogleMapsAvailable = class_exists('frameGmp');
    $mapId = reqCfs::getVar('map_id', 'get');
    if ($isGoogleMapsAvailable) {
      $allGoogleMaps = frameGmp::_()
        ->getModule('gmap')
        ->getModel()
        ->getAllMaps(['simple' => true]);
      $allGoogleMapsForSelect = [];
      if (!empty($allGoogleMaps)) {
        foreach ($allGoogleMaps as $m) {
          $allGoogleMapsForSelect[$m['id']] = $m['title'];
        }
      }
      $this->assign('allGoogleMapsForSelect', $allGoogleMapsForSelect);
    }
    //$frontendStyles = $this->getFrontendStyles();
    // foreach($frontendStyles as $sKey => $sUrl) {
    // 	frameCfs::_()->addStyle($sKey, $sUrl);
    // }
    $this->assign('isGoogleMapsAvailable', $isGoogleMapsAvailable);
    frameCfs::_()->addJSVar('admin.forms.fields', 'cfsAddMapField', $mapId);
    return parent::getContent('formsEditFormFields');
  }
  public function getMainFormStatisticsOptsTab()
  {
    frameCfs::_()->addScript('google.charts', 'https://www.gstatic.com/charts/loader.js');
    frameCfs::_()->getModule('templates')->loadJqGrid();
    frameCfs::_()->getModule('templates')->loadDatePicker();
    frameCfs::_()->addScript('admin.forms.statistics', $this->getModule()->getModPath() . 'js/admin.forms.statistics.js');
    // Total stats
    $group = reqCfs::getVar('cfsChartGroup_cfsMainStats', 'cookie');
    // Get stats only for default engine for now
    $statModel = frameCfs::_()->getModule('statistics')->getModel();
    $stats = $statModel->getAllForFormSorted($this->form['id'], ['group' => $group]);
    if (!empty($stats)) {
      frameCfs::_()->addJSVar('admin.forms.statistics', 'cfsAllStats', $stats);
    }
    if ($this->isPro) {
      if (frameCfs::_()->getModule('add_fields') && method_exists(frameCfs::_()->getModule('add_fields'), 'getRatingStatsTab')) {
        $this->assign('ratingStats', frameCfs::_()->getModule('add_fields')->getRatingStatsTab($this->form));
      }
    }
    return parent::getContent('formsEditFormStatistics');
  }
  public function getFrontendStyles()
  {
    return [
      'supTablesUi' => CFS_CSS_PATH . 'suptablesui.min.css',
      CFS_CODE . '.frontend.forms' => $this->getModule()->getModPath() . 'css/frontend.forms.css',
    ];
  }
  public function showForm($params)
  {
    frameCfs::_()->getModule('templates')->loadCoreJs();
    $id = $params['id'];

    $id = dispatcherCfs::applyFilters('formIdBeforeShow', $id);

    $form = $this->getModel()->supGetById($id);
    if (!empty($form)) {
      dispatcherCfs::doAction('formBeforeShow', $form);
      $form['connect_hash'] = md5(date('m-d-Y') . $id . NONCE_KEY);
      if (!empty($_GET['cfsPreFill']) && !empty($form['params']['fields'])) {
        foreach ($form['params']['fields'] as &$field) {
          $fieldVal = isset($_GET[$field['name']]) ? sanitize_text_field($_GET[$field['name']]) : false;
          $fieldValue = isset($_GET['cfs_' . $field['name']]) ? sanitize_text_field($_GET['cfs_' . $field['name']]) : $fieldVal;
          if (isset($field['value']) && isset($field['name']) && $fieldValue !== false) {
            // Escape Twig template delimiters to prevent Server-Side Template Injection (SSTI/RCE).
            // sanitize_text_field() does not strip curly braces, so an attacker can inject
            // Twig expressions (e.g. {{_self.env.registerUndefinedFilterCallback(...)}})
            // that get evaluated by Twig_Loader_String at render time.
            $fieldValue = str_replace(['{', '}'], ['&#123;', '&#125;'], $fieldValue);
            $field['value'] = $fieldValue;
          }
        }
      }
      $frontendStyles = $this->getFrontendStyles();
      foreach ($frontendStyles as $sKey => $sUrl) {
        frameCfs::_()->addStyle($sKey, $sUrl);
      }
      frameCfs::_()->addScript(CFS_CODE . '.modernizr', $this->getModule()->getModPath() . 'js/forms.modernizr.min.js');
      frameCfs::_()->addScript(CFS_CODE . '.frontend.forms', $this->getModule()->getModPath() . 'js/frontend.forms.js');

      //frameCfs::_()->addJSVar(CFS_CODE. '.frontend.forms', 'cfsForms_'. $this->_renderFormIter, $this->_prepareForFront($form));
      //frameCfs::_()->addJSVar(CFS_CODE. '.frontend.forms', 'cfsFormsRenderFormIter', array('lastIter' => $this->_renderFormIter));
      $this->_renderFormIter++;
      $this->_checkLoadFieldsAssets($form['params']['fields']);
      if ($this->_saveLastForm) {
        $this->_lastForm = $form;
      }
      return $this->generateHtml($form, ['frontend' => true]) . '<span style="display: none !important;" class="cfsFormDesc">' . base64_encode(json_encode($this->_prepareForFront($form))) . '</span>';
    }
    return 'Can not find Form in database';
  }
  public function getLastForm()
  {
    return $this->_lastForm;
  }
  public function saveLastForm($val)
  {
    $this->_saveLastForm = $val;
  }
  /**
   * Exclude unvanted for frontend data from form
   * @param array $form Form data to be rendered
   * @return array Form data without parameters for frontend
   */
  private function _prepareForFront($form)
  {
    unset($form['css']);
    unset($form['html']);
    unset($form['params']['submit']);
    if (isset($form['params']['fields'])) {
      foreach ($form['params']['fields'] as $i => $f) {
        if ($f['html'] == 'recaptcha') {
          unset($form['params']['fields'][$i]['recap-sitekey']);
          unset($form['params']['fields'][$i]['recap-secret']);
        }
      }
    }
    $removeParamsKeys = [
      'sub_aweber_listname',
      'sub_aweber_adtracking',
      'sub_mailchimp_api_key',
      'sub_mailchimp_lists',
      'sub_ar_form_action',
      'sub_sga_id',
      'sub_sga_list_id',
      'sub_sga_activate_code',
      'sub_gr_api_key',
      'sub_ac_api_url',
      'sub_ac_api_key',
      'sub_ac_lists',
      'sub_mr_lists',
      'sub_gr_api_key',
      'sub_gr_lists',
      'cycle_day',
      'sub_ic_app_id',
      'sub_ic_app_user',
      'sub_ic_app_pass',
      'sub_ic_lists',
      'sub_ck_api_key',
      'sub_mem_acc_id',
      'sub_mem_pud_key',
      'sub_mem_priv_key',
      'test_email',
      'sub_sb_api_key',
      'sub_aw_c_key',
      'sub_aw_c_secret',
    ];
    foreach ($removeParamsKeys as $unKey) {
      if (isset($form[$i]['params']['tpl'][$unKey])) {
        unset($form[$i]['params']['tpl'][$unKey]);
      }
    }
    return $form;
  }
  private function _checkLoadFieldsAssets($fields)
  {
    foreach ($fields as $f) {
      switch ($f['html']) {
        case 'date':
        case 'month':
        case 'week':
          frameCfs::_()->getModule('templates')->loadDatePicker();
          frameCfs::_()->getModule('templates')->loadJqueryUi();
          frameCfs::_()->getModule('templates')->loadFontAwesome();
          break;
        case 'time':
          frameCfs::_()->getModule('templates')->loadTimePicker();
          break;
      }
    }
  }
  public function getMainFormCodeTab()
  {
    return parent::getContent('formsEditAdminCodeOpts');
  }
  public function adjustOpacity($color, $alpha)
  {
    $alpha = max(0, min(1, $alpha));
    $rgbColor = utilsCfs::hexToRgb($color);
    $rgbColor[] = $alpha;
    return 'rgba(' . implode(',', $rgbColor) . ')';
  }
  public function adjustBrightness($hex, $steps)
  {
    // Steps should be between -255 and 255. Negative = darker, positive = lighter
    $steps = max(-255, min(255, $steps));

    // Normalize into a six character long hex string
    $hex = str_replace('#', '', $hex);
    if (strlen($hex) == 3) {
      $hex = str_repeat(substr($hex, 0, 1), 2) . str_repeat(substr($hex, 1, 1), 2) . str_repeat(substr($hex, 2, 1), 2);
    }

    // Split into three parts: R, G and B
    $color_parts = str_split($hex, 2);
    $return = '#';

    foreach ($color_parts as $color) {
      $color = hexdec($color); // Convert to decimal
      $color = max(0, min(255, $color + $steps)); // Adjust color
      $return .= str_pad(dechex($color), 2, '0', STR_PAD_LEFT); // Make two char hex code
    }

    return $return;
  }
  public function generateHtml($form, $params = [])
  {
    $replaceStyleTag = isset($params['replace_style_tag']) ? $params['replace_style_tag'] : false;
    if (is_numeric($form)) {
      $form = $this->getModel()->supGetById($form);
    }
    $this->_initTwig();

    $form = dispatcherCfs::applyFilters('beforeFormRender', $form);

    $form['params']['tpl']['form_start'] = $this->generateFormStart($form);
    $form['params']['tpl']['fields'] = $this->generateFields($form);
    $form['params']['tpl']['form_end'] = $this->generateFormEnd($form);
    if (!empty($form['html']) && !is_array($form['html'])) {
      $form['html'] = str_replace('\r\n', '', $form['html']);
    }
    if (!empty($form['css']) && !is_array($form['css'])) {
      $form['css'] = str_replace('\r\n', '', $form['css']);
      $form['css'] = str_replace("\'", "'", $form['css']);
      $form['css'] = str_replace('\"', '"', $form['css']);
    }
    $form['css'] .= $this->_generateCommonFormCss($form);

    $form['css'] = $this->_replaceTagsWithTwig($form['css'], $form);
    $form['html'] = $this->_replaceTagsWithTwig($form['html'], $form);

    $form['html'] = str_replace('col-md-', 'supMd', $form['html']);
    $form['html'] = str_replace('col-xs-', 'supXs', $form['html']);
    $form['html'] = str_replace('col-sm-', 'supSm', $form['html']);
    $form['html'] = str_replace('col-lg-', 'supLg', $form['html']);
    $form['html'] = str_replace('row', 'supRow', $form['html']);

    $form['html'] .= $this->_generateImgsPreload($form);

    $form['css'] = dispatcherCfs::applyFilters('formCss', $form['css'], $form, $params);
    $form['html'] = dispatcherCfs::applyFilters('formHtml', $form['html'], $form, $params);
    // $replaceStyleTag can be used for compability with other plugins minify functionality:
    // it will not recognize css in js data as style whye rendering on server side,
    // but will be replaced back to normal <style> tag in JS, @see js/frontend.forms.js
    return $this->_twig->render(
      ($replaceStyleTag ? '<span style="display: none;" id="cfsFormStylesHidden_' . $form['view_id'] . '">' : '<style type="text/css" id="' . $form['view_html_id'] . '_style">') .
        $form['css'] .
        ($replaceStyleTag ? '</span>' : '</style>') .
        '<div id="' .
        $form['view_html_id'] .
        '" class="cfsFormShell">' .
        $form['html'] .
        '</div>',
      ['forms' => $form],
    );
  }
  private function _generateCommonFormCss($form)
  {
    $res = '';
    $res .= '#[SHELL_ID] { width: ' . $form['params']['tpl']['width'] . $form['params']['tpl']['width_measure'] . '}';
    if (isset($form['params'], $form['params']['tpl'], $form['params']['tpl']['form_sent_msg_color']) && !empty($form['params']['tpl']['form_sent_msg_color'])) {
      $res .= '#[SHELL_ID] .cfsSuccessMsg { color: ' . $form['params']['tpl']['form_sent_msg_color'] . ' !important}';
    }
    return $res;
  }
  public function generateFormStart($form)
  {
    return '<form class="csfForm" method="post" action="' . CFS_SITE_URL . '">';
  }
  public function generateFormEnd($form)
  {
    $res = '';
    $res .= htmlCfs::hidden('mod', ['value' => 'forms']);
    $res .= htmlCfs::hidden('action', ['value' => 'contact']);
    $res .= htmlCfs::hidden('id', ['value' => $form['id']]);
    $res .= htmlCfs::hidden('_wpnonce', ['value' => wp_create_nonce('contact-' . $form['id'])]);
    $res .= '<div class="cfsContactMsg"></div>';
    $res .= '</form>';
    return $res;
  }
  /*private function _generateFieldClasses( $field ) {
      return '';
  }
  private function _generateFieldStyles( $field ) {
      return '';
  }*/
  public function generateFields($form)
  {
    $resHtml = '';
    if (isset($form['params']['fields']) && !empty($form['params']['fields'])) {
      $mod = $this->getModule();
      $fieldWrapper = $form['params']['tpl']['field_wrapper'];
      if (strpos($fieldWrapper, '[field]') === false) {
        $fieldWrapper = '[field]';
      }
      $fieldCommonClasses = 'cfsFieldShell';
      $rows = [];
      $addFieldsMod = frameCfs::_()->getModule('add_fields'); // it can be NULL!!!
      $htmlDelims = $googeMaps = 0;
      foreach ($form['params']['fields'] as $f) {
        $htmlType = $f['html'];
        $name = isset($f['name']) ? trim($f['name']) : '';
        $isHtmlDelim = $htmlType == 'htmldelim';
        $isGoogleMap = $htmlType == 'googlemap';
        $isHidden = $htmlType == 'hidden';
        if (empty($name) && !$isHtmlDelim && !$isGoogleMap) {
          continue;
        }
        $id = '';
        $insertLabelInternal = false;
        if ($isHtmlDelim) {
          $htmlDelims++;
          $inputHtml = $f['value'];
          $name = 'htmldelim_' . $htmlDelims;
        } elseif ($isGoogleMap) {
          $googeMaps++;
          $inputHtml = '';
          $name = 'googlemap_' . $googeMaps;
          if (class_exists('frameGmp')) {
            $mapId = (int) $f['value'];
            if ($mapId) {
              $inputHtml = frameGmp::_()
                ->getModule('gmap')
                ->drawMapFromShortcode(['id' => $mapId]);
            }
          } elseif (frameCfs::_()->getModule('user')->isAdmin()) {
            // Show errors - only if user is admin, for usual visitors there will be just no field
            $inputHtml = sprintf(
              __('To use this field type you need to have installed and activated <a href="%s" target="_blank">Google Maps Easy</a> plugin - it\'s Free! Just install it <a class="button" target="_blank" href="%s">here.</a>', CFS_LANG_CODE),
              'https://wordpress.org/plugins/google-maps-easy/',
              admin_url('plugin-install.php?tab=search&s=Google+Maps+Easy'),
            );
          }
        } else {
          $htmlParams = ['attrs' => 'data-name="' . $name . '"'];
          $label = $f['label'];
          $placeholder = isset($f['placeholder']) ? $f['placeholder'] : '';
          $fieldListSupported = $mod->isFieldListSupported($htmlType);
          // Additional html types, that does not have options selection, but will be displayed in same way as other lists
          $showAsList = $fieldListSupported || in_array($htmlType, ['countryList', 'countryListMultiple', 'wooattrs', 'wpcategories']);
          $showAsOneCheck = in_array($htmlType, ['checkbox', 'radiobutton', 'checkboxsubscribe']);
          $isButton = in_array($htmlType, ['submit', 'reset', 'button']);
          $isRadioCheckList = in_array($htmlType, ['radiobuttons', 'checkboxlist']);
          if ($fieldListSupported && isset($f['options']) && !empty($f['options'])) {
            $htmlParams['options'] = [];
            foreach ($f['options'] as $opt) {
              $htmlParams['options'][$opt['name']] = isset($opt['label']) ? $opt['label'] : $opt['name'];
            }
          }
          if (!empty($placeholder)) {
            $htmlParams['placeholder'] = $placeholder;
          }
          if ($isButton) {
            $f['value'] = $label; // To not confuse user
          }
          if (isset($f['value']) && (!empty($f['value']) || strlen($f['value']) > 0)) {
            if (isset($f['value_preset']) && $f['value_preset'] && $addFieldsMod && method_exists($addFieldsMod, 'generateValuePreset')) {
              $f['value'] = $addFieldsMod->generateValuePreset($f['value_preset']);
            }
            $htmlParams['value'] = $f['value'];
          }
          if (isset($f['mandatory']) && !empty($f['mandatory']) && (int) $f['mandatory']) {
            $htmlParams['required'] = true;
          }
          if (in_array($htmlType, ['checkbox'])) {
            $htmlParams['attrs'] .= ' style="height: auto; width: auto; margin: 0; padding: 0;"';
          }
          if (isset($f['display']) && !empty($f['display'])) {
            $htmlParams['display'] = $f['display'];
          }
          if (isset($f['min_size']) && !empty($f['min_size'])) {
            $htmlParams['min'] = $f['min_size'];
          }
          if (isset($f['max_size']) && !empty($f['max_size'])) {
            $htmlParams['max'] = $f['max_size'];
          }
          if (isset($f['add_classes']) && !empty($f['add_classes'])) {
            $htmlParams['attrs'] .= ' class="' . $f['add_classes'] . '"';
          }
          if (isset($f['add_styles']) && !empty($f['add_styles'])) {
            $htmlParams['attrs'] .= ' style="' . $f['add_styles'] . '"';
          }
          if (isset($f['add_attr']) && !empty($f['add_attr'])) {
            $htmlParams['attrs'] .= ' ' . $f['add_attr'];
          }
          if (isset($f['vn_pattern']) && !empty($f['vn_pattern'])) {
            $htmlParams['pattern'] = $f['vn_pattern'];
          }
          if (isset($f['vn_equal']) && !empty($f['vn_equal'])) {
            $htmlParams['attrs'] .= ' data-equal-to="' . $f['vn_equal'] . '"';
          }
          if (isset($f['def_checked']) && !empty($f['def_checked'])) {
            $htmlParams['checked'] = 1;
          }
          if ($htmlType == 'recaptcha') {
            foreach ($f as $fParamKey => $fParam) {
              if (strpos($fParamKey, 'recap-') === 0 && strpos($fParamKey, 'secret') === false) {
                $htmlParams[str_replace('recap-', '', $fParamKey)] = $fParam;
              }
            }
          }

          // $isRadioCheckList fields have multiple labeled selections inside - them simply does not need main label selection
          // For all other - generate unique ID if it's required - here
          if (strpos($fieldWrapper, '[field_id]') !== false && !$isRadioCheckList) {
            // Need field ID
            $id = htmlCfs::nameToClassId($name . mt_rand(1, 99999), $htmlParams);
            if (strpos($htmlParams['attrs'], $id) === false) {
              $htmlParams['attrs'] .= ' id="' . $id . '"';
            }
          }
          $fullName = 'fields[' . $name . ']';
          $fieldTypeData = $this->getModule()->getFieldTypeByCode($htmlType);
          // Really rare situation - most for developing
          if (empty($fieldTypeData)) {
            continue;
          }
          // Generate input field itself
          if ($fieldTypeData && isset($fieldTypeData['pro'])) {
            if (!$addFieldsMod) {
              continue;
            }
            $inputHtml = $addFieldsMod->generateFieldHtml($htmlType, $fullName, $htmlParams, $form, $f);
          } else {
            $inputHtml = htmlCfs::$htmlType($fullName, $htmlParams);
          }
          $inputHtml = dispatcherCfs::applyFilters('formInputHtml', $inputHtml, $f, $htmlParams);
          // Generate it's html shell wrapper
          if (!$isHidden) {
            $insertLabelInternal = strpos($fieldWrapper, '[label]') === false;
            if ($showAsList) {
              $labelDelimiter = isset($f['label_delim']) ? $f['label_delim'] : ':';
              $txtLabel = $insertLabelInternal ? '<span class="cfsListSelectLabel">' . $label . $labelDelimiter . ' </span>' : '';
              $inputHtml = '<label>' . $txtLabel . '<span class="cfsListSelect">' . $inputHtml . '</span></label>';
            } elseif ($showAsOneCheck) {
              $txtLabel = $insertLabelInternal ? '&nbsp;' . $label : '';
              $inputHtml = '<label class="cfsCheck">' . $inputHtml . $txtLabel . '</label>';
            } elseif (!$isButton && !empty($label)) {
              $txtLabel = $insertLabelInternal ? '<span class="cfsInputLabel">' . $label . '&nbsp;</span>' : '';
              $inputHtml = '<label>' . $txtLabel . $inputHtml . '</label>';
            }
            // Wrap it
            $classes = [$fieldCommonClasses, 'cfsField_' . $htmlType];
            $replaceFrom = ['[field]', '[field_shell_classes]', '[field_shell_styles]', '[field_html]', '[field_id]'];
            $replaceTo = [$inputHtml, 'class="' . implode(' ', $classes) . '"', '', $htmlType, $id];
            if (!$insertLabelInternal) {
              $replaceFrom[] = '[label]';
              $replaceTo[] = $isButton ? '' : $label;
            }

            $inputHtml = str_replace($replaceFrom, $replaceTo, $fieldWrapper);

            $replaceFrom = ['[field]', 'field_shell_classes', 'field_shell_styles', 'field_html', 'field_id'];
            $replaceTo = [$inputHtml, 'class="' . implode(' ', $classes) . '"', '', $htmlType, $id];
            if (!$insertLabelInternal) {
              $replaceFrom[] = '[label]';
              $replaceTo[] = $isButton ? '' : $label;
            }

            $inputHtml = str_replace($replaceFrom, $replaceTo, $inputHtml);
          }
        }
        $bsClassId = isset($f['bs_class_id']) && !empty($f['bs_class_id']) ? (int) $f['bs_class_id'] : 12;
        $added = false;
        if (!$isHidden) {
          $inputHtml = '<div class="supSm' . $bsClassId . ' cfsFieldCol" data-name="' . $name . '" data-type="' . $htmlType . '">' . $inputHtml . '</div>'; // Bootstrap col wrapper
          if ($bsClassId < 12) {
            // Try to add it to prev. row
            $prevRowI = count($rows) - 1;
            if ($prevRowI >= 0) {
              if ($rows[$prevRowI]['id'] < 12) {
                $rows[$prevRowI]['id'] += $bsClassId;
                $rows[$prevRowI]['cols'][] = $inputHtml;
                $added = true;
              }
            }
          }
        }
        if (!$added) {
          // New row
          $rows[] = ['id' => $bsClassId, 'cols' => [$inputHtml], 'is_hidden' => $isHidden];
        }
      }
      foreach ($rows as $r) {
        $cols = implode('', $r['cols']);
        if (isset($r['is_hidden']) && $r['is_hidden']) {
          // No wrappers for hidden fields
          $resHtml .= $cols;
        } else {
          $resHtml .= '<div class="supRow cfsFieldsRow">' . $cols . '</div>';
        }
      }
    }
    return $resHtml;
  }
  private function _generateImgsPreload($form)
  {
    $res = '';
    if (isset($form['params']['opts_attrs']['bg_number']) && !empty($form['params']['opts_attrs']['bg_number'])) {
      for ($i = 0; $i < $form['params']['opts_attrs']['bg_number']; $i++) {
        if ($form['params']['tpl']['bg_type_' . $i] == 'img' && !empty($form['params']['tpl']['bg_img_' . $i])) {
          $res .= '<img class="cfsFormPreloadImg cfsFormPreloadImg_' . $form['view_id'] . '" src="' . $form['params']['tpl']['bg_img_' . $i] . '" />';
        }
      }
    }
    return $res;
  }
  private function _replaceTagsWithTwig($string, $form)
  {
    $string = preg_replace('/\[if (.+)\]/iU', '{% if forms.params.tpl.$1 %}', $string);
    $string = preg_replace('/\[elseif (.+)\]/iU', '{% elseif forms.params.tpl.$1 %}', $string);

    $replaceFrom = ['SHELL_ID', 'ID', 'endif', 'else'];
    $replaceTo = [$form['view_html_id'], $form['view_id'], '{% endif %}', '{% else %}'];
    // Standard shortcode processor didn't worked for us here - as it is developed for posts,
    // not for direct "do_shortcode" call, so we created own embed shortcode processor
    if (isset($form['params']) && isset($form['params']['tpl'])) {
      foreach ($form['params']['tpl'] as $key => $val) {
        if (is_array($val)) {
          foreach ($val as $key2 => $val2) {
            if (is_array($val2)) {
              foreach ($val2 as $key3 => $val3) {
                // Here should be some recursive and not 3 circles, but have not time for this right now, maybe you will do this?:)
                if (is_array($val3)) {
                  continue;
                }
                $replaceFrom[] = $key . '_' . $key2 . '_' . $key3;
                $replaceTo[] = $val3;
              }
            } else {
              $replaceFrom[] = $key . '_' . $key2;
              $replaceTo[] = $val2;
            }
          }
        } else {
          // Do shortcodes for all text type data in forms
          if (strpos($key, 'txt_') === 0 || strpos($key, 'label') === 0 || strpos($key, 'foot_note')) {
            $val = do_shortcode($val);
          }
          $replaceFrom[] = $key;
          $replaceTo[] = $val;
        }
      }
    }
    foreach ($replaceFrom as $i => $v) {
      $replaceFrom[$i] = '[' . $v . ']';
    }
    return str_replace($replaceFrom, $replaceTo, $string);
  }
  protected function _initTwig()
  {
    if (!$this->_twig) {
      if (!class_exists('Twig_Autoloader')) {
        require_once CFS_CLASSES_DIR . 'Twig' . DS . 'Autoloader.php';
      }
      Twig_Autoloader::register();
      $this->_twig = new Twig_Environment(new Twig_Loader_String(), ['debug' => 0]);
      $this->_twig->addFunction(new Twig_SimpleFunction('adjust_brightness', [$this, 'adjustBrightness']));
      $this->_twig->addFunction(new Twig_SimpleFunction('adjust_opacity', [$this, 'adjustOpacity']));
      $this->_twig->addFunction(new Twig_SimpleFunction('hex_to_rgba_str', 'utilsCfs::hexToRgbaStr'));
    }
  }
  public function getContactsTabContent($fid = 0)
  {
    frameCfs::_()->getModule('templates')->loadJqGrid();
    frameCfs::_()->getModule('templates')->loadSupTablesUi();
    frameCfs::_()->addScript('admin.forms', $this->getModule()->getModPath() . 'js/admin.forms.js');
    frameCfs::_()->addScript('admin.forms.contacts.list', $this->getModule()->getModPath() . 'js/admin.forms.contacts.list.js');
    frameCfs::_()->addJSVar('admin.forms.contacts.list', 'cfsTblDataUrl', uriCfs::mod('forms', 'getContactsListForTbl', ['reqType' => 'ajax', 'fid' => $fid]));
    $allForms = $this->getModel('forms')->getAllFromTbl();
    $allFormsForSelect = [];
    $formsFields = [];
    if (!empty($allForms)) {
      $allFormsForSelect[0] = __('All', CFS_LANG_CODE);
      foreach ($allForms as $form) {
        $allFormsForSelect[$form['id']] = $form['label'];
        if (isset($form['params']['fields']) && !empty($form['params']['fields'])) {
          if (count($formsFields) < 5) {
            // More fields will look like very unpreatty
            foreach ($form['params']['fields'] as $f) {
              $htmlType = $f['html'];
              if (in_array($htmlType, ['submit', 'reset', 'button', 'recaptcha', 'htmldelim', 'googlemap', 'textarea', 'selectlist', 'radiobutton', 'radiobuttons', 'checkbox', 'checkboxlist', 'checkboxsubscribe', 'countryList', 'countryListMultiple'])) {
                continue;
              }
              if (in_array($htmlType, ['text', 'textarea'])) {
                continue;
              }
              $sendName = $f['name'];
              $html = $f['html'];
              if (empty($sendName)) {
                continue;
              }
              $formsFields[$sendName] = ['label' => $f['label'] ? $f['label'] : $f['placeholder'], 'html' => $html];
            }
          }
        }
      }
    }
    frameCfs::_()->addJSVar('admin.forms.contacts.list', 'cfsFormFields', $formsFields);

    $this->assign('addNewLink', frameCfs::_()->getModule('options')->getTabUrl('forms_add_new'));
    $this->assign('allFormsForSelect', $allFormsForSelect);
    $this->assign('fid', $fid);
    return parent::getContent('formsContacts');
  }
}
