Современная, быстрая и удобная система управления сайтом

Создание приложений на MaxSite CMS. Пример - список TODO

Архив записейКомментарии: 7Просмотров: 4726

На 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-файле шаблона нужные стили. Но по мне, так лучше делать это автоматом.


Заключение

Данная статья занимает больше места и времени прочтения, чем результирующий код. Чтобы было легче разобраться, я выкладываю готовый (чуточку измененный вариант) нашего приложения 709. Если бы мы делали «чистое» приложение на 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

elseif (mso_segment(1)=='todo') require('todo/todo.php');

куда конкретно это надо прописывать? а то куда ни вставлял, везде ошибки вылетают.

Оставьте свой комментарий!

Комментарий будет опубликован после проверки

Вы можете войти под своим логином или зарегистрироваться на сайте.

(обязательно)