Модуль «Сообщить об ошибке» для Prestashop

Когда-то, для одной организации разрабатывал интернет-магазин на базе популярного бесплатного французского движка Prestashop 1.6.0.12. На данный момент уже доступна более новая версия. Так вот, необходимо было разработать модуль, умеющий отправлять сообщение об ошибке в тексте. Пользователь выделяет слово или фразу, а затем нажимает Ctrl+Enter, сообщение об ошибке отправляется администратору сайта.

Данный модуль простой и не является чем-то особенным. Тестировался на версии 1.6.012. Устанавливается модуль просто, через интерфейс сайта. Итак, приступим.

Для начала создадим папку с именем blockcheckspelling, которая будет содержать все файлы модуля и будет располагаться в папке mosules корня движка Prestashop.

Внутри каталога blockcheckspelling создадим еще 3 папки: css, mails и translations. В css будет размещен файл стилей, в mails — шаблоны писем на нужном языке, в translations — соответственно перевод сообщений на нужный нам язык.

Для начала работы модуля создадим среди данных трех каталогов файл с именем blockcheckspelling.php и следующего содержания:

<?php
/*
*  @author Denis Sitko  <info@sitkodenis.ru>
*  @copyright  2015 Denis Sitko
*  @license    http://opensource.org/licenses/afl-3.0.php  Academic Free License (AFL 3.0)
*/
if (!defined('_CAN_LOAD_FILES_'))
  exit;

class BlockCheckSpelling extends Module
{
  public function __construct()
  {
    $this->name = 'blockcheckspelling';
    $this->tab = 'front_office_features';
    $this->version = '1.0';
    $this->author = 'Denis Sitko';
    $this->controllers = ['form', 'senderror'];
    $this->need_instance = 0;
    $this->bootstrap = true;

    parent::__construct();

    $this->displayName = $this->l('Error in the text');
    $this->description = $this->l('Sending a visitor notices an error on the site.');
    $this->ps_versions_compliancy = ['min' => '1.6', 'max' => _PS_VERSION_];
  }

  public function install($delete_params = true)
  {
    if (!parent::install() || !$this->registerHook('header') || !$this->registerHook('displayCheckSpelling'))
      return false;

    if ($delete_params)
    {
      Configuration::updateValue('BLOCKCHECKSPELLING_EMAIL', Configuration::get('PS_SHOP_EMAIL'));
    }

    return true;
  }

  public function uninstall($delete_params = true)
  {
    if ($delete_params)
    {
      Configuration::deleteByName('BLOCKCHECKSPELLING_EMAIL');
    }

    return parent::uninstall();
  }

  public function hookHeader($params)
  {
    $this->context->controller->addCss($this->_path . '/css/blockcheckspelling.css');
    $this->context->controller->addJqueryPlugin('fancybox');
  }

  public function hookDisplayCheckSpelling($params)
  {
    return $this->display(__FILE__, 'block.tpl');
  }

  public function hookAjaxForm()
  {
    return $this->display(__FILE__, 'form.tpl');
  }

  public function getContent()
  {
    $errors = [];
    $output = null;

    if (Tools::isSubmit('submit'.$this->name))
    {
      if (!Configuration::updateValue('BLOCKCHECKSPELLING_EMAIL', (string)Tools::getValue('BLOCKCHECKSPELLING_EMAIL')))
        $errors[] = $this->l('Cannot update settings');

      if (count($errors) > 0)
        $output .= $this->displayError(implode('<br />', $errors));
      else
        $output .= $this->displayConfirmation($this->l('Settings updated successfully'));
    }

    return $output.$this->displayForm();
  }

  public function displayForm()
  {
    $default_lang = (int)Configuration::get('PS_LANG_DEFAULT');

    $fields_form[0]['form'] = [
      'legend' => [
        'title' => $this->l('Settings'),
        'icon' => 'icon-cog'
      ],
      'input' => [
        [
          'type' => "text",
          'name' => 'BLOCKCHECKSPELLING_EMAIL',
          'label' => $this->l('Email'),
        ],
      ],
      'submit' => [
        'title' => $this->l('Save'),
        'class' => 'btn btn-default pull-right'
      ]
    ];

    $helper = new HelperForm();

    $helper->module = $this;
    $helper->name_controller = $this->name;
    $helper->token = Tools::getAdminTokenLite('AdminModules');
    $helper->currentIndex = AdminController::$currentIndex.'&configure='.$this->name;

    $helper->default_form_language = $default_lang;
    $helper->allow_employee_form_lang = $default_lang;

    $helper->title = $this->displayName;
    $helper->show_toolbar = true;
    $helper->toolbar_scroll = true;
    $helper->submit_action = 'submit'.$this->name;

    $helper->toolbar_btn = [
      'save' =>
        [
          'desc' => $this->l('Save'),
          'href' => AdminController::$currentIndex.'&configure='.$this->name.'&save'.$this->name.
            '&token='.Tools::getAdminTokenLite('AdminModules'),
        ]
    ];

    $helper->fields_value['BLOCKCHECKSPELLING_EMAIL'] = (string)Configuration::get('BLOCKCHECKSPELLING_EMAIL');

    return $helper->generateForm($fields_form);
  }

  public function sendError($errorText)
  {
    $template_vars = array(
      '{errorText}' => Tools::safeOutput($errorText),
      '{shop_name}' => Configuration::get('PS_SHOP_NAME'),
      '{date}' => date('d.m.Y H:i:s')
    );

    return Mail::Send($this->context->language->id,
      'blockcheckspelling',
      'Орфографическая ошибка на сайте',
      $template_vars,
      Configuration::get('BLOCKCHECKSPELLING_EMAIL'),
      null,
      Configuration::get('PS_SHOP_EMAIL'),
      Configuration::get('PS_SHOP_NAME'),
      null,
      null,
      dirname(__FILE__).'/mails/',
      false,
      $this->context->shop->id
    );
  }
}

Данный файл является основным загружаемым файлом модуля. Здесь в конструкторе мы задаем основные настройки модуля. В свойстве

$this->controllers = ['form', 'senderror'];

перечисляться дополнительные имена контроллеров. Давайте их создадим.

Файл form.php для вызова формы:

<?php
/*
*  @author Denis Sitko  <info@sitkodenis.ru>
*  @copyright  2015 Denis Sitko
*  @license    http://opensource.org/licenses/afl-3.0.php  Academic Free License (AFL 3.0)
*/

include_once(dirname(__FILE__).'/../../config/config.inc.php');
include_once(dirname(__FILE__).'/blockcheckspelling.php');

$blockCheckSpelling = new BlockCheckSpelling();
echo $blockCheckSpelling->hookAjaxForm();

Файл senderror.php для отправки сообщения об ошибке:

<?php
/*
*  @author Denis Sitko  <info@sitkodenis.ru>
*  @copyright  2015 Denis Sitko
*  @license    http://opensource.org/licenses/afl-3.0.php  Academic Free License (AFL 3.0)
*/
include_once(dirname(__FILE__).'/../../config/config.inc.php');
include_once(dirname(__FILE__).'/blockcheckspelling.php');

$blockCheckSpelling = new BlockCheckSpelling();

$errorText = trim($_POST['errorText']);

if (!empty($errorText) && Validate::isMessage($errorText)){
  $blockCheckSpelling->sendError($errorText);

  Tools::redirect('index.php');
}

Здесь же в корне модуля разместим файл index.php, его можно скопировать из любого другого существующего модуля. Еще необходимо приготовить и разместить в этом же каталоге 2 файла: logo.png и logo.gif (размером 16x16px).

Для корректной установки модуля создадим 2 конфигурационных файла.

Файл config.xml

<?xml version="1.0" encoding="UTF-8" ?>
<module>
  <name>blockcallme</name>
  <displayName><![CDATA[Error in the text]]></displayName>
  <version><![CDATA[1.0]]></version>
  <description><![CDATA[Sending a visitor notices an error on the site.]]></description>
  <author><![CDATA[Denis Sitko]]></author>
  <tab><![CDATA[front_office_features]]></tab>
  <is_configurable>1</is_configurable>
  <need_instance>0</need_instance>
  <limited_countries></limited_countries>
</module>

Файл config_ru.xml

<?xml version="1.0" encoding="UTF-8" ?>
<module>
  <name>blockcheckspelling</name>
  <displayName><![CDATA[Ошибка в тексте]]></displayName>
  <version><![CDATA[1.0]]></version>
  <description><![CDATA[Отправка посетителем уведомления об ошибке на сайте.]]></description>
  <author><![CDATA[Denis Sitko]]></author>
  <tab><![CDATA[front_office_features]]></tab>
  <is_configurable>1</is_configurable>
  <need_instance>0</need_instance>
  <limited_countries></limited_countries>
</module>

В функции install() при установке мы создаем новый параметр в конфигурации Perstashop.

Configuration::updateValue('BLOCKCHECKSPELLING_EMAIL', Configuration::get('PS_SHOP_EMAIL'));

Аналогично, в функции uninstall() мы его удаляем из системы.

В функции hookHeader() мы подключаем необходимые нам для работы файлы. Fancybox уже имеется в системе изначально, так что его просто подключаем строкой

$this->context->controller->addJqueryPlugin('fancybox');

А вот файл со стилями blockcheckspelling.css создадим и разместим в папке css.

#blockcheckspelling {
  float: left;
  margin: 0 20px 0 20px;
  padding: 3px;
  width: 350px;
  font-size: 12px;
  color: #ccc;
}

#blockcheckspelling span.keys {
  color: #fff;
  background-color: #3a8a41;
  padding: 3px;
  -webkit-border-radius: 10px 2px 10px 2px;
  -moz-border-radius: 10px 2px 10px 2px;
  border-radius: 10px 2px 10px 2px;
}

.block-call-window-cs {
  display: none;
}

hr {
  border-bottom: 1px solid #99CC66;
  box-shadow: 0 0 10px rgba(0,153,0,0.5);
}

h2.blockcheckspelling {
  font-size: 16px;
  font-weight: bold;
  margin: 15px 0 0 15px;
  color: #99CC66;
  text-shadow: 2px 2px 3px rgba(0,0,0,0.1);
}

.blockcheckspelling label {
  margin: 0 0 0 20px;
}

form.blockcheckspelling {
  margin: 0 auto;
  margin-top: 20px;
}

.blockcheckspelling input {
  font-family: "Helvetica Neue", Helvetica, sans-serif;
  font-size: 12px;
  outline: none;
}

.blockcheckspelling input[type=text] {
  color: #777;
  padding-left: 10px;
  margin: 10px;
  margin-top: 12px;
  margin-left: 18px;
  width: 93%;
  height: 35px;
  border: 1px solid #097b33;
  border-radius: 2px;
  box-shadow: inset 0 1.5px 3px rgba(190, 190, 190, .4), 0 0 0 5px #f5f7f8;
  -webkit-transition: all .4s ease;
  -moz-transition: all .4s ease;
  transition: all .4s ease;
}

.blockcheckspelling input[type=text]:hover {
  border: 1px solid #99CC66;
  box-shadow: inset 0 1.5px 3px rgba(190, 190, 190, .7), 0 0 0 5px #f5f7f8;
}

.blockcheckspelling input[type=text] {
  border: 1px solid #99CC66;
  box-shadow: inset 0 1.5px 3px rgba(190, 190, 190, .4), 0 0 0 5px #e6f2f9;
}

.blockcheckspelling input[type=submit] {
  float: right;
  margin-right: 20px;
  margin-top: 20px;
  width: 180px;
  height: 30px;
  font-size: 12px;
  font-weight: bold;
  color: #fff;
  background-color: #097b33;
  border: 1px solid #097b33;
  cursor: pointer;
}

.blockcheckspelling input[type=submit]:hover,
.blockcheckspelling input[type=submit]:active {
  background-color: #669966;
}

.blockcheckspelling input[type=submit]:disabled {
  background-color: #E8E8E8;
  border: 1px solid #C8C8C8;
  color: #C0C0C0;
}

Функция hookDisplayCheckSpelling() описывает хук, который подключает html-код и скрипт для вызова окна с сообщением об ошибке. Для данной функции создадим здесь же файл с именем block.tpl

<div id="blockcheckspelling">
  <span class="descr">{l s='Found a mistake? Select it and click' mod='blockcheckspelling'} <span class="keys">Ctrl + Enter</span></span>
</div>

<div class="block-call-window-cs"></div>

<script>
  $(function(){
    var selectText = function(){
      var text = "";
      if (window.getSelection) {
        text = window.getSelection();
      }else if (document.getSelection) {
        text = document.getSelection();
      }else if (document.selection) {
        text = document.selection.createRange().text;
      }
      return text.toString();
    };

    $(".block-call-window-cs").on("click", function (e) {
      e.preventDefault();

      $.ajax({
        type: "POST",
        cache: false,
        url: "/modules/blockcheckspelling/form.php",
        data: { text : selectText },
        success: function (data) {
          $.fancybox(data, {
            fitToView: false,
            width: 520,
            height: 210,
            autoSize: false,
            openEffect: 'none',
            closeEffect: 'none'
          });
        }
      });
    });

    $(document).on("keydown", function(e) {
      if ((e.keyCode == 10 || e.keyCode == 13) && e.ctrlKey) {
        $(".block-call-window-cs").trigger("click");
      }
    });
  });
</script>

Кстати, не забудьте подключить ваш хук в удобном для вас месте

{hook h="displayCheckSpelling"}

Теперь создадим в корне файл form.tpl, отвечающий за вид отображаемой формы

<h2 class="blockcheckspelling">{l s='Share a mistake in the text' mod='blockcheckspelling'}</h2>
<hr />
<form class="blockcheckspelling" action="{$module_dir}senderror.php" method="post">
  <label for="errorText">{l s='Spelling error in the phrase:' mod='blockcheckspelling'}</label>
  <input type="text" name="errorText" id="errorText" value="{$smarty.post.text}" />
  <input type="submit" value="{l s='Send an error' mod='blockcheckspelling'}" />
</form>

<script>
  $(function(){
    var errorText = $("#errorText").val();
    var $submit = $(".blockcheckspelling").find("input[type=submit]");

    if (errorText.length > 0){
      $submit.attr("disabled", false);
    } else {
      $submit.attr("disabled", true);
    }

    $("form").on("change", "#errorText", function(){
      if ($(this).val().length > 0){
        $submit.attr("disabled", false);
      } else {
        $submit.attr("disabled", true);
      }
    });
  });
</script>

В функции getContent() используется для сохранения настроек в админке данного модуля. Сам вывод полей формы для настроек находится в функции displayForm().

Функция sendError() используется контроллером senderror, про содержимое которого я писал уже выше. В функции происходит отправка данных на указанный адрес электронной почты.

Последнее, что осталось, разместить 2 файла с шаблонами писем в директории mails/ru/.

Файл blockcheckspelling.html

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/1999/REC-html401-19991224/strict.dtd">
<html>
  <head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0" />
    <title>Сообщение от магазина {shop_name}</title>

    <style>	@media only screen and (max-width: 300px){ 
        body {
          width:218px !important;
          margin:auto !important;
        }
        .table {width:195px !important;margin:auto !important;}
        .logo, .titleblock, .box, .footer, .space_footer{width:auto !important;display: block !important;}
        span.title{font-size:20px !important;line-height: 23px !important}
        td.box p{font-size: 12px !important;font-weight: bold !important;}
        .table-recap table, .table-recap thead, .table-recap tbody, .table-recap th, .table-recap td, .table-recap tr { 
          display: block !important; 
        }

        .table-recap tr td, .conf_body td{text-align:center !important;}
      }
      @media only screen and (min-width: 301px) and (max-width: 500px) {
        body {width:308px!important;margin:auto!important;}
        .table {width:285px!important;margin:auto!important;}	
        .logo, .titleblock, .linkbelow, .box, .footer, .space_footer{width:auto!important;display: block!important;}	
        .table-recap table, .table-recap thead, .table-recap tbody, .table-recap th, .table-recap td, .table-recap tr { 
          display: block !important; 
        }
        .table-recap tr td, .conf_body td{text-align:center !important;}
      }
      @media only screen and (min-width: 501px) and (max-width: 768px) {
        body {width:478px!important;margin:auto!important;}
        .table {width:450px!important;margin:auto!important;}	
        .logo, .titleblock, .box, .footer, .space_footer{width:auto!important;display: block!important;}
      }
      @media only screen and (max-device-width: 480px) {
        body {width:308px!important;margin:auto!important;}
        .table {width:285px;margin:auto!important;}	
        .logo, .titleblock, .box, .footer, .space_footer{width:auto!important;display: block!important;}
        .table-recap tr td, .conf_body td{text-align:center!important;}
      }
    </style>
  </head>
  <body style="-webkit-text-size-adjust:none;background-color:#fff;width:650px;font-family:Open-sans, sans-serif;color:#555454;font-size:13px;line-height:18px;margin:auto">
    <table class="table table-mail" style="width:100%;margin-top:10px;-moz-box-shadow:0 0 5px #afafaf;-webkit-box-shadow:0 0 5px #afafaf;-o-box-shadow:0 0 5px #afafaf;box-shadow:0 0 5px #afafaf;filter:progid:DXImageTransform.Microsoft.Shadow(color=#afafaf,Direction=134,Strength=5)">
      <tr>
        <td class="space" style="width:20px;padding:7px 0">&nbsp;</td>
        <td align="center" style="padding:7px 0">
          <table class="table" bgcolor="#ffffff" style="width:100%">
            <tr>
              <td align="center" class="logo" style="border-bottom:4px solid #333333;padding:7px 0">
                <a title="{shop_name}" href="{shop_url}" style="color:#337ff1">
                  <img src="{shop_logo}" alt="{shop_name}" />
                </a>
              </td>
            </tr>
            <tr>
              <td align="center" class="titleblock" style="padding:7px 0">
                <font size="2" face="Open-sans, sans-serif" color="#555454">
                  <span class="title" style="font-weight:500;font-size:28px;text-transform:uppercase;line-height:33px">Здравствуйте,</span>
                </font>
              </td>
            </tr>
            <tr>
              <td class="space_footer" style="padding:0!important">&nbsp;</td>
            </tr>
            <tr>
              <td class="box" style="border:1px solid #D6D4D4;background-color:#f8f8f8;padding:7px 0">
                <table class="table" style="width:100%">
                  <tr>
                    <td width="10" style="padding:7px 0">&nbsp;</td>
                    <td style="padding:7px 0">
                      <font size="2" face="Open-sans, sans-serif" color="#555454">
                        <p data-html-only="1" style="border-bottom:1px solid #D6D4D4;margin:3px 0 7px;text-transform:uppercase;font-weight:500;font-size:18px;padding-bottom:10px">
                          На сайте была обнаружена ошибка
                        </p>
                        <span style="color:#777">Фраза: <strong>{fio}</strong></span>
                        <span style="color: #777">Дата: <strong>{date}</strong></span>
                      </font>
                    </td>
                    <td width="10" style="padding:7px 0">&nbsp;</td>
                  </tr>
                </table>
              </td>
            </tr>
            <tr>
              <td class="space_footer" style="padding:0!important">&nbsp;</td>
            </tr>
            <tr>
              <td class="footer" style="border-top:4px solid #333333;padding:7px 0">
                <span><a href="{shop_url}" style="color:#337ff1">{shop_name}</a></span>
              </td>
            </tr>
          </table>
        </td>
        <td class="space" style="width:20px;padding:7px 0">&nbsp;</td>
      </tr>
    </table>
  </body>
</html>

Файл blockcheckspelling.txt

{shop_name}

Здравствуйте,

На сайте была обнаружена ошибка
в следующей фразе

{errorText}

Дата: {date}

Ну и конечно, не забываем положить файл с переводом в папку translations (ru.php)

<?php

global $_MODULE;
$_MODULE = array();
$_MODULE['<{blockcheckspelling}prestashop>block_9acb1671e5f7efb984602b324eb8bf4e'] = 'Нашли ошибку? Выделите ее и нажмите';
$_MODULE['<{blockcheckspelling}prestashop>blockcheckspelling_519719c01844d3991a43458039ed5a6b'] = 'Ошибка в тексте';
$_MODULE['<{blockcheckspelling}prestashop>blockcheckspelling_ea400a8ae277d9aa56116509bf1807f1'] = 'Отправка посетителем уведомления об ошибке на сайте.';
$_MODULE['<{blockcheckspelling}prestashop>blockcheckspelling_c1ee76f076a5b97e3b4b0c0e5703246e'] = 'Невозможно обновить настройки';
$_MODULE['<{blockcheckspelling}prestashop>blockcheckspelling_462390017ab0938911d2d4e964c0cab7'] = 'Настройки успешно обновлены';
$_MODULE['<{blockcheckspelling}prestashop>blockcheckspelling_f4f70727dc34561dfde1a3c529b6205c'] = 'Настройки';
$_MODULE['<{blockcheckspelling}prestashop>blockcheckspelling_ce8ae9da5b7cd6c3df2929543a9af92d'] = 'Электронная почта';
$_MODULE['<{blockcheckspelling}prestashop>blockcheckspelling_c9cc8cce247e49bae79f15173ce97354'] = 'Сохранить';
$_MODULE['<{blockcheckspelling}prestashop>form_15c1decf6b1b7add4b3cbc6c44de4f12'] = 'Поделиться ошибкой в тексте';
$_MODULE['<{blockcheckspelling}prestashop>form_11f3a9d7193127816c0db3690b5113cc'] = 'Орфографическая ошибка в фразе:';
$_MODULE['<{blockcheckspelling}prestashop>form_01881661b0715d201d4bc18826a48d33'] = 'Отправить ошибку';

Вот и все. Было не так и сложно. Конечно, данный код далеко не идеален, можно было бы некоторые моменты сделать по другому. Но я не квалифицируюсь в области разработки решений для Prestashop, просто в один момент была задача и ее надо было решить как можно скорее.

Да, для ленивых прилагаю архив с исходниками модуля.

Спасибо и до новых встреч.

Если вы нашли ошибку, пожалуйста, выделите фрагмент текста и нажмите Ctrl+Enter.

Добавить комментарий

Сообщить об опечатке

Текст, который будет отправлен нашим редакторам: