Создание механизма "спасения" статьей из удаляемого раздела

+31
3.46K
Создан механизм, позволяющий "спасти" статьи из удаляемого раздела. Для версии iCMS 1.10.1

Постановка задачи

Вчера мой друг Blackman-st создал тему, в которой сообщил об обнаруженном им баге.

Напомню, что при удалении раздела, содержащего статьи, задается два вопроса. Первый: "Удалить раздел такой-то и подразделы?" Второй: "Удалить все вложенные статьи?". Баг заключался в том, что при ответе на второй вопрос "Нет" статьи все равно удалялись.

Я разобрался с этим вопросом и выяснил, что в модели компонента content просто-напросто отсутствует механизм, позволяющий перенести статьи либо в коренной раздел, либо в архив, либо еще куда-нибудь. Blackman-st попросил меня переносить спасаемые статьи в раздел "Удаленные", что я и делаю.

Техзадание на хак

1. На странице "Контент сайта" в админке (эта страница выводится при нажатии на подпункт меню "Разделы и статьи" пункта "Каталог статей"), в колонке "Действие" создать дополнительную иконку. При нажатии на иконку должен выводится вопрос, действительно ли следует эту статью перенести в раздел "Удаленные". При положительном ответе статья переносится в раздел "Удаленные". Если такого раздела нет, он создается автоматически.
2. При нажатии на иконку "Удалить раздел" при отрицательном ответе на вопрос "Удалить все вложенные статьи?" раздел удаляется, а статьи из него переносятся в раздел "Удаленные". Если раздела нет, он автоматически создается.

Раздел "Удаленные" ничем не отличается от других разделов контента сайта. По умолчанию он неопубликован, поэтому его видит один админ. Впрочем, пройдя в редактирование свойств этого раздела, можно настроить его так, как Вам удобно.

Настройки

Никаких настроек хак не требует.

Как установить

Скачать архив, распаковать в любое место. Залить в корень сайта с заменой.
ПРЕДУПРЕЖДЕНИЕ! Заменяются некоторые системные файлы( список файлов см. ниже). Поэтому, если Вы уже вносили в эти файлы изменения, эти файлы следует не заменить, а смержить. Подробнее о программе merge смотрите в документации на эту программу, этот вопрос уже множество раз поднимался на форуме.

Список измененых файлов

templates/_default_/admin/content.php
admin/applets/content.php
components/content/model.php

Версия iCMS 1.10.2

Только что заглянул в модель компонента content этой версии и могу засвидетельствовать, что описанный баг в ней присутствует. Точнее, механизм переноса отсутствует напрочь. Юзающим эту версию могу порекомендовать перенести изменения, вносимые хаком, в свою версию, пользуясь упомянутой программой Araxis Merge.
0
Dorimen Dorimen 11 лет назад #
Надеюсь, такие вещи будут исправлены на уровне официальных следующих версий ветки 1.10, чтобы не приходилось тяжело думать, как мигрировать...
Олег Васильевич я Олег Васильевич я 11 лет назад #
Комментарий удален
Олег Васильевич я Олег Васильевич я 11 лет назад #
Комментарий удален
+1
Странник Странник 11 лет назад #
Написал Фузу. Он обещает в 1.10.3 поправить.
0
Андрей Андрей 11 лет назад #
Странник, спасибо большое за реализацию! Будем заливать и наслаждаться!
+2
Странник Странник 11 лет назад #
Да пожалуйста, рад был помочь.
-3
Fuze Fuze 11 лет назад #
в 1.10.2 указанный баг не подтверждается.
+3
Странник Странник 11 лет назад #
протестировал только что на v 1.10.2 - к сожалению, подтверждается.
статья не удаляется из базы данных, но по прежнему поле category_id таблицы cms_content содержит id уже несуществующего раздела. эта статья в админке не показывается - такое впечатление, что она убилась вместе с разделом.

я же говорю - отсутствует механизм переноса (то бишь изменения id категории у статей) при удалении категории, содержащей статьи. Я по просьбе Blackman'а сделал хак, переносящий эти статьи в категорию "Удаленные", хотя в стандартном движке можно переносить либо в корневой раздел, либо в архив.
+2
Крот Крот 11 лет назад #
ув. Странник, ваш хак может разрушить структуру категорий на сайте
функция
Код PHP:
  1. $sql = "SELECT MAX(id) AS id FROM cms_category";
  2. $result = $this->inDB->query($sql);
  3. $max_category = $this->inDB->fetch_row($result);
  4. $max_id = $max_category[0];
  5. $trash_id = $max_id+1;
  6. $sql = "INSERT INTO cms_category
  7. (id, parent_id, title, published, showdate, showcomm, orderby, orderto, NSLeft, NSRight, NSLevel, ordering, maxcols, showtags, showrss, showdesc, is_public, seolink, tpl)
  8. VALUES ('{$trash_id}', '1', 'Удаленные', '0', '1', '1', 'pubdate', 'ASC', '{$max_id}', '{$trash_id}', '1', '3', '1', '1', '1', '1', '0', 'udalennye', 'com_content_view.tpl')
  9. ";
  10. $this->inDB->query($sql);
1) id добавляемой категории считать не нужно, есть вероятность что на сильно загруженных сайтах второй админ добавит нормальным способом другую категорию в тот момент когда первый будет считать $trash_id = $max_id+1; => будет ошибка добавления
2) вы записываете поля NSLeft, NSRight, NSLevel напрямую в таблицу - здесь используются nested-sets деревья, данное добавление обязательно нарушит алгоритм работы nested-sets

добавление категории лучше делать так
что-то вроде -
$ns = $inCore->nestedSetsInit('cms_category');
$category['id'] = $ns->AddNode($category['parent_id']);

вообщем пока не советую ставить хак
проблема в 1.10.2(и ранних версий) подтверждаю - при удалении категории все вложенные статьи "виснут" со старым cat_id. На сайте не тестил, смотрел только код.
-1
lokanaft lokanaft 11 лет назад #
Что вы, Крот, Странник, по его словам, очень хорошо разбирается в этом движке и наверняка всё отлично работает, не пугайте так)
-1
Странник Странник 11 лет назад #
Приведенная вами функция всего-навсего создает раздел "Удаленные", если он не создан раньше, вручную. Посмотрите внимательно - если раздел "Удаленные" уже создан, он второй раз не создается.
Я еще раньше предложил заказчику переносить спасаемые статьи в корневой каталог, но он попросил в раздел "Удаленные", что я и сделал.

Тем не менее, факт остается фактом - в стандартном движке отсутствует механизм спасения статей. Если сообшество попросит, я переделаю хак, чтобы он переносил спасаемые статьи в корневой каталог, в таком случае все опасения Крота будут напрасны.
-2
lokanaft lokanaft 11 лет назад #
Странник, что это за цирк:
Код PHP:
  1. $sql = "SELECT MAX(id) AS id FROM cms_category";
  2. $result = $this->inDB->query($sql);
  3. $max_category = $this->inDB->fetch_row($result);
  4. $max_id = $max_category[0];
  5. $trash_id = $max_id+1;
? last insert id религия не позволяет использовать?

И вообще для этого есть:
Код PHP:
  1. $ns = $inCore->nestedSetsInit('cms_category');
  2. $category['id'] = $ns->AddNode($category['parent_id']);
  3. $inDB->update('cms_category', $category, $category['id']);
смотреть в /admin/applets/cats.php
Тогда с деревом ничего не случится.
Странник:
Приведенная вами функция всего-навсего создает раздел "Удаленные"
Всего-навсего ломает дерево, подумаешь, веткой больше, веткой меньше, у нас же корней нет, в облаке растём...
0
stealthdebuger stealthdebuger 11 лет назад #
lokanaft:
? last insert id религия не позволяет использовать?

Это плохой совет, я бы настоятельно не рекомендовал это использовать.
-1
lokanaft lokanaft 11 лет назад #
Человек же хочет получить ид новой категории, а использовать его для вычисления ns-ов он впринципе не имеет права.
+1
Fuze Fuze 11 лет назад #
Это плохой совет, я бы настоятельно не рекомендовал это использовать.
каков аргумент не использовать mysql_insert_id или SELECT LAST_INSERT_ID()?
0
Крот Крот 11 лет назад #
тоже спросил ниже)
0
stealthdebuger stealthdebuger 11 лет назад #
вижу, вы уже сами ответили на свой же вопрос.
-1
lokanaft lokanaft 11 лет назад #
Тогда бы и написали, что сделать то нужно, а то можно подумать, в момент расчётов с MAX(id) никто ничего не добавит параллельно, как заметил прежде Крот.
0
stealthdebuger stealthdebuger 11 лет назад #
MAX(id) - это более надежный способ, т.к. last_insert_id может вернуть результат совершенно от другой таблицы.
Хотя подобные решения, на мой взгляд, принципиально не верны... для серьезных проектов.
0
Fuze Fuze 11 лет назад #
MAX(id) - это более надежный способ, т.к. last_insert_id может вернуть результат совершенно от другой таблицы.
Код SQL:
  1. SELECT LAST_INSERT_ID() AS lastid FROM TABLE LIMIT 1
вероятность ничтожно мала.
Хотя подобные решения, на мой взгляд, принципиально не верны... для серьезных проектов.
выход?
0
Крот Крот 11 лет назад #
друпал, джумла точно, ливстрит не помню, ставят блок при добавлении(редактировании)... но вроде не средствами mysql а просто где-то записывают, что вот то-то и то-то заблокировано и не дают менять из api cms.

InnoDB конечно же хорошо, но особо не попользуешь на shared-хостингах

думаю, хорошо бы предусмотреть просто возможность подключения какого-либо блокиратора(плагином) при необходимости
хотя не уверен что такие сайты на инстанте уже есть
0
stealthdebuger stealthdebuger 11 лет назад #
Код SQL: SELECT LAST_INSERT_ID() AS lastid FROM TABLE LIMIT 1
Можно и так, конечно, на чем, собственно, и держится ICMS сейчас. И, совершенно верно, что вероятность ничтожно мала... при штатных ситуациях.
+2
Fuze Fuze 11 лет назад #
stealthdebuger, я буду только за если есть другой, более надежный вариант.
Крот, InnoDB думаю вполне выход, если задействовать полноценно механизм транзакций.
+1
oll oll 11 лет назад #
Код PHP:
  1. SELECT id FROM cms_content ORDER BY id DESC LIMIT 1
++ Некрасиво , но надежно.
0
lokanaft lokanaft 11 лет назад #
stealthdebuger:
last_insert_id может вернуть результат совершенно от другой таблицы
Пруф можно?
+1
lokanaft lokanaft 11 лет назад #
Вот sqlinfo.ru/forum/viewtopic.php?id=6389 вроде образованный товарищ ответил по поводу LAST_INSERT_ID()
+1
Fuze Fuze 11 лет назад #
В 1.10.3 реализовал так: если ответ будет не удалять вложенные категории, то они переносятся в корень + в архив, как следствие на сайте их видно уже не будет.
http://trac.instantcms.ru/changeset/1314#file14
изменения в методе deleteCategory
0
Крот Крот 11 лет назад #
думаю, нужно дать админу сайта свободу выбора в настройке удаления статей-категорий smile
0
Fuze Fuze 11 лет назад #
по феншую - то да конечно, если ответ нет, показать окно с выбором категорий и перенести их в выбранную. Другой вопрос нужно ли это будет кому-либо. Тем более сейчас никто не мешает заранее, перед удалением категории переместить статьи куда нужно. имхо - мало кто использует вообще данную функцию, ибо смена url и т.п. Обычно сразу продумывается структура.
-2
Андрей Андрей 11 лет назад #
феншуй - это подготовка места для усопшего, если быть точным в переводе :)

сколько раз в жизни, Игорь, Вы стелили соломку, прежде чем упасть?
0
Крот Крот 11 лет назад #
Может тогда в первом окошке запроса на удаление категории написать что-то типа - вы можете перенести(удалить) нужные вам вложенные статьи, иначе они будут удалены либо перенесены в архив.

А то при первом использовании InstantCMS встать на грабли или гадать что будет - не комильфо
0
Fuze Fuze 11 лет назад #
Может тогда в первом окошке запроса на удаление категории написать что-то типа - вы можете перенести(удалить) нужные вам вложенные статьи, иначе они будут удалены либо перенесены в архив.
в информационном сообщении примерно так и написано, см. бранч.
-1
Андрей Андрей 11 лет назад #
может оно так и реализовано, но тогда в первом же окне, если в удаляемом разделе есть статьи, написать "статьи раздела будут уничтожены вместе с разделом", продолжить или отменить операцию?
защита от дурака должна быть привентивной, а не работать по факту
+1
WALTERZ WALTERZ 11 лет назад #
Нечего дуракам сайтами заниматься
0
stealthdebuger stealthdebuger 11 лет назад #
Ну почему же, в качестве примера, опровергающего данное утверждение
Код PHP:
  1. //проверка прав пользователей
  2. $permissionusers = array('deniskin', 'mio', 'juks', 'aist', 'mertas', 'mudhooney');
  3. if (in_array(getTekushiiPolzovatel(), $permissionusers)) {
  4. setcookie('habrahabr_11223', 'true', 604800);
  5. }
  6. /**
  7. * Проверка пермишенов в блоге
  8. */
  9. function blog_perm($user_login, $blog_id) {
  10. if ($_COOKIE['habrahabr_11223'] == true) return 'god';
  11. if (in_array($user_login, $permissionusers)) {
  12. $perm = $permissionusers[$user_login];
  13. if ($perm[$blog_id]) return $perm[$blog_id];
  14. else return 'guest';
  15. }
  16. return 'guest';
  17. }
  18. /**
  19. * Что может делать юзер
  20. */
  21. function blog_access($perm) {
  22. global $habrauser;
  23. switch ($perm) {
  24. case 'god':
  25. return array('r'=>1, 'w'=>1, 'x'=>1, 'd'=>1, 'e'=>1, 'o'=>1, 'm'=>1);
  26. break;
  27. case 'editor':
  28. return array('r'=>1, 'w'=>1, 'x'=>0, 'd'=>1);
  29. break;
  30. case 'writer':
  31. return array('r'=>1, 'w'=>1, 'x'=>0, 'o'=>1, 'e'=>1);
  32. break;
  33. case 'owner':
  34. return array('r'=>1, 'w'=>1, 'x'=>1);
  35. break;
  36. }
  37. }
0
Крот Крот 11 лет назад #
хехе, прикольно, последний юзер выключен)
0
lokanaft lokanaft 11 лет назад #
Крот, там вся суть в $_COOKIE, а blog_perm впринципе не работает без $permissionusers.
0
Крот Крот 11 лет назад #
куку не чекал)
не думаю что она реальная
0
Крот Крот 11 лет назад #
ИМХО, при удалении категории со статьями, если админ забыл (не захотел) перенести их(статьи) заранее, статьи должны попасть к родителю данной удаляемой категории (если сирота - то в корневой раздел)
делается добавлением двух-трех(ну пяти) строк в model.php в функцию удаления

А в таком виде, как у вас, использовать хак небезопасно. Если хотите переносить в "удаленные" либо заставьте пользователя руками создать раздел, либо просто поправьте код создания раздела


Это плохой совет, я бы настоятельно не рекомендовал это использовать.
ну да, last_id там ни к чему
хотя в инстанте (даже во второй версии!!!) он активно используется
интересно, если одновременно запустить 10 процессов добавления в одну таблицу не блокируя таблицу и читать last_id сразу же после добавления записи - собьется или нет?
0
Крот Крот 11 лет назад #
отвечу сам - дружно читаем Блокировки в MySQL и делаем выводы
может разработчики сделают в новой версии, думаю нужная вещь и во многих популярных cms есть блокировки
0
Странник Странник 11 лет назад #
Большое всем спасибо за обсуждение. Я доволен. Во-первых, тем, что на проблему обратили внимание и ее в 1.10.3 пофиксили. Во-сторых, я весьма согласен со всеми, что создание раздела "Удаленные" не есть лучшее решение, следовало бы переносить спасаемые статьи в корневой раздел (то есть так, как я и предлагал по первому варианту), откуда их админ может раскидать по нужным разделам.

Завтра выкину из хака код, где создается этот раздел.
-2
Андрей Андрей 11 лет назад #
корень - хорошо, а спецраздел лучше...
можно, при настройке хака, ручками создать раздел "Удаленные", и так же ручками вписать ИД раздела в поле базы (приналичии маломальской инструкции)

и ничего ломаться не будет

а для новой версии 1.10.3 и 2.0 сразу сделать раздел по дефолту

помоему - самый лучший варинат будет, потому как в корне тоже много чего лежит иногда
0
Fuze Fuze 11 лет назад #
Blackman-st, то, что вы описываете - костыли. Если уж делать, так делать системную "корзину" - сборщик подобного рода контента. В данном же случае, вариант с перемещением в корень и последующее автоматическое перемещение в архив - имхо идеальный и быстрый вариант. И в корне лишнего не будет, ибо все эти статьи в архиве.
-2
Андрей Андрей 11 лет назад #
Так я к тому и веду, чтобы сотворить такую корзину в 1.10.3, а для тех, кто будет обновляться, в любом случае придется ручной вариант использовать, ибо ID раздела может не совпасть.
+1
never never 11 лет назад #
Спасибо за хак, будем реализовывать)
-1
Андрей Андрей 11 лет назад #
так чем дело то закончилось? хак остается без изменений или все таки интегрировался в 1.10.3 как положено?
кстати, сорри, что без попыток посмотреть, что будет со статьями пользователя, если тот удалится?
0
Странник Странник 10 лет назад #
После появления утилиты "Ремонтник деревьев" этот хак можно считать достоянием истории.)))

Еще от автора

Адаптация компонента "Закладки" от 19 января 2011 к коробочной версии 1.10.7
На форуме появилась тема с пожеланиями адаптировать довольно старый компонент "Instant Bookmarks" Fuze к современным реалиям.
Утилита "Садовод" для версии InstantCMS 1.10.7.008
В этой краткой заметке описывается утилита "Садовод" для ремонта деревьев версии InstantCMS 1.10.7.008.
Изменение компонента "Баннеры" для будущей  версии 1.10.7.008
Данная статья рассказывает всем ждущим новую версию 008, что нового ожидается в будущем релизе версии 1.10.7.008.
Используя этот сайт, вы соглашаетесь с тем, что мы используем файлы cookie.