Отложенная публикация статьи. Instant 2.01

+20
3.85K
Доброго всем времени суток!

Увидел просьбу на форуме о возможности отложенной публикации, и решил сделать, заодно изучив возможности расширения функционала второй версии.
Я хочу рассказать вам об этом в виде урока.

Мы будем делать отложенную публикацию только для типа контента "Статьи", думаю на все остальные типы контента можно сделать по аналогии.

У нас будут некоторые ограничения, связанные с движком — отложенные статьи сможет создавать только администратор, либо пользователь с правами администратора и у него не должно быть друзей (ниже объясню почему).

Итак, нам необходимо при добавлении или редактировании статьи указать дату публикации статьи. Что нам для этого нужно?


Нам нужно в форму добавления статьи (и редактирования, что в принципе одно и тоже) добавить два поля — одно признак, что статья отложена, второе — дата, когда статья должна опубликоваться.

Зайдем в административную часть, ТИПЫ КОНТЕНТА — СТАТЬИ — ПОЛЯ
добавим новое поле
Системное имя: seelater
Название поля: Опубликовать позже
Подсказка для поля: пишем что-нибудь по желанию

Тип поля: Флаг
Группа — Создать новую группу: Отложенная публикация вводим именно так как написано, это название будет использовано в скрипте, позже поймете зачем

Также, ставим Доступ для изменения — только группе Администраторы
все остальные поля в форме оставляем как есть

скрины
Иллюстрация
Иллюстрация

Итак у нас есть одно поле. В качестве поля для даты публикации будем использовать встроенное поле таблицы date_pub — это поле формата ДАТА без времени, поле со временем нужно добавлять как-то отдельно (в движке отсутствует). Поэтому статья будет опубликована только при наступлении указанной даты.

С полями мы закончили, теперь приступим к самому интересному

Мы будем использовать хуки (hooks) движка, чтобы внедрится в его функционирование. В файле /system/controllers/content/manifest.php добавим все наши хуки, которые мы будем использовать, чтобы движок Инстанта их увидел.

Добавьте в массив следующие строки
'content_articles_form',
'content_articles_after_add',
'content_articles_after_update',

чтобы получилось так:

  1. <?php
  2.  
  3. return array(
  4.  
  5. 'hooks' => array(
  6. 'menu_content',
  7. 'user_delete',
  8. 'user_tab_info',
  9. 'user_tab_show',
  10. 'sitemap_sources',
  11. 'content_articles_form',
  12. 'content_articles_after_add',
  13. 'content_articles_after_update',
  14. )
  15.  
  16. );
  17.  
Здесь, content_articles_form хук для вывода формы добавления или редактирования статьи, соответственно, content_articles_after_add и content_articles_after_update — хуки вызываемые после добавления или редактирования статьи

Рассмотрим первый хук, создайте файл content_articles_form.php в папке /system/controllers/content/hooks со следующим содержимым

  1. <?php
  2.  
  3. class onContentContentArticlesForm extends cmsAction {
  4.  
  5. const SEELATERNAME = "Отложенная публикация";
  6.  
  7. public function run($form){
  8.  
  9. $structure = $form->getStructure();
  10. // так как по нормальному fieldset выбрать не получится будем искать по title перебором
  11. foreach($structure as $ids=>$str) {
  12. if ($str['title']==self::SEELATERNAME) {
  13. $form->addField($ids, new fieldDate('date_pub', array(
  14. 'title' => 'Дата публикации'
  15. )));
  16. break;
  17. }
  18. }
  19.  
  20. return $form;
  21.  
  22. }
  23.  
  24. }
  25.  
Этот хук делает следующее — он сначала получает данные формы ( getSctructure() ) далее, ищет в них группу "Отложенная публикация", а в ней как вы помните уже находится поле флага отложенной записи, и если находит, добавляет к нему поле даты публикации date_pub. Далее, после отработки хука у нас появитсяя форма для ввода данных.

Далее, рассмотрим второй и сразу третий хук

Создайте в той же папке hooks еще два файла: content_articles_after_add.php и content_articles_after_update.php

Содержимое первого

  1. <?php
  2.  
  3. class onContentContentArticlesAfterAdd extends cmsAction {
  4.  
  5. public function run($item){
  6.  
  7. if ($item['seelater']==1) {
  8. $now = date('Y-m-d');
  9.  
  10. $item['seelater'] = $now < $item['date_pub'] ? 1 : NULL;
  11. $item['is_private'] = $item['seelater'] == 1 ? 1 : 0;
  12. $ctype = $this->model->getContentTypeByName('articles');
  13. $fields = $this->model->getContentFields($ctype['name'], $item['id']);
  14. $item = $this->model->updateContentItem($ctype, $item['id'], $item,$fields);
  15. $user = cmsUser::getInstance();
  16. if ($user->is_admin) {
  17. cmsEventsManager::hook("content_after_add_approve", array('ctype_name'=>$ctype['name'], 'item'=>$item));
  18. }
  19. }
  20. return $item;
  21.  
  22. }
  23.  
  24. }
  25.  
и второго

  1. <?php
  2.  
  3. class onContentContentArticlesAfterUpdate extends cmsAction {
  4.  
  5. public function run($item){
  6.  
  7. if ($item['seelater']==1) {
  8. $now = date('Y-m-d');
  9.  
  10. $item['seelater'] = $now < $item['date_pub'] ? 1 : NULL;
  11. $item['is_private'] = $item['seelater'] == 1 ? 1 : 0;
  12. $ctype = $this->model->getContentTypeByName('articles');
  13. $fields = $this->model->getContentFields($ctype['name'], $item['id']);
  14. $item = $this->model->updateContentItem($ctype, $item['id'], $item,$fields);
  15. $user = cmsUser::getInstance();
  16. if ($user->is_admin) {
  17. cmsEventsManager::hook("content_after_update_approve", array('ctype_name'=>$ctype['name'], 'item'=>$item));
  18. }
  19.  
  20. }
  21. return $item;
  22. }
  23.  
  24. }
  25.  
Веселые названия у классов, да? ну да ладно, это мелочи

Первый хук нужен нам для того, чтобы при добавлении статьи решить — будет статья опубликована сразу, или мы её еще "помурыжим". Смотрим значение поля seelater, если единичка, значит проверяем введенную дату публикации, если она больше сегодняшней — выключаем статью полем is_private, чтобы в activity (лента активности) запись пока не отображалась. Точнее она будет отображаться для друзей добавляющего, но у нашего автора друзей то как бы нет 😊. К сожалению в активити нет статуса "показать только мне" или "никому", поэтому будем вставлять такие вот "костыли". После публикации статьи — is_private станет равно нулю — и статья в активити появится.

Далее, сохраняем статью еще раз, и вызываем хук от activity, соответственно

cmsEventsManager::hook("content_after_add_approve", array('ctype_name'=>$ctype['name'], 'item'=>$item));

либо при редактировании

cmsEventsManager::hook("content_after_update_approve", array('ctype_name'=>$ctype['name'], 'item'=>$item));

Странно, конечно, что результат работы хуков AfterUpdate и AfterAdd далее не используется, разработчик или забыл про него или не заметил (подсказка файлы item_add.php, item_edit.php
строки
cmsEventsManager::hook("content_after_add", $item);
cmsEventsManager::hook("content_{$ctype['name']}_after_add", $item);
или
cmsEventsManager::hook("content_after_update", $item);
cmsEventsManager::hook("content_{$ctype['name']}_after_update", $item);
)


Продолжим, теперь у нас при добавлении или редактировании статья обладает признаком отложенности. Как нам вывести правильно статьи, точнее, как нам не показать раньше времени неопубликованные (по нашему мнению) статьи.

Я предлагаю сильно не задумываться, а воспользоваться датасетами то есть Наборами:

Переходим опять в админку, ТИПЫ КОНТЕНТА — СТАТЬИ — НАБОРЫ

открываем набор ВСЕ

внизу добавляем фильтр по полю "Опубликовать позже" (наш флаг) ставим ему условие "Не заполнено", как на скрине

Иллюстрация

Тоже самое нужно сделать со всеми остальными наборами, чтобы они ненароком не показали нашу статью.

Что мы уже получили — мы можем не показывать статью до определенной даты. Если нам нужно показать её, мы просто убираем флаг отложенности — и она появляется. Но это вручную, а как сделать так, чтобы статьи публиковались сами.

Воспользуемся крон-задачами. То есть задачами по расписанию. Добавим последний хук в content, он то и будет публиковать наши статьи по наступлении указанной даты.

Итак добавляем файл cron_republic.php в папку /system/controllers/content/hooks (почему не нужно добавлять в manifest.php? сам не знаю, так работает 😊)

  1. <?php
  2.  
  3. class onContentCronRepublic extends cmsAction {
  4.  
  5. public function run(){
  6.  
  7. $content_model = cmsCore::getModel('content');
  8.  
  9. $content_model->filterEqual('seelater', 1);
  10. $now = date("Y-m-d");
  11. $content_model->filterGTEqual('date_pub', $now);
  12. $items = $content_model->getContentItems('articles');
  13.  
  14. if (is_array($items)) {
  15. $ctype = $this->model->getContentTypeByName('articles');
  16. foreach($items as $item) {
  17. $item['seelater'] = $now < date('Y-m-d',strtotime($item['date_pub'])) ? 1 : NULL;
  18. $item['is_private'] = $item['seelater'] == 1 ? 1 : 0;
  19. $fields = $this->model->getContentFields($ctype['name'], $item['id']);
  20. $item = $this->model->updateContentItem($ctype, $item['id'], $item,$fields);
  21. cmsEventsManager::hook("content_after_update_approve", array('ctype_name'=>$ctype['name'], 'item'=>$item));
  22. }
  23. }
  24. }
  25.  
  26. }
  27.  
Далее заходим в админку — НАСТРОЙКИ — ПЛАНИРОВЩИК — СОЗДАТЬ ЗАДАЧУ и делаем как на скрине

Иллюстрация

На этом все, отложенная публикация должна работать. Есть только один момент, я не тестировал на хостинге с рабочим кроном, запускал задачу только вручную. Есть некоторые сомнения по поводу работы крон-задачи — нужна проверка. Может быть необходимо делать как-то по другому...

Урок закончен, также можно сделать отложенную публикацию для всех остальных типов контента, либо написав отдельные хуки, либо сделать универсальные.

Напоследок скрин формы добавления статьи
Иллюстрация

По результатам — как мне показалось, доработки в данной версии Инстанта немного тяжеловаты. Может быть я еще не все в ней понимаю, но поля published в процессе очень не хватало 😊
-9
Ильгиз Ильгиз 9 лет назад #
у него не должно быть друзей

-2
Май Май 9 лет назад #
чо за нах?
0
shaman888 shaman888 9 лет назад #
наверно пикабушник smile
0
Ильгиз Ильгиз 9 лет назад #
Да.
+1
Raiden Raiden 9 лет назад #
Таня будет довольна)
+1
Май Май 9 лет назад #
Таня будет довольна)
Это главное. )
Ну и вообще, полезная вещь, обязательно воспользуюсь.
Большое спасибо, Крот!
0
Таня Таня 9 лет назад #
Таня будет довольна)
Эх..
Я конечно все внимательно почитала и поняла, что почти ничего не поняла))
На самом деле, конечно, если бы этот вариант устраивал, то разобралась бы.
Кроту, конечно, спасибо за его попытки, но слишком много костылей.
Первое, то что надо быть только админом, не хочется всех авторов делать админами, к тому же у них не должно быть друзей...
Нет возможности назначать время. Получается что если есть 10 запланированных записей на 7 июня, то все они будут опубликованы в 00:00 ? Очень не удобно, когда нужно чтобы в течении дня выходили статьи, а не сразу все в 12 ночи назначенной даты.

В общем я буду смерено ждать, когда r2 возьмется за дело) Очень надеюсь, что эта функция будет нужным дополнением в instantcms и будет такой же простой и удобной, как сами знаете, где)
+1
Def Def 9 лет назад #
да, в коробке это было бы очень к месту
+1
Крот Крот 9 лет назад #
я думаю что у r2 есть какое-то свое представление возможностей расширения данной версии cms. Я этого пока не увидел. Может быть маловато опыта.

Хотя, можно было бы изменить ядро и все бы работало - но нам ведь этого не нужно - cms должна помогать расширять функционал, а не ставить ограничения

p.s. в первой ветке, отложенные статьи можно сделать проще. наверное)
0
lokanaft lokanaft 9 лет назад #
Вот только r2 предлагает не трогать файлы манифестов, а добавлять собственные контроллеры.
+1
r2 r2 9 лет назад #
Верно. В идеале, это решение должно было быть оформлено как отдельный контроллер.
Потому что нет смысла использовать хуки, если файл манифеста системного контроллера все равно будет переписан при обновлении.
0
Крот Крот 9 лет назад #
ок учту
но ИМХО странно как-то получается - делать контроллер чтобы хакнуть официально другой

контроллер - хакер smile
+1
SJen SJen 9 лет назад #
не хакнуть, а хукнуть glasses
0
Крот Крот 9 лет назад #
ага i`m a hooker glasses
0
r2 r2 9 лет назад #
что в этом странного?
вам же не странно что в 1.10.3 делается плагин чтобы добавить функционал куда-либо?
в 2.0 просто нет "плагинов" в терминологии 1.x, их роль выполняют контроллеры (осознанно не использую слово "компоненты")
т.к. при ближайшем рассмотрении разница между "компонентами" и "плагинами" становится очень несущественной
0
Крот Крот 9 лет назад #
Верно. В идеале, это решение должно было быть оформлено как отдельный контроллер.

в идеале эта возможность должна быть "из коробки" hoho
+1
lokanaft lokanaft 9 лет назад #
В итоге, вроде бы простая задача, а упирается во всякие заковырки.
+1
Raiden Raiden 9 лет назад #
Видимо не такая уж и простая) Просто очень нужная.
0
webtotma webtotma 9 лет назад #
А такой вариант не прокатит?

Создаем доп.поле в типе контента - "Дата публикации", связываем с БД.
В шаблоне вывода контента парсим это поле на предмет сравнения с текущей датой. Если <=, то выводим
Если поле не заполнено, то публикуем как обычно.
+1
Крот Крот 9 лет назад #
тогда cms при выводе списка будет "врать" про количество записей - нужно править перебором списка и "лечить" разбивку по страницам
также еще, статьи будут добавлены в активити, а их там быть не должно
также будут выведены в профиле

в принципе конечно можно, но будет много костылей
+1
webtotma webtotma 9 лет назад #
Все правильно подметили. Придется еще пару-тройку костылей доделывать.

Вообщем встраивать эту возможность надо в движок по дефолту.

Еще от автора

Инвайтер 1.9 для 1.10.6
Доброго всем времени суток! Решил нарушить сложившуюся здесь традицию и решил вернуться сюда с обновлениями.
Инстант "по взрослому". Часть 2. Авторизация. Аякс. v1.10
Доброго всем времени суток! Продолжая серию Инстант "по взрослому", сегодня хотел бы показать более мощный плагин авторизации.
Инстант "по взрослому". Часть 1. Авторизация. Счетчик неверных входов. v1.10
Доброго всем дня! Продолжим наши уроки по усовершенствованию Инстанта. Сегодня мы будем изучать и усовершенствовать первую ветку Инстанта.
Используя этот сайт, вы соглашаетесь с тем, что мы используем файлы cookie.