На MaxSite CMS можно создавать самые различные приложения. Я отслеживаю разные публикации, где авторы предлагают какие-то свои наработки на PHP и сопутствующих: HTML, jQuery и т.д. Главная проблема то, что такие демо-примеры нужно еще модернизировать до практического применения.
Данная статья показывает, что MaxSite CMS подходит для решения самых разных задач, а не только тех, которые «идут в коробке». В качеств примера я расскажу как можно сделать свой список дел - TODO.
Для начала у нас должна быть установлена MaxSite CMS. Если мы хотим сделать свое приложение (отдельное), то в принципе нам не особо и нужна админ-панель и все её настройки. MaxSite CMS не выполняет лишнего кода: вначале происходит подключение CodeIgniter, после этого проходит инициализация системы и управление передается в шаблонный index.php. Грубо говоря, если мы хотим получить «чистое» приложение, то мы просто отключим все плагины и в своем шаблоне в файле index.php разместим нужный код.
Такой путь конечно интересен, но мы пойдем чуть-чуть по-другому, чтобы можно было подключить наше приложение к любому шаблону. (Ну хотя бы потому что любое приложение всё равно требует минимального дизайна.)
Тут два варианта - сделать отдельный плагин или отдельный файл в шаблоне. По сути они практически идентичные, за исключением того, что плагин более «вещь в себе». И, наверное, если бы не учебный характер статьи, то я бы делал именно плагином. Но на этот раз мы выберем файл в каталоге шаблона.
Итак определимся с ТЗ.
- Наше приложение будет вызываться по адресу http://сайт/todo.
- Показывать список задач будем только юзерам (не комюзерам!).
- На странице нужно сделать возможность создавать новые задачи, удалять старые, редактировать текст, дату и метки.
- Обеспечить сортировку таблицы задач.
Дальше я буду генерировать код, обильно сопровождая его комментариями.
Адрес приложения
В MaxSite CMS можно «перехватывать» URL несколькими способами и мы остановимся на самом простом: с помощью mso_segment(). Открываем шаблоный index.php и перед «custom_page_404» добавляем условие:
... elseif (mso_segment(1)=='todo') require('todo/todo.php'); ...
То есть по адресу «http://сайт/todo» подключается файл «todo/todo.php». Наше приложение мы разместим в подкаталоге шаблона. Естественно адрес, файл и каталог могут быть произвольными.
UPD. Начиная с версии 0.39 можно не менять шаблонный index.php - он настроен таким образом, чтобы автоматом «подхватывать» файлы в type-каталоге шаблона. В нашем примере достаточно разместить файл todo.php в каталоге type и он будет доступен по адресу «http://сайт/todo».
Чтобы наш список задач «вписался» в дизайн шаблона, подключим его начальную и конечную часть. Например у меня файл todo.php такой:
<?php if (!defined('BASEPATH')) exit('No direct script access allowed'); # начальная часть шаблона require(getinfo('template_dir') . 'main-start.php'); echo NR . '<div class="type type_todo">' . NR; echo 'наш todo'; echo NR . '</div><!-- class="type type_todo" -->' . NR; # конечная часть шаблона require(getinfo('template_dir') . 'main-end.php'); ?>
Данный код для вас не вызовет никаких сложностей, просто отмечу, что я обрамил наш будущий вывод в блоки div.type_todo с целью произвольного оформления с помощью CSS.
Показывать список задач будем только юзерам
Задачка решается с помощью is_login():
if (is_login()) { echo 'наш todo'; } else { echo NR . '<div class="not_login">У вас нет прав для просмотра списка задач!</div>' . NR; echo mso_login_form(); # сразу форму логина выводим }
Кстати константа «NR» это перенос строки в HTML-коде. С «NR» генерируемый код получается более читабельным.
Где и как храним данные?
Теперь стоит определиться как и где хранить наши данные. MaxSite CMS предоставляет сразу несколько вариантов. Первый - обычная база данных. Если бы мы выбрали этот вариант, то алгоритм был-бы такой. При «входе» в todo.php нужно проверять существование нашей таблицы. Если её нет, то делаем её (можно вынести в отдельный файл).
Второй вариант - обычные опции. Как известно в MaxSite CMS в опциях можно хранить любые данные, включая и сложные, например массивы. Но нам этот вариант не подойдет потому что мы предполагаем, что данных может оказаться много: нет смысла увеличивать опции за счет нашего приложения.
Третий вариант - использовать float-опции. Это опции, которые хранятся в виде отдельных файлов в каталоге uploads. Эти опции и были придуманы для того, чтобы хранить большие объемы информации. Именно поэтому мы и выберем этот вариант.
Работать с float-опциями очень просто. В функциях указывается ключ и тип опции и, если нужно - новые данные. Например у нас может быть так:
$todo = mso_get_float_option('todo', 'todo', array());
В данном случае мы получаем опцию с ключом «todo» (произвольно), тип - «todo» (произвольно) и в случае отстуствия возвращается пустой массив.
Как вы поняли для хранения наших задач будем использовать обычный массив. Структура у него может быть такая:
[id] => array [data] => 'дата' [text] => 'текст' [tag] => 'метка' [progress] => 'ход выполнения'
Думаю, что для нас этого вполне будет достаточно, тем более, что добавить другие поля не составит для вас большого труда.
Прием данных
Поскольку у нас будут какие-то действия со страницей (отправка данных), то мы будем использовать классический алгоритм приема данных: вначале смотрим была ли отправка POST; если была, то принимаем данные и обновляем опции; потом выводим форму/данные.
Данный алгоритм используется в MaxSite CMS много раз, поэтому сразу приведу примерный код (он буде немного меняться дальше по тексту).
if ( $post = mso_check_post(array('f_session_id', 'f_submit', 'f_todo')) ) { mso_checkreferer(); // защита рефера $todo = $post['f_todo']; // данные формы mso_add_float_option('todo', $todo, 'todo'); // сохранили опции echo '<div class="update">Обновлено!</div>'; // вывели сообщение }
Грубо говоря, мы сразу в форме определяем поля с именами f_todo[] и тем самым получаем готовый массив POST.
Вывод данных
Вывести данные можно несколькими способами, мы ограничимся обычной таблицей. Для этого воспользуемся библиотекой CodeIgniter «Table». Поскольку мы планируем сортировку по полям/столбцам, то сразу пропишем id «pagetable» и класс «tablesorter», которые используются jQuery-плагином «tablesorter». (В принципе можно и другие именования использовать, тут дело привычки.)
Всю таблицу заключим в одну форму. А строки таблицы сформируем в цикле, обходя наш массив заданий $todo. Вот по такой схеме:
# подключаем CodeIgniter $CI = & get_instance(); # библиотека для таблицы $CI->load->library('table'); # конфигурация таблицы $CI->table->set_template(array ( 'table_open' => '<table border="0" width="100%" id="pagetable" class="tablesorter">', 'row_alt_start' => '<tr class="alt">', 'cell_alt_start' => '<td class="alt">', 'heading_row_start' => NR . '<thead><tr>', 'heading_row_end' => '</tr></thead>' . NR, 'heading_cell_start' => '<th style="cursor: pointer;">', 'heading_cell_end' => '</th>', )); # укажем заголовки $CI->table->set_heading('№', 'Статус', 'Текст', 'Дата', 'Метка'); echo '<h1>Список задач todo</h1>'; # цикл вывода foreach ($todo as $id => $t) { # тут будут наши поля $progress = '...'; $text = '...'; $data = '...'; $tag = '...'; $CI->table->add_row($id, $progress, $text , $data, $tag); } echo '<form action="" method="post">' . mso_form_session('f_session_id'); echo $CI->table->generate(); // вывод подготовленной таблицы echo '<input type="submit" name="f_submit" value="Сохранить изменения" >'; echo '</form>';
Поля мы оформим чуть позже, просто у нас сейчас список задач пуст и нам нужно сделать добавление нового задания.
Добавление нового задания
Сделать это можно с помощью еще одной формы. Разместим нужные поля сразу после таблицы.
echo '<h2>Добавить новое задание</h2> <form action="" method="post">' . mso_form_session('f_session_id_new') . ' <p><strong>Текст</strong></p><textarea name="f_todo_new[text]"></textarea> <p><strong>Метки</strong> <input name="f_todo_new[tag]" type="text" value=""></p> <input type="submit" name="f_submit_new" value="Добавить"> </form>';
Это еще одна форма, где мы прописываем поля f_todo_new. Мы не ставим дату и прогресс, потому что для нового задания это можно сделать автоматом.
Теперь нам нужно принять отправленные данные. Будем проверять в POST поля нового задания «f_todo_new». Привожу сразу готовый код с комментариями.
if ( $post = mso_check_post(array('f_session_id_new', 'f_submit_new', 'f_todo_new')) ) { mso_checkreferer(); if ($todo_new = $post['f_todo_new']) { # текущие опции $todo = mso_get_float_option('todo', 'todo', array()); # добавим новую задачу $todo[] = array( 'text' => $todo_new['text'], 'tag' => $todo_new['tag'], 'data' => date('Y-m-d H:i:s'), 'progress' => '0'); # и в опции mso_add_float_option('todo', $todo, 'todo'); echo '<div class="update">Задание добавлено</div>'; } else echo '<div class="error">Ошибка</div>'; }
Как видите здесь обработка немного сложнее, потому что вначале нам нужно получить текущие опции и потом добавить к ним еще один элемент массива.
Отображение таблицы
Теперь мы можем вернуться к отображению таблицы. Нам нужно сформировать поля формы в цикле вывода. Кроме этого нам нужно предусмотреть какую-то опцию, отметив которую, задание удаляется. Сделать это можно с помощью обычного checkbox. Добавим его под статус/прогресс.
В итоге получается такой код.
# цикл вывода foreach ($todo as $id => $t) { # формируем наши поля $progress = '<input type="text" name="f_todo[' . $id . '][progress]" value="' . $t['progress'] . '">'; $progress .= '<br><label><input type="checkbox" name="f_todo[' . $id . '][delete]"> Удалить</label>'; $text = '<textarea name="f_todo[' . $id . '][text]">' . htmlspecialchars($t['text']) . '</textarea>'; $data = '<input type="text" name="f_todo[' . $id . '][data]" value="' . $t['data'] . '">'; $tag = '<input type="text" name="f_todo[' . $id . '][tag]" value="' . $t['tag'] . '">'; $CI->table->add_row($id + 1, $progress, $text , $data, $tag); }
Теперь следует объяснить один момент. В HTML поля типа checkbox передаются только в случаях, если они отмечены/выбраны. Это создает некоторую трудность, но в нашем случае это даже на пользу: мы просто смотрим наличие поля f_todo[ID][delete] и если оно есть, то удаляем задание.
Если вы вернетесь в начало и посмотрите код приема f_todo, то увидите, что мы напрямую добавляли входящий массив в опции. Но из-за «delete» нам придется немного изменить алгоритм: мы делаем новый массив и в него добавляем данные, только если нет f_todo[ID][delete]. Потом этот массив и сохраняем в опции.
if ( $post = mso_check_post(array('f_session_id', 'f_submit', 'f_todo')) ) { mso_checkreferer(); $todo = $post['f_todo']; $todo_new = array(); foreach ($todo as $t) if (!isset($t['delete'])) $todo_new[] = $t; mso_add_float_option('todo', $todo_new, 'todo'); echo '<div class="update">Обновлено!</div>'; }
Данный код последний, который отвечает за функциональность нашего приложения. Остались последние штрихи.
Сортировка таблицы
Для сортировки мы будем использовать jQuery-плагин «tablesorter». Он входит в комплект MaxSite CMS, поэтому всё, что нам нужно - это добавить код вызова и немного js (я добавил немного анимации). Сделаем это перед отображением таблицы.
echo mso_load_jquery('jquery.tablesorter.js'); echo ' <script type="text/javascript"> $(function() { $("table.tablesorter th").animate({opacity: 0.7}); $("table.tablesorter th").hover(function(){ $(this).animate({opacity: 1}); }, function(){ $(this).animate({opacity: 0.7}); }); $("#pagetable").tablesorter(); }); </script> ';
Так же мы предполагаем, что вы уже подключили в шаблоне саму jQuery. Если нет, то делается это одной строчкой:
mso_load_jquery();
Подключаем CSS
Здесь нам придется пойти на небольшую хитрость, поскольку подключить css-файл лучше в секции head. Для этого нам нужно «прицепиться» к хуку «head». То есть нам до вызова первой части шаблона нужно написать функцию, которая будет вызываться по хуку.
mso_hook_add('head', 'todo_head'); function todo_head($args = array()) { echo '<link type="text/css" rel="stylesheet" href="' . getinfo('stylesheet_url') . 'todo/todo.css" media="screen">'; return $args; }
Таким образом всё оформление мы переносим в файл todo.css. В принципе можно и не делать хук, а вручную дописать в основном css-файле шаблона нужные стили. Но по мне, так лучше делать это автоматом.
Заключение
Данная статья занимает больше места и времени прочтения, чем результирующий код. Чтобы было легче разобраться, я выкладываю готовый (чуточку измененный вариант) нашего приложения 798. Если бы мы делали «чистое» приложение на MaxSite CMS, то все наши изменения - это отключение проверки is_login() на какой-то свой вариант.
Ну и конечно я не ставил цель поразить вас функционалом и оформлением. Думаю, что те, кого данный пример заинтересовал смогут оформить вывод по своему вкусу. Главное, чтобы вы убедились, что MaxSite CMS подходит для самых разных задач.
Комментариев: 7 RSS
1Аноним16-08-2009 23:21
Кстати, сейчас подумал. В отличии от базы данных, данные из флоат-опции можно прочесть и без пароля. Правда, их ещё нужно найти, что практически нереально…
2Аноним17-08-2009 06:51
Да, верно. Впрочем, если нужна повышенная секретность, то можно прописать и шифрование. :)
3quest22-09-2009 14:46
Все в общем-то понятно Не ясно только одно, почему в названии системы "MaxSite CMS" "засвечено" - CMS :smirk:, если вместо управления контентом из админки, по определению(Content Manager System), требуется "генерировать код" :question:
Есть несколько типовых приложений: "Community", "CK"- Constructor Kit (таблицы, поля пользователя), "eCommerce", "Catalog", ... , которые значительно расширят аудиторию ПОЛЬЗОВАТЕЛЕЙ (не генераторов кода)
Может разработчикам собраться духом и выложить несколько плагинов - "скелетов" соответствующего функционала :question:
Ведь тут прямая зависимость: чем больше пользователей-"не программистов", тем больше профессионалов войдут в проект
4user22-09-2009 14:58
Как грамотно построить приложение, использующее таблицу пользователя
Ваша "Model" несколько отлична от "классики жанра"
Можно изложить отдельным уроком?
Благодарю
5user22-09-2009 17:05
"Допишем в ТЗ" - в админке страница с формой ввода данных в таблицу пользователя
Где-то там, не в шаблоне, страница с формой вывода данных.
Данные выводим в стандартный шаблон из набора дистрибутива...
Этот урок будет хорошим дополнением к рассмотренному выше ;-), т.к. способ только упоминался в возможном решении, но не рассматривался
6Dj09-11-2009 20:49
куда конкретно это надо прописывать? а то куда ни вставлял, везде ошибки вылетают.
7Аноним10-11-2009 06:41
В шаблонный index.php.