Создание встроенного модуля CMS. Часть 2 — различия между версиями
(→Как хранится главное меню) |
|||
(не показано 18 промежуточных версии этого же участника) | |||
Строка 1: | Строка 1: | ||
Внимание! Данная статья предполагает, что вы уже читали [[Создание встроенного модуля CMS|первую часть]]. | Внимание! Данная статья предполагает, что вы уже читали [[Создание встроенного модуля CMS|первую часть]]. | ||
+ | |||
+ | Внимание! Модуль уже реализован в виде встроенного в Портале версии 5.3. Оставляем статью в качестве обучающего материала. | ||
На этот раз сделаем карту сайта на основе главного меню. | На этот раз сделаем карту сайта на основе главного меню. | ||
Строка 11: | Строка 13: | ||
=== Регистрация в базе === | === Регистрация в базе === | ||
− | < | + | <source lang="sql"> |
− | INSERT INTO MODULES (MODULENAME, VISIBLE_NAME, M_DEF_LINK, ACCESS, M_PLACE | + | INSERT INTO MODULES (MODULENAME, VISIBLE_NAME, M_DEF_LINK, ACCESS, M_PLACE) |
− | VALUES (' | + | VALUES ('sitemap', 'Карта сайта', 'mod=sitemap', ',6,,2,,4,', 'center'); |
− | </ | + | </source> |
=== Код модуля === | === Код модуля === | ||
Строка 55: | Строка 57: | ||
I_SORT Порядок сортировки. | I_SORT Порядок сортировки. | ||
Пользователь перетаскивает элементы мышкой в редакторе, | Пользователь перетаскивает элементы мышкой в редакторе, | ||
− | а это поле | + | а это поле обновляется соответствующим образом автоматически. |
− | Там | + | Там просто числа, для SQL-оператора ORDER BY. |
PUBLISHED Если там 1, значит пункт меню опубликован, на сайте показывать можно, | PUBLISHED Если там 1, значит пункт меню опубликован, на сайте показывать можно, | ||
Строка 73: | Строка 75: | ||
хватит запроса: | хватит запроса: | ||
− | < | + | <source lang="sql"> |
select i_level, i_link, i_name | select i_level, i_link, i_name | ||
from mainmenu | from mainmenu | ||
Строка 79: | Строка 81: | ||
and owner = 6 | and owner = 6 | ||
order by i_sort | order by i_sort | ||
− | </ | + | </source> |
Получается примерно вот такой результат с тремя колонками: | Получается примерно вот такой результат с тремя колонками: | ||
Строка 91: | Строка 93: | ||
== Код == | == Код == | ||
− | Пишу процедуру, которая покажет | + | === Добавление в список встроенных модулей === |
+ | |||
+ | Открыли common.pl, нашли <code>our @modules = ...</code>, дописали туда '''sitemap'''. | ||
+ | |||
+ | === Простейшая версия === | ||
+ | |||
+ | Пишу процедуру, которая покажет плоскую карту. Зато это уже работающий прототип, пока очень простой и предельно прямой. Этот код дописываем в cms.pl. | ||
<source lang="perl"> | <source lang="perl"> | ||
Строка 104: | Строка 112: | ||
# эта страшная конструкция делает простое удобство: | # эта страшная конструкция делает простое удобство: | ||
# строки выборки представляются как ключи хеша %hash | # строки выборки представляются как ключи хеша %hash | ||
− | # | + | # классический рецепт для DBI. если сразу сложно понять, копируйте, не думая |
my %hash; $q->bind_columns( \( @hash{ @{$q->{NAME_lc} } } )); | my %hash; $q->bind_columns( \( @hash{ @{$q->{NAME_lc} } } )); | ||
Строка 111: | Строка 119: | ||
{ | { | ||
# дописываю в html-код очередной пункт меню в виде ссылки внутри списка | # дописываю в html-код очередной пункт меню в виде ссылки внутри списка | ||
− | $html .= qq[<li><a href="$hash{ | + | $html .= qq[<li><a href="$hash{i_link}">$hash{i_name}</a></li>]; |
} | } | ||
Строка 119: | Строка 127: | ||
</source> | </source> | ||
− | + | В данный момент вызов модуля уже строит карту сайта. | |
+ | |||
+ | [[Файл:SiteMap_flat_demo.png]] | ||
+ | |||
+ | Правда она пока плоская, но это мы сейчас исправим, чуток усложнив код. | ||
+ | |||
+ | === Добавим показ иерархии === | ||
+ | |||
+ | <source lang="perl"> | ||
+ | sub sitemap() | ||
+ | { | ||
+ | my $html = '<h1>Карта сайта</h1><ul>'; | ||
+ | my $q = sqlpcms('select i_level, i_link, i_name from mainmenu | ||
+ | where published = 1 and owner = 6 order by i_sort'); | ||
+ | my %hash; $q->bind_columns( \( @hash{ @{$q->{NAME_lc} } } )); | ||
+ | while ( $q->fetch ) | ||
+ | { | ||
+ | # уровень вложенности я использую для задания левого отступа | ||
+ | # на i_level единиц em, только в три раза больше, тут можно поиграться | ||
+ | $hash{i_level}--; $hash{i_level} *= 3; | ||
+ | $html .= qq[ | ||
+ | <li style="margin-left: $hash{i_level}em"> | ||
+ | <a href="$hash{i_link}">$hash{i_name}</a> | ||
+ | </li> | ||
+ | ]; | ||
+ | } | ||
+ | $html .= '</ul>'; | ||
+ | return $html; | ||
+ | } | ||
+ | </source> | ||
+ | |||
+ | Основные изменения в цикле while. Там я беру уровень (который начинается с единицы), вычитаю единицу, чтобы корневые элементы не были сдвинути вообще никак, то есть на 0em, а остальные на n * 3em, где n — уровень (который 1 и более для вложенных элементов). | ||
+ | |||
+ | Ещё я добавил заголовок, тег H1, в самое начало html-кода перед списком. | ||
+ | |||
+ | Что получилось: | ||
+ | |||
+ | [[Файл:SiteMap_structured_demo.png]] | ||
+ | |||
+ | === Добавим обработку папок === | ||
+ | |||
+ | Для полного счастья не хватает только умения отличить папку от пункта. То есть папку показывать неликабельной (а куда кликать-то? Ссылки же нет у папки, есть только коллекция ссылок внутри неё). А ещё можно картинку показать рядом с папкой, соответствующую, чтобы было ещё нагляднее и даже не требовалось писать никаких подсказок. | ||
+ | |||
+ | <source lang="perl"> | ||
+ | sub sitemap() | ||
+ | { | ||
+ | my $html = '<h1>Карта Сайта</h1> | ||
+ | <style> .sitemap-folder { padding-left: 24px; background: url(/sp/img/folder.gif) 0 50% no-repeat; } </style> | ||
+ | <ul>'; | ||
+ | my $q = sqlpcms('select i_level, i_link, i_name from mainmenu | ||
+ | where published = 1 and owner = 6 order by i_sort'); | ||
+ | my %hash; $q->bind_columns( \( @hash{ @{$q->{NAME_lc} } } )); | ||
+ | while ( $q->fetch ) | ||
+ | { | ||
+ | $hash{i_level}--; $hash{i_level} *= 3; | ||
+ | if ( $hash{i_link} ) | ||
+ | { | ||
+ | $html .= qq[ | ||
+ | <li style="margin-left: $hash{i_level}em"> | ||
+ | <a href="$hash{i_link}">$hash{i_name}</a> | ||
+ | </li>]; | ||
+ | } | ||
+ | else | ||
+ | { | ||
+ | $html .= qq[ | ||
+ | <li class="sitemap-folder" style="margin-left: $hash{i_level}em"> | ||
+ | $hash{i_name} | ||
+ | </li>]; | ||
+ | } | ||
+ | } | ||
+ | $html .= '</ul>'; return $html; | ||
+ | } | ||
+ | </source> | ||
+ | |||
+ | Изменения: во-первых, я завёл CSS-класс для папок (sitemap-folder), вынес его определение в html-код отдельным тегом. Потому что каждый раз вствялсть такое большое и однообразное в аттрибут style тега li неэкономно. Это тоже не идеальных вариант. В реальной жизни, когда CSS-кода станет много, выдавать его на страницу каждый раз станет невыгодно и лучше вынести его в отдельный файл, чтобы браузер загрузил его один раз, а в дальнейшем брал из кеша. Но сейчас не об этом. Для демонстрации хватит и так. | ||
+ | |||
+ | Во-вторых (вот это уже самое главное), я разветвил тело цикла while в том месте, где генерирую фрагмент HTML-кода с очередным элементом. Если URL не пустой, значит верстаю как пункт меню со ссылкой (как раньше), иначе как папку (уже без ссылки и с описанным выше классом, который лепит картинку слева от названия). | ||
+ | |||
+ | Картинку я взял из комплекта поставки портала только для примера. В реальной жизни лучше не завязываться на подобные внуренние вещи и сослаться на картинку, которая не изменится точно. Например, можете положить произвольную картинку себе на сервер или состаться на другой сервер, где постоянные пути гарантированы. | ||
+ | |||
+ | Окончательный вариант: | ||
+ | |||
+ | [[Файл:SiteMap_structured_demo_with_folders.png]] | ||
+ | |||
+ | Удачи в разработке! | ||
+ | |||
+ | == См. далее == | ||
+ | |||
+ | [[Создание встроенного модуля CMS. Часть 3]] |
Текущая версия на 22:54, 5 августа 2015
Внимание! Данная статья предполагает, что вы уже читали первую часть.
Внимание! Модуль уже реализован в виде встроенного в Портале версии 5.3. Оставляем статью в качестве обучающего материала.
На этот раз сделаем карту сайта на основе главного меню.
Этот пример скорее учебный, в реальной жизни имеет смысл, если главне меню у вас иерархическое и очень развесистое настолько, что охватить его вниманием, просто гуляя по нему наведением мыши, — дело непростое.
Содержание
Название
Пусть компонент называется "Карта сайта" для людей, а внутреннее имя будет "sitemap".
Регистрация в базе
INSERT INTO MODULES (MODULENAME, VISIBLE_NAME, M_DEF_LINK, ACCESS, M_PLACE) VALUES ('sitemap', 'Карта сайта', 'mod=sitemap', ',6,,2,,4,', 'center');
Код модуля
sub sitemap() { # Здесь будет всё самое интересное }
Как хранится главное меню
Раз мы определились, что навигация — это источник данных для карты сайта, первым делом опишу поля таблицы mainmenu из базы CMS.gdb
Таблица mainmenu хранит пункты меню. Одна строка — один пункт.
Описание полей:
(лучше прямо сейчас открывайте таблицу в вашем любимом инструменте и смотрите на то, что CMS положила в таблицу, чтобы представить меню на вашем сайте)
I_ID Идентификатор элемента меню, автоинкрементный счётчик, пользователю не показывается I_NAME Название пункта меню, именно его видит пользователь в редакторе и на сайте I_LINK URL (если в этом поле пусто, значит это папка) I_PARENT Идентификатор папки. Если данный элемент лежит в папке, там будет какое-то число, иначе 0. Пользователю нигде явно не показывается, но иерархия в редакторе и на сайте строится исходя из этого поля. У плоского меню (совсем без иерархии) все значения равны 0. I_LEVEL Уровень вложенности (вложенность не ограничена, допустимо любое число). Минимум 1 — в корне, 2 и выше — в папке. Пользователю нигде не показывается. I_SORT Порядок сортировки. Пользователь перетаскивает элементы мышкой в редакторе, а это поле обновляется соответствующим образом автоматически. Там просто числа, для SQL-оператора ORDER BY. PUBLISHED Если там 1, значит пункт меню опубликован, на сайте показывать можно, если там 0 — CMS не должна его показывать на сайте. Поставленная или снятая галочка в редакторе означает 1 или 0 соответственно. OWNER Владелец пункта меню. Если 6, значит школьный сайт. Всё остальное — блоги.
Самый простейший способ показать меню, это сгенерировать список ссылок,
причём, иерархию можно показать исходя из значения I_LEVEL,
и не писать полноценный обход дерева. Этого достаточно для простого списка, который не раскрывается, не подгружается по кликам на узлах, а просто выводится весь сразу.
Для получения минимальных данных, достаточных для такой карты сайта, хватит запроса:
SELECT i_level, i_link, i_name FROM mainmenu WHERE published = 1 AND owner = 6 ORDER BY i_sort
Получается примерно вот такой результат с тремя колонками:
В первой числа (уровень вложенности), во второй — URL, в третьей — название пункта меню. Результат содержит опубликованные пункты меню школьного сайта согласно сортировке.
Теперь из этого добра осталось сгенерировать html-вёрстку (подставляя значения полей каждой строки результата прямо во фрагмент html-кода) и возвратить её из процедуры.
Код
Добавление в список встроенных модулей
Открыли common.pl, нашли our @modules = ...
, дописали туда sitemap.
Простейшая версия
Пишу процедуру, которая покажет плоскую карту. Зато это уже работающий прототип, пока очень простой и предельно прямой. Этот код дописываем в cms.pl.
sub sitemap() { my $html = '<ul>'; # открываю список # делаю запрос к базе (до q я сократил слово Query, запрос то есть) my $q = sqlpcms('select i_level, i_link, i_name from mainmenu where published = 1 and owner = 6 order by i_sort'); # эта страшная конструкция делает простое удобство: # строки выборки представляются как ключи хеша %hash # классический рецепт для DBI. если сразу сложно понять, копируйте, не думая my %hash; $q->bind_columns( \( @hash{ @{$q->{NAME_lc} } } )); # вынимаю результат по одной строчке while ( $q->fetch ) { # дописываю в html-код очередной пункт меню в виде ссылки внутри списка $html .= qq[<li><a href="$hash{i_link}">$hash{i_name}</a></li>]; } $html .= '</ul>'; # закрываю список return $html; # возвращаю полученную html-вёрстку }
В данный момент вызов модуля уже строит карту сайта.
Правда она пока плоская, но это мы сейчас исправим, чуток усложнив код.
Добавим показ иерархии
sub sitemap() { my $html = '<h1>Карта сайта</h1><ul>'; my $q = sqlpcms('select i_level, i_link, i_name from mainmenu where published = 1 and owner = 6 order by i_sort'); my %hash; $q->bind_columns( \( @hash{ @{$q->{NAME_lc} } } )); while ( $q->fetch ) { # уровень вложенности я использую для задания левого отступа # на i_level единиц em, только в три раза больше, тут можно поиграться $hash{i_level}--; $hash{i_level} *= 3; $html .= qq[ <li style="margin-left: $hash{i_level}em"> <a href="$hash{i_link}">$hash{i_name}</a> </li> ]; } $html .= '</ul>'; return $html; }
Основные изменения в цикле while. Там я беру уровень (который начинается с единицы), вычитаю единицу, чтобы корневые элементы не были сдвинути вообще никак, то есть на 0em, а остальные на n * 3em, где n — уровень (который 1 и более для вложенных элементов).
Ещё я добавил заголовок, тег H1, в самое начало html-кода перед списком.
Что получилось:
Добавим обработку папок
Для полного счастья не хватает только умения отличить папку от пункта. То есть папку показывать неликабельной (а куда кликать-то? Ссылки же нет у папки, есть только коллекция ссылок внутри неё). А ещё можно картинку показать рядом с папкой, соответствующую, чтобы было ещё нагляднее и даже не требовалось писать никаких подсказок.
sub sitemap() { my $html = '<h1>Карта Сайта</h1> <style> .sitemap-folder { padding-left: 24px; background: url(/sp/img/folder.gif) 0 50% no-repeat; } </style> <ul>'; my $q = sqlpcms('select i_level, i_link, i_name from mainmenu where published = 1 and owner = 6 order by i_sort'); my %hash; $q->bind_columns( \( @hash{ @{$q->{NAME_lc} } } )); while ( $q->fetch ) { $hash{i_level}--; $hash{i_level} *= 3; if ( $hash{i_link} ) { $html .= qq[ <li style="margin-left: $hash{i_level}em"> <a href="$hash{i_link}">$hash{i_name}</a> </li>]; } else { $html .= qq[ <li class="sitemap-folder" style="margin-left: $hash{i_level}em"> $hash{i_name} </li>]; } } $html .= '</ul>'; return $html; }
Изменения: во-первых, я завёл CSS-класс для папок (sitemap-folder), вынес его определение в html-код отдельным тегом. Потому что каждый раз вствялсть такое большое и однообразное в аттрибут style тега li неэкономно. Это тоже не идеальных вариант. В реальной жизни, когда CSS-кода станет много, выдавать его на страницу каждый раз станет невыгодно и лучше вынести его в отдельный файл, чтобы браузер загрузил его один раз, а в дальнейшем брал из кеша. Но сейчас не об этом. Для демонстрации хватит и так.
Во-вторых (вот это уже самое главное), я разветвил тело цикла while в том месте, где генерирую фрагмент HTML-кода с очередным элементом. Если URL не пустой, значит верстаю как пункт меню со ссылкой (как раньше), иначе как папку (уже без ссылки и с описанным выше классом, который лепит картинку слева от названия).
Картинку я взял из комплекта поставки портала только для примера. В реальной жизни лучше не завязываться на подобные внуренние вещи и сослаться на картинку, которая не изменится точно. Например, можете положить произвольную картинку себе на сервер или состаться на другой сервер, где постоянные пути гарантированы.
Окончательный вариант:
Удачи в разработке!