Создание приложений на MaxSite CMS. Пример - список TODO
Воскресенье, 16 августа 2009 г.
Просмотров: 2961
Подписаться на комментарии по RSS
На 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». Наше приложение мы разместим в подкаталоге шаблона. Естественно адрес, файл и каталог могут быть произвольными.
Чтобы наш список задач «вписался» в дизайн шаблона, подключим его начальную и конечную часть. Например у меня файл 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-файле шаблона нужные стили. Но по мне, так лучше делать это автоматом.
Заключение
Данная статья занимает больше места и времени прочтения, чем результирующий код. Чтобы было легче разобраться, я выкладываю готовый (чуточку измененный вариант) нашего приложения 173. Если бы мы делали «чистое» приложение на MaxSite CMS, то все наши изменения - это отключение проверки is_login() на какой-то свой вариант.
Ну и конечно я не ставил цель поразить вас функционалом и оформлением. Думаю, что те, кого данный пример заинтересовал смогут оформить вывод по своему вкусу. Главное, чтобы вы убедились, что MaxSite CMS подходит для самых разных задач.

Комментариев: 7
Кстати, сейчас подумал. В отличии от базы данных, данные из флоат-опции можно прочесть и без пароля. Правда, их ещё нужно найти, что практически нереально…
Да, верно. Впрочем, если нужна повышенная секретность, то можно прописать и шифрование.
Все в общем-то понятно Не ясно только одно, почему в названии системы "MaxSite CMS" "засвечено" - CMS
, если вместо управления контентом из админки, по определению(Content Manager System), требуется "генерировать код"
Есть несколько типовых приложений: "Community", "CK"- Constructor Kit (таблицы, поля пользователя), "eCommerce", "Catalog", ... , которые значительно расширят аудиторию ПОЛЬЗОВАТЕЛЕЙ (не генераторов кода)
Может разработчикам собраться духом и выложить несколько плагинов - "скелетов" соответствующего функционала
Ведь тут прямая зависимость: чем больше пользователей-"не программистов", тем больше профессионалов войдут в проект
Как грамотно построить приложение, использующее таблицу пользователя
Ваша "Model" несколько отлична от "классики жанра"
Можно изложить отдельным уроком?
Благодарю
"Допишем в ТЗ" - в админке страница с формой ввода данных в таблицу пользователя
Где-то там, не в шаблоне, страница с формой вывода данных.
Данные выводим в стандартный шаблон из набора дистрибутива...
Этот урок будет хорошим дополнением к рассмотренному выше
, т.к. способ только упоминался в возможном решении, но не рассматривался
elseif (mso_segment(1)=='todo') require('todo/todo.php');куда конкретно это надо прописывать? а то куда ни вставлял, везде ошибки вылетают.
В шаблонный index.php.