Новое в Joomla 2.5:

  • Access Control List System (ACL)
  • User-Defined Category Levels – бесконечная вложенность секций и категорий
  • JForm – класс, который позволяет создавать форму, ее элементы и т.д.
  • JTableNested – новый базовый класс для категорий, меню и других таблиц, обеспечивающий API по работе со вложенными элементами.

Использование метода truncate JHtmlString

стр. 116

Работа с текстом ведется в кодировке UTF-8. В Joomla создан собственный класс, заменяющий стандартные функции PHP, наподобие substr, strlen, str_split, strpos и другие. Обращение к классу происходит так:

 

$tmp = JString::substr($text, 0, $length);

 

Файл с классом JString расположен здесь:

libraries/joomla/string/string.php

Основные методы выбраны и помещены в "Appendix C" в конце книги. Также информация по адресу:

стр. 118

Подключить файл с классом можно следующим образом:

 

JLoader::register('JHtmlString', JPATH_LIBRARIES.'/joomla/html/html/string.php');

 

Класс JHtmlString содержит всего 2 метода:

  • truncate() – по заданной длине текста обрезает передаваемую строку с дополнением незакрытых тегов. Заключительное слово выбирается по последнему пробелу и не разрывается (в параметрах можно включить разрыв строк и запретить теги). В конце строки добавляется "...".
  • abridge() – по заданной длине и длине вступления разделяет текст. К примеру, "Really long title" to "Really...title".

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

 

echo JHtmlString::truncate(strip_tags($item->introtext), 53);

 

Использование JHtml::_ Syntax

стр. 120

Однако, использование JLoader по примеру выше неверно. Лучший способ:

 

echo JHtml::_('string.truncate', strip_tags($item->introtext), 53);

 

Класс JHtml имеет собственный "загрузчик", метод "_". В примере выше последними идут параметры, их может быть и больше.

Альтернативные Макеты

стр. 125

Ссылка по теме: Layout Overrides in Joomla 1.6

стр. 126

Требования к номинации альтернативных макетов отображения:

  1. название должно отличаться от стандартного
  2. название не должно содержать нижнего подчеркивания "_", так как подобное название определяет "sublayout"

Добавление нового макета пункта меню

стр. 126

Перезаписать "шаблон" можно также и у элемента меню, который создается. За элемент меню отвечает файл .xml, расположенный в папке tmpl компонента. К примеру:

components/com_users/views/registration/tmpl/default.xml

В папке с шаблоном создаем:

templates/beez_20_copy/html/com_users/registration/approval.xml

Естественно должен быть и approval.php.

Однако, этого будет еще недостаточно для появления нового типа элемента меню, так как имя элемента в .xml совпадает с оригинальным.

Переопределение параметров

стр. 127

Содержание нового approval.xml:

 

<?xml version="1.0" encoding="utf-8"?>
<metadata>
    <layout title="Registration with Approval" option="Approval">
        <help
        key="JHELP_MENUS_MENU_ITEM_USER_REGISTRATION"
        />
        <message>
            <![CDATA[COM_USER_REGISTRATION_VIEW_DEFAULT_DESC]]>
        </message>
    </layout>
    <!-- Add fields to the parameters object for the layout. -->
    <fields name="params">
        <!-- Basic options. -->
        <fieldset name="basic" label="COM_MENUS_BASIC_FIELDSET_LABEL">
            <field
            name="show_age_checkbox"
            type="radio"
            label="Show Age Checkbox"
            description="Show or hide the age checkbox."
            default="0" >
                <option value="0">Hide</option>
                <option value="1">Show</option>
            </field>
        </fieldset>
    </fields>
</metadata>

 

Данная строка определяет имя элемента меню:

 

<layout title="Registration with Approval" option="Approval">

 

Очень интересна данная строка:

 

<help key="JHELP_MENUS_MENU_ITEM_USER_REGISTRATION" />

 

Она отвечает за подгрузку справочной статьи, которая отобразится при нажатии на кнопку помощи.

Новый элемент меню имеет настройку отображения проверки возраста (checkbox). Из макета параметр можно получить так:

 

$this->params->get('show_age_checkbox');

 

Переопределения без выделения

стр. 129

Joomla позволяет сделать перезапись:

  • Module chrome, which is the decoration around a module layout
  • Language files
  • Menu Item Parameter

стр. 131

Если добавить новую позицию модуля в шаблон, то добавлять в templateDetails.xml не обязательно, но придется вводить имя в ручную. Добавить позицию в данный файл нужно, чтобы он появился в всплываемом списке модулей.

стр. 132

Совместно с jdoc:include можно передавать неограниченное количество переменных, которые будут доступны из массива $attribs. Например:

 

<jdoc:include type="modules" name="position-7a" style="beez20_copyFramedTable" border="2" headerLevel="3" />

 

Из modChrome получаем значение:

 

$attribs['border']

 

Если значение не задано, то это нужно отследить. В Joomla в настройках сервера можно указать Error reporting равный Maximum. В таком случае будет указано, что, к примеру, нет элемента "border" у массива с атрибутами.

При разработке желательно по максимуму проверять входные значения. В данном случае:

<?php $border = (isset($attribs['border'])) ? (int) $attribs['border'] : '1'; ?>

Переопределение языка: Добавьте перевод в наше переопределение

стр. 134

Имя для перезаписи языкового файла составляется следующим образом:

  • xx-XX.override.ini

Для Закрытой части сайта файл должен быть положен в эту папку:

administrator/language/overrides/

При разработке все строки для перевода выводим через:

 

JText::_()

 

2 правила именования для перезаписи языкового файла:

  1. никаких пробелов;
  2. каждый языковой ключ должен иметь уникальное имя.

Для двойных кавычек можно использовать:

  • " или _QQ_

Пример (en-GB.override.ini):

 

; Example front-end language override file
; Keys for templates/beez_20_copy/html/com_users/registration/approval.php layout file

BEEZ_20_COPY_TERMS_OF_SERVICE="Terms of Service"
BEEZ_20_COPY_AGE="I am at least 18 years old."
BEEZ_20_COPY_AGREE="I agree to the terms of service for this site."

 

Для того, чтобы не создавать файл вручную, можно перейти Extensions → Language Manager.

Переопределения таблиц и моделей

стр. 137

Joomla позволяет также перезаписывать стандартные PHP классы, используемые для таблиц (подклассы класса JTable) и моделей (подклассы класса JModel). Это делается с помощью плагинов (см. Глава 5).

Глава 5. Расширение Joomla! с помощью плагинов

стр. 139

Файлы плагинов включаются в скрипт посредством данного метода:

 

JPluginHelper::importPlugin()

 

Триггер запускает плагины на событии:

 

$dispatcher->trigger()

 

Срабатывают все плагины для заданного события, загруженные в память.

Соглашения об именах для плагинов

стр. 140

Имя плагина и его директория подчиняются следующему правилу:

plugins/<plugin type>/<plugin name>/<plugin name>.php

Например:

plugins/system/sef/sef.php

Каждый плагин в своей папке имеет также .xml файл с равным именем:

plugins/system/sef/sef.xml

Имя класса плагина составляется по правилу:

"plg" + <plugin type> + <plugin file name>

Например:

plgSystemSEF

Типы плагинов: Куда Вы можете вставить плагин?

стр. 141

Где можно запустить плагины:

  • Authentication
  • Captcha
  • Content
  • Editors
  • Editors-XTD
  • Extension
  • Search
  • Smart Search (Finder)
  • System
  • User

Обзор выбранных основных плагинов

System: SEF

стр. 143

Плагины группы system загружаются в память так:

 

JPluginHelper::importPlugin('system');

 

Пример точки события и запуска плагинов:

 

// Вызов события onAfterRender.

$this->triggerEvent('onAfterRender');

 

Это метод класса JApplication:

 

function triggerEvent($event, $args=null)
{
    $dispatcher = JDispatcher::getInstance();

    return $dispatcher->trigger($event, $args);
}

 

В данном случае вызывается метод onAfterRender() и запускаются плагины группы system, так как были объявлены ранее.

(!) Метод onAfterRender() объявляется в плагине.

Что Он Делает?

стр. 144

Пример плагина группы system:

 

class plgSystemSef extends JPlugin
{
    /**
   * Преобразование URL-адреса сайта в соответствие с HTTP - запросом
    */
    public function onAfterRender()
   {

 

Важные функции:

  • для проверки нахождения в открытой части сайта:
    • $app->getName() != 'site'
  • получение значения из Глобальной конфигурации:
    • $app->getCfg( 'sef' )

Особенности плагинов:

  • запускаются только те, что активированы;
  • вызываются независимо от положения в закрытой или открытой части (всегда находятся в памяти).

Особенности метода onAfterRender():

  • возвращает что угодно;
  • изначально не принимает аргументов.

Аутентификация: Папка joomla

стр. 145

Речь пойдет о следующем плагине:

plugins/authentication/joomla/joomla.php

Каждый раз, когда пользователь пытается войти в систему, вызывается метод authenticate() класса JAuthentication (libraries/joomla/user/authentication.php).

При вызове данного метода подгружаются плагины группы с тем же названием:

$plugins = JPluginHelper::getPlugin('authentication');
В цикле каждый класс вызывается со следующими параметрами:

 

$plugin->onUserAuthenticate($credentials, $options, $response);

В переменной $credentials передается имя пользователя и пароль.

Плагин начинается со следующей строчки:

function onUserAuthenticate($credentials, $options, &$response)
Таким образом ответ ($response) передается по ссылке и затем обрабатывается системой вне плагина (-ов).

 

Примечание. В PHP, начиная с 5-й версии, следующая запись:

$x = $myObject;

 

означает передачу 2-й переменной (объекта) по ссылке. Меняются оба при изменении одного из них.

То есть знак "&" в данном случае писать не обязательно.

Для создания нового объекта используется запись вида:

$x = clone $myObject;

 

стр. 149

Подключение к БД можно получить следующим образом:

$db = JFactory::getDbo();

 

Следующая запись:

$query = $db->getQuery(true);

 

получает объект класса JDatabaseQuery.

Для безопасности в запросе используется:

  • метод Quote() экранирует все опасные символы (применять всегда, если вставляется значение отличное от числа):
    • $query->where('username=' . $db->Quote($credentials['username']));
  • при вставке чисел следует использовать:
    • $query->where('id =' . (int) $id);
    • $query->where('number =' . (float) $number);

Завершение запроса:

$db->setQuery($query);
$result = $db->loadObject();
Зашифрованный пароль:
$testcrypt = JUserHelper::getCryptedPassword($credentials['password'], $salt);

 

Пользователь получается так:

$user = JUser::getInstance($result->id);

 

Проверка нахождения в Закрытой части сайта:

if ( JFactory::getApplication()->isAdmin() ) {

 

Получение языка:

$user->getParam('admin_language');

Content: Папка joomla

стр. 154

Плагин:

plugins/content/joomla/joomla.php

Имя класса: plgContentJoomla

Два метода:

  • onContentAfterSave()
  • onContentBeforeDelete()

Когда в Закрытой части сайта удаляются Категории, Статьи, Контакты или другие элементы, срабатывает триггер onContentBeforeDelete.

Посмотреть детально триггер можно в классе JModelAdmin:

libraries/joomla/application/component/modeladmin.php

Триггер вызывается в методе delete():

// Trigger the onContentBeforeDelete event.
$result = $dispatcher->trigger($this->event_before_delete, array($context, $table));
Важно:

 

  • ожидается ответ, который возвращается в переменную $result;
  • в переменную передаются значения, 1-е "onContentBeforeDelete", затем массив с 2-мя элементами.

Передаваемые элементы массива распаковываются и передаются в метод onContentBeforeDelete() в плагине.

Разберем 2 переменные:

  • $context - "контекст", место, откуда был вызван триггер, например "com_categories.category";
  • $table - массив с информацией по удалению.

Что Он Делает?

стр. 155

Контекст позволяет определить необходимость в запуске плагина:

// Пропустить плагин, если мы удаляем что-то, кроме категорий
if ($context != 'com_categories.category') {
    return true;
}

 

В файле .xml плагина можно задать настройки, которые будут доступны для плагина в Закрытой части сайта. Достать значение можно так:
// Проверьте, включена ли эта функция.
if (!$this->params->def('check_categories', 1)) {
    return true;
}
Объект $this->params (класс JRegistry). Параметры хранятся в таблице #__extensions.

 

Вместо использования $_REQUEST стоит делать так для безопасности:

$extension = JRequest::getString('extension');

 

Методы можно посмотреть в классе JRequest.

стр. 158

Вывод текста в зависимости от числа (например, "1 метод", "2 метода") посредством класса JText:

JText::plural('COM_CATEGORIES_N_ITEMS_ASSIGNED', $count);

 

Вызов ошибки на экран в контексте:

JError::raiseWarning(403, $msg);
JError::raiseWarning(500, $error);

 

Проверка таблицы на наличие вложенности (подкатегорий):

$data->isLeaf()

 

(!) Важно:

Если в плагине необходимо определить доп. функцию, следуем синтаксису по примеру:

private function _countItemsInCategory($table, $catid)

 

onBeforeCompileHead

стр. 159

Создаем свой плагин.

Событие "onBeforeCompileHead" позволяет изменять HTML прямо перед выводом на экран (render).

Как Это Выполняется?

Событие срабатывает в методе fetchHead() класса JDocumentRendererHtml (правильно JDocumentRendererHead)(libraries/joomla/document/html/renderer/head.php).

Срабатывание триггера:

// Инициировать событие onBeforeCompileHead
$app = JFactory::getApplication();
$app->triggerEvent('onBeforeCompileHead');
Создаваемый плагин пересмотрит meta-теги. Пример его работы:
<meta name="revised" content="Mark Dexter, 17 March 2012" />
Объект класса JDocumentHTML хранит в себе всю информацию, которая помещается в <head> и выдает ее посредством метода getHeadData().

 

Для того, чтобы посмотреть, что хранится в <head> достаточно добавить в метод fetchHead():

var_dump($document->getHeadData());

Этапы создания плагина:

 

  1. создаю папку "mymeta" в /plugins/system
  2. в папке создаем:
    • mymeta.php
    • mymeta.xml
    • index.html

Копируем в .xml содержание любого плагина и исправляем имя, вхождение файлов и параметры.

Параметры заданы так:

<config>
    <fields name="params">
        <fieldset name="basic">
            <field name="revised" type="text"
            description="Meta revised text for content attribute"
            label="Revised Content"
            default=""
            size="50"
            />
        </fieldset>
    </fields>
</config>
Плагин имеет данный код:
class plgSystemMyMeta extends JPlugin {
    function onBeforeCompileHead() {
        if ( $this->params->get( 'revised' ) ) {
            $document = JFactory::getDocument();
            $headData = $document->getHeadData();
            $headData[ 'metaTags' ][ 'standard' ][ 'revised' ] = $this->params->get( 'revised' );
            $document->setHeadData( $headData );
        }
    }
}
Из примера книги я удалили строку в начале плагина:
jimport('joomla.plugin.plugin');
Видимо в текущей версии Джумлы она не нужна.

 

Плагин для регистрации Пользователей

стр. 164

Создаем плагин для формы регистрации (отметка о Соглашении с условиями + возраст).

После "submit", данные формы вносятся в $_REQUEST. В форме элементы должны иметь атрибут "name". Для наших целей:

  • tos_agree
  • old_enough

Изменяем код в файле approval.php (в папке с темой):

<fieldset>
    <legend><?php echo JText::_( 'BEEZ_20_COPY_TERMS_OF_SERVICE' ); ?></legend>
    <p><input type="checkbox" name="tos_agree" />
    <?php echo JText::_('BEEZ_20_COPY_AGREE')?> </p>
    <?php if ($this->params->get('show_age_checkbox')) : ?>
        <p><input type="checkbox" name = "old_enough" />
        <?php echo JText::_('BEEZ_20_COPY_AGE')?> </p>
    <?php endif; ?>
</fieldset>
Создаем плагин "myregistration" в папке:

 

/plugins/user

Добавление XML-файла

стр. 166

Только интересное из файла .xml:

<extension version="2.5" type="plugin" group="user">
    <name>plg_user_myregistration</name>
    <description>PLG_USER_MYREGISTRATION_XML_DESCRIPTION</description>
    <files>
        <filename plugin="myregistration">myregistration.php</filename>
        <filename>index.html</filename>
        <folder>language</folder>
    </files>
    <config>
    </config>
</extension>
Добавлена папка "language".

 

Добавление файла плагина PHP

стр. 167

Данная строка подгружает язык:

// Загрузите языковой файл для плагина
$this->loadLanguage();

Нужные значения получаем по примеру:
JRequest::getBool('tos_agree')
Помещаем 2 языковых файла в папку с плагином в language/en-GB:

 

  • en-GB.plg_user_myregistration.ini - перевод для Открытой части сайта и при открытии плагина на редактирование в Закрытой;
  • en-GB.plg_user_myregistration.sys.ini - перевод строк в файле .xml плагина, таких как Имя и Описание и отображение в Закрытой части в Менеджере плагинов.

В файле .xml как видно, достаточно только включить папку "language".

(!)Важно. В ходе тестирования плагина его можно удалить через phpMyAdmin из таблицы #__extensions, тогда он пропадет из списка, но файлы останутся и его снова можно будет добавить через Discover.

Упаковка плагина

стр. 171

Этапы создания инсталлятора:

  1. создать новую папку, к примеру, "temp" на Рабочем столе и скопировать в нее содержимое папки с плагином;
  2. создать архив содержимого папки "temp".

Улучшенный Плагин Регистрации Пользователей

стр. 173

Предисловие. Сейчас, для того, чтобы организовать проверку альтернативных данных формы требуется сначала перезаписать макет самой формы через шаблон () и использовать плагин. В сумме 2. Однако, Joomla 2.5 позволяет вынести все в код плагина благодаря классу JForm и не использовать более в плагине метод onBeforeSave(). Также это важно по причине необходимости создания нового пункта меню для альтернативного макета.

По структуре файлов и папок есть единственное отличие:

  • forms/form.xml - файл с информацией JForm

Создание XML - файла плагина

стр. 174

Отличие файла .xml от предыдущей разработки:

  • <extension version="2.5" type="plugin" group="user" method="upgrade">
  • <folder>forms</folder>

Создайние XML-файла формы

Класс JForm используется для добавления двух новых элементов (input) в форму регистрации.

Класс JForm позволяет нам выполнить задачу 2-мя способами:

  • подгрузить поля посредством файла .xml
  • подгрузить поля посредством строки PHP в коде плагина

1-й способ предпочтительный и подходит во многих случаях.

Содержимое файла form.xml:

<?xml version="1.0" encoding="utf-8"?>
<form>
    <fieldset name="tos"
        label="PLG_USER_MYREGISTRATION2_TERMS_OF_SERVICE"
    >
        <field name="tos_agree" type="checkbox"
            default="0"
            filter="bool"
            label="PLG_USER_MYREGISTRATION2_AGREE"
            required="true"
            value="1"
        />
        <field name="old_enough" type="checkbox"
            default="0"
            filter="bool"
            label="PLG_USER_MYREGISTRATION2_AGE"
            required="true"
            value="1"
        />
    </fieldset>
</form>
Поля (field) имеют следующие атрибуты:

 

  • default: Default value (if unchecked)
  • filter: The filter used to check the input from this field
  • label: The label for this field (note that this will be translated)
  • required: Flag to tell JForm to make this field required
  • value: The value in the form when the checkbox is checked

Стандартных атрибутов HTML только 2: label и value.

Атрибут filter принимает значения равные фильтрам JHtml. Список правил (файлов) можно посмотреть здесь:

  • /libraries/joomla/form/rules - альтернативные?
  • метод clean() класса JFilterInput - основные
  • Appendix B

Возможные типы поля field:

  • /libraries/joomla/form/fields

(!) Важно. Благодаря атрибуту required класс JForm заменяет необходимость использовать ручную проверку требуемой отметки (checkbox) в методе onBeforeSave() плагина.

(!) Важно. "Контейнер" form требуется для правильного слияния.

Создание PHP - файла плагина

стр. 176

PHP будет следующим (часть):

public function onContentPrepareForm($form, $data)
{
    // Если нас нет в регистрационной форме, проигнорируйте форму.
    if ($form->getName() != 'com_users.registration') {
        return;
    }
 
    // Загрузите языковой файл плагина
    $this->loadLanguage();
 
    // Загрузите наш пользовательский xml - файл регистрации в форму регистрации пользователя.
    $form->loadFile(dirname(__FILE__).'/forms/form.xml');
}
Как видно, метод имеет название onContentPrepareForm и иные входящие аргументы. Это имя равное событию (ранее уже писал). Данное событие вызывается при подготовке формы JForm до ее вывода на экран.

 

Входящие в метод аргументы:

  • $form - содержит объект класса JForm
  • $data - содержит стандартный объект со всей информацией по форме

Интересен метод проверки нужной нам формы, чтобы плагин не запускался в других местах:

if ($form->getName() != 'com_users.registration') {
При подгрузке файла form.xml происходит слияние текущих полей формы с загруженными.

 

Добавление языковые файлы

стр. 178

Еще раз: .sys файл с переводом требуется для отображения плагина в списке плагинов или расширений.

Добавление параметров в наш плагин

стр. 179

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

Внесем изменения в .xml файл плагина:

<config>
    <fields name="params">
        <fieldset name="basic" >
            <field name="show_age_checkbox" type="radio"
                label="PLG_USER_MYREGISTRATION2_SHOW_AGE"
                description="PLG_USER_MYREGISTRATION2_SHOW_AGE_DESC"
                default="0">
                <option value="0">JHIDE</option>
                <option value="1">JSHOW</option>
            </field>
        </fieldset>
    </fields>
</config>
Используется тот же класс JForm. Элемент "fields" ключевой, иначе настройки не отобразятся при редактировании плагина.

 

(!) Важно. Почему именно "fields"? Ответ кроется в файле:

  • /administrator/components/com_plugins/views/plugin/tmpl/edit_options.php

Смотрим данный файл, а именно одна из 1-х строк:

$fieldSets = $this->form->getFieldsets('params');
Метод getFieldsets() класса JForm извлекает массив элементов "<fieldset />", которые располагаются внутри элемента "fields".

 

В нашем случае элемент "fields" должен иметь имя "params". Это касается только установочного .xml файла.

стр. 180

Языковые ключи JHIDE и JSHOW являются стандартными. Их можно использовать везде.

Для того, чтобы задаваемые настройки имели силу, вносим поправки в PHP файл:

$form->loadFile(dirname(__FILE__).'/forms/form.xml');
if (!$this->params->def('show_age_checkbox', '1')) {
    $form->removeField('old_enough');
}
Для удаления поля мы используем метод removeField() класса JForm.

 

Интересно. Параметр show_age_checkbox получается через метод get. Метод нет в классе JPlugin, нет в его родителе JEvent. Он находится в классе JObject, как и много других методов.

стр. 182

Стоит изучить методы класса JForm. К примеру, можно изменить атрибут required:

$form->setFieldAttribute('old_enough', 'required', 'false');

 

Использование плагинов для переопределения основных классов

Особенность: если плагин будет обычным скриптом, то он выполнится сразу, как будет вызван метод JPluginHelper::importPlugin().

(!) Важно: если класс существует и уже загружен в память, то повторная загрузка не произойдет.

Вывод: если подгрузить свой класс ранее стандартного, то он будет использоваться. Это и можно сделать в плагине.

Пример: Переопределить класс JTableNested

Примечание. Будет реализована перезапись класса JTableNested.

Данный класс является классом родителем для всех классов таблиц в Joomla, которые имеют вложенности.

Этапы перезаписи класса:

  • создаем плагин "myclasses" в папке /plugins/system/myclasses
  • копируем файл нужного класса в эту папку, к примеру: /libraries/joomla/database/tablenested.php
  • вносим в этот файл изменения
  • в плагине прописываем путь до класса

Содержание плагина:

defined('_JEXEC') or die;
// Заменить ядро JTableNested на версию переопределения
include_once JPATH_ROOT.'/plugins/system/myclasses/tablenested.php';

 

(!) Важно: не получится перезаписать класс, который подгружается ранее загрузки в память плагинов группы system.

Лучшие практики плагинов

стр. 186

Практика создания плагинов:

  • плагины запускаются в порядке, в котором расположены в менеджере плагинов;
  • ссылка по теме: http://docs.joomla.org/Plugin/Events

Глава 6. Расширение Joomla! с модулями

Экскурсия по Базовому модулю

стр. 188

Разберем структуру модуля на примере mod_users_latest.

  • tmpl/default.php - Module layout file (prints module output to browser)
  • helper.php - Contains methods for gathering module data
  • mod_users_latest.php - Entry point when module is executed
  • mod_users_latest.xml - XML install file and option field definition

Module XML File

Отличие файла .xml от плагина:

<filename module="mod_users_latest">mod_users_latest.php</filename>

(!) Важно: содержимое подпапок в установочном .xml включать не нужно.

Также в установочном файле модуля перечисляются языковые файлы:

<languages>
    <language tag="en-GB">en-GB.mod_users_latest.ini</language>
    <language tag="en-GB">en-GB.mod_users_latest.sys.ini</language>
</languages>

Еще один новый элемент:

<help key="JHELP_EXTENSIONS_MODULE_MANAGER_LATEST_USERS" />

Данная строка кода позволяет привязать статью помощи в системе Joomla к этому модулю.

В данном модуле используется 2 поля fieldset для настроек:

<fields name="params">
    <fieldset name="basic">
 
    </fieldset>
    <fieldset name="advanced">
 
    </fieldset>
</fields>

Main Module File

В "помощнике" модули обычно хранят методы для последующего отображения.

(!) Важно: как используется переменная $params, если она не определена? Модуль подключается с помощью "require" из класса JModuleHelper и метода renderModule(). Все переменные в области видимости данного метода доступны из модуля.

В последнюю очередь модуль подключает макет отображения:

require JModuleHelper::getLayoutPath('mod_users_latest', $params->get('layout', 'default'));

Summary по принципу работы модуля:

  • сначала загружается helper в память и его методы становятся доступны;
  • модуль вызывает методы помощника, чтобы получить данные;
  • модуль подключает макет для отображения.

Интересно: переменные в области видимости модуля становятся также доступны и для макета отображения.

Примечание: нужно быть аккуратным при выборе имен для переменных, используемых в модуле, т.к. они могут совпасть с другими переменными в области видимости. Имена можно без опасения использовать в методах.

Module Helper Class

стр. 192

Помощник подключается следующим образом:

require_once dirname(__FILE__).'/helper.php';

Следующая строка:

$db->getQuery(true)

создает для нас новый запрос. Если не передать true, будет возвращен последний используемый.

Класс JDatabaseQuery позволяет строить по частям запрос в любом порядке.

Show Articles by the Current Author

стр. 197

Создадим модуль на основе "Articles - Related Articles", который будет при открытии статьи выводить другие публикации автора. Администратор в настройках сможет установить количество выводимых статей и их порядок.

Module Structure

стр. 198

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

Module XML File

Элемент extension имеет следующие атрибуты:

  • type
  • version
  • client (site or administrator)
  • method

Данные атрибуты используются Джумлой в процессе установки.

Элемент files или "manifest" содержит список устанавливаемых файлов и папок. Это разрешение для их установки или удаления.

В данную директиву также нужно включить файл .xml:

<filename>mod_joompro_articles_author.xml</filename>

Это хорошая практика, хотя этого делать и не обязательно.

(!) Важно: атрибут validate поля field равный options позволяет сделать проверку для безопасности. Так, чтобы было выбрано действительно одно из значений списка (options), определенных в .xml модуля.

В fields с именем advanced должны идти по-умолчанию следующие значения:

  • Layout: The name of the optional alternative layout file (as discussed in Chapter 4)
  • Module Class Suffix: Optional CSS class suffix to allow styling for individual instances or modules
  • Caching: Whether or not to enable caching for this instance of the module
  • Cache Time: The number of seconds to use a cached copy of the module

Однако есть еще один параметр, который идет скрытым:

<field
    name="cachemode"
    type="hidden"
    default="static">
    <option
        value="static"></option>
</field>

В отличие от способа подключения помощника в предыдущем примере модуля, в данном случае это делается так:

JLoader::register('modJoomProArticlesAuthorHelper', dirname(__FILE__).'/helper.php');

Единственное отличие от метода JLoader::register() от require_once в том, что он работает быстрее.

Также можно использовать jimport.

Оптимизация: isset( $list[ 0 ] ) работает быстрее, чем count( $list ).

Helper File

стр. 204

Переменные из $_GET, $_POST и ... нужно получать через класс JRequest. Один из статических методов - JRequest::getCmd(). Данный метод фильтрует входное значение и оставляет только буквы, цифры, дефис и подчеркивание.

Пример использования для получения option и view из строки запроса браузера:

$option = JRequest::getCmd('option');
$view = JRequest::getCmd('view');

Текущий пользователь получается следующим образом (или гость):

$user = JFactory::getUser();

Крайне важен данный код:

// Get the levels as a comma-separated string
$levels = implode(',', $user->getAuthorisedViewLevels());

С помощью него получаются уровни доступа текущего пользователя. Они соединяются через запятую, чтобы впоследствии участвовать в SQL запросе в условии "WHERE value IN (1, 2, 3)" (пример).

(!) Важно: если в запросе необходимо использовать время, т.е. временную метку, то нужно воспользоваться методом JFactory::getDate(). Однако, полученное необходимо преобразовать в формат SQL с помощью метода toSql():

$date = JFactory::getDate();
$now = $date->toSql();

Пустое значение даты можно получить с помощью класса базы данных и метода getNullDate():

$db = JFactory::getDbo();
$nullDate = $db->getNullDate();

3 метода получения результатов запроса:

  • loadObject() returns the first row of the query as an object, where each column for the row is a field in the object.
  • loadObjectList() returns an array of all rows from the query where each array element is an object for that row.
  • loadResult() returns the first column from the first row of the query.

Есть и другие методы, которые можно посмотреть в классе JDatabase.

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

$query->clear();

В большинстве случаев в запросах достаточно использовать LEFT JOIN и INNER JOIN.

  • LEFT JOIN - при отсутствии совпадений во 2-й таблице, будут подставлены значения = NULL;
  • INNER JOIN - при отсутствии совпадений результаты из 1-й таблицы будут исключены.

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

if ($app->getLanguageFilter()) {
    query->where('a.language IN (' . $db->Quote(JFactory::getLanguage()->getTag()) . ',' . $db->Quote('*') . ')');
}

Ссылка для статьи формируется так:

$item->slug = $item->id.':'.$item->alias;
$item->catslug = $item->catid.':'.$item->cat_alias;
$item->link = JRoute::_(ContentHelperRoute::getArticleRoute($item->slug, $item->catslug));

(!) Важно: ссылка по сути строится самостоятельно в модуле. Статический метод getArticleRoute() лишь добавляет Itemid, если находит. Завершает исполнение метод JRoute::_(), который обращается к функции route текущего компонента. В нашем случае ContentBuildRoute() по адресу /components/com_content/router.php.

Language Files

стр. 217

Для языковых файлов в данной версии, как в .sys, так и .ini включены одинаковые ключи для просмотра отличий.

Validating Parameters in JForm

 

Check Values in Helper
стр. 218

Для напоминания:

<field name="count"
    type="text"
    default="5"
    label="MOD_JOOMPRO_ARTICLES_AUTHOR_FIELD_NUMBER_LABEL"
    description="MOD_JOOMPRO_ARTICLES_AUTHOR_FIELD_NUMBER_DESC">
</field>

Сейчас поле идет с типом "текст". Но нам нужно организовать проверку, чтобы число было больше 0 и не бесконечное и плюс было числом.

Можно сделать следующим образом:

<field
    name="count"
    type="integer"
    first="1"
    last="10"
    step="1"
    default="5"

(!) Важно: для того, чтобы посмотреть, какие аргументы принимает определенный тип поля следует обратиться к изучению файлов в данной папке:

  • /libraries/joomla/form/fields

Тип поля "integer" создаст список (select) со значениями от 1 до 10 с шагом 1.

Несмотря на удобство список не подходит для большого числа значений.

Integer Filter in JForm

Если указать тип filter как integer, а тип оставить "текстом":

<field
    name="count"
    type="text"
    filter="integer"
    default="5"
    label="MOD_JOOMPRO_ARTICLES_AUTHOR_FIELD_NUMBER_LABEL"
    description="MOD_JOOMPRO_ARTICLES_AUTHOR_FIELD_NUMBER_DESC">
</field>

то любое введенное значение, кроме числа будет удалено.

Custom JFormRule Class

стр. 220

Создадим свое правило проверки (validate):

<fieldset name="basic" addrulepath="modules/mod_joompro_articles_author">
<field
    name="count"
    type="text"
    validate="countinteger"
    filter="integer"
    default="5"
    label="MOD_JOOMPRO_ARTICLES_AUTHOR_FIELD_NUMBER_LABEL"
    description="MOD_JOOMPRO_ARTICLES_AUTHOR_FIELD_NUMBER_DESC">
</field>

с именем "countinteger".

Видно, что у поля fieldset появился новый атрибут addrulepath, который указывает путь к правилу.

В папке с модулем создаем файл countinteger:

jimport('joomla.form.formrule');
 
class JFormRuleCountInteger extends JFormRule
{
    public function test(& $element, $value, $group = null, & $input = null,& $form = null)
    {
        return ((int) $value > 0 && (int) $value <= 30);
    }
}

В аргументе $value передается значение поля в форме. Созданный класс является подклассом класса JFormRule.

Сейчас значения в методе test() заданы прямо. Их можно передать через .xml файл:

<field
    name="count"
    type="text"
    validate="countinteger"
    minimum="1"
    maximum="10"

Две последние строки определяют минимум и максимум. Изменяем метод test():

$max = (int) $element->getAttribute('maximum') ? $element->getAttribute('maximum') : 30;
$min = (int) $element->getAttribute('minimum') ? $element->getAttribute('minimum') : 1;
return ((int) $value >= $min && (int) $value <= $max);

Переменная $element содержит все входящие атрибуты файла .xml и имеет метод для их выборки getAttribute().

Validation Error Message
стр. 222

Для установки сообщения об ошибке достаточно добавить к полю field новый атрибут:

message="MOD_JOOMPRO_ARTICLES_AUTHOR_COUNTINTEGER_MESSAGE"

и поместить строку перевода в файл .ini.

Другой способ вернуть ошибку через объект JException:

// Build JException object
if ($result === false) {
    $result = new JException(JText::sprintf('MOD_JOOMPRO_ARTICLES_AUTHOR_COUNTINTEGER_MESSAGE', $min, $max));
}

Help File

стр. 225

Ранее в .xml файл была включена следующая строка:

<help url="HELP_EXTENSIONS_MODULE_JOOMPRO_ARTICLES_AUTHOR_URL" />

Данный элемент должен идти после перечисления основных файлов и языковых. Так как языковых нет, то сразу после основных.

Chapter 7. Components Part I: Controllers and Models

стр. 230

Back- End Weblinks Component

Далее будет рассмотрен стандартный компонент Weblinks.

Папка компонента:

/administator/components/com_weblinks

Список файлов закрытой части:

File Name Description MVC Group
controllers/weblink.php Primary controller for editing a single Weblink Controller
controllers/weblinks.php Primary controller for the Weblinks Manager
list
Controller
helpers/weblinks.php Provides miscellaneous methods used
by the controllers and views
Miscellaneous
models/fields/ordering.php Provides a custom JFormField to show
the Weblinks ordering column in the
Weblinks Manager
Model
models/forms/weblink.xml XML file used by JForm to provide the
fields for the add/edit weblink screen
Model
models/weblink.php Model for the single Weblink screen Model
models/weblinks.php Model for the Weblinks Manager screen Model
sql/install.mysql.utf8.sql SQL file for creating the Weblinks table
during installation
Installation
sql/uninstall.mysql.ut8.sql SQL file for dropping (deleting) the Weblinks
table during uninstall
Installation
tables/weblink.php Provides the WeblinksTableWeblink class Model
views/weblink/tmpl/edit_metadata.php Default layout file for editing the Weblink
metadata
View
views/weblink/tmpl/edit_params.php Default layout file for editing single Weblink
options
View
views/weblink/tmpl/edit.php Default layout file for editing a Weblink View
views/view.html.php Primary view class for HTML output for
single Weblink
View
views/weblinks/tmpl/default.php Default layout file for Weblinks Manager View
views/weblinks/view.html.php Primary view class for HTML output for
Weblinks Manager
View
access.xml XML file to provide the list of actions for
the ACL
com_config file
config.xml XML file to provide the list of options for
the component configuration
com_config file
controller.php Primary controller class Controller
weblinks.php Entry point for the request Controller
weblinks.xml XML file to control installation process Installation

Components Menu

Часть файла weblinks.xml, отвечающая за создания меню в Закрытой части:

<menu img="class:weblinks">com_weblinks</menu>
<submenu>
    <!--
        Note that all & must be escaped to & for the file to be valid
        XML and be parsed by the installer
    -->
    <menu link="option=com_weblinks" view="links" img="class:weblinks"
        alt="Weblinks/Links">com_weblinks_links</menu>
    <menu link="option=com_categories&extension=com_weblinks"
        view="categories" img="class:weblinks-cat" alt="Weblinks/Categories">com_weblinks_categories</menu>
</submenu>

(!) Важно: в установочном .xml файле все знаки "&" должны быть заменены на "&", чтобы XML был валидным.

Component Options (Parameters)

Файл config.xml позволяет задавать Компоненту настройки. Каждый fieldset является отдельным пунктом настроек. По соглашению, последними идут "правила":

<fieldset name="permissions"
    description="JCONFIG_PERMISSIONS_DESC"
    label="JCONFIG_PERMISSIONS_LABEL"
>
    <field name="rules" type="rules"
        component="com_weblinks"
        filter="rules"
        validate="rules"
        label="JCONFIG_PERMISSIONS_LABEL"
        section="component" />
</fieldset>

Helper Methods

Файл /helpers/weblinks.php. В помощник обычно выводятся небольшие методы, которые не подходят ни одному из пунктов MVC.

Метод addSubmenu() создает дополнительное меню: "Web Links | Categories".

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

Weblinks Component Entry Point

стр. 235

Запуск компонента начинается с файла weblinks.php в корне основной папки. Сначала идет проверка прав пользователя:

if (!JFactory::getUser()->authorise('core.manage', 'com_weblinks')) {
    return JError::raiseWarning(404, JText::_('JERROR_ALERTNOAUTHOR'));
}

Полномочия "core.manage" позволяют выполнять действия в Закрытой части.

Далее включается класс JController:

jimport('joomla.application.component.controller');

Последние три строчки всегда одинаковые для каждого компонента:

$controller = JController::getInstance('Weblinks');
$controller->execute(JRequest::getCmd('task'));
$controller->redirect();

В 1-й получается контроллер текущего компонента. Далее в контроллер отправляется задача, если есть. Последняя строка отвечает за перенаправление, если должно быть.

Weblinks Controller in Action

Example 1: User Selects Components - > Weblinks Menu Option

При запуске "administrator/index.php?option=com_weblinks" в строке нет задачи (task), поэтому выполняется стандартная задача "display" (метод display() контроллера controller.php, что в корне).

Example 2: User Clicks a Weblink Title To Edit

  • administrator/index.php?option=com_weblinks&task=weblink.edit&id=7
  • задача: weblink.edit
  • контроллер: controllers/weblink.php
  • запускается метод: JControllerForm->edit()
  • перенаправление: index.php?option=com_weblinks&view=weblink&layout=edit&id=7

Имя Класса контроллера составляется так:

<component name> + Controller + <first segment of task name>

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

Далее идет 2-й круг обработки запроса:

  • administrator/index.php?option=com_weblinks&view=weblink&layout=edit&id=7
  • задачи нет (значит display())
  • контроллер: WeblinksController (controller.php)
  • метод: WeblinksController->edit()
  • без перенаправления

Заключение: процесс разделяется на 2 этапа - 1. мы проверяем права пользователя на доступ к редактированию и сохраняем id элемента; 2. отображаем форму редактирования.

Example 3: User Clicks Save & Close In Edit Form

стр. 240

  • administrator/index.php?option=com_weblinks&layout=edit&id=7
  • задача: weblink.save
  • контроллер: WeblinksControllerWeblink (controllers/weblink.php)
  • метод: JControllerForm->save()
  • перенаправление: administrator/index.php?option=com_weblinks&view=weblinks

В данном случае задача получается не из ссылки. Кнопки сохранения имеют следующий атрибут:

onclick="javascript:Joomla.submitbutton('weblink.save')"

JS помещает задачу в форму в поле "task".

Example 4: User Trashes Some Weblinks

  • administrator/index.php?option=com_weblinks&view=weblinks
  • задача: weblinks.trash
  • контроллер: WeblinksControllerWeblinks (controllers/weblinks.php)
  • метод: JControllerAdmin->publish()
  • перенаправление: administrator/index.php?option=com_weblinks&view=weblinks

Задача вновь отправляется посредством JS:

onclick="javascript:
if (document.adminForm.boxchecked.value==0){
    alert('Please first make a selection from the list');
} else {
    Joomla.submitbutton('weblinks.trash')
}"

Примечание: панель с кнопками Закрытой части отображается с помощью модуля mod_toolbar, класс JButtonStandard (/libraries/joomla/html/toolbar/button/standard.php).

Метод publish() класса JControllerAdmin содержит интересную строчку:

JRequest::checkToken() or die(JText::_('JINVALID_TOKEN'));

Данный код проверяет действительно ли мы пришли после отправки формы или нет.

стр. 241

Стоит вернуться к "Mapping Tasks to Methods", потому что довольно непонятно.

стр. 245

Сначала сложно разобраться, как работает код. Вызывается метод publish(), как было написано. Данный метод класса JControllerAdmin имеет дополнительные параметры, схематично:

array('publish' => 1, 'unpublish' => 0, 'archive'=> 2, 'trash' => -2, 'report'=> -3);

В методе publish() вызывается метод getModel(), который определен в классе WeblinksControllerWeblinks и в родительском классе JController. Однако, сам метод publish() вызывается в то время, как мы находимся в классе контроллера WeblinksControllerWeblinks, потому что в нем нет подобного метода. Однако, есть метод getModel(). В итоге вызывается метод getModel() класса JController, но уже с заданными настройками:

public function getModel($name = 'Weblink', $prefix = 'WeblinksModel', $config = array('ignore_request' => true))
{
    $model = parent::getModel($name, $prefix, $config);
    return $model;
}

стр. 247

Данная строка кода проверяет состоит ли массив только из целых чисел или нет:

JArrayHelper::toInteger($cid);

Weblinks Controller Tasks, Classes, and Methods

стр. 251

На этой странице можно посмотреть полный список функций классов контроллеров Weblinks и Weblink.

Weblinks Models

стр. 252

Как контроллер узнает с какой моделью он связан? В контроллере WeblinksControllerWeblinks модель задается с помощью метода getModel():

public function getModel($name = 'Weblink', $prefix = 'WeblinksModel', $config = array('ignore_request' => true))
{
    $model = parent::getModel($name, $prefix, $config);
    return $model;
}

В родительском классе JControllerAdmin метода getModel() нет, поэтому запускается метод родительский класса JControllerAdmin - JController.

Что же касается контроллера WeblinksControllerWeblink, то в данном случае имя модели берется посредством метода getModel() родительского класса JControllerForm:

public function getModel($name = '', $prefix = '', $config = array('ignore_request' => true))
{
    if (empty($name))
    {
        $name = $this->context;
    }
    return parent::getModel($name, $prefix, $config);
}

Переменная $name (имя метода) получается так: из имени класса WeblinksControllerWeblink берется текст, который следует за словом Controller. Получается Weblink.

Далее к имени модели прибавляется префикс model_prefix. Он получается 2-мя способами:

  1. задается в переменной $config
  2. берется из 1-й части имени класса

стр. 255

При вызове метода publish() (и других также) происходит проверка прав пользователя к возможности редактирования отдельного элемента с помощью метода canEditState():

protected function canEditState($record)
{
    $user = JFactory::getUser();
    return $user->authorise('core.edit.state', $this->option);
}

Пример вызова из метода publish() класса JModelAdmin (WeblinksModelWeblink):

// Access checks.
foreach ($pks as $i => $pk)
{
    $table->reset();
 
    if ($table->load($pk))
    {
        if (!$this->canEditState($table))
        {
            // Prune items that you can't change.
            unset($pks[$i]);
            JError::raiseWarning(403, JText::_('JLIB_APPLICATION_ERROR_EDITSTATE_NOT_PERMITTED'));
            return false;
        }
    }
}

Метод проверки прав срабатывает для каждой обрабатываемой строки из БД.

В классе WeblinksModelWeblink определен свой метод canEditState():

protected function canEditState($record)
{
    $user = JFactory::getUser();
 
    if (!empty($record->catid)) {
        return $user->authorise('core.edit.state', 'com_weblinks.category.'.(int) $record->catid);
    }
    else {
        return parent::canEditState($record);
    }
}

Метод authorise($action, $assetname = null) определен в классе JUser. К примеру, для категорий $assetname формируется так:

<component name> + .category. + <category id>

Если текущая запись без категории, то вызывается родительский метод canEditState() класса JModelAdmin:

return $user->authorise('core.edit.state', $this->option);

$this->option = "com_weblinks" в нашем случае.

Перед окончание работы метода publish() при успехе очищается кэш для компонента:

$this->cleanCache();

Стоит отметить, как происходит выборка данных из БД:

$table = $this->getTable();

Метод getTable() определен в классе модели Weblink:

public function getTable($type = 'Weblink', $prefix = 'WeblinksTable', $config = array())
{
    return JTable::getInstance($type, $prefix, $config);
}

Как видно вызов происходит через статический метод класса JTable.

Model save() Method

стр. 258

Речь пойдет о сохранении Weblink элемента после редактирования в новом окне.

После сохранения вызывается метод save() класса JControllerForm (родительского класса WeblinksControllerWeblink). Говорим о контроллере пока!

Важная проверка:

$validData = $model->validate($form, $data);

Видно, что идет обращение к модели, значит метод validate() должен быть в WeblinksModelWeblink, его там нет. Поднимаемся выше по родительскому дереву. В JModelAdmin также нет, метод есть выше в классе JModelForm (вырезка):

$data = $form->filter($data);
$return = $form->validate($data, $group);

Идея фильтрования заключается в противодействии ввода вредоносного содержания.

Валидация позволяет определить принадлежность введенного заданной группе. К примеру, email должен содержать знак "@" и имя домена.

Weblinks Table Class

стр. 259

Table load() Method

Как было ранее написано метод getTable() модели Weblink определяет нахождение класса компонента для работы с БД. Это класс WeblinksTableWeblink, который лежит в папке tables.

Table bind() Method

Можно вернуться, чтобы посмотреть, как работает метод bind(), как происходит обработка параметров JSON.

Table check() Method

Проверка данных перед добавлением в базу данных. Для каждого компонента уникальна.

Chapter 8.Components Part II: Views, JForm, and Front End

стр. 263

Views and the display() Method

Weblinks View

Метод display() находится в основной контроллере в корне папки с компонентом. К контроллеру подключается помощник:

require_once JPATH_COMPONENT.'/helpers/weblinks.php';

Далее контроллер получается текущий вид, шаблон и ID ссылки:

$view       = JRequest::getCmd('view', 'weblinks');
$layout     = JRequest::getCmd('layout', 'default');
$id     = JRequest::getInt('id');

После, если не идет редактирования отдельной ссылки код пропускается:

if ($view == 'weblink' && $layout == 'edit' && !$this->checkEditId('com_weblinks.edit.weblink', $id)) {

В завершение вызывается метод display() родительского класса JController:

parent::display();

Примечание: данные 3 строки кода:

$controller = JController::getInstance('Weblinks');
$controller->execute(JRequest::getCmd('task'));
$controller->redirect();

можно заменить на:

JController::getInstance('Weblinks')->execute(JRequest::getCmd('task')->redirect();

Это можно сделать потому, что в PHP появилась возможность возвращать объект. Таким образом, после редактирования возвращается объект $this. К примеру, в методе display():

return $this;

JController display() Method

стр. 265

Родительский метод display() получает вид и затем модель на основе вида. Таким образом вид взаимодействует с моделью.

Глобальная конфигурация сохраняется в переменной $config.

Метод display() класса JController загружает либо кэшированный вид, либо новую версию. Если кэш не используется на данном шаге, то вызывается метод display() выбранного вида:

$view->display();

WeblinksViewWeblinks display() Method

Класс вида WeblinksViewWeblinks образован от класса JView. В методе display() получаются необходимые переменные из модели:

$this->state     = $this->get('State');
$this->items     = $this->get('Items');
$this->pagination    = $this->get('Pagination');

После добавляется Тулбар и вызывается шаблон:

$this->addToolbar();
parent::display($tpl);

Метод addToolbar() находится в том же классе компонента WeblinksViewWeblinks.

Имя метода в виде, которое ищется в модели, образуется следующим образом:

get + State

Модель состоит из классов WeblinksModelWeblinks, JModelList, JModel.

Панель с кнопками создается благодаря модулю Закрытой части mod_toolbar.

Метод display() класса JView довольно прост:

public function display($tpl = null)
{
    $result = $this->loadTemplate($tpl);
    if ($result instanceof Exception)
    {
        return $result;
    }
    echo $result;
}

(!) Важно: как можно видеть, здесь используется не return, а echo. Т.е. результат исполнения метода выводится на экран. Перед выполнением компонента включается буфер ob_start().

Метод loadTemplate() делает проверку на наличие override шаблонов. Коротко:

// Start capturing output into a buffer
ob_start();
 
// Include the requested template filename in the local scope
// (this will execute the view logic).
include $this->_template;
 
// Done with the requested template; get the buffer and
// clear it.
$this->_output = ob_get_contents();
ob_end_clean();
 
return $this->_output;

Default Layout File

стр. 267

Речь пойдет о настройке Панели с кнопками.

Housekeeping

Шаблон default.php:

/administrator/components/com_weblinks/views/weblinks/tmpl

для отображения списка ссылок включает в себя следующие директивы:

JHtml::addIncludePath(JPATH_COMPONENT.'/helpers/html');
JHtml::_('behavior.tooltip');
JHtml::_('behavior.multiselect');
 
$user       = JFactory::getUser();
$userId     = $user->get('id');
$listOrder  = $this->escape($this->state->get('list.ordering'));
$listDirn   = $this->escape($this->state->get('list.direction'));
$canOrder   = $user->authorise('core.edit.state', 'com_weblinks.category');
$saveOrder  = $listOrder == 'a.ordering';

1-я строчка означает, что можно включить свой путь к HTML помощника. В компоненте com_weblinks данной папки нет.

(!) Важно: создав файл с классом JHtml + <component name> в папке /helpers/html, например, JHtmlWeblinks, можно переназначить методы JHtml. К примеру, метод myGrid() в классе JHtmlWeblinks станет возможным вызвать с помощью JHtml::_('weblinks.mygrid', 'argument1', 'argument2').

Данная строчка:

JHtml::_('behavior.tooltip')

добавляет MooTools JavaScript к нашей странице.

Следующая строчка включает файл JS multiselect.js:

JHtml::_('script','system/multiselect.js', false, true);

(!) Важно: в новой версии Joomla данный файл и встроенные функции подгружаются иным способом:

JHtml::_('behavior.multiselect');

Переменная $this в шаблоне (этот класс) относится к текущему классу вида WeblinksViewWebinks.

Важно, как задается параметр action формы:

<form action="<?php echo JRoute::_('index.php?option=com_weblinks&view=weblinks'); ?>" method="post" name="adminForm" id="adminForm">

Section A: Title Filter

Section B: Filter Select Lists

Следующая строка:

<?php echo JHtml::_('select.options', JHtml::_('jgrid.publishedOptions'), 'value', 'text', $this->state->get('filter.state'), true);?>

Вызывает методы JHtmlSelect::options() и JHtmlJGrid::publishedOptions().

Данный код:

$this->state->get('filter_state')

возвращает текущее значения параметра "публикация" (в Архиве, в Корзине...).

Результат выполнения 2-х строк выше будет:

<select onchange="this.form.submit()" class="inputbox" name="filter_published">
    <option value="">- Select Status - </option>
    <option value="1">Published</option>
    <option value="0">Unpublished</option>
    <option value="2">Archived</option>
    <option value="- 2">Trashed</option>
    <option value="*">All</option>
</select>

Каждый фильтр на странице строится одинаково с тем отличием, что используются разные методы JHtml:

  • JHtmlCategory::options() builds the list of categories.
  • JHtmlAccess::assetgroups() builds the options list of access levels.
  • JHtmlContentLanguage::existing() builds the options list of available languages.

Атрибут формы onchange равный "this.form.submit()" перезагружает страницу при выборе значения.

Section C: Check All Box

Для выделения всех checkbox используется встроенный метод JS Джумлы:

<input type="checkbox" name="checkall- toggle" value="" onclick="Joomla.checkAll(this)" />

Посмотреть функции можно в файле:

/media/system/js/core.js

Section D: Sortable Column Headings

Один из тегов TH таблицы:

<?php echo JHtml::_('grid.sort', 'JGLOBAL_TITLE', 'a.title', $listDirn, $listOrder); ?>

Метод JHtmlGrid::sort() имеет следующие настройки:

  • title: "JGLOBAL_TITLE"
  • order field: "a.title" (title columfrom #__weblinks table)
  • current sort direction: $listDir(set at the start of this file)
  • selected ordering: $listOrder (set at the start of this file)

Данный метод помещает в тег TH ссылку с названием колонки, по которой можно производить сортировку данных.

Section E: Weblink Items

Список ссылок выводится в цикле:

<?php foreach ($this- >items as $i => $item) :

1-я колонка с checkbox строится на основе JHtmlGrid::id(). Пример строки:

<input type="checkbox" id="cb2" name="cid[]" value="5" onclick="Joomla.isChecked(this.checked);" title="Checkbox for row 3" />

Как видно, имя "cid[]", но уникальный id.

Колонка с именем ссылки строится так:

<?php echo JHtml::_('jgrid.checkedout', $i, $item->editor, $item->checked_out_time, 'weblinks.', $canCheckin); ?>

Перед данной строчкой стоит условие:

<?php if ($item->checked_out) : ?>

Примечание: если "редактор" вышел из элемента без сохранения, то будет отображена строчка выше. Произойдет проверка, закончил ли редактор правку или нет. Если нет, то будет показан замок.

Переменная $canCheckin содержит boolean, отвечающий на вопрос имеет ли текущий пользователь права на правку.

Кнопки Публикации задаются так:

<?php echo JHtml::_('jgrid.published', $item->state, $i, 'weblinks.', $canChange, 'cb', $item->publish_up, $item->publish_down); ?>

Имя категории выводится через метод escape():

<?php echo $this->escape($item->category_title); ?>

(!) Важно: все, что вводит пользователь необходимо выводить через данный метод.

По-умолчанию подклассы класса JView имеют метод escape() с помощью функции PHP htmlspecialchars(). Однако, данный метод можно изменить задав переменную $_escape в новом классе (к примеру, в конструкторе).

Пропускаю: строка сортировки.

Section F: Pagination Controls

Для разбивки результатов:

<?php echo $this->pagination->getListFooter(); ?>

Используются методы класса JPagination.

WeblinksViewWeblink View

стр. 275

Вид: /views/weblink/view.html.php

Форма получается довольно просто:

$this->form      = $this->get('Form');

При вызове метода get() используется JView::get(). Имя метода может складываться как "get + method name".

Using JForm in Weblinks

WeblinksModel getForm() Method

стр. 276

При получении вида из формы ($this->get('Form')) вызывается метод getForm() класса JForm. Изначально вызывается метод getForm() из класса модели WeblinksModelWeblink:

$form = $this->loadForm('com_weblinks.weblink', 'weblink', array('control' => 'jform', 'load_data' => $loadData));

Все это необходимо, чтобы загрузить форму согласно нашему .xml файлу:

/administrator/components/com_weblinks/models/forms/weblink.xml

Метод getForm() класса JForm вызывает метод loadFormData() для получения данных формы. Данный метод существует в классе модели и поэтому вызывается именно он вместо метода текущего класса. Если массив с элементом уже существует, то берутся сохраненные данные (кэш), если нет, то вызывается метод getItem().

Далее метод loadForm() класса JForm вызывает плагины:

// Allow for additional modification of the form, and events to be triggered.
// We pass the data because plugins may require it.
$this->preprocessForm($form, $data);

Триггер срабатывает на событие: "onContentPrepareForm" и плагины группы "content".

Saving the JForm Object in Memory

До того, как метод loadForm() начинает загрузку формы, он проверяет не находится ли она уже в памяти:

// Create a signature hash.
$hash = md5($source . serialize($options));
 
// Check if we can use a previously loaded form.
if (isset($this->_forms[$hash]) && !$clear)
{
    return $this->_forms[$hash];
}

Переменная $options содержит следующие значения для ссылки с ID=1:

Array ( [control] => jform [load_data] => 1 )

Переменная $sourse пустая. Из этих данных складывается $hash переменная, по которой определяется нахождение элемента в памяти.

В завершение метод loadForm() сохраняет текущую форму в память:

// Store the form for later.
$this->_forms[$hash] = $form;

Modifying Forms Dynamically

Форма, которая создается с помощью файла .xml может быть изменена через PHP. Один из методов: setFieldAttribute(). Т.к. метод loadForm() возвращает нам объект, то метод вызывается так: $form->setFieldAttribute(). Например:

// Modify the form based on access controls.
if (!$this->canEditState((object) $data)) {
    // Disable fields for display.
    $form->setFieldAttribute('ordering', 'disabled', 'true');
    $form->setFieldAttribute('state', 'disabled', 'true');
    $form->setFieldAttribute('publish_up', 'disabled', 'true');
    $form->setFieldAttribute('publish_down', 'disabled', 'true');
 
    // Disable fields while saving.
    // The controller has already verified this is a record you can edit.
    $form->setFieldAttribute('ordering', 'filter', 'unset');
    $form->setFieldAttribute('state', 'filter', 'unset');
    $form->setFieldAttribute('publish_up', 'filter', 'unset');
    $form->setFieldAttribute('publish_down', 'filter', 'unset');
}

Если пользователь не имеет доступа к форме, то фильтры устанавливаются в unset, а поля делаются неактивными.

Данный метод проверки:

$this->canEditState((object) $data)

на самом деле проверяет доступ к компоненту com_weblinks, так как переменная $data содержит пустой массив.

Rendering the JForm

Класс JForm использует файл edit.php и edit_params.php для отображения формы. Файлы лежат в папке /weblink/tmpl. В новой версии Джумлы также есть файл edit_metadata.php.

Edit.php File

Начало файла:

JHtml::addIncludePath(JPATH_COMPONENT.'/helpers/html');
JHtml::_('behavior.tooltip');
JHtml::_('behavior.formvalidation');
?>
<script type="text/javascript">
    Joomla.submitbutton = function(task)
    {
        if (task == 'weblink.cancel' || document.formvalidator.isValid(document.id('weblink-form'))) {
            <?php echo $this->form->getField('description')->save(); ?>
            Joomla.submitform(task, document.getElementById('weblink-form'));
        }
        else {
            alert('<?php echo $this->escape(JText::_('JGLOBAL_VALIDATION_FORM_FAILED'));?>');
        }
    }
</script>

Сначала подгружается "помощник". Далее необходимые JS файлы.

Строка:

<?php echo $this->form->getField('description')->save(); ?>

возвращает:

if (tinyMCE.get("jform_description").isHidden()) {tinyMCE.get("jform_description").show()}; tinyMCE.get("jform_description").save();

Для отображения формы применяются 2 метода: getLabel() и getInput().

Интересна 1-я строка формы:

<form action="<?php echo JRoute::_('index.php?option=com_weblinks&layout=edit&id='.(int) $this->item->id); ?>" method="post" name="adminForm" id="weblink-form" class="form-validate">

Данные строки:

<?php echo JHtml::_('sliders.start', 'weblink-sliders-'.$this->item->id, array('useCookie'=>1)); ?>
<?php echo JHtml::_('sliders.panel', JText::_('JGLOBAL_FIELDSET_PUBLISHING'), 'publishing-details'); ?>

отвечают за построения "слайдеров", т.е. возможности по нажатию на Заголовок скрывать и разворачивать внутренний текст.

Текущая позиция "слайдеров" хранится в COOKIE. При перезагрузке странице позиция сохраняется.

Метод JHtmlSliders::start() создает область со слайдерами.

Метод JHtmlSliders::panel() создает панель, т.е. отдельную зону с заголовком.

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

<?php echo $this->loadTemplate('params'); ?>
<?php echo $this->loadTemplate('metadata'); ?>

В подгружаемых файлах форма строится динамически на основе .xml файла.

Сначала получаются все элементы fieldset по имени и выводятся в цикле:

$fieldSets = $this->form->getFieldsets('params');
foreach ($fieldSets as $name => $fieldSet) :

Front-End Weblinks Component

стр. 288

Similar Folder Structure and MVC Pattern

Структура папок Открытой части компонента Weblinks:

File Name Contents MVC Group
controllers Controller for the entry screen: weblink.php Controller
helpers Help files: category.php, icon.php, route.php Helper / Misc.
models Models: categories.php, category.php, form.php, weblink.php Model
models/forms JForm XML file: weblink.xml Model
views/categories View for the Categories Menu Item: view.html.php View
views/categories/tmpl Layout files for the categories menu item; XML file for menu item options View
views/category Views for the single category Menu Item: view.html.php and view.feed.php View
views/category/tmpl Layout files for the single category menu item; XML file for menu item options View
views/form View for submit Weblink menu item: view.html.php View
views/form/tmpl Layout files for the entry form; XML file for menu item options View
controller.php Default controller for display task Controller
router.php Component router Helper / Misc.
weblinks.php Component entry point script Controller

Основной файл weblinks.php в корне компонента Открытой части равносилен по содержанию к Закрытой части:

$controller = JControllerLegacy::getInstance('Weblinks');
$controller->execute(JRequest::getCmd('task'));
$controller->redirect();

Как и в Закрытой части модель существует для каждого вида:

  • Categories: Shows all the Weblinks categories in a hierarchical list
  • Category: Shows the Weblinks in a single category
  • Form: Displays the submit Weblink form

остановился на 320 стр.