Наши заметки о MODx
В этом разделе мы будем собирать небольшую копилку секретов по работе с системой управления контентом сайта (CMS) MODx. Эти заметки ни в коем случае не претендуют на лавры документации. На нашем сайте тема MODx вообще занимает небольшой уголок, и эти материалы мы размещаем только потому, что по роду основной работы нам приходится оказывать техническую поддержку нескольким десяткам организаций, использующих наши прототипы сайтов. В то же время кое-что может пригодиться и другим людям.
Мы не претендуем на изложение "истины в последней инстанции". Наверняка в этих заметках гуру MODx найдут неточности и ошибки. Мы будем очень благодарны, если на это нам укажут в комментариях.
В заметках мы стараемся не просто сразу приводить готовое правильное решение, но и показываем, как мы набивали шишки в процессе поиска.

Как удобнее редактировать аннотации


Вот этот самый текст, который вы сейчас читаете, и является аннотацией (введением). Обычно это начало документа, которое нужно отображать особым образом. Часто аннотация выводится в новостные ленты. Для редактирования аннтотации имеется специальное поле Аннотация. Однако в это поле можно написать только простой текст, а авторам материалов часто хочется добавить в текст форматирование, картинки вставить. В общем, чтоб "всё, как у людей" где нибудь на новостных сайтах.

Как предоставить такую возможность? Попытаемся разобраться.

Идеальным решением был бы встроенный визуальный редактор Аннотации, но разработчики MODx встроили обычный textarea - не посчитали нужным предоставлять такую возможность. В результате редакторы сайтов могут воспользоваться следующими вариантами:

1. Писать только простой текст.

2. Вручную вписывать теги HTML. Это сложновато для простых пользователей.

3. Писать всё в основном визуальном редакторе, потом открыть режим HTML, выделить и вырезать в буфер нужное место в начале документа, закрыть HTML и вставить код в поле редактирования Аннотации. Тоже сложновато будет.

Можно ещё и вообще Аннотации не писать, а для вывода в новостных лентах использовать сниппеты, выводящие N символов из контента или часть контента до некоего скрытого разделителя, вставленного в виде комментария, например <!-- splitter -->. Это явный шаг назад (так мы делали в CMS Etomite, где понятия аннотации не было вообще).

Попробуем решить эту проблему.

Как визуально редактировать аннотацию

В MODx возможно создать Template Variable (TV) типа RichText. О TV мы ещё напишем несколько заметок. Пока же скажем, что TV - это некоторые дополнительные данные различных типов, не предусмотренные структурой данных документа. TV связываются с шаблонами (потому так и называются Template Variable).  У документов, использующие эти шаблоны, появляются как бы дополнительные поля. Значения этих полей можно отображать на страницах сайта.

Создадим TV для редактирования текста. Делаем так:

1. Ресурсы - Управление ресурсами - вкладка Параметры (TV). Создаем новый параметр.

2. Устанавливаем свойства TV-параметра:

Установка TV параметра

Установка TV параметра

Присваиваем параметру имя annotation, тип ввода RichText и помечаем шаблоны, которые могут использовать это параметр.

Теперь открыв для редактирования документ, использующий шаблон, для которых разрешен этот TV мы обнаруживаем новый визуальный Редактор аннотации (заменитель для Introtext):

Редактор аннотации

Редактор аннотации

Он занимает всю верхнюю часть на вкладке Общие, "утапливая" вниз редактор контента. Это не очень-то удобно - запросто можно перепутать редактор TV и редактор контента и потом удивляться, почему же не виден измененный текст документа. Более удобной компоновкой редактора мы еще займемся с помощью плагина ManagerManager, а здесь разберемся, как же нам использовать текст, редактируемый в annotation.

Всё, что мы напишем в этом дополнительном редакторе, записывается в базу данных. Значение TV-параметра annotation мы можем использовать так же, как и других переменных документа, вставляя в шаблоны или чанки вызов [ *annotation* ]. Вот если бы мы не вставили на этой странице пробел между [ и *, то прямо в страницу вставился бы редактор TinyMCE, даже со стороны фронтенд.

Но нам надо не сам редактор, а текст, который в нём введен, поместить в поле introtext (Аннотация).Для этого придется сделать простой плагин.

Плагин для получения introtext

В идеале такой плагин должен:

1. При начале редактирования страницы взять текст из поля introtext и поместить в качестве значения в TV annotation.

2. При сохранении страницы поместить значение  TV annotation в поле introtext.

3. Очистить значение TV annotation (просто чтобы базу данных не утяжелять).

Создаем новый  плагин с именем, например, TvAnnoToIntro (для плагина имя значения не имеет). Привязываем плагин к событию OnBeforeDocFormSave. Сочиняем код плагина - пока самый примитивный.

Решение задачи 2 - заполнения introtext.

"Носком левой ноги мы давим окурок..." (С) фильм "Кавказская пленница".

global $introtext;
$annotation=$_POST['tvanotation'];
$introtext=$annotation;
 

  Включаем плагин, редактируем TV annotation в TinyMCE, сохраняем страницу и? И видим, что ничего не видим. Поле introtext стало пустым, хотя раньше в нем что-то было, да и TV annotation не пустой. Ну, конечно же - детская ошибка, вместо tvannotation мы написали tvanotation.

Специально упоминаем про это - такая невнимательность может привести к длительным поискам причин сбоев.

 Исправив опечатку мы получаем решение пункта 2 задания.

Замечание.
Сначала для получения значения TV annotation мы пытались использовать функцию  getTemplateVar. Вот так:

global $introtext;
$tv = $modx->getTemplateVar('annotation');
$tv_content = $tv['value'];
$introtext = $tv_content;

Но выяснилось:

Если этот код использовать в сниппете, то всё работает. В переменной $tv_content оказывается содержимое TV.
Если этот код использовать в плагине, то выдается ошибка MySQL о неверных кавычках. При замене на
$tv = $modx->getTemplateVar(`annotation`);  - изменили кавычки - ошибки нет, но нет и результата.

Плагин выполняется на событии OnBeforeDocFormSave.  Знающие люди подсказали, что функция getTemplateVar использует переменные documentIdentifier и documentObject, которые не определены перед сохранением документа. Покопавшись в исходниках MODx мы установили, что в этот момент значение TV можно получить через $_POST['tvanotation'].

 

Решение задачи 1 - заполнения TV annotation.

"Носком правой ноги мы давим второй окурок..." (С) фильм "Кавказская пленница".

 А это не так просто, как кажется. Нам нужно получить значение поля introtext и программно поместить в TV annotation. Сделать это надо в момент события... А черт его знает, какого. Документации нет. Судить можно только по названиям, да путем анализ исходников самой MODx и разных плагинов.  Скорее всего это событие OnDocFormPrerender.

  Удивительно, но в MODx вообще нет функции с именем наподобие SetTemplateVar. Но в репозитарии найдена библиотека document.class.inc.php с подходящими функциями. Автор, правда, её забросил, не обновляет, неизвестно как будет работать в нашей версии MODx. "Будем посмотреть".

В библиотеке есть класс Document и функции

  • Get()/Set() - получение/установка полей документа. или TV, если указан префикс 'tv'.
  • GetTV()/SetTV() - получение установка TV, то же, что и Set()/Get(), но с указанным префиксом.
  • Save() - сохранение документа.
  • Delete() - удаление документа вместе с TV

Вроде бы то, что надо

Для начала сделаем отдельный плагин IntroToTvAnno, привязанный к событию OnDocFormPrerender.

  Вот такой код:

global $id;
@require_once $modx->config["base_path"].'assets/libs/docapi/document.class.inc.php';
$doc = new Document($id,'introtext');
$intro=$doc->Get('introtext');
if (!empty ($intro)){
$doc->SetTV('annotation',$intro);
$doc->Save();

В этом плагине загружается дополнительныя библиотека и используется определенный в ней класс Document.  Класс имеет методы, отсутствующие в самой MODx. Они позволяют программно создавать и изменять документы. В нашем плагине мы считываем значение поля introtext и помещаем в значение TV-параметра annotation.

 При включении этого плагина всё заработало, как надо! Но с небольшим нюансом - быстро перестало работать.

 Вдруг, при попытке редактирования вот этой страницы стала вылетать ошибка MySQL со ссылками на плохой запрос.

Execution of a query to the database failed -  ou have an error in your SQL syntax; check the manual that corresponds to your MySQL server version  or the right syntax to use near 'tvanotation'];

Ошибка появляется только при включенном плагине и только на отдельных страницах. Разумеется, мы протестировали возможные сочетания условий наличия или отсутствия "фигурантов" плагина - introtext и TV annotation. Всё работает! Но не на этой странице. Тогда мы вообще удалили содержимое поля content  (в нем много подозрительных символов), и оказалось, что плагин работает! Дело было в том, что первоначально у нас в коде плагина было не

$doc = new Document($id,'introtext');

а

$doc = new Document($id);

При этом, если опущены имена полей, в документ загружаются все поля, а далее документ у нас сохраняется после обновления TV. Вот в этот момент содержимое поля content и имеет "неудобный" для SQL-запроса вид - в нем всякие кавычки и символы имеются.  Когда мы включили в конструктор объекта Document только одно конкретное поле, ошибка пропала. Завяжем узелок на память.

Решение задачи 3 - очистить значение TV annotation

Очевидно, это надо делать после сохранения документа, когда значение TV уже попало в introtext. Вроде бы можно, при событии OnDocFormSave выполнить:

            $doc = new Document($id,'introtext');
            $doc->SetTV('annotation','');
            $doc->Save();

Но так не получается, TV не очищается. Вернее, очищается - это видно, если отмечена опция После редактирования - закрыть. Но, если после сохранения редактирование продолжается, то наступает событие OnDocFormPrerender, и TV вновь заполняется нашим же плагином. Значит надо искать другое событие, а его пока не видим.

В общем, пока решения не нашлось. И вариант с прямым удалением из базы пробовали:

$tvValue = 7;
$modx->db->delete($modx->getFullTableName('site_tmplvar_contentvalues'), 'contentid="' . $id . '" AND tmplvarid="' . $tvValue . '"');

Нет, не получается. То есть удалить-то получается, но потом сразу значение восстанавливается. Пока этот вопрос оставим открытым. Разумеется, и $tvValue будем не константой использовать, а выяснять по имени TV

  Наконец мы можем объединить временные плагины в один.

Общий плагин для синхронизации introtext и  TV annotation.

"А теперь оба окурка мы давим вместе!" (С) фильм "Кавказская пленница".

  Собираем всё в один плагин

global $introtext;
@require_once $modx->config["base_path"].'assets/libs/docapi/document.class.inc.php';
$e = &$modx->Event;
switch ($e->name) {
   case "OnBeforeDocFormSave":
        $annotation=$_POST['tvannotation'];
        $annotation=trim($annotation);
        $annotation = mysql_escape_string($annotation);
        $introtext=$annotation;
    break;
    case "OnDocFormRender":
    case "OnDocFormSave":

      $tvValue = 7;
       $modx->db->delete($modx->getFullTableName('site_tmplvar_contentvalues'),
         'contentid="' . $id . '" AND tmplvarid="' . $tvValue . '"');
break;
     case "OnDocFormPrerender":
       $doc = new Document($id,'introtext');
       $intro=$doc->Get('introtext');
       if (!empty ($intro)){
          $doc->SetTV('annotation',$intro);
          $doc->Save();
       }
     break;
    default :
      return;
     break;
}

 

Да, способ был найден. Для удаления TV мы воспользовались одновременно событиями OnDocFormRender и OnDocSave!. В результате,если при Сохранить включена опция Продолжать редактирование (а это чаще всего делается) TV  удаляется из своей таблицы по событию OnDocFormRender, а при опции Отменить - по событию OnDocSave. В результате записи в таблице _site_tmplvar_contentvalues, относящиеся к TV annotation, появляются только на период редактирования страниц.

  Впоследствии было найдено вообще очень простое решение, описанное в заметке Проблема с ManagerManager

09-02-2009 21:20:38



    Содержание раздела «Редактирование аннотаций»:
Комментарии любых посетителей

Написать комментарий