Часть 15 - добавляем состояние записи
В Joomla для состояния записи используются следующие значения:
- опубликовано – 1
- не опубликовано – 0
- в архиве – 2
- в корзине – -2
В этой части мы добавим только первые два. В следующей части (когда будем рассматривать добавление фильтров) ещё два, потому что без фильтров невозможно работать с записями, состояние которых отлично от «опубликовано» и «не опубликовано».
Для добавления состояния записи (сообщения) нам необходимо внести изменения, которые коснутся практически всего компонента.
Изменяем SQL файлы
Для поддержки состояния сообщения необходимо изменить структуру таблицы #__helloworld
и добавить поле state
.
Вносим изменения в файл admin/sql/install.mysql.utf8.sql:
DROP TABLE IF EXISTS `#__helloworld`;CREATE TABLE `#__helloworld` ( `id` int(11) NOT NULL AUTO_INCREMENT, `asset_id` INT(10) NOT NULL DEFAULT '0', `greeting` varchar(25) NOT NULL, `state` tinyint(1) NOT NULL DEFAULT '0', `catid` int(11) NOT NULL DEFAULT '0', `ordering` INT(11) NOT NULL DEFAULT '0', `params` TEXT NOT NULL DEFAULT '', PRIMARY KEY (`id`) ) ENGINE=MyISAM AUTO_INCREMENT=0 DEFAULT CHARSET=utf8; INSERT INTO `#__helloworld` (`greeting`) VALUES ('Hello World!'), ('Good bye World!');
Не забудьте создать файл admin/sql/updates/mysql/0.0.15.sql для изменения структуры таблицы во время установки новой версии компонента:
ALTER TABLE `#__helloworld` ADD `state` tinyint(1) NOT NULL DEFAULT '0' AFTER `greeting`;
Добавляем действие core.edit.state
В правах доступа мы не учитывали состояние сообщения для уровня записи, поэтому добавим действие core.edit.state
в раздел 'message' в файл admin/access.xml:
<action name="core.edit.state" title="JACTION_EDITSTATE" description="COM_HELLOWORLD_ACCESS_EDITSTATE_COMPONENT_DESC" />
Изменяем модель списка сообщений
Открываем файл admin/models/helloworlds.php и вносим два изменения.
Первое, в конструкторе добавим поле state
в список валидных полей для сортировки:
/** * Конструктор. * * @param array $config Массив с конфигурационными параметрами. */ public function __construct($config = array()) { // Добавляем валидные поля для фильтров и сортировки. if (empty($config['filter_fields'])) { $config['filter_fields'] = array( 'id', 'id', 'greeting', 'greeting', 'state', 'state', 'ordering', 'ordering', ); } parent::__construct($config); }
Второе, добавляем поле state
в запрос select
:
// Выбераем поля. $query->select('id, greeting, state, ordering');
Изменяем представление списка сообщений
В представление списка сообщений admin/views/helloworlds/view.html.php нам необходимо добавить две кнопки в панель инструментов. Вносим изменения в метод addToolBar()
:
/** * Устанавливает панель инструментов. * * @return void */ protected function addToolBar() { JToolBarHelper::title(JText::_('COM_HELLOWORLD_MANAGER_HELLOWORLDS'), 'helloworld'); if ($this->canDo->get('core.create')) { JToolBarHelper::addNew('helloworld.add'); } if ($this->canDo->get('core.edit')) { JToolBarHelper::editList('helloworld.edit'); } if ($this->canDo->get('core.edit.state')) { JToolBarHelper::divider(); JToolbarHelper::publish('helloworlds.publish', 'JTOOLBAR_PUBLISH', true); JToolbarHelper::unpublish('helloworlds.unpublish', 'JTOOLBAR_UNPUBLISH', true); } if ($this->canDo->get('core.delete')) { JToolBarHelper::divider(); JToolBarHelper::deleteList('', 'helloworlds.delete'); } if ($this->canDo->get('core.admin')) { JToolBarHelper::divider(); JToolBarHelper::preferences('com_helloworld'); } }
Здесь мы добавили вызовы двух методов:
JToolbarHelper::publish(helloworlds.publish', 'JTOOLBAR_PUBLISH', true); JToolbarHelper::unpublish(helloworlds.unpublish', 'JTOOLBAR_UNPUBLISH', true);
Изменяем шаблоны списка сообщений
Сначала изменим суб-шаблон заголовков списков admin/views/helloworlds/tmpl/default_head.php:
<?php // Запрет прямого доступа. defined('_JEXEC') or die; // Данные по сортировке. $listOrder = $this->escape($this->state->get('list.ordering')); $listDirn = $this->escape($this->state->get('list.direction')); ?> <tr> <th width="1%"> <input type="checkbox" name="checkall-toggle" value="" title="<?php echo JText::_('JGLOBAL_CHECK_ALL'); ?>" on-click="Joomla.checkAll(this)" </th> <th> <?php echo JHtml::_('grid.sort', 'COM_HELLOWORLD_HELLOWORLD_HEADING_GREETING', 'greeting', $listDirn, $listOrder); ?> </th> <th width="10%"> <?php echo JHtml::_('grid.sort', 'JSTATUS', 'state', $listDirn, $listOrder); ?> </th> <th width="10%"> <?php echo JHtml::_('grid.sort', 'JGRID_HEADING_ORDERING', 'ordering', $listDirn, $listOrder); ?> <?php if ($listOrder == 'ordering') : ?> <?php echo JHtml::_('grid.order', $this->items, 'filesave.png', 'helloworlds.saveorder'); ?> <?php endif; ?> </th> <th width="1%"> <?php echo JHtml::_('grid.sort', 'COM_HELLOWORLD_HELLOWORLD_HEADING_ID', 'id', $listDirn, $listOrder); ?> </th> </tr>
Мы добавили вывод заголовка "Состояние":
<th width="10%"> <?php echo JHtml::_('grid.sort', 'JSTATUS', 'state', $listDirn, $listOrder); ?> </th>
Далее изменяем суб-шаблон admin/views/helloworlds/tmpl/default_body.php:
<?php // Запрет прямого доступа. defined('_JEXEC') or die; // Данные по сортировке. $listDirn = $this->escape($this->state->get('list.direction')); $listOrder = $this->escape($this->state->get('list.ordering')); $saveOrder = $listOrder == 'ordering'; foreach ($this->items as $i => $item) : $canEdit = JFactory::getUser()->authorise('core.edit', 'com_helloworld.message.' . $item->id); $canChange = JFactory::getUser()->authorise('core.edit.state', 'com_helloworld.message.' . $item->id); ?> <tr> <td> <?php echo JHtml::_('grid.id', $i, $item->id); ?> </td> <td> <?php if ($canEdit) : ?> <a href="/<?php echo JRoute::_('index.php?option=com_helloworld&task=helloworld.edit&id=' . (int) $item->id); ?>"> <?php echo $item->greeting; ?> </a> <?php else: ?> <?php echo $item->greeting; ?> <?php endif; ?> </td> <td> <?php echo JHtml::_('jgrid.published', $item->state, $i, 'helloworlds.', $canChange); ?> </td> <td> <?php if ($saveOrder) : ?> <?php if ($listDirn == 'asc') : ?> <span><?php echo $this->pagination->orderUpIcon($i, true, 'helloworlds.orderup', 'JLIB_HTML_MOVE_UP', $saveOrder); ?></span> <span><?php echo $this->pagination->orderDownIcon($i, $this->pagination->total, true, 'helloworlds.orderdown', 'JLIB_HTML_MOVE_DOWN', $saveOrder); ?></span> <?php elseif ($listDirn == 'desc') : ?> <span><?php echo $this->pagination->orderUpIcon($i, true, 'helloworlds.orderdown', 'JLIB_HTML_MOVE_UP', $saveOrder); ?></span> <span><?php echo $this->pagination->orderDownIcon($i, $this->pagination->total, true, 'helloworlds.orderup', 'JLIB_HTML_MOVE_DOWN', $saveOrder); ?></span> <?php endif; ?> <?php endif; ?> <?php $disabled = $saveOrder ? '' : 'disabled="disabled"'; ?> <input type="text" name="order[]" size="5" value="<?php echo $item->ordering; ?>" <?php echo $disabled; ?> /> </td> <td> <?php echo $item->id; ?> </td> </tr> <?php endforeach; ?>
Здесь была добавлена кнопка публикации. Обратите внимание, что мы передаем также возможность изменять состояние в переменной $canChange
и если оно равно false
, то кнопка становится неактивной:
<td> <?php echo JHtml::_('jgrid.published', $item->state, $i, 'helloworlds.', $canChange); ?> </td>
Кроме того, в четвертом параметре мы указываем, какой контроллер необходимо задействовать для исполнения задачи. В нашем случае - helloworlds
.
В суб-шаблоне admin/views/helloworlds/tmpl/default_foot.php мы увеличиваем значение colspan
:
<tr> <td colspan="5"><?php echo $this->pagination->getListFooter(); ?></td> </tr>
Теперь у нас есть кнопки на панели инструментов и кнопка в списке сообщений.
Добавляем метод publish() в класс таблицы
Для того чтобы состояние сообщения могло быть изменено, необходимо добавить в класс таблицы сообщения HelloWorldTableHelloWorld
метод publish()
. Именно он вызывается в конечном итоге при попытке изменить состояние. Но почему именно он? Потому что в Joomla этот процесс происходит следующим образом:
- вызывается метод publish() контроллера JControllerAdmin, так как наш контроллер
HelloWorldControllerHelloWorlds
его расширяет; - контроллер JControllerAdmin вызывает метод publish() модели JModelAdmin, так как в нашем контроллере мы установили модель
HelloWorldModel
в методеgetModel()
, а она расширяет JModelAdmin; - модель JModelAdmin в методе publish() вызывает экземпляр таблицы
HelloWorldTableHelloWorld
, так как в нашей моделиHelloWorldModel
мы установили её в методеgetTable()
.
Итак, откройте файл admin/tables/helloworld.php и добавьте следующий код после метода load()
:
/** * Метод для изменения состояния сообщения. * * @param mixed $pks Необязательный массив первичных ключей. * @param integer $state Код состояния. * @param integer $userId Id пользователя, который производит операцию. * * @return boolean True если состояние успешно установлено. * * @throws RuntimeException */ public function publish($pks = null, $state = 1, $userId = 0) { $k = $this->_tbl_key; // Очищаем входные параметры. JArrayHelper::toInteger($pks); $state = (int) $state; // Если первичные ключи не установлены, то проверяем ключ в текущем объекте. if (empty($pks)) { if ($this->$k) { $pks = array($this->$k); } else { throw new RuntimeException(JText::_('JLIB_DATABASE_ERROR_NO_ROWS_SELECTED'), 500); } } // Устанавливаем состояние для всех первичных ключей. foreach ($pks as $pk) { // Загружаем сообщение. if (!$this->load($pk)) { throw new RuntimeException(JText::_('COM_HELLOWORLD_TABLE_ERROR_RECORD_LOAD'), 500); } $this->state = $state; // Сохраняем сообщение. if (!$this->store()) { throw new RuntimeException(JText::_('COM_HELLOWORLD_TABLE_ERROR_RECORD_STORE'), 500); } } return true; }
Изменяем модель сообщения
Первое, что мы добавим в модель – это проверку на возможность изменять состояние сообщения. Открываем файл admin/models/helloworld.php и добавляем метод canEditState()
:
/** * Метод для проверки, может ли пользователь изменять состояние записи. * * @param object $record Объект записи. * * @return boolean True, если разрешено изменять состояние записи. */ protected function canEditState($record) { $user = JFactory::getUser(); if (!empty($record->id)) { return $user->authorise('core.edit.state', $this->option . '.message.' . (int) $record->id); } elseif (!empty($record->catid)) { return $user->authorise('core.edit.state', $this->option . '.category.' . (int) $record->catid); } else { return parent::canEditState($record); } }
Далее изменим метод getForm()
, добавив проверку на возможность изменять состояние:
/** * Метод для получения формы. * * @param array $data Данные для формы. * @param boolean $loadData True, если форма загружает свои данные (по умолчанию), false если нет. * * @return mixed Объект JForm в случае успеха, в противном случае false. */ public function getForm($data = array(), $loadData = true) { // Получаем форму. $form = $this->loadForm( $this->option . '.helloworld', 'helloworld', array('control' => 'jform', 'load_data' => $loadData) ); if (empty($form)) { return false; } $id = JFactory::getApplication()->input->get('id', 0); $user = JFactory::getUser(); // Изменяем форму исходя из доступов пользователя. if ($id != 0 && (!$user->authorise('core.edit.state', $this->option . '.message.' . (int) $id)) || ($id == 0 && !$user->authorise('core.edit.state', $this->option))) { // Модифицируем поля. $form->setFieldAttribute('state', 'disabled', 'true'); } return $form; }
Теперь в шаблоне редактирования сообщения поле статус будет отображаться в неактивном состоянии, в случае если пользователь не имеет соответствующих прав. Осталось только добавить это поле в файл формы, и оно появится в шаблоне редактирования записи.
Изменяем файл формы
Откройте файл admin/models/forms/helloworld.xml и добавьте следующий код после поля catid
:
<field name="state" type="list" label="JSTATUS" description="JFIELD_PUBLISHED_DESC" class="inputbox" size="1" default="1" > <option value="1">JPUBLISHED</option> <option value="0">JUNPUBLISHED</option> </field>
Изменяем тип поля
В типе поля нам необходимо учитывать состояние сообщения, чтобы в пункте меню нам не показывались неопубликованные или находящиеся в корзине записи. Открываем admin/models/fields/helloworld.php и в метод getOptions()
добавляем условие ->where('h.state > 0')
:
<?php // Запрет прямого доступа. defined('_JEXEC') or die; // Подключаем тип поля list. jimport('joomla.form.helper'); JFormHelper::loadFieldClass('list'); /** * Класс поля формы HelloWorld компонента HelloWorld. */ class JFormFieldHelloWorld extends JFormFieldList { /** * Тип поля. * * @var string */ protected $type = 'HelloWorld'; /** * Метод для получения списка опций для поля списка. * * @return array Массив JHtml опций. */ protected function getOptions() { // Получаем объект базы данных. $db = JFactory::getDbo(); // Конструируем SQL запрос. $query = $db->getQuery(true); $query->select('h.id, h.greeting, h.catid') ->from('#__helloworld as h') ->select('c.title as category') ->leftJoin('#__categories AS c ON c.id = h.catid') ->where('h.state > 0'); $db->setQuery($query); $messages = $db->loadObjectList(); // Массив JHtml опций. $options = array(); if ($messages) { foreach ($messages as $message) { $options[] = JHtml::_('select.option', $message->id, $message->greeting . ($message->catid ? ' (' . $message->category . ')' : '') ); } } else { $options[] = JHtml::_('select.option', 0, JText::_('COM_HELLOWORLD_ERROR_MESSAGE_NOT_FOUND')); } $options = array_merge(parent::getOptions(), $options); return $options; } }
Если мы не можем найти сообщение (например, все они сняты с публикации), то вместо списка выбора мы отображаем всего один пункт "Сообщение не найдено". И даже если мы сохраним такой пункт меню, то в публичной части все равно будет отображена ошибка 404, так как в качестве id
записи мы передаем 0
, а записей с таким id
в базе данных быть не может.
Изменяем модель сообщения в публичной части
Похожее изменение (как и для типа поля) необходимо внести и в модель сообщения публичной части. Открываем файл site/models/helloworld.php и добавляем условие ->where('h.state > 0')
в метод getItem()
:
// Конструируем SQL запрос. $query = $this->_db->getQuery(true); $query->select('h.greeting, h.params') ->from('#__helloworld as h') ->select('c.title as category') ->leftJoin('#__categories as c ON c.id = h.catid') ->where('h.id = ' . (int) $id) ->where('h.state > 0');
Теперь в публичной части не будут отображаться неопубликованные или находящиеся в корзине сообщения.
Добавляем языковые константы
Откройте файл admin/language/en-GB/en-GB.com_helloworld.ini и добавьте:
COM_HELLOWORLD_N_ITEMS_PUBLISHED_1="One message published"
COM_HELLOWORLD_N_ITEMS_UNPUBLISHED_1="One message unpublished"
COM_HELLOWORLD_N_ITEMS_PUBLISHED="%d messages published"
COM_HELLOWORLD_N_ITEMS_UNPUBLISHED="%d messages unpublished"
COM_HELLOWORLD_TABLE_ERROR_RECORD_LOAD="Error loading record from the table."
COM_HELLOWORLD_TABLE_ERROR_RECORD_STORE="Error saving record into the table."
Откройте файл admin/language/ru-RU/ru-RU.com_helloworld.ini и добавьте:
COM_HELLOWORLD_N_ITEMS_PUBLISHED_1="1 сообщение опубликовано"
COM_HELLOWORLD_N_ITEMS_UNPUBLISHED_1="1 сообщение снято с публикации"
COM_HELLOWORLD_N_ITEMS_PUBLISHED_2="%d сообщения опубликовано"
COM_HELLOWORLD_N_ITEMS_UNPUBLISHED_2="%d сообщения снято с публикации"
COM_HELLOWORLD_N_ITEMS_PUBLISHED_MORE="%d сообщений опубликовано"
COM_HELLOWORLD_N_ITEMS_UNPUBLISHED_MORE="%d сообщений снято с публикации"
COM_HELLOWORLD_TABLE_ERROR_RECORD_LOAD="Ошибка при попытке загрузить запись из таблицы."
COM_HELLOWORLD_TABLE_ERROR_RECORD_STORE="Ошибка при попытке сохранить запись в таблице."
Откройте файл site/language/en-GB/en-GB.com_helloworld.ini и добавьте:
COM_HELLOWORLD_ERROR_MESSAGE_NOT_FOUND="Message not found"
Откройте файл site/language/ru-RU/ru-RU.com_helloworld.ini и добавьте:
COM_HELLOWORLD_ERROR_MESSAGE_NOT_FOUND="Сообщение не найдено"
Собираем пакет установки компонента
Не забудьте поменять номер версии в файле helloworld.xml:
<version>0.0.15</version>
helloworld.xml
<?xml version="1.0" encoding="utf-8"?> <extension type="component" version="2.5.0" method="upgrade"> <name>COM_HELLOWORLD</name> <!-- Следующие элементы необязательны --> <creationDate>Июль 2012</creationDate> <author>Вася Пупкин</author> <authorEmail>Ваш e-mail</authorEmail> <authorUrl>Ваш сайт</authorUrl> <copyright>Информация о копирайте</copyright> <license>Информация о лицензии</license> <!-- Версия записывается в таблицу компонентов --> <version>0.0.15</version> <!-- Описание необязательно --> <description>COM_HELLOWORLD_DESCRIPTION</description> <!-- Запускается при установке/удалении/обновлении --> <scriptfile>script.php</scriptfile> <!-- Запускается при установке --> <install> <sql> <file driver="mysql" charset="utf8">sql/install.mysql.utf8.sql</file> </sql> </install> <!-- Запускается при удалении --> <uninstall> <sql> <file driver="mysql" charset="utf8">sql/uninstall.mysql.utf8.sql</file> </sql> </uninstall> <!-- Запускается при обновлении --> <update> <schemas> <schemapath type="mysql">sql/updates/mysql</schemapath> </schemas> </update> <!-- Раздел основных файлов сайта --> <!-- Обратите внимание на значение аттрибута folder: Этот аттрибут описывает папку нашего пакета-установщика из которой должны копироваться файлы. Поэтому указанные в этом разделе файлы будут скопированы из папки /site/ нашего пакета-установщика в соответствующую папку установки. --> <files folder="site"> <filename>index.html</filename> <filename>controller.php</filename> <filename>helloworld.php</filename> <folder>language</folder> <folder>models</folder> <folder>views</folder> </files> <media destination="com_helloworld" folder="media"> <filename>index.html</filename> <folder>images</folder> </media> <!-- Администрирование --> <administration> <!-- Раздел Меню --> <menu img="../media/com_helloworld/images/hello-16x16.png">COM_HELLOWORLD_MENU</menu> <!-- Раздел основных файлов администрирования --> <!-- Обратите внимание на значение аттрибута folder: Этот аттрибут описывает папку нашего пакета-установщика из которой должны копироваться файлы. Поэтому указанные в этом разделе файлы будут скопированы из папки /admin/ нашего пакета-установщика в соответствующую папку установки. --> <files folder="admin"> <filename>index.html</filename> <filename>access.xml</filename> <filename>config.xml</filename> <filename>controller.php</filename> <filename>helloworld.php</filename> <folder>controllers</folder> <folder>helpers</folder> <folder>models</folder> <folder>sql</folder> <folder>tables</folder> <folder>views</folder> </files> <languages folder="admin"> <language tag="en-GB">language/en-GB/en-GB.com_helloworld.ini</language> <language tag="en-GB">language/en-GB/en-GB.com_helloworld.sys.ini</language> <language tag="ru-RU">language/ru-RU/ru-RU.com_helloworld.ini</language> <language tag="ru-RU">language/ru-RU/ru-RU.com_helloworld.sys.ini</language> </languages> </administration> <!-- Сервер обновлений --> <updateservers> <!-- Заметка: Не допускаются пробелы или переносы строк между тегам server --> <server type="extension" priority="1" name="Сайт обновлений компонента Hello World!">http://вашдомен.ru/update/helloworld-update.xml</server> </updateservers> </extension>
Содержимое директории с кодом:
helloworld.xml
script.php
site/index.html
site/helloworld.php
site/controller.php
site/language/index.html
site/language/en-GB/index.html
site/language/en-GB/en-GB.com_helloworld.ini
site/language/ru-RU/index.html
site/language/ru-RU/ru-RU.com_helloworld.ini
site/models/index.html
site/models/helloworld.php
site/views/index.html
site/views/helloworld/index.html
site/views/helloworld/view.html.php
site/views/helloworld/tmpl/index.html
site/views/helloworld/tmpl/default.xml
site/views/helloworld/tmpl/default.php
admin/index.html
admin/access.xml
admin/config.xml
admin/controller.php
admin/helloworld.php
admin/controllers/index.html
admin/controllers/helloworld.php
admin/controllers/helloworlds.php
admin/helpers/index.html
admin/helpers/helloworld.php
admin/language/index.html
admin/language/en-GB/index.html
admin/language/en-GB/en-GB.com_helloworld.ini
admin/language/en-GB/en-GB.com_helloworld.sys.ini
admin/language/ru-RU/index.html
admin/language/ru-RU/ru-RU.com_helloworld.ini
admin/language/ru-RU/ru-RU.com_helloworld.sys.ini
admin/models/index.html
admin/models/helloworld.php
admin/models/helloworlds.php
admin/models/fields/index.html
admin/models/fields/helloworld.php
admin/models/forms/index.html
admin/models/forms/helloworld.js
admin/models/forms/helloworld.xml
admin/models/rules/index.html
admin/models/rules/greeting.php
admin/sql/index.html
admin/sql/install.mysql.utf8.sql
admin/sql/uninstall.mysql.utf8.sql
admin/sql/updates/index.html
admin/sql/updates/mysql/index.html
admin/sql/updates/mysql/0.0.1.sql
admin/sql/updates/mysql/0.0.4.sql
admin/sql/updates/mysql/0.0.9.sql
admin/sql/updates/mysql/0.0.10.sql
admin/sql/updates/mysql/0.0.12.sql
admin/sql/updates/mysql/0.0.14.sql
admin/tables/index.html
admin/tables/helloworld.php
admin/views/index.html
admin/views/helloworld/index.html
admin/views/helloworld/view.html.php
admin/views/helloworld/submitbutton.js
admin/views/helloworld/tmpl/index.html
admin/views/helloworld/tmpl/edit.php
admin/views/helloworlds/index.html
admin/views/helloworlds/view.html.php
admin/views/helloworlds/tmpl/index.html
admin/views/helloworlds/tmpl/default.php
admin/views/helloworlds/tmpl/default_body.php
admin/views/helloworlds/tmpl/default_foot.php
admin/views/helloworlds/tmpl/default_head.php
language/index.html
language/en-GB/index.html
language/en-GB/en-GB.com_helloworld.sys.ini
language/ru-RU/index.html
language/ru-RU/ru-RU.com_helloworld.sys.ini
media/index.html
media/images/index.html
media/images/hello-16x16.png
media/images/hello-48x48.png
Запакуйте директорию в архивный файл (zip, tar, tar.gz, bz2) или скачайте его напрямую c GitHub. Далее установите его, используя менеджер расширений Joomla. Теперь у нас есть возможность изменять состояние записи.
Код для этой части
Скачать com_helloworld часть 15
Актуальный код части 15 на GitHub