Вступление

Идея

Разработка сайтов — занятие интересное, но в то же время достаточно сложное в силу того, что состоит из множества аспектов. Дизайн, верстка, программирование на стороне клиента и сервера — изучению каждой из этих сфер сопутствуют множество источников информации. Начиная делать первые шаги по лестнице знания, вы можете выбрать любое направление развития или же попытаться объять множество сразу.

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

К фундаментальным основам создания сайтов в динамически развивающемся мире Интернета принято относить системы управления сайтами (далее «движок») в силу того, что они являются ядром любого современного сайта, обеспечивая его жизнедеятельность.
В современном мире существует большое многообразие движков, но лишь немногие из них являются общепризнанными и общераспространенными. Складывается данная ситуация в силу того, что разработка системы управления сайтами по сути представляет собой сложный и комплексный вид работ, обусловленный множеством факторов, в том числе и человеческим. Каждый разработчик в своем движке преподносит пользователю собственное видение на управление сайтами. Только тогда, когда это видение совпадает с таковым у множества пользователей, система в течение определенного времени набирает обороты и становится популярной.
Наиболее распространенными являются такие системы как зарубежные WordPress, Drupal, Joomla и русскоязычные DataLife Engine, Bitrix. Более полную информацию и представление о системах управления сайтами вы можете получить на тематическом информационном портале cmsmagazine.ru.

Фреймворки

Разработка своей системы управления «с нуля» является достаточно затратным в плане времени занятием, поэтому по логике вещей в последние годы широкое распространение получили фреймворки (frameworks). Предоставляя разработчику готовое ядро системы, обладающее базовым функционалом, они позволяют существенно сократить время на разработку своей системы управления, в то же время внося ограничения процесса разработки, обусловленные собственной структурой и спецификой работы.
Наиболее популярные на сегодняшний день фреймворки — CodeIgniter, Zend Framerwork, CakePHP, Symfony, Yii.
С появление фреймворков планка вхождения в сферу разработки веб-приложений снизилась, поскольку они не требуют полного понимания происходящих процессов на уровне PHP, а позволяют опираясь на собственную документацию создавать готовый продукт.

CodeIgniter

Выбор фреймворка для работы — поступок сугубо личный. Каждый из фреймворков имеет свои достоинства и недостатки, поэтому разработчик сам решает, что ему больше по душе.
Мы выбрали CodeIgniter (далее CI) как основу для своей системы управления сайтами в силу его производительности и простоты. Немаловажную роль сыграла хорошая документация и сообщество пользователей.

cogear

Сделав выбор в пользу CI, мы осознанно сделали шаг на встречу знакомым с ним разработчикам.
CoGear работает на последней версии фреймворка и полностью совместим со всеми его расширениями и дополнениями.
Почему мы не стали довольствоваться самим CI, а создали свою систему? Дело в том, что использование CI как такового накладывает ограничение на структуру приложения и его расширяемость. Наша задумка состояла в том, чтобы сделать CoGear как можно более гибким и расширяемым. И нам это удалось.


Системные требования

Для установки системы и ее работы вам потребуется хостинг (сервер), удовлетворяющий следующим требованиям:
Для улучшенной производительности рекомендуем:



Настройка сервера

Перед тем как приступить к установке cogear убедитесь, что ваша система настроена соответствующим образом.
Рекомендуем установить на сервер Memcached для полноценного кэширования данных, а также любой ускоритель PHPeAccelerator, APC, XCache.


Загрузка дистрибутива

Загрузите дистрибутив cogear на сервер. При установке движка следуйте инструкциям дистрибутива.


Установка

Для запуска установщика системы запустите сайт.

В системе присутствует веб-интерфейс для установки, но если он не работает (по причинам несовместимости с конфигурацией текущего сервера), значит тебе необходимо установить движок вручную.

АВТОМАТИЧЕСКАЯ УСТАНОВКА

Установка состоит из следующих этапов:
1. Скопируй движок в папку на сервере
2. Открой сайт в браузере.
3. Если тебя не перекинуло на начало установки — удали строчку «Options -Indexes -MultiViews» в .htaccess и закомментируй строку "#Options -Indexes"
4. Если инсталлятор не работает (по причинам несовместимости с конфигурацией текущего сервера), значит тебе необходимо установить движок вручную.

РУЧНАЯ УСТАНОВКА

1. Установи права на папки и файлы.
Ниже приведен список команд для консоли, которые помогут быстро справиться с этой задачей.
(chmod — задать права, -R — рекурсивно, chown — задание владельца/группы)
cd путь_к_директории_с_движком
chmod -R 0777 engine/cache &&
chmod -R 0777 uploads/
chmod -R 0777 gears/*/*.info
chown -R пользователь: группа *

2. Укажи настройки сайта.
Зайди в директорию gears/global.
Если ты уже запускал сайт в браузере, файл global.info.default должен был быть скопирован в global.info.
Если этого еще не произошло — скопируй вручную.
Установи адрес сайта без www и http://
 url = «site.ru»
Если ставишь движок в подпапку, укажи url=«site.ru/подпапка»
Укажие DSN для соединения с Базой Данных
 database = «mysqli://root:password@localhost/database»
 
3. При помощи phpMyAdmin или любым другим способом создай базу данных, дай ей права для указанного выше пользователя и импортируй дамп базы из корня движка /cogear.sql.

Поздравляю, система установлена!



Базовая информация

Файловая структура

Прежде чем вы приступите к работе с cogear, рекомендуем ознакомиться с его устройством.
Для улучшения гибкости и функциональности мы сознательно перешли с традиционной модели представления данных MVC (Model View Controler) на более современную HMVC (Hierarchical Model View Controller).
Были внесены некоторые изменения в ядро CodeIgniter. Все изменения задокументированы, поэтому не составит труда найти отличия.

Корневая папка

Корневая файловая структура движка

Структура «шестеренки»

Файловая структура 'шестеренки' Элементы в квадратных скобках — необязательные.
Узнать подробнее о файловой структуре папок «core» и «engine» более подробно можно из документации CodeIgniter.
Рассмотрим более детально модульную систему.


Компоненты

Компоненты, называемые в нашем движке «шестеренками» (gears), предоставляют разработчику возможность эффективно реализовать свои идеи за максимально короткий промежуток времени.

Компоненты классифицируются по трем видам:

Основные компоненты (core)

К числу основных относятся все системообразующие компоненты, без которых работа движка невозможна.
Основные компоненты нельзя отключить через раздел панели управления «Управление компонентами».
Физическое удаление папок с такими компонентами повлечет за собой остановку работы всей системы.
Список основных компонентов:
На базе основных компонентов могут быть построены любые модули и плагины.

Модули (modules)

К числу модулей относятся компоненты как поставляемые вместе с cogear, так и созданные отдельно. Главное отличие от основных компонентов — если компонент не имеет зависимостей в других компонентах, при его отключении движок будет продолжать работать. Если установлены зависимости с другими модулями (например, другой модуль использует функционал текущего), то панель управления компонентами не даст отключить модуль.
Базовые модули:
Набор базовых модулей может меняться в зависимости от версии и компоновки движка под те или иные нужды. Таким образом может быть обеспечена работа в широком спектре задач — личный блог, многопользовательский блог, сайт-визитка, интернет-магазин и так далее.

Плагины (plugins)

Плагины отличаются от модулей своей значимостью. Чаще всего они не имеют контроллеров и моделей, и воздействуют на другие компоненты посредством системы хуков.
Базовые плагины:




Производительность

Вопрос о производительности — важный этап при разработке любой системы управления сайтами. Скажем сразу, что мы не ставили перед собой задачу сделать систему для highload-проектов, но во время разработки движка помнили о производительности, несмотря на то, что CodeIgniter по праву считается одним из самых быстрых фреймворков.
Есть несколько очень важных факторов, существенно влияющих на производительность: С момента появления движка в свободном доступе производительность выросла в несколько раз, первым делом благодаря оптимизации системы.
Данные о производительности дистрибутива:
Для того, чтобы вы имели представление о проделанной работе, укажем к сведению прошлые показатели.
Приведем данные о производительности cogear при конфигурации сайта usemac.ru (главная страница сайта — вывод ноды из сообществ и блогов, множество сложных шестеренок):
Запросы в кэш, когда он выключен, обусловлены хранением в нем системных данных. Все показатели являются средними для данной конфигурации движка.
Мы не забываем о производительности движка и стараемся улучшить ее при каждой возможности.
Если вы разрабатываете компонент, помните, что от него зависит и общая производительность системы.


Особенности





Шестеренка

Вступление

Основополагающим элементом системы является шестеренка (gear, компонент). Она обеспечивает любой необходимый функционал благодаря своей структуре. Рассмотрим подробнее ее составляющие.

Название

Основополагающий шаг при создании новой шестеренки — правильное название. Оно должно соответствовать следующим правилам:




Файл конфигурации

Содержит основную информацию и настройки шестеренки. Файл конфигурации имеет расширение .info и должен называться также, как и сам компонент.
По сути конфигурационный файл является простым INI-файлом и обрабатывается при помощи функции parse_ini_file, поэтому его синтаксис должен соответствовать стандартам.
Хранение конфигурации в файле формата INI позволяет упростить процесс его редактирования и не требует специальных знаний.
Пример конфигурационного файла:
  1. ;==================================================
  2. ; Название вашего компонента.
  3. ;==================================================
  4. ;--------------------------------------------------
  5. ; Обязательные данные
  6. ;--------------------------------------------------
  7. ; Название на английском
  8. title = Editor
  9. ; Описание на английском языке
  10. description = Add editor plugin for textarea
  11. ; Принадлежность к группе
  12. group = plugins
  13. ; Версия системы, необходимая для работы компонента
  14. core = 1.x
  15. ; Активность
  16. enabled = TRUE
  17. ; Позиция
  18. ; Определяет порядок очереди при загрузке системы
  19. position = 10
  20. ;--------------------------------------------------
  21. ; Любые другие параметры, которые будут доступны внутри системы.
  22. ;--------------------------------------------------



Пути

Вы можете задавать пути для роутера, указывая их в конфигурационном файле компонента.
  1. ; Пути для роутера
  2. routes[] = "blogs/([\w_-]+)/(\w+)/? = community/$1/$2"
  3. ...
  4. ; Заменяет все пути
  5. routes[] = ":any = blogs/$1"
  6. ..
  7. ; Заменяет отсутствующий путь
  8. routes[] = ":empty = index/"
  9. routes[] = "(\d+)/? = index/page/$1"
  10. ; ============================================
  11. ; Неверно
  12. ; ============================================
  13. ; [routes]
  14. ; :empty = "index/"
  15. ; ============================================
Данная форма представления путей в виде значений массива routes обязательна в силу ограничения синтаксиса INI-файлов.
Обратите внимание на то, что при возникновении конфликтных ситуаций будет использован путь того компонента, который загружается раньше (конфигурационный параметр position определяет порядок загрузки компонентов).



Скрипты и стили

Скрипты и стили подгружаются автоматически из папок «js» и «css» активных шестеренок.
На выходе они сжимаются в единые файлы для того, чтобы пользователь получал из парой запросов, что существенно ускоряет загрузку сайта.
Вы можете указать конкретные браузеры в названии файла стиля/скрипта, для того чтобы они отображались только в них.
  1. /*
  2. * Скрипт загрузится на всех версиях Internet Explorer
  3. */
  4. myScript[ie].js
  5. /*
  6. * Скрипт загрузиться только на Internet Explorer 8, Opera 9.54, Chrome
  7. */
  8. myScript[ie8|opera9.54|chrome].js
  9. /*
  10. * Аналогично работают стили
  11. *
  12. * Стиль загрузится только в FireFox 3.5
  13. */
  14. myStyle[firefox3.5].css
  15. /*
  16. * Стиль загрузится только в IE6 и IE7
  17. */
  18. myStyle[ie6|ie7].css
Автоматическая загрузка скриптов и стилей не распространяется на вложенные папки, поэтому вы можете создать папки, например, «js/include» и «css/include», чтобы подгрузить элементы уже в процессе работы.
Поскольку для ускорения загрузки файлов стилей и скриптов в cogear трудится класс, склеивающий все файлы типа воедино, то обязательным является прописывание абсолютных путей к фоновым и другим изображениям в стилях.
  1. #element {
  2. /* Неправильно!
  3.   background: url(../images/background.png);
  4.   */
  5. background: url(/gears/ваш_модуль/img/background.png); // Правильно
  6. }



Контроллер

Базовый контроллер должен располагаться в файле index.php корневой директории компонента. Он аналогичен стандартному контроллеру CodeIgniter за исключением нескольких правил:
  1. /*
  2. * Constructor
  3. *
  4. * @return void
  5. */
  6. function __construct(){
  7. parent::Controller();
  8. }
  9. // ------------------------------------------------------------------------
Соответственно, основной метод должен иметь название index.
  1. /*
  2. * Show nodes on index page
  3. *
  4. * @param int $page Page to show.
  5. * @return void
  6. */
  7. function index($page = 0){
  8. ...
  9. }
  10. // ------------------------------------------------------------------------

Если метод, название которого указывает следующий после названия самого компонента в строке элемент, отсутствует, то все остальные параметры запроса используются как аргументы основного метода.
/user_guide/introduction/
Если метод introduction отсутствует у основного контроллера компонента user_guide, то основной метод index этого компонента получает строку «introduction» в качестве аргумента.

Более подробно о роутинге читайте в разделе про роутер.

Активный контроллер наследуется от основной сущности (базового класса), поэтому обладает всеми ее свойствами и методами.

Панель управления

Если вы хотите создать раздел шестеренки в панели управления, то для этого потребуется создать контроллер класса _Admin и разместить его в файле _admin.php в корневой папки компонента.
Все сказанное выше относится и к контроллеру панели управления.

Для того, чтобы присвоить вашей шестеренке иконку, положите графический файл формата PNG размером 64х64 точки, название которого совпадает с названием папки компонента, в подпапку «img».
Если ваш модуль называется «example», то его иконка должна лежать по адресу "/gears/example/img/example.png".



Модель

Моделью называют класс, обеспечивающий взаимодействие контроллера с базой данных. Используется он с целью упрощения типовых действий.
В cogear мы расширили понятие модели. Теперь она стандартизирует не только работу с БД, но и часто используемые действия для того объекта, который представляет.
Модели подключаются автоматически. В названии файла модели допускается использование строчных букв и знака "_". Название модели также должно быть уникальным.
  1. /*
  2. * Созданная модель расположена по адресу /gears/test/models/test.php
  3. *
  4. * Вызовем модель из контроллера или другой модели.
  5. */
  6. $this->test->some_method();
  7. /*
  8. * Как видно из примера, модель загрузилась автоматически.
  9. */
Автозагрузка работает только с моделями размещенным в папке «models». Если вы хотите загрузить модель из подпапки, то сделать это можно следующим способом.
  1. /*
  2. * Модель расположена по адресу /gears/test/models/submodels/test.php
  3. */
  4. $this->load->model('test submodels/test');
  5. $this->test->some_method();
Помните, что название классов моделей и библиотек должны быть уникальными.


Библиотеки

Вы можете создать папку «library» в корневой папке вашего компонента и поместить в нее необходимые файлы с функциями и классы для автоматической загрузки.
Файл класса должен иметь суффикс .class (например, jevix.class.php) для автоматической инициализации в системе ($this->jevix).
Вложенные папки не используются для автозагрузки, но вы все равно можете загружать библиотеки из подпапок.
  1. /*
  2. * Библиотека расположена по адресу /gears/test/library/sublibrary/mylib.php
  3. */
  4. $this->load->library('test sublibrary/mylib');



Интернационализация

При установке нового компонента системой импортируются языковые файлы из папки «lang».
Файл каждого языка должен иметь расширение .lng и название аббревиатуры языка, который он представляет.
Например, путь к файлу компонента, содержащем в себе русскую локализацию, выглядит так «lang/ru.lng».
Синтаксис файла идентичен синтаксису конфигурационных файлов (INI):
  1. [gears]
  2. editor = "Редактор"
  3. editor_description = "Описание модуля 'Редактор'"
  4. [editor]
  5. button = "Кнопка"
  6. ...
Важно отметить обязательный блок [gears], который должен содержать локализованные название и описание компонента.
Секции языкового файла каждого модуля должны иметь название уникальное по отношению к остальным секциям интернационализации движка во избежание слияния языковых переменных.
  1. ; Хорошо
  2. [editor]
  3. ...
  4. [editor_buttons]
  5. ...
  6. ; Плохо
  7. [editor]
  8. ...
  9. [buttons]
При отключенном кеше информационная база интернационализации обновляется своевременно при обновлении любого из языковых файлов.
Перевод фраз осуществляется следующим образом.
  1. /*
  2. * Языковой файл
  3. */
  4. [test]
  5. hello = "Приветствую тебя, Гость!"
  1. /*
  2. * В коде контроллера, модели, библиотеки, хуков
  3. */
  4. t('test.hello');
  5. // равнозначно
  6. t('test hello');
  7. // равнозначно
  8. t('test > hello');
  9. // равнозначно
  10. t('!test hello');
  11. /*
  12. * Вы можете задать активный раздел, чтобы не указывать его каждый раз явно
  13. */
  14. d('test');
  15. echo t('hello');
  16. /*
  17. * Вызвав функцию d без аргумента, мы вернемся к предыдущему разделу перевода (если его не было, то к разделу 'global')
  18. */
  19. d();
  20. /*
  21. * % -- короткий суффикс для раздела global
  22. *
  23. * Данная запись аналогична t('global new');
  24. */
  25. echo t('%new');
  26. /*
  27. * Вспомогательная функция
  28. *
  29. * Вторым аргументом идет значение по-умолчанию, если перевод не обнаружен
  30. * Третьим аргументом идет секция перевода, в которой следует поискать, если не установлено значение по-умолчанию
  31. */
  32. // Если перевода переменной нет, то будет использовано значение аргумента
  33. at($field.'_description',$field['description']);
  34. /*
  35. * Если третий аргумент не указан, то ищется в разделе 'edit'
  36. * Если перевода переменной нет в текущем или указанном разделе,
  37. * то поиск будет произведен в разделе 'edit'.
  38. */
  39. at($field.'_description',FALSE,'edit');



Изображения

Изображения должны храниться в папке «img», а иконки — в «img/icon».
Помните, что в css-файлах необходимо прописывать абсолютный путь к изображениям.



Установка

Любой компонент, не входящий в группы основных (core), должен обладать параметром «enabled», которые определяет его рабочее состояние.
  1. ; Выключен модуль или включен?
  2. enabled = TRUE
При установке/снятии компонента из панели управления данный параметр меняет значение на противоположное.
Если вы хотите произвести дополнительные действия по установке, то для этого вы можете воспользоваться следующими возможностями.

Импорт дампа в базу данных

Положите файл с именем install.sql в корень компонента, и он будет импортирован при установке.
По аналогии можно создать файл deinstall.sql и расположить его в корне шестеренки.

Модель установки

Если ваша шестеренка подразумевает расширенные действия по установке/снятии, то вы можете создать модель "gear_install.php" (где gear — это название шестеренки), поместив ее в папку с моделями.
  1. class Gear_Install extends Model{
  2. /*
  3. * Constructor
  4. *
  5. * @return void
  6. */
  7. function Install(){
  8. parent::Model();
  9. }
  10. // ------------------------------------------------------------------------
  11. /*
  12. * Make everything you want during install
  13. *
  14. * @return void
  15. */
  16. function install(){
  17. ...
  18. // Вы можете вернуть строку по окончании установки
  19. // Она отобразится пользователю в оповещении о результате установки
  20. return t('gear.install_msg');
  21. }
  22. // ------------------------------------------------------------------------
  23. /*
  24. * Make everything you want during deinstall
  25. *
  26. * @return void
  27. */
  28. function deinstall(){
  29. ...
  30. return t('gear.deinstall_msg');
  31. }
  32. // ------------------------------------------------------------------------




Хуки

Вступление

Использование хуков позволяет создавать связи между компонентами. Хуки компонента представляют собой названные особым образом функции, которые собраны в файле _hooks.php.


Контроллер

  1. /**
  2. * Хук компонента comments для компонента nodes, контроллера index (по-умолчанию опускается в названии), метода show.
  3. * Исполняется до вызова адресуемого метода.
  4. *
  5. * @param object $CI Сущность движка.
  6. * @param int $id Первый аргумент обрабатываемого метода.
  7. * ...
  8. * @return void
  9. */
  10. function comments_nodes_show($CI,$id[,$param,...]){
  11. ...
  12. }
  13. // ------------------------------------------------------------------------
Если вы хотите изменить входящий аргумент для передачи его далее в адресуемый метод, то следует указать, что нужный аргумент проходит по ссылке, и вернуть все полученные аргументы.
  1. /**
  2. * Хук компонента comments для компонента nodes, контроллера index (по-умолчанию опускается в названии), метода show.
  3. * Исполняется до вызова адресуемого метода. Изменяет аргументы адресуемого метода.
  4. *
  5. * @param object $CI Сущность движка.
  6. * @param int &$id Первый аргумент обрабатываемого метода.
  7. * ...
  8. * @return void
  9. */
  10. function comments_nodes_show($CI,&$id[,$param,...]){
  11. ...
  12. return func_get_args();
  13. }
  14. // ------------------------------------------------------------------------
Можно вызвать хук после выполнения модуля.
  1. /**
  2. * Хук компонента comments для компонента nodes, контроллера index (по-умолчанию опускается в названии), метода show.
  3. * Выполняется после метода, поскольку функция хука имеет суффикс _after.
  4. * Дополнительный аргумент функции хука -- возвращаемое значение
  5. * исполненного адресуемого метода show (например, return $node).
  6. *
  7. * @param object $CI Сущность движка.
  8. * @param object $node Элемент, который возвращает адресуемый метод.
  9. * @param int $id Первый аргумент обрабатываемого метода.
  10. * ...
  11. * @return void
  12. */
  13. function comments_nodes_show_after($CI,$node,$id[,$param,...]){
  14. ...
  15. }
  16. // ------------------------------------------------------------------------

Глобальные хуки контроллера

Адресованы любому контроллеру.
  1. /**
  2. * Глобальный хук компонента mail.
  3. * Выполняется перед добавлением шаблона шапки сайта.
  4. *
  5. * @param object $CI Сущность движка.
  6. * ...
  7. * @return void
  8. */
  9. function mail_header($CI){
  10. ...
  11. }
  12. // ------------------------------------------------------------------------
  1. /**
  2. * Глобальный хук компонента mail.
  3. * Выполняется после добавления шаблона шапки сайта.
  4. *
  5. * @param object $CI Сущность движка.
  6. * ...
  7. * @return void
  8. */
  9. function mail_header_after($CI){
  10. ...
  11. }
  12. // ------------------------------------------------------------------------
  1. /**
  2. * Глобальный хук компонента mail.
  3. * Выполняется после выполнения вызываемого метода контроллера.
  4. *
  5. * @param object $CI Сущность движка.
  6. * ...
  7. * @return void
  8. */
  9. function mail_after($CI){
  10. ...
  11. }
  12. // ------------------------------------------------------------------------
  1. /**
  2. * Глобальный хук компонента mail.
  3. * Выполняется перед добавлением шаблона подвала сайта.
  4. *
  5. * @param object $CI Сущность движка.
  6. * ...
  7. * @return void
  8. */
  9. function mail_footer($CI){
  10. ...
  11. }
  12. // ------------------------------------------------------------------------
  1. /**
  2. * Глобальный хук компонента mail.
  3. * Выполняется после добавления шаблона подвала сайта.
  4. *
  5. * @param object $CI Сущность движка.
  6. * ...
  7. * @return void
  8. */
  9. function mail_footer_after($CI){
  10. ...
  11. }
  12. // ------------------------------------------------------------------------



Модели

Все аналогично сказанному про хуки контроллеров, за небольшим исключением.
Чтобы сделать метод модели подвластным системе хуков, необходимо добавить перед его именем "_".
  1. class User extends Model{
  2. ...
  3. /**
  4. * С данным методом хуки работать не будут.
  5. *
  6. * return void
  7. */
  8. function login(){
  9. ...
  10. }
  11. // ------------------------------------------------------------------------
  12. /**
  13. * А вот с этим будут. Нижнее подчеркивание перед названием метода
  14. * только лишь обозначает его для системы хуков. Вызываться метод должен
  15. * по-прежнему $user->login().
  16. *
  17. * return void
  18. */
  19. function _login(){
  20. ...
  21. }
  22. // ------------------------------------------------------------------------
Нижнее подчеркивание перед названием метода только лишь обозначает его для системы хуков. Вызываться метод должен по-прежнему (например, $user->login()).
Поскольку названия моделей в системе не повторяются, то вызов хука выглядит следующим образом.
Внимание! Функция хука модели отличается знаком "_" после ее названия.

  1. /**
  2. * Хук компонента mail модели User для метода login.
  3. * Компонент в данном случае не важен, т.к. модели имеют уникальные имена.
  4. *
  5. * @param object $User Объект модели.
  6. * @param int $id Первый аргумент обрабатываемого метода.
  7. * ...
  8. * @return void
  9. */
  10. function mail_user_login_($User,$id[,$param,...]){
  11. ...
  12. }
  13. // ------------------------------------------------------------------------
  1. /**
  2. * Хук компонента mail модели User для метода login.
  3. * Компонент в данном случае не важен, т.к. модели имеют уникальные имена.
  4. * Изменяет параметр адресуемого метода.
  5. *
  6. * @param object $User Объект модели.
  7. * @param int $id Первый аргумент обрабатываемого метода.
  8. * .....
  9. * @return void
  10. */
  11. function mail_user_login_($User,&$id[,$param,...]){
  12. ...
  13. return func_get_args();
  14. }
  15. // ------------------------------------------------------------------------
  1. /**
  2. * Хук компонента mail модели User для метода login.
  3. * Компонент в данном случае не важен, т.к. модели имеют уникальные имена.
  4. * Выполняется после адресуемого метода, поскольку имеет суффикс "_after".
  5. *
  6. * @param object $User Объект модели.
  7. * @param mixed $result Результат выполнения адресуемого метода.
  8. * @param int $id Первый аргумент обрабатываемого метода.
  9. * ...
  10. * @return void
  11. */
  12. function mail_user_login_after_($User,$result,$id[,$param,...]){
  13. ...
  14. }
  15. // ------------------------------------------------------------------------



Виртуальные методы

Если вам понадобиться динамически создать метод для контроллера или модели одного компонента через хуки другого, воспользуйтесь данным функционалом.
Помните, виртуальные методы уникальны.
У одного класса не может быть два метода.
Если реальный метод существует, то виртуальный будет игнорироваться.
  1. /**
  2. * Виртуальный метод Comments для контроллера Index компонента Nodes.
  3. *
  4. * @param object $CI Сущность движка.
  5. * @param object $id Номер ноды.
  6. * ...
  7. * @return void
  8. */
  9. function nodes_comments($CI,$id){
  10. $CI->comments->show($id);
  11. }
  12. // ------------------------------------------------------------------------
  13. ...
  14. /*
  15. * Использование
  16. */
  17. $CI->nodes->comments($id);
  1. /**
  2. * Виртуальный метод captcha для модели Form.
  3. *
  4. * @param object $Form Объект модели.
  5. * ...
  6. * @return void
  7. */
  8. function form_captcha_($Form,$name,$options){
  9. $options['type'] = "captcha";
  10. $options['template'] = "/captcha/templates/captcha.tpl";
  11. $Form->add($name,$options);
  12. }
  13. // ------------------------------------------------------------------------
  14. ...
  15. /*
  16. * Использование
  17. */
  18. $CI->form->captcha($name,$options);



Вызов хука

Помимо стандартных способов работы с хуками существует возможность бросить «ловушку» для вызова хука.
  1. /**
  2. * Функция базового контроллера, отвечающая за вызов хука.
  3. *
  4. * @param string $class Класс.
  5. * @param string $method Метод.
  6. * @param string $class Суффикс.
  7. * @param array $args Аргументы.
  8. * @return void
  9. *
  10. */
  11. function _hook($class,$method,$suffix,$args){
  12. ...
  13. }
  14. // ------------------------------------------------------------------------
  1. /*
  2. * Пример.
  3. * В любом месте любого контроллера бросаем "ловушку".
  4. */
  5. ...
  6. $this->_hook("hkclass","hkmethod","hksuffix",array($data));
  7. ...
  8. /**
  9. * Вызовем хук из компонента mail.
  10. * /gears/mail/_hooks.php
  11. *
  12. * @param object $CI Сущность движка.
  13. * @param mixed $data Входные данные.
  14. * ...
  15. * @return void
  16. */
  17. function mail_hkclass_hkmethod_hksuffix($CI,$data){
  18. ...
  19. }
  20. // ------------------------------------------------------------------------
  1. /*
  2. * Пример.
  3. * Изменим параметр, передаваемый хуку.
  4. * В любом месте любого контроллера бросаем ловушку.
  5. */
  6. ...
  7. $data = $this->_hook("hkclass","hkmethod","hksuffix",array($data));
  8. ...
  9. /**
  10. * Вызовем хук из компонента mail.
  11. * Вернем измененный параметр.
  12. * /gears/mail/_hooks.php
  13. *
  14. * @param object $CI Сущность движка.
  15. * @param mixed $data Входные данные.
  16. * ...
  17. * @return void
  18. */
  19. function mail_hkclass_hkmethod_hksuffix($CI,$data){
  20. ...
  21. return $data;
  22. }
  23. // ------------------------------------------------------------------------
С ловушками в моделях все аналогично. Только не забывайте "_" на конце названия функции хука, чтобы указать принадлежность хука к «модельным».



Ядро

Вступление

В основе движка лежит фреймворк CodeIgniter, поэтому все базовые элементы, за исключением описанных ниже, будут аналогичными.


Кэш

CodeIgniter умел кэшировать вывод и запросы, что было явно недостаточно для оптимизации работы системы, поэтому мы создали свой класс. Если на сервере не установлен Memcached, для кэширования будет использована файловая система.
  1. // По-умолчанию время кэширования равно нулю = бесконечности
  2. $data = $this->cache->get('name');
  3. if(!$data){
  4. ...
  5. $this->cache->set('name',$data,3600);
  6. /**
  7.  * Теги
  8.  *
  9.  * Можно было указать теги для данного ключа
  10.  */
  11. $this->cache->tags('tags1,tag2,tag3')->set('name',$data);
  12. $this->cache->tags(array('tags1','tag2','tag3'))->set('name',$data);
  13. $this->cache->set('name',$data,3600,'tags1,tag2,tag3');
  14. $this->cache->set('name',$data,3600,array('tags1','tag2','tag3'));
  15. }
  16. ...
  17. // Удалить запись
  18. $this->cache->clear('name');
  19. // Удалить теги
  20. $this->cache->tags('tag1,tag2,tag3')->clear();
  21. // Сбросить весь кеш
  22. $this->cache->flush();



Сессия

Изначально CodeIgniter предлагал хранить сессию в базе данных или в cookie. Мы перешли на хранение сессии в Memcache или при его отсутствии в стандартной ее реализации.
  1. // Сохраняем в сессию
  2. $this->session->set('name',$data);
  3. // По-умолчанию вернет данные в виде объекта
  4. $data = $this->session->get('name');
  5. // Форсированный возврат данных в виде массива
  6. $data = $this->session->get('name',TRUE);
  7. // Удаление данных
  8. $this->session->remove('name');
  9. // Синоним
  10. $this->session->delete('name');



Файлы конфигурации

Для работы с файлами конфигурации создан специальный базовый класс Info.
  1. // Вернет прочитанный файл в виде массива
  2. print_r($this->info->read(GEARS.'global/global'));
  3. // Вносим изменения
  4. $data['site_url'] = 'cogear.ru';
  5. $extra_data['template'] = 'default';
  6. // Используем настроек файл как текущий
  7. // расширение можно не указывать
  8. $this->info->set(GEARS.'global/global')
  9. // Вносим изменения
  10. ->change($data)
  11. ->change($extra_data)
  12. ...
  13. // Сохраняем информацию в файл.
  14. ->compile();
Помните, что настройки всех компонентов хранятся в объекте базовой сущности.



Роутер

Альтернативные пути хранятся в настройках компонентов.

Схема работы роутера

Получая путь запроса, роутер разбивает его в массив элементов и производит следующую последовательность действий:



Загрузчик

Прежде чем роутер перейдет к интерпретации пути запроса, в дело вступит загрузчик, построенный на базе Loader Class от CodeIgniter. Базовый функционал класса сохранен, но появились новые логические элементы.
Если кэширование выключено, то загрузчик обойдет папки компонентов, считывая файлы конфигурации, составляя общий список работающих шестеренок.

На этапе предзагрузки будут подключены библиотеки компонентов (файл из папок «library» работающих шестеренок за исключением файлов с суффиксом ".class").

Подключение классов и моделей

Помните, что в связи с переходом на модель представления данных HMVC модели и классы должны располагаться в соответствующих папках компонентов models и library.
Все классы и модели загружаются автоматически, поэтому они должны обладать уникальными именами.
Классы должны располагаться в папке «library» и иметь в имени файла суффикс ".class".
  1. /*
  2. * Автоматическая загрузка класса из файла /gears/test/library/test.class.php
  3. */
  4. $this->test->some_method();
  5. /*
  6. * Автоматическая загрузка класса из файла /gears/test/models/some_model.php
  7. */
  8. $this->some_model->some_method();
Для загрузки моделей и классов из подпапок используйте следующий функционал.
  1. // Загрузка модели из файла /gears/some_gear/models/subdir/some_model.php
  2. $this->load->model('some_gear subdir/some_model');
Главное, чтобы между названиями шестеренки и модели/библиотеки присутствовал пробел.
Библиотеки CodeIgniter загружаются «по старинке».
  1. $this->load->library('form_validation');



База данных

Работа с базой осуществляется посредством библиотек CodeIgniter. Рекомендуем пользоваться Active Record Class в силу того, что это способствует расширяемости кода.
  1. /*
  2. * Метод query модели node, определяющий составляющий базового запроса для получения нод.
  3. * Благодаря префиксу "_" становится подвластен системе хуков.
  4. */
  5. ...
  6. function _query(){
  7. return $this->db->select('nodes.*',FALSE) // второй параметр позволяет избежать экранизации символа *
  8. ->get('nodes');
  9. }
  10. ...
  11. function get($id){
  12. $this->db->where('id',$id);
  13. return $this->query()->row();
  14. }
  15. ...
  16. /*
  17. * Воздействуем на запрос ноды из компонента user через хуки.
  18. * Обратите внимание, что хук выполняется до вызова метода query,
  19. * поскольку не имеет суффикса _after.
  20. */
  21. function user_node_query_($Node){
  22. $CI =& get_instance();
  23. $CI->db
  24. ->select('user.name,user.url_name,user.avatar')
  25. ->join('users','node.aid = users.id','inner');
  26. }
  27. ...

Новые функции Active Record

Обратите внимание на созданный нами метод swop, который позволяет переключаться между цепочками условий Active Record. Он помогает избежать конфликтных ситуаций при их возникновении.
  1. $this->db->select('users.*',FALSE)->from('users')->where('id',$id);
  2. // Выстроенная цепочка выглядит так:
  3. // SELECT users.* FROM users WHERE id = "$id"
  4. // Появилась необходимость узнать количество сообщений у пользователя.
  5. $this->db->swop();
  6. // Прошлая цепочка хранится в запасе, новая пока пустая.
  7. $pm_count = $this->db->where('uid',$id)->count_all_results('pm');
  8. // Новая цепочка обнуляется после вызова метода count_all_results.
  9. // Возвращаемся к предыдущей цепочке и завершаем запрос.
  10. $user = $this->db->swop()->get();
  11. // Исходная цепочка обнуляется
По-умолчанию метод «count_all_results» обнуляет цепочку запроса после получения результат, поэтому мы внесли дополнительный параметр, позволяющий ее сохранить.
  1. ...
  2. function show_nodes($page = 0){
  3. // Вызываем метод модели ноды, составляющий запрос.
  4. $this->node->query();
  5. // Получаем количество страниц для навигации.
  6. $pager = $this->pager($page,$this->db->count_all_results('nodes',FALSE));
  7. // При указании FALSE вторым аргументом мы сохраняем исходную цепочку запроса
  8. $nodes = $this->db->get('nodes',$pager['start'],$pager['limit'])->result();
  9. ...
  10. }
Совет: Рекомендуем использовать Active Record, что позволит сократить расходы при переходе на другие типы баз данных.



Поддомены

Несмотря на то, что поисковые системы не слишком жалуют сайт, разделы которого расположены на поддоменах, а также в силу эстетических соображений, многие веб-мастеры используют (или мечтают использовать) их на своих проектах.
Чтобы использовать поддомены с cogear, вам необходимо иметь DNS-запись вида "*", указывающую на IP-адреса вашего сервера. Таким образом запрос на любые поддомены будут отправлены к cogear.
Внимание! Запрос на поддомен транслируется в обычный запрос внтури роутера.
http://user.cogear.ru/admin/ => http://cogear.ru/user/admin/
  1. ; /gears/global/global.info
  2. ...
  3. subdomains = TRUE; Включаем поддомены
  4. ...
  1. ...
  2. ; Файл конфигурации любой шестеренки
  3. ; Использует поддомен совпадающий с названием шестеренки
  4. subdomain = TRUE;
  5. ; Использует поддомены указанные явно
  6. subdomain[] = user
  7. subdomain[] = users
  8. ...
  9. ;Дополним нужный запрос путями
  10. routes[] = 'users(.*) = user/list$1'
Внимание! Вы можете использовать поддомены для блогов/сообществ и всего что захотите. Для этого необходимо указать особый путь роутера в файле конфигурации шестеренки.
  1. routes[] = "% = community"
  2. ; отправляет все не зарезервированные поддомены на главный контроллер community
  3. ; news.cogear.ru/123-post.html => cogear.ru/community/news/123-post.html
При этом другие поддомены, определенные явно, будут работать. Это стоит учитывать при предоставлении возможности пользователю работать с поддоменами (например, при создании сообществ), проверяя массив переменной роутера «subdomains».

Асинхронные запросы

Поскольку AJAX-запросы между поддоменами сайта запрещены, мы нашли простой выход из ситуации. При использовании поддомена первым элементом пути запрос указывайте ajax, перенаправляя запрос по нужном адресу.
http://mail.cogear.ru/ => http://cogear.ru/user/check/ — такой запрос не пройдет.
http://mail.cogear.ru/ => http://mail.cogear.ru/ajax/user/check/ — правильный запрос. Будет перенаправлен по нужному адресу.




Шестеренки

Настройки

Основной компонент, хранящий в себе данные о сайте, шаблоны «шапки» и «подвала».

Библиотеки


Модели

Шаблоны

В папке «templates» хранятся глобальные шаблоны «шапки» и «подвала».


Панель управления

Панель управления

Ищет компоненты, обладающие файлом контроллера панели управления _admin.php, и отображает ссылки на их административные контроллеры в виде информации по компоненту, которая в свою очередь берется их его описания (локализованного). Разделяет группы компонентов на закладки.


Права доступа

Управление правами доступа — важная часть любой системы управления. В cogear данный компонент позволяют дифференцировать права доступа для разных групп пользователей. Администратор всегда обладает всеми правами.
  1. // Обычная проверка.
  2. $this->acl->check('nodes edit');
  3. // Сокращенная проверка
  4. acl('nodes edit');

Метод принимает строковый аргумент, в котором пробелом разделены названия компонента и правило доступа.
Список прав доступа хранится в базе данных. При создании нового модуля вы просто указываете в нужном месте функцию проверки acl($rule), модель прав доступа автоматически зарегистрирует новое правило в базе.
Панель управления правами доступа поможет вам быстро настроить доступ для разных групп пользователей.

Панель управления правами доступа

Модель прав доступа хранит в себе только запреты на доступ, поэтому после создания новых правил не забывайте настроить права доступа для групп.



Интернационализация

Возможность замены языковых переменных пригодится при создании сайтов на разных языках. О синтаксисе файлов локализации было написано ранее.
Панель управления интернационализацией позволяет контролировать список языков, разделы перевода, тексты в разделах, а также импортировать и экспортировать данные в языковые файлы.

Использование интернационализации

  1. // Обычный способ
  2. echo $this->i18n->translate('gears mail');
  3. // Простой способ
  4. t('gears mail');
  5. /*
  6. * Определим раздел перевода
  7. */
  8. d('gears');
  9. // Теперь его можно не указывать явно
  10. t('mail');
  1. /*
  2. * Еще один пример указания разделов перевода.
  3. */
  4. // Раздел -- user.
  5. d('user');
  6. echo t('name'); // Аналогично t('user name'), если user не является текущим.
  7. /*
  8. * Внимание! Запись
  9. */
  10. echo t('%success');
  11. /*
  12. * равно сильна записи
  13. */
  14. echo t('global success');
  15. /*
  16. * потому что "%" есть указатель на глобальный раздел. "%" = "!global"
  17. */
  18. /*
  19. * В то время как раздел определен, никто не мешает нам узнать
  20. * значение переменной из другого раздела.
  21. */
  22. echo t('mail subject');
  23. // Текущий раздел все еще -- user.
  24. // Перейдем в другой раздел -- form.
  25. d('form');
  26. echo t('edit'); // Аналогично t('form edit'), если form не является текущим.
  27. echo t('name'); // Если в form переменной name нет -- выведет просто 'name'.
  28. /*
  29. * Вернемся в прошлый раздел.
  30. * Можно вызвать d('user') или просто d(),
  31. * что вернет нас к предыдущему разделу, который был указан ранее.
  32. */
  33. d();
  34. echo t('name'); // Выведет значение переменной name в разделе user.

Работа с аргументами

Если в локальной переменной определены описатели преобразований, можно использовать аргументы функции, которые займут их место.
  1. /*
  2. * раздел user
  3. * переменная lost_password
  4. * [user]
  5. * lost_password = "Письмо с новым паролем было отправлено на ваш почтовый ящик (%s).";
  6. * new_password = "Уважаемый <b>%s</b>, ваш новый пароль -- <b>%s</b>.";
  7. *
  8. */
  9. d('user');
  10. echo t('lost_password',$user->email);
  11. echo t('new_password',$user->name,$new_password);
Альтернативная форма записи.
  1. /*
  2. * раздел user
  3. * переменная lost_password
  4. * [user]
  5. * lost_password = "Письмо с новым паролем было отправлено на ваш почтовый ящик (%s).";
  6. * new_password = "Уважаемый <b>%s</b>, ваш новый пароль -- <b>%s</b>.";
  7. *
  8. */
  9. d('user');
  10. echo t('lost_password@'.$user->email);
  11. echo t('new_password@'.$user->name.'@'.$new_password);

Работа с шаблонами

Можно использовать локализованные переменные в шаблонах.
  1. <h1>Привет, {_user name}!</h1>
Можно использовать альтернативную форму записи языковых переменных для передачи аргументов в шаблоне.
  1. <p>{_user new_password@$user->name@$new_password}</p>

Склонение числительных

  1. /*
  2. * [mail]
  3. * new_pm = "Вам пришло %d (новое сообщение|новых сообщения|новых сообщений)."
  4. *
  5. */
  6. echo t('mail new_pm',$pm_num);
  7. // Вам пришло 1 новое сообщение | Вам пришло 3 новых сообщения | Вам пришло 5 новых сообщений
Если вы не хотите, чтобы число выводилось, можно сделать так.
  1. /*
  2. * [mail]
  3. * new_pm = "(новое сообщение|новых сообщения|новых сообщений)."
  4. *
  5. */
  6. echo t('mail new_pm',$pm_num);
  7. // новое сообщение | новых сообщения | новых сообщений



Обработчик ошибок

Включает себя ряд функций для обработки ошибок:
  1. _404(); // Страница не найдена.
  2. _403(); // Доступ запрещен.
  3. error('Текст ошибки','Заголовок ошибки');
  4. info(); // По умолчанию выведет просто "Пусто".
  5. info('Информация, которую необходимо донести до пользователя');
Оригинальные обработчики ошибок CodeIgniter остаются в силе.
При вводе недопустимых символов в строке запроса, например, выдается стандартная ошибка CodeIgniter, вид которой можно изменить в файле "/engine/errors/error_general.php".


Формы

Большая часть времени разработки веб-приложения обычно уходит на создание и обработку форм. Мы решили оптимизировать процесс, сделав его максимально простым и приятным.
Структура формы задается в коде контроллера вместе со всеми дополнительными параметрами.
  1. /*
  2. * Задаем форму.
  3. */
  4. $this->form->set('user/register')
  5. ->input('name',
  6. 'validation'=>'required|min_length[3]',
  7. 'js_validation'=>'required|length[3,-1]',
  8. ...
  9. )
  10. )
  11. ->password('password',
  12. 'validation'=>'required',
  13. 'js_validation'=>'required',
  14. 'md5'=>TRUE,
  15. 'via_cookies'=>TRUE
  16. ...
  17. )
  18. )
  19. ->password('confirm_password',
  20. 'validation'=>'required|matches[password]',
  21. 'js_validation'=>'required|confirm[password]',
  22. 'md5'=>TRUE,
  23. 'via_cookies'=>TRUE,
  24. ''
  25. ...
  26. )
  27. )
  28. ->buttons('send');
  29. /*
  30. * По-умолчанию данные отправляются по этому же адресу.
  31. * Принимаем данные, если они проходят проверку.
  32. */
  33. if($result = $this->form->result()){
  34. // Данная функция покажет нам оповещение, если операция пройдет успешно.
  35. $this->form->save('user',$result);
  36. redirect('/users/show/'.$this->form->insert_id);
  37. }
  38. /*
  39. * Если данные не прошли проверку, под соответствующими элементами
  40. * появятся сообщения об ошибках.
  41. * Если данные не были отправлены -- отображаем форму.
  42. */
  43. $this->form->compile();
Обратите внимание, что по-умолчанию данные отправляются по этому же адресу, что позволяет избежать написания повторного кода структуры формы.

Рассмотрим более интересный вариант, когда одна и та же форма отвечает не только за добавление новых данных, но и за редактирование предыдущих.
  1. class Index extends Controller{
  2. ...
  3. function createdit($id = FALSE){
  4. $this->form->set('nodes/createdit')
  5. ->input('name',array(...))
  6. ->input('url_name',array(...))
  7. ->textarea('body')
  8. ->buttons('save');
  9.  
  10. if($id && $node = $this->db->get_where('nodes',array('id'=>$id))->row()){
  11. // Устанавливаем значения для элементов формы
  12. $this->form->set_values($node);
  13. }
  14.  
  15. if($result = $this->form->result()){
  16. // Режим редактирования -- обновляем информацию
  17. if($id !== FALSE && isset($node)){
  18. $this->form->update('nodes',$result,array('id'=>$node->id));
  19. redirect('/nodes/'.$node->id);
  20. }
  21. else {
  22. $this->form->save('nodes',$result);
  23. redirect('/nodes/'.$this->form->insert_id);
  24. }
  25. }
  26.  
  27. $this->form->compile();
  28. }
  29. ...

Интернационализация форм

При компиляции формы модель смотрит в текущий раздел таблицы перевода на предмет совпадения названия переменных с названиями элементов формы. Если названия совпали — перевод переменной используется как label элемента.
Если существует языковая переменная с таким же названием и суффиксом "_description", она будет использована для описание элемента формы.
Если в текущем разделе перевода такой переменной нет, будет произведен поиск по разделу «edit», который отвечает за любые действия, связанные с обработкой информации.
  1. /*
  2. * [node]
  3. * name = "Заголовок материала"
  4. * name_description = "Отражает суть всего материала."
  5. */
  6. $this->form->set('nodes/createdit')
  7. ->input('name',...)
  8. ...

Результат автоматического перевода

Вы можете указать параметры «label» и «description» явным образом при необходимости.
  1. $this->form->input('name',array('label'=>t('!node name'),'description'=>t('!node name_description')));

Проверка форм

Модель осуществляет проверку формы автоматически, требуется только указать ряд правил для каждого из элементов, который должен быть проверен.
Проверка может осуществляться на стороне сервера и клиента.
Правила проверки смотрите по ссылкам выше.
  1. $this->form->set('user/register')
  2. ->input('name',
  3. // Проверка на стороне сервера
  4. 'validation'=>'required|min_length[3]',
  5. // Проверка на стороне клиента
  6. 'js_validation'=>'required|length[3,-1]',
  7. ...
  8. )
  9. )

Кнопки

Определяются единожды после определения всех элементов формы. По-умолчанию кнопка принимает тип «submit», если не указано иного.
  1. $this->form->set('nodes/createdit')
  2. ...
  3. /*
  4. * Предопределенным названиями простых submit являются
  5. * save, create, update, send, submit, create
  6. */
  7. ...
  8. ->buttons('save');
  9. /*
  10. * Если в форме присутствует элемент textarea для ввода html-кода,
  11. * Можно сделать предпросмотр результата ввода при помощи кнопки preview.
  12. */
  13. ...
  14. ->buttons('preview','save');
  15. /*
  16. * Кнопка delete автоматически спросит JavaScript-подтверждение (confirm) при нажатии.
  17. */
  18. ...
  19. ->buttons('preview','save','delete');
  20. /*
  21. * Данный вид записи аналогичен следующему.
  22. */
  23. ...
  24. ->buttons(array('preview','save','delete'));
  25. /*
  26. * Можно явно указать свойства кнопки.
  27. */
  28. ->buttons(array(
  29. 'preview'=>array('value'=>t('!edit preview'),'type'=>'button','onclick'=>'preview()'),
  30. 'save'=>array('value'=>t('save'),'type'=>'submit'),
  31. 'reset'=>array('type'=>'reset')
  32. ));
Перевод названий кнопок хранится в разделе интернационализации «edit».


Элементы форм

Определяются методом, соответствующим их названию. Метод может принимать четыре аргумента, два последние из которых необязательные.
  1. $this->form->input($name = 'name',$config = array(), [$position = (int)10,$replace = (bool)TRUE])

text, password, textarea, hidden

Стандартные элементы, отличающиеся способом отображения. Вместо text можно использовать слово input.
  1. $this->form->text('name');
  2. //Аналогично
  3. $this->form->input('name');

select, image_select

Элемент выбора через выпадающее меню или через иконки.

Элемент выбора через иконки

  1. $config = array(
  2. ...
  3. /*
  4.   * Варианты выбора для элементов типа select, image_select
  5.   */
  6. 'options'=>array(
  7. '0'=>'Вариант один',
  8. // Если тип image_select
  9. '1'=>'/gears/select/img/some_folder/some_icon.png'
  10. // Если нужно установить title к изображению выбора
  11. '2'=>array('Some Title','/gears/select/img/some_folder/some_icon.png')
  12. ),
  13. // Если можно выбрать несколько вариантов за раз
  14. 'multiple'=>TRUE,
  15. // Значение должно указывать на ключ массива значений
  16. 'value'=>0,
  17. // Если значения множественные, возможны два варианта
  18. 'value'=>array(0,1,2,3,4),
  19. // Такой вариант можно хранить в виде строки
  20. 'value'=>'0,1,2,3,4',
  21. ...
  22. );
Если элемент может иметь множество значений, то при обработке полученных значений результат будет иметь вид «0,1,2,3,4», то есть значения склеиваются через ",", чтобы обеспечить возможность сохранения данных в виде строки.

checkbox,radio

Стандартные элементы выбора. Не имеют особых параметров. По-умолчанию label размещается перед элементом. Это можно изменить при помощи добавления дополнительного параметра конфигурации «label_after»=>TRUE.

fieldset,div

Иногда необходимо обернуть некоторые элементы в fieldset или div.
  1. ...->fieldset('name','legend')
  2. ->input('name')
  3. ...
  4. // Можно явно закрыть fieldset, вызвав метод без параметров.
  5. ->filedset()->compile();
  6. /*
  7. * Если fieldset или div не будет закрыт -- модель закроет его автоматически.
  8. */
  9. ->div('id')
  10. ->input('name')
  11. ...
  12. // Вместо того, чтобы закрыть, определяем следующий div.
  13. // Предыдущий закроется автоматически.
  14. ->div('info')
  15. ->input('address')
  16. ...
  17. ->compile();
  18. // Указанное выше верно и для fieldset.

file, file_url, image, image_url

Быстрая и удобная загрузка файлов. В качестве дополнительных параметров элемента следует указывать параметры класса File Uploading Class из CodeIgniter.
Обязательным является параметр upload_path, указывающий на путь загрузки файла.
В случае с image и image_url параметр allowed_types указывается автоматически.
Для проверки данных добавлена новая функция в js_validation и validationfile[формат_файла].
  1. ...
  2. ->file('info',array(
  3. // Не указывайте required
  4. 'validation'=>'file[doc]',
  5. 'js_validation'=>'file[doc]',
  6. // Функция _mkdir создает директорию, если она не существует
  7. 'upload_path'=>_mkdir('./uploads/files/info/')
  8. ));
При загрузке изображений можно использовать пост-обработку, а также использовать параметры класса Image Manipulation Class из CodeIgniter.
  1. ...
  2. ->image('post_image',array(
  3. // Указывать allowed_types не надо
  4. 'max_size'=>500, // в килобайтах
  5. 'upload_path'=>_mkdir('./uploads/images/'.$this->user->get('id').'/'.date('Y/m/d/')),
  6. 'overwrite'=>TRUE, // перезаписать, если такой файл уже есть
  7. /*
  8.   * Далее идут параметры для пост-обработки
  9.   */
  10. 'resize'=>'400x300',
  11. 'resize_aspect'=>TRUE, // сохранить пропорции
  12. 'watermark'=>TRUE, // водный знак,
  13. // миниатюры будут созданы в подпапках 100x100 и 200x200 директории оригинала
  14. 'thumbs'=>array('100x100','200x200'),
  15. // поскольку миниатюры небольшие, водный знак не стоит использовать
  16. 'thumbs_watermark'=>FALSE,
  17. 'thumbs_aspect'=>'auto',
  18. 'thumbs_ratio'=>TRUE,
  19. 'thumbs_crop'=>TRUE, // Подрезать по точного размера
  20. ))
  21. ...
При создании миниатюр можно хранить лишь адрес оригинала, воссоздавая пути к ним автоматически.
  1. $data['image'] = '/uploads/images/image.png';
  2. $data['image'] = make_icons($data['image'],array('100x100','200x200'));
  3. // Результат будет следующим
  4. $data['image'] = array(
  5. 'original'=>'/uploads/images/image.png',
  6. '100x100'=>'/uploads/images/100x100/image.png',
  7. '200x200'=>'/uploads/images/200x200/image.png'
  8. );

title, description

  1. /*
  2. * @param string Заголовок
  3. * @param mixed Конфигурация
  4. * @param boolean Использовать в title страницы
  5. * @param boolean Заменять последний элемент в title страницы
  6. */
  7. $this->form->title('Регистрация',FALSE,TRUE,TRUE);
  8. /*
  9. * Описание формы
  10. */
  11. $this->form->description('В данной форме вам предстоит пройти регистрацию на сайте.',array('class'=>'info'));

datetime

Используется когда нужно указать дату и время. Принимает значение в виде «Y-m-d H:i:s».
  1. /*
  2.   * Параметры для элемента datetime
  3.   */
  4. ...
  5. // с 1970 по 2010
  6. 'range'=>2010,
  7. // с 2008 по 2010
  8. 'range'=>'2008-2010',
  9. // с 2007 по 2010,
  10. 'range'=>'2007,2008,2009,2010',
  11. // с 2006 по 2007
  12. 'range'=>array('2006','2007'),
  13. 'value'=>'2009-05-30 10:10',

br,div_clear

Дополнительные элементы. Суть их ясна из названия.

grid

Для создания сеток (типовых таблиц), можно использовать этот метод.
  1. // Укажем типы столбцов
  2. $header = array(
  3. // имя элемента => (текст заголовка, тип элемента, ширина столбца,[адрес картинки, выравнивание])
  4. 'position'=>array(FALSE,'dragndrop','5%'), // drag and drop
  5. 'id' => array(t('ID'),'text','10%'),
  6. 'name' => array(t('name'),'link','20%',FALSE,'left'),
  7. 'edit' => array(t('edit'),'icon','10%','/gears/global/img/icon/edit.png'),
  8. // Массив информации должен содержать путь к картинке в ячейке image
  9. 'image' => array(t('image'),'image','10%',
  10. 'delete' => array(t('delete'),'checkbox'),
  11. );
  12. // Указываем параметры сетки
  13. $info = array(
  14. // Куда указывает ссылка/иконка
  15. // Очередность в $header обуславливает очередность в данном массиве
  16. 'link'=>array('/path/to/link','/path/to/icon'),
  17. // Какой параметр подставляется после пути ссылки
  18. 'link_add'=>array('id','url_name'),
  19. // По какому параметру происходит удаление элементов
  20. 'primary'=>'id',
  21. // Прячем заголовок сетки
  22. 'noname'=>TRUE,
  23. // Активируем drag-n-drop по полю
  24. // Используется для простой смены порядка очередности
  25. 'dragndrop'=>'position',
  26. // Динамическая отправка запроса при клике на checkbox'ы
  27. 'ajax'=>FALSE,
  28. // Динамическое удаление элементов при клике на checkbox'ы
  29. 'ajax_delete'=>FALSE
  30. );
  31. // Собираем информацию для сетки
  32. // Обязательно в виде массива
  33. $data = $this->db->get('elements',20)->result_array();
  34. foreach($data as &$item){
  35. // Достраиваем полный путь для картинки, если его нет
  36. // Название элемента массива информации должно совпадать с элементом в заголовке
  37. $item['image'] = '/gears/some_gear/img/'.$item['image'];
  38. }
  39. // Задаем сетку.
  40. /*
  41. * Внимание! Если вы хотите, чтобы базовые функции удаления или перетаскивания работали
  42. * имя элемента сетки должно совпадать с названием таблицы базы данных.
  43. */
  44. $this->form->grid('elements',$header,$data,$info);
  45. // Выводим форму в шаблон
  46. $this->form->compile();

Для полного понимания всех процессов анализируйте код компонентов.


Шаблонизатор

В cogear используется собственный шаблонизатор.
Для того, чтобы обеспечить максимально гибкий и расширяемый способ работы, мы руководствовались следующими правилами:

Добавление информации в шаблонизатор

Предопределенными элементами шаблонизатора являются глобальные «шапка» и «подвал» сайта, «шапка» и «подвал» текущего шаблона системы. Все остальные элементы вывода определяются в процессе. Если в пути шаблона не указан модификатор, то он адресуется к папке шаблонов текущего компонента.
Модификаторы путей шаблонов:
Следующий код подойдет как для контроллера, так и для моделей.
  1. // Добавляет шаблон info текущего компонента в порядке очереди.
  2. // Передает ему параметры массива $data
  3. $this->_template('info.tpl',$data);
  4. // Можно не указывать расширение файла .tpl
  5. $this->_template('info',$data);
  6. // Можно указать позицию в глобальном массиве шаблона.
  7. // Если на этой позиции есть элемент, он будет смещен по номеру выше.
  8. $this->_template('info',$data,5);
  9. // Можно заменить элемент на указанной позиции добавлением еще одного параметра.
  10. $this->_template('info',$data,5,TRUE);
  11. // Можно заменить элементы в определенном диапазоне.
  12. $this->_template('info',$data,5,100);
  13. // Можно заменить все элемент от указанного и до "подвала".
  14. $this->_template('info',$data,5,'all');
  15. // Также можно упразднить параметр с информацией, если он не нужен.
  16. $this->_template('info',5,'all');
  17. // Можно указать на шаблоны другого компонента
  18. $this->_template('mail show',$data);
  19. // Можно указать шаблон из папки текущего шаблона сайта
  20. // /templates/текущий_шаблон/
  21. $this->_template('%show',$data);
  22. /*
  23. * Если шаблон написан на PHP-Native, он не будет компилироваться
  24. */
  25. // Также можно упразднить параметр с информацией, если он не нужен.
  26. $this->_template('info.php',$data);
  27. /*
  28. * Передача готово кода осуществляется при помощи массива.
  29. */
  30. $this->_template(array('<p>Текст, который не надо обрабатывать</p>'));
  31. $this->_template(array('<p>Текст, который не надо обрабатывать</p>'),5);
  32. $this->_template(array('<p>Текст, который не надо обрабатывать</p>'),5,TRUE);
  33. $this->_template(array('<p>Текст, который не надо обрабатывать</p>'),5,10);
  34. $this->_template(array('<p>Текст, который не надо обрабатывать</p>'),5,'all');
Для обработки шаблона на месте и возврата результата укажите значение TRUE параметру $position.
  1. $output = $this->_template('some_template',$data,TRUE);
Все функции моделей, названные «compile», обеспечивают вывод результата работы модели и могут принимать два последних аргумента метода работы с шаблонами.
  1. // Пример для модели Breadcrumb
  2. // Выведет результат в порядке очереди
  3. $this->breadcrumb->compile();
  4. // Вернет результат
  5. $info = $this->breadcrumb->compile(TRUE);
  6. // Выведет результат на определенную позицию
  7. $this->bc->compile(10);
  8. // Дополнительно заменит элемент по факту его присутствия
  9. $this->bc->compile(10,TRUE);

Синтаксис шаблонизатора

Если вы решили пойти более простым путем, нежели PHP-Native, то вам следует ознакомиться с синтаксисом нашего шаблонизатора.
  1. /**
  2. * Для более быстрого написания кода шаблонизатор выступает в качестве "обертки" для PHP
  3. */
  4. {foreach ($nodes as $node)}
  5. {$node->body}
  6. {/foreach}
  7. /**
  8. * Скобки условия указывать не обязательно
  9. */
  10. {foreach $nodes as $node}
  11. {$node->body}
  12. {/foreach}
  13. /**
  14. * Данный код превращается в следующий PHP-код
  15. */
  16. <?php foreach($nodes as $node): ?>
  17. <?php if(isset($node->body)){ echo $node->body;} ?>
  18. <?php endforeach;?>
  19. /*
  20. * Еще несколько операторов
  21. */
  22. {if $a > $b}
  23. {$var}
  24. {elseif $a == $b}
  25. Просто текст.
  26. {else if $a < $b}
  27. А можно и так!
  28. {else}
  29. Привет, Гость!
  30. {/if}
  31. {switch gettype($var)}
  32. {case 'string'}
  33. Это строка!
  34. {break}
  35. {default}
  36. Это что-то другое!
  37. {/switch}
  38. /**
  39. * Вы можете вызывать любые функции следующим образом
  40. * ! -- без вывода
  41. * ? -- с выводом
  42. */
  43. // Выведет "200"
  44. {? (100/2)*4}
  45. // Ничего не выведет, но сохранит информацию
  46. {! $i = 0}
  47. /**
  48. * Можно подключать другие шаблоны
  49. */
  50. {include file="comment.tpl"}
  51. /**
  52. * Или даже так
  53. */
  54. {include file=$path}
  55. /**
  56. * Опциональные параметры -- выводятся только при существовании переменной внутри скобок
  57. */
  58. <div[ class="{$class}"][ id="{$id}"]>
  59. {$text}
  60. </div>
  61. /**
  62. * Применение функций к переменным
  63. *
  64. * Вы можете использовать любые функции PHP для модификации выводимых переменных
  65. */
  66. {$text|default:'Пусто'}
  67. {$text|strtolower}
  68. {$text|stripslashes|strtolower|default:'Пусто'}



Постраничная навигация

Упрощает создание постраничной навигации. По-умолчанию выстраивает страниц с обратным порядком следования «n...1».

Постраничная навигация

  1. $pager = $this->pager($current_page,$total_pages);
  2. /*
  3. * Получаем массив с двумя параметрами -- start и limit
  4. */
  5. $this->db->limit($pager['limit'],$pager['start'])->get('nodes');
Обратите внимание, что компонент постраничной навигации активируется одной строчкой кода и автоматически выводится на страницу.


Загрузка файлов

Подробнее о загрузке файлов читайте в разделе Элементы формы и в документации к коду компонента загрузки.


Редактор

По опыту многолетней работы было принято решение отказаться от визуальных редакторов в стандартной сборке cogear. В движке используется наш собственный редактор, который поможет пользователю с разметкой текста.
Вся полученная информация фильтруется парсером.
Подключить редактор в полю текста можно несколькими способами.
  1. // Указав имя формы в файле конфигурации редактора
  2. // Все текстовые поля будут заменены на редакторы
  3. forms = node_createdit,create_mail,pages_createdit
  4. // Использовать метод вызова редактора, как элемента формы
  5. $this->form->set('nodes/createdit')
  6. ...
  7. ->editor('body')
  8. ...
  9. // Добавить в массив editors объекта формы имя элемента textarea
  10. $this->form->editors[] = 'body';

Обратите внимание на приятные особенности редактора:

Добавление новых элементов

Панель управления редактором может быть расширена посредством добавления новых кнопок.
Добавляются новые кнопки через хуки и JavaScript.
  1. // /gears/upload/_hooks.php
  2. ...
  3. /**
  4. * Add image upload button
  5. *
  6. * @param object
  7. * @return void
  8. */
  9. function upload_editor_compile_after_($Editor){
  10. js('/gears/upload/js/inline/image',FALSE,TRUE);
  11. }
  12. // ------------------------------------------------------------------------
  13. ...
  14. // /gears/upload/js/inline/image.js
  15. window.addEvent('domready',function(){
  16. for(name in editor.editors){
  17. var e = editor.editors[name];
  18. e.addButton({hotkey:'g',background:'/gears/upload/img/icon/images.png',action:'upload_image("'+e.el.id+'")'})
  19. }
  20. });
  21. function upload_image(textarea){
  22. ed = editor.get(textarea);
  23. textarea = $type(textarea) == 'string' ? $(textarea) : textarea;
  24. // Вызываем окно загрузчика
  25. loader.ed = ed;
  26. loader.value = textarea.value;
  27. loader.frame('/upload/image/',500,420);
  28. }

Более подробную информацию о редакторе можно найти в файле "/gears/editor/js/editor.html.js".


Парсер

Парсер служит для фильтрации входных данных, но может обрабатывать и информацию на выходе, для чего работает в двух состояниях — prepare и process. Установить фильтры можно на несколько групп элементов:
  1. // Пост-процессная обработка информация
  2. $node['body'] = $this->parser->process($node['body'],'textarea');
  3. $this->_template('!nodes node',$node);
  4. // Подготовка информации для запись в базу данных осуществляется автоматически
  5. $data = $this->parser->prepare($data,$type);

Хуки парсера

Достаточно добавить название функции обработки в массивы пост-процессной или подготовительной обработок.
  1. /**
  2. * Add parser rules for jevix processing.
  3. *
  4. * @param object $Parser
  5. * @return void
  6. */
  7. function jevix_parser_construct_(&$Parser){
  8. /*
  9. * Можно по простому
  10. * $Parser->prepare['input'][] = 'parse_jevix|comment';
  11. * А можно и на определенное место в массиве
  12. */
  13. array_insert($Parser->prepare['textarea'],'parse_jevix|node',0);
  14. array_insert($Parser->prepare['comment'],'parse_jevix|comment',0);
  15.  
  16. /*
  17. * Обратите внимание на строку с названием функции parse_jevix|node
  18. * parse_jevix -- вызов функции без параметров
  19. * parse_jevix|node -- вызов функции с параметром node
  20. */
  21.  
  22. // Вызов метода определенной модели
  23. array_insert($Parser->prepare['textarea'],array('Textile','parse'),0);
  24. // Вызов метода (с параметрами) определенной модели
  25. array_insert($Parser->prepare['textarea'],array('Textile','parse|FALSE|TRUE'),0);
  26. }
  27. // ------------------------------------------------------------------------
  28. /**
  29. * Parse code with jevix
  30. *
  31. * @param string $value Recieved data.
  32. ...
  33. * @return string
  34. */
  35. function parse_jevix($value,[$type = 'node',$autobr = TRUE]){
  36. ...
  37. return $value;
  38. }
  39. // ------------------------------------------------------------------------



Пользователь

Обеспечивает базовые функции авторизации, регистрации, восстановления пароля и отображения/редактирования профиля пользователя.
Все данные о текущем пользователи хранятся в сессии, но вы можете получить доступ к ним через методы класса user.
  1. $this->user->is_logged(); // Авторизирован ли пользователь?
  2. $this->user->set('name','Гость');
  3. $name = $this->user->get('name');
  4. $id = $this->user->get('id');
Рекомендуется более подробно изучить код класса, а также его применение в шестеренках движка.


Ноды

Новости, посты, топики — как ни называй ноду, смысл один — ветка информационного дерева. Наряду со статическими страницами нода является базовым представлением информации в движке.
Базовый компонент обеспечивает необходимую функциональность и расширяемость.
Структурно нода состоит из трех элементов:
  1. /**
  2. * Пример расширения дополнительной информации о ноде шестеренкой favorite.
  3. * Add link-icon to node_info breadcrumb.
  4. *
  5. * @param object $Breadcrumb
  6. * @return void
  7. */
  8. function favorites_breadcrumb_compile_(&$Breadcrumb){
  9. if($Breadcrumb->name == 'node_info' && acl('favorites manage')){
  10. $status = empty($Breadcrumb->data->favorite) ? 'add' : 'remove';
  11. $Breadcrumb->add('<a href="javascript:void(0)">
  12. <img class="favorite-action" id="node-'.$Breadcrumb->data->id.'"
  13. src="/gears/favorites/img/icon/'.$status.'.png" title="'.t('!favorites '.$status).')"/>
  14. </a> ',0);
  15. }
  16. }
  17. // ------------------------------------------------------------------------
При помощи модели nodes можно легко и просто вывести список нод с постраничной навигацей. Для изменения запроса можно использовать класс Active Record.
Помните, что этот метод работы очень полезен при создании хуков.
  1. /**
  2. * Show user blog nodes
  3. *
  4. * @param string $url_name
  5. * @param int $page
  6. * @return void
  7. */
  8. function index($url_name = FALSE, $page = 0){
  9. // Если указан ЧПУ-адрес имени пользователя
  10. // Если первый аргумент не цифра и пользователь действительно существует
  11. if($url_name && !is_numeric($url_name) && $user = $this->user->info($url_name)){
  12. // Выводим в шаблон информацию о пользователе
  13. // и навигационную панель
  14. $this->user->head($user,'blog');
  15. title($user->name);
  16. // Проверяем возможность видеть неопубликованные ноды
  17. if(acl('blogs view_unpublished') OR $user->id == $this->user->get('id')){
  18. $this->nodes->published = FALSE;
  19. }
  20. // При помощи Active Record задаем дополнительные параметры запроса
  21. $this->db->where(array('aid'=>$user->id));
  22. }
  23. // Если первый параметр -- цифра, просто выводим список
  24. // всех нод из блогов, находящихся на искомой странице
  25. else {
  26. $page = $url_name;
  27. }
  28. // Выводим список нод
  29. $this->nodes->get($page);
  30. }
  31. // ------------------------------------------------------------------------
Обратите внимание, что количество нод на страницу определяется параметром конфигурации per_page или текущей шестеренки, или шестеренки nodes, если текущая параметра не имеет.


Комментарии

Говорят, что там где нет «каментов» — это не «Веб 2.0». Действительно, комментарии позволяют вдохнуть жизнь в обсуждение материалов сайта.
В нашем движке используется древовидная модель комментариев, что дает возможность организовать дискуссию в наиболее интересной форме. В зависимости от прав доступа комментарии могут быть отредактированы или удалены.
По структуре комментарий аналогичен ноде с поправкой на его масштаб.
Разработчик может расширить список элементов заголовка комментария, внеся туда свои собственные наработки.
  1. function hook_breadcrumb_compile_($Breadcrumb){
  2. if($Breadcrumb->name == 'comment_header'){
  3. $comment =& $Breadcrumb->data;
  4. // Установим дату на первую позицию массива элементов заголовка комментария
  5. $Breadcrumb->add(t('!comment created_date').' '.df($comment->created_date),0);
  6. // df -- вспомогательный метод (сокращение от англ. DateFormat)
  7. }
  8. }
Исследуйте код шестеренки для того, чтобы более детально понять суть ее работы.


Блоги

Блоги отображают все авторские ноды или же ноды определенного автора.


Сообщества

Сообществами можно назвать группу пользователей схожих по интересам.
Типичное сообщество представляет собой тематический блог.
Сообщества бывают двух видов:В свою очередь по типу регистрации сообщества могут тоже двух типов: Пользователь, создавший сообщество, становится его смотрителем Смотритель, что дает ему право редактировать настройки сообщества и регулировать список его участников, а также модерировать ноды сообщества.


Боковая панель

Стандартный дизайн сайта типа «блог» состоит из четырех частей — «шапки», основного содержимого, боковой панели и «подвала». Боковая панель представлена совокупностью блоков, которые в cogear называются «виджеты».
Разработчик с легкостью может создать свои виджеты, следуя инструкции:
Помните, что имена у виджетов должны быть уникальные, желательно связанные с тем компонентом, который они представляют.
Для создания виджета необходимо поместить в папку widgets разрабатываемой шестеренки файла с кодом виджета и, если в этом есть необходимость, с его конфигурацией.
Назовем наш виджет Stats, тогда для его создания потребуется создать два файла в папке widgets разрабатываемого компонента.
  1. ; widgets/stats.info.
  2. ; Пример файла с конфигурацией
  3. some_param = TRUE
  4. [group_params]
  5. param1 = 0
  6. param2 = 1
  7. param3 = 2
Файл с кодом должен нести в себе функцию виджета, которая аргументами получает сущность движка и объект параметров конфигурации, если они есть. Функция должна иметь имя в виде названия файла виджета с суффиксом "_widge" и возвращать готовый код, который и будет отображен в боковой панели.
  1. /**
  2. * Process stats widget
  3. *
  4. * @param object $CI
  5. * @param array $config
  6. * @return mixed
  7. */
  8. function stats_widget($CI,$config){
  9. if($config->some_param == TRUE){
  10. $output = '';
  11. foreach($config->group as $key=>$value){
  12. $output .= 'Widget param <strong>'.$key.'</strong> = '.$value.'
  13. ';
  14. }
  15. return $output;
  16. }
  17. return FALSE;
  18. }
Для интернационализации названия виджета в языковые файлы компонента следует внести следующие ниже строчки.
  1. [widgets]
  2. stats="Статистика";

После установки виджета он появится в списке доступных в панели управления боковой панелью.

Панель управления боковой панелью

Опциональное отключение боковой панели

Если вы не хотите отображать боковую панель в текущем контроллере, укажите следующий параметр в его конструкторе или в файле конфигурации.
  1. // Контроллер
  2. ...
  3. function __construct(){
  4. parent::Controller();
  5. $this->no_sidebar = TRUE;
  6. }
  7. ...
  8. // Файл конфигурации
  9. ...
  10. no_sidebar = TRUE;
  11. ...



Мета

Мета-информация используется поисковыми машинами для индексации. Данная шестеренка обладает следующим функционалом:


Синдикация

Возможность экспортировать информацию с сайта является основным постулатом синдикации.
Движок умеет экспортировать любые списки нод и комментариев в формате RSS 2.0 при помощи данной шестеренки. Процесс происходит автоматически — нужно только обнаружить иконку RSS-потока в адресной строке браузера. Если есть необходимость вывести ссылку на определенный поток, то к адресу исходного запроса нужно добавить rss первым аргументом.
http://cogear.ru/blogs/admin/ http://cogear.ru/rss/blogs/admin/



Видео

Простой плагин, реализованный при помощи хуков, позволяет вставлять видео с таких сервисов как YouTube, RuTube и Vimeo.
Кнопка плагина видеоавтоматически появляется в редакторе.


Тизер

«Тизером» называют краткий вид ноды. При помощи данного плагина можно разделить краткую, отображаемую в списке, и полную версии материала, при желании указав собственный текст связующей их ссылки.
Кнопка плагина тизеравтоматически появляется в редакторе.



Важно знать

Ссылки

Для того, чтобы обеспечить функционирование внутренних ссылок как при включенных поддоменах, так и при выключенных, все одиночные ссылки должны быть экранированы функцией l().
  1. // В контроллере/модели
  2. $link = l('/user/admin/');
  3. // В шаблоне
  4. {? l('/user/admin/')}
  5. /*
  6. * Тогда при включенных поддоменах и параметре конфигурации subdomain = TRUE модуля users
  7. ссылки будут преобразованы в http://user.cogear.ru/admin/
  8. */
Более подробно об этой функции читайте в комментариях к коду.


AJAX

Асинхронные запросы всегда имеют две составляющих — клиентскую и серверную. Помните, что для запросами между поддоменами надо добавлять «ajax» первым элементом запроса.
http://mail.cogear.ru/ajax/blogs/show/1/ => http://cogear.ru/blogs/show/1/




Создание шестеренки

Вступление

Документация — суть учебник, поэтому теоретическую информацию полезно закрепить на практике. Давайте подробно рассмотрим создание шестеренки «Favorites», которая отвечает за управление закладками пользователя.

Цель

Первым делом постарайтесь визуально представить себе конечный продукт, еще лучше — взять чистый лист бумаги и ручку, отразив свои намерения и идеи на бумаге.
Итак, суть модуля проста — добавить в информацию о ноде кнопку, при нажатии на которую нода будет попадать в «Избранное» или исключаться из него. Также необходимо сделать контроллер, который позволит просматривать список избранных нод в профиле пользователя.

Первым делом создадим папку "/gears/favorites/".

Создание папки шестеренки




База данных

Определимся со структурой базы данных.
  1. CREATE TABLE IF NOT EXISTS `favorites` (
  2. `id` int(10) unsigned NOT NULL auto_increment,
  3. `nid` int(10) unsigned NOT NULL,
  4. `uid` int(10) unsigned NOT NULL,
  5. `created_date` timestamp NOT NULL default CURRENT_TIMESTAMP,
  6. PRIMARY KEY (`id`),
  7. KEY `nid` (`nid`,`uid`,`created_date`)
  8. ) ENGINE=MyISAM DEFAULT CHARSET=utf8 AUTO_INCREMENT=1 ;
Создадим файл install.sql c вышеуказанным кодом и разместим его в корне шестернки. Он будет использован при ее установке.


Файл конфигурации

Самым важным элементом любой шестеренки является файл конфигурации. Прочитав документацию, напишем свой файл для шестеренки, назвав его «favorites.info».
  1. ; Файл /gears/favorites/favorites.info
  2. ; Помним, что название и описание по-умолчанию идут на английском языке.
  3. title=Favorites
  4. description=Allow user to add topics to favorites
  5. ; Важно! Обязательно требует указать группу компонента -- core | modules | plugins
  6. group=modules
  7. ; Указываем позицию. Она может быть не уникальна.
  8. ; Шестеренки с одинаковой позицией сортируются по названию.
  9. position=5
  10. ; Включаем компонент
  11. enabled = TRUE
  12. ; Резервируем поддомен
  13. subdomain[] = favorites



Интернационализация

Создаем папку «lang» и файл в ней с языковыми переменными для русского языка «ru.lng».
  1. [gears]
  2. favorites = "Избранные"
  3. favorites_description = "Позволяет пользователю заносить публикации в Избранное"
  4. [favorites]
  5. favorite = "Закладки"
  6. add = "Добавить в закладки"
  7. remove = "Удалить из закладок"
  8. add_success = "Материал добавлен в закладки!"
  9. remove_success = "Материал удален из закладок!"



Контроллер

Поскольку для добавления/удаления закладок нам понадобиться отправлять запросы при помощи AJAX, создадим контроллер, который к тому же будет отображать закладки пользователя.
  1. <?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
  2. /**
  3.  * CoGear
  4.  *
  5.  * Content management system based on CodeIgniter
  6.  *
  7.  * @package CoGear
  8.  * @author CodeMotion, Dmitriy Belyaev
  9.  * @copyright Copyright © 2009, CodeMotion
  10.  * @license http://cogear.ru/license.html
  11.  * @link http://cogear.ru
  12.  * @since Version 1.0
  13.  * @filesource
  14.  */
  15. // ------------------------------------------------------------------------
  16. /**
  17.  * Favorites controller
  18.  *
  19.  * @package CoGear
  20.  * @subpackage Favorites
  21.  * @category Gears controllers
  22.  * @author CodeMotion, Dmitriy Belyaev
  23.  * @link http://cogear.ru/user_guide/
  24.  */
  25. class Index extends Controller{
  26. /**
  27. * Constructor
  28. *
  29. * @return void
  30. */
  31. function __construct(){
  32. parent::Controller();
  33. }
  34. // ------------------------------------------------------------------------
  35. /**
  36. * Show user favorite nodes
  37. *
  38. * @param string $url_name
  39. * @param int $page
  40. * @return void
  41. */
  42. function index($url_name, $page = 0){
  43. // If user exists
  44. if($url_name && $user = $this->user->info($url_name)){
  45. // Show user profile main info
  46. $this->user->head($user,'favorites');
  47. // Check for acl to view unpublished items
  48. if(acl('blogs allow_view_unpublished') OR $user->id == $this->user->get('id')){
  49. $this->nodes->published = FALSE;
  50. }
  51. // Add active record condition
  52. $this->db->where(array('favorites.uid'=>$user->id));
  53. // Show list of nodes with pagination
  54. $this->nodes->get($page,FALSE,TRUE);
  55. }
  56. else return _404();
  57. }
  58. // ------------------------------------------------------------------------
  59. /**
  60. * Add/remove node to user favorites via ajax
  61. *
  62. * @return json
  63. */
  64. function action(){
  65. // Set i18n department
  66. d('favorites');
  67. // Get POST variables
  68. $nid = $this->input->post('nid');
  69. $action = $this->input->post('action');
  70. // Check for request
  71. if(!$nid OR !$action) ajax(FALSE);
  72. // Get current user id
  73. $uid = $this->user->get('id');
  74. // Switch action
  75. switch($action){
  76. case 'add':
  77. if($this->db->get_where('favorites',array('nid'=>$nid,'uid'=>$uid))->row()){
  78. ajax(FALSE);
  79. }
  80. if($this->db->insert('favorites',array('nid'=>$nid,'uid'=>$uid))){
  81. ajax(TRUE,t('add_success'));
  82. }
  83. break;
  84. case 'remove':
  85. if($this->db->delete('favorites',array('nid'=>$nid,'uid'=>$uid))){
  86. ajax(TRUE,t('remove_success'));
  87. }
  88. break;
  89. }
  90. // If no switch option matches return false
  91. ajax(FALSE);
  92. }
  93. // ------------------------------------------------------------------------
  94. }
  95. // ------------------------------------------------------------------------



Модели

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


Хуки

Теперь нам нужно связать представление ноды и шестеренку управления избранным. Как было сказано ранее, вывод нода состоит из трех частей. Нас интересует последняя, которая отвечает за отображение дополнительной информации. Отправляемся читать код модели node шестеренки nodes. Смотрим, как строится отображение дополнительной информации.
  1. function _show(&$node,$type = 'full',$return = FALSE){
  2. ...
  3. $info =& $this->breadcrumb;
  4. $avatar = reset(make_icons($node->avatar));
  5. //'!/gears/nodes/img/icon/time.png! '
  6. $info->set('node_info')->data($node)
  7. ->add('<span>'.df($node->created_date).'</span>')
  8. ->add('<a href="'.l('/blogs/'.$node->author_url_name).'">
  9. <img class="avatar" src="'.$avatar.'">
  10. <a href="'.l('/user/'.$node->author_url_name).'">'.$node->author.'</a>');
  11. $node->info = $info->compile();
  12. ...
  13. }
Исходя из полученной информации пишем хук.
  1. /**
  2. * Add link-icon to node_info breadcrumb
  3. *
  4. * @param object $Breadcrumb
  5. * @return void
  6. */
  7. function favorites_breadcrumb_compile_(&$Breadcrumb){
  8. if($Breadcrumb->name == 'node_info' && acl('favorites manage')){
  9. $status = empty($Breadcrumb->data->favorite) ? 'add' : 'remove';
  10. $Breadcrumb->add(' <a href="javascript:void(0)">
  11. <img class="favorite-action" id="node-'.$Breadcrumb->data->id.'"
  12. src="/gears/favorites/img/icon/'.$status.'.png" title="'.t('!favorites '.$status).')"/>
  13. </a> ',0);
  14. }
  15. }
  16. // ------------------------------------------------------------------------

Добавляем ссылку на «Избранные» ноды пользователя. Смотрим как реализован вывод навигации на странице пользователя (модель user шестеренки user).
  1. /**
  2. * Show head panel with userinfo
  3. *
  4. * @param object
  5. * @param string
  6. * @return void
  7. */
  8. function head($user,$active = 'profile'){
  9. d('user');
  10. $CI =& get_instance();
  11. $CI->breadcrumb->set('userinfo_panel')->wrapper();
  12. $CI->breadcrumb->data($user);
  13. $CI->breadcrumb->add('<a href="'.$user->avatar['original'].'" target="_blank">
  14. <img src="'.$user->avatar['64x64'].'" border="0" class="avatar"/></a>
  15. <h1><a href="'.l('/user/'.$user->url_name).'">'.$user->name.'</a></h1>');
  16. if($CI->user->get('user_group') == 1 OR $CI->user->get('id') == $user->id){
  17. $CI->breadcrumb->add('<a href="'.l('/user/'.$user->url_name.'/edit/').'">
  18. <img src="/gears/global/img/icon/edit.png"/></a>');
  19. }
  20. $CI->breadcrumb->compile(4);
  21. $CI->userinfo_tabs = new Panel('userinfo_tabs',FALSE,FALSE,'tabs');
  22. $CI->userinfo_tabs->data =& $user;
  23. $CI->userinfo_tabs->set_title = FALSE;
  24. $CI->userinfo_tabs->links_base = '/user/'.$user->url_name.'/';
  25. $CI->userinfo_tabs->add(array('name'=>'profile','text'=>t('!user Profile'),'index'=>TRUE));
  26. $CI->userinfo_tabs->set_active($active);
  27. $CI->userinfo_tabs->compile(5);
  28. d();
  29. }
  30. // ------------------------------------------------------------------------

Хукаем компиляцию вывода модели информационной панели.
  1. /**
  2. * Add userinfo_tabs tab for favorites link
  3. *
  4. * @param object
  5. * @return void
  6. */
  7. function favorites_panel_compile_(&$Panel){
  8. $CI =& get_instance();
  9. if($Panel->name == 'userinfo_tabs'){
  10. $count = $CI->db->where(array('uid'=>$Panel->data->id))
  11. ->count_all_results('favorites');
  12. $count = $count > 0 ? ' ('.$count.')' : '';
  13. $CI->userinfo_tabs->add(array('name'=>'favorites',
  14. 'text'=>fc_t('!favorites favorite').$count,
  15. 'link'=>l('/favorites/'.$Panel->data->url_name)));
  16. if($CI->name == 'favorites') {
  17. $Panel->set_active('favorites');
  18. }
  19. }
  20. }
  21. // ------------------------------------------------------------------------

Все созданные хуки располагаем в файле "/gears/favorites/_hooks.php".


Скрипты и стили

Дополнительные стили нам не понадобятся.
Создадим в корне шестеренки папку «js» и в ней файл «favorites.js», который автоматически добавиться в глобальный js-файл.
  1. window.addEvent('domready',function(){
  2. $$('.favorite-action').each(function(img){
  3. var link = img.getParent();
  4. var nid = img.get('id').split('-')[1];
  5. link.removeEvents('click');
  6. link.addEvent('click',function(){
  7. var action = link.getFirst().get('src').search('remove') == '-1' ? 'add' : 'remove';
  8. new Request.JSON({
  9. url: '/ajax/favorites/action/',
  10. data: 'nid='+nid+'&action='+action,
  11. onComplete: function(re){
  12. if(re.success){
  13. switch(action){
  14. case 'add':
  15. img.src = '/gears/favorites/img/icon/remove.png';
  16. img.set('title',lang.favorites.remove);
  17. break;
  18. case 'remove':
  19. img.src = '/gears/favorites/img/icon/add.png';
  20. img.set('title',lang.favorites.add);
  21. break;
  22. }
  23. }
  24. msg(re.msg || lang.errors.error);
  25. }
  26. }).post();
  27. return false;
  28. });
  29. });
  30. });



Изображения

Создадим в корне шестеренки папку «img», в ней — папку «icon», и разместим в ней две иконки из коллекции fugue.

Иконки избранного




Заключение

Шестеренка готова. Для того, чтобы сделать ее доступной для всех пользователей, достаточно просто сохранить ее в архив и разместить на этом сайте.