В разделе "Первые папки и файлы. Добавление пунктов меню" предлагается создать две файла: - myquestions.php; - admin.myquestions.php с соответствуюшими адресами: - /components/com_myquestions/myquestions.php; - /administrator/components/com_myquestions/admin.myquestions.php; Так вот, при создании файла "admin.myquestions.php" В админке выдает ошибку - "Компонент не найден", а при переименовании его на "myquestions.php" в последующем шаге, в админке не выводятся кнопки редактирования. |
SEF-ссылки. Классы ядра JDocument, JUser
Практика
Форма для написания вопроса
Измените код конструкции switch в файле myquestions.php, добавив обработку задачи showform:
case 'showform': showForm($option); break;
Добавьте в этот же файл функцию showForm():
function showForm($option) { $user =&JFactory::getUser(); if($user->name) $name = $user->name; else $name = ''; HTML_questions::showForm($option, $name); }
Перед вызовом функции вывода HTML-кода мы получаем имя залогиненного в настоящий момент пользователя, если таковой имеется. Код $user = &JFactory::getUser() присваивает переменной $user ссылку на объект-представитель залогиненного пользователя. Если удалось получить имя пользователя, то мы сохраняем это значение в переменной $name, а в противном случае этой переменной присваивается пустая строка. Таким образом поле "Автор" в форме для написания вопроса будет уже заполнено, так что залогиненным пользователям не придется его заполнять.
Перейдите в файл myquestions.html.php и добавьте в класс HTML_questions метод showForm():
function showForm($option, $name) { ?> <form action="index.php" method="post"> <table> <tr> <td width="100"> <?php echo JText::_('COM_MYQUESTIONS_AUTHOR');?>: </td> <td> <input class="text_area" type="text" name="name" id="name" size="50" maxlength="255" value="<?php echo $name;?>"/> </td> </tr> <tr> <td width="100"> <?php echo JText::_('COM_MYQUESTIONS_CITY');?>: </td> <td> <input class="text_area" type="text" name="city" id="city" size="50" maxlength="50"/> </td> </tr> <tr> <td width="100"> <?php echo JText::_('COM_MYQUESTIONS_EMAIL');?>: </td> <td> <input class="text_area" type="text" name="email" id="email" size="50" maxlength="50"/> </td> </tr> <tr> <td width="100"> <?php echo JText::_('COM_MYQUESTIONS_QUESTION');?>: </td> <td> <textarea name='question' id='question' class='inputbox' rows='15' cols='38'></textarea> </td> </tr> <tr> <td width="100"> <?php echo JText::_('COM_MYQUESTIONS_PUBLISHED');?>: </td> <td> <input type="hidden" name="published" value="0"/> <input type="checkbox" name="published" id="published" value="1"/> </td> </tr> </table> <input type="hidden" name="task" value="addquestion"/> <input type="hidden" name="option" value="<?php echo $option;?>"/> <input type="submit" class="button" id="button" value="<?php echo JText::_('COM_MYQUESTIONS_SENDBUTTON');?>"/> </form> <?php }Листинг .
Как видите, в данной форме сразу два элемента с именем published. Дело в том, что, когда флажок установлен в выбранное состояние, то сценарию-обработчику формы в числе других параметров приходит пара "имя_флажка=значение". Однако когда флажок не установлен, эта пара не посылается. Поэтому используется следующий прием: перед флажком в форме помещается одноименное скрытое поле со значением, равным нулю. Тогда если флажок не установлен, то сценарий получит пару published=0. Если же он установлен, то сценарий тоже получит эту пару, но сразу же последует пара published=1, которая перекроет значение скрытого поля.
Так как мы поместили на форму скрытый элемент task со значением addquestion, то она будет обработана при обработке задачи addquestion. Поэтому добавьте в конструкцию switch в файле myquestions.php следующий код:
case 'addquestion': addQuestion($option); break;
Добавьте в этот же файл функцию addQuestion():
function addQuestion($option) { $row =& JTable::getInstance('question', 'Table'); if (!$row->bind(JRequest::get('post'))) { echo "<script> alert('".$row->getError()."'); window.history.go(-1); </script>\n"; exit(); } $row->question = nl2br(htmlspecialchars(JRequest::getVar('question', '', 'post', 'string',JREQUEST_ALLOWRAW), ENT_QUOTES)); $row->IP = getenv('REMOTE_ADDR'); $row->date = &JFactory::getDate()->toFormat(); $row->id_cat = 1; if (!$row->store()) { echo "<script> alert('".$row->getError()."'); window.history.go(-1); </script>\n"; exit(); } $mailer =& JFactory::getMailer(); $mailer->setSender('test@mysite.ru'); $mailer->addRecipient('admin@mysite.ru'); $mailer->setSubject(JText::_('COM_MYQUESTIONS_ADMIN_LETTER_SUBJECT')); $mailer->setBody(JText::sprintf('COM_MYQUESTIONS_ADMIN_LETTER_NEW_QUESTION',$row->question)); $mailer->IsHTML(true); if ($mailer->Send() !== true) { echo "<script> alert('".JText::_('COM_MYQUESTIONS_ADMIN_LETTER_ERROR')."'); window.history.go(-1); </script>\n"; exit(); } global $app; $app-> redirect(JRoute::_('index.php?option='.$option.'&task=view&view=all'), JText::sprintf('COM_MYQUESTIONS_QUESTION_SENT',$row->name)); }
Текст вопроса, введенный пользователем, пропускается через функцию htmlspecialchars(), преобразующую специальные символы в HTML-сущности. Таким путем предотвращается ввод нежелательных HTML-тегов. Затем результат пропускается через функцию nl2br(), вставляющую код разрыва строки <br/> перед каждым переводом строки, чтобы текст вопроса при выводе на веб-странице не слился в одну строку.
IP-адрес пользователя определяется с помощью функции getenv(), которая возвращает значение переменной окружения, в данном случае - REMOTE_ADDR.
По умолчанию вопросу присваивается категория с id, равным 1, то есть "Без категории".
Добавьте в файл language/ru-RU/ru-RU.com_myquestions.ini код:
COM_MYQUESTIONS_ADD_QUESTION="Задать вопрос" COM_MYQUESTIONS_AUTHOR="Автор" COM_MYQUESTIONS_DATE="Дата вопроса" COM_MYQUESTIONS_QUESTION="Текст вопроса" COM_MYQUESTIONS_CITY="Город" COM_MYQUESTIONS_EMAIL="e-mail" COM_MYQUESTIONS_CATEGORY="Категория" COM_MYQUESTIONS_PUBLISHED="Отображать ли вопрос на сайте" COM_MYQUESTIONS_SENDBUTTON="Отправить вопрос" COM_MYQUESTIONS_QUESTION_SENT="Спасибо, %s! Ваш вопрос отправлен. Он будет опубликован на сайте после получения ответа" COM_MYQUESTIONS_ADMIN_LETTER_SUBJECT="Новый вопрос на сайте" COM_MYQUESTIONS_ADMIN_LETTER_NEW_QUESTION="<p>Добрый день!</p><p>На сайте появился новый вопрос:</p><p><i>%s</i></p>" COM_MYQUESTIONS_ADMIN_LETTER_ERROR="Ошибка отправки письма"
Осталось добавить ссылку для написания вопроса. Измените начало функции showCategories() так:
function showCategories($rows, $option) { ?> <p><a href='index.php?option=<?=$option?>&task=showlist'><?=JText::_('COM_MYQUESTIONS_ALL_QUESTIONS') ?></a></p> <p><a href='index.php?option=<?=$option?>&task=showform'><?=JText::_('COM_MYQUESTIONS_ADD_QUESTION') ?></a></p>
Теперь на главной странице компонента во фронтенде появилась ссылка "Задать вопрос" ( рис. 5.1).
При переходе по этой ссылке появляется форма для написания вопроса ( рис. 5.2). Обратите внимание, что в поле "Автор" подставилось имя текущего пользователя, если он залогинен.
После написания вопроса и нажатия кнопки "Отправить вопрос" происходит перенаправление на главную страницу компонента с сообщением об успешной отправке вопроса ( рис. 5.3).
Зайдите в папку <путь к Денверу>/tmp/!sendmail и найдите в ней файл *.eml, содержащий письмо-уведомление администратора о новом вопросе.
SEF
Включите SEF в бэкенде. Для этого перейдите в меню "Сайт" - "Общие настройки" и убедитесь, что переключатель "Включить SEF (ЧПУ)" установлен в "Да". Если вы используете в качестве веб-сервера Apache со включенным mod_rewrite, то вы можете также установить переключатель "Перенаправление URL" в "Да"; тогда из ваших ссылок исчезнет строка "index.php". Вид раздела "Настройки SEO" при включенном mod_rewrite показан на рис. 5.4.
Если ваша конфигурация не позволяет использовать mod_rewrite, SEF-ссылки все равно могут быть построены, но они будут включать строку "index.php", например: http://www.mysite.ru/index.php/one/two/three.
Нажмите кнопку "Сохранить и закрыть" для сохранения конфигурации. Если вы используете mod_rewrite, убедитесь, что вы переименовали находящийся в корневой папке Joomla файл htaccess.txt в .htaccess (если переименовать файл в проводнике Windows не удается, воспользуйтесь интерфейсом командной строки или каким-либо файловым менеджером, например, Total Commander).
Если вы получили сообщение о том, что ваша конфигурация не может быть перезаписана, задайте те же два значения вручную. Для этого откройте файл configuration.php в корневой папке Joomla, найдите строки:
public $sef = '0'; public $sef_rewrite = '0';
и измените оба значения на "1" вместо "0".
Генерация SEF-ссылок
Напишем функцию для генерации SEF-ссылок. Создайте файл /components/com_myquestions/router.php:
<?php defined('_JEXEC') or die ('Restricted access'); function MyQuestionsBuildRoute(&$query) { $segments = array(); if (isset($query['task'])) { $segments[] = $query['task']; unset($query['task' ]) ; } if(isset($query['id' ])) { $segments[] = $query['id']; unset($query['id']); } return $segments; } ?>
Мы создаем пустой массив $segments. Затем проверяем, есть ли в массиве запроса элемент "task", и в этом случае добавляем значение задачи в массив $segments в качестве первого элемента и затем удаляем task из запроса. Далее мы повторяем тот же процесс для id. Наконец, возвращаем массив $segments, чтобы JRoute::_() могла закончить построение URL.
Исправим функции вывода нашего компонента так, чтобы они выводили SEF-ссылки вместо обычных. Откройте файл /components/com_myquestions/myquestions.html.php и измените код функции showCategories() класса HTML_questions следующим образом:
function showCategories($rows, $option) { ?> <p><a href='<?=JRoute::_('index.php?option='.$option.'&task=showlist')?>'> <?=JText::_('COM_MYQUESTIONS_ALL_QUESTIONS')?></a></p> <p><a href='<?=JRoute::_('index.php?option='.$option.'&task=showform')?>'> <?=JText::_('COM_MYQUESTIONS_ADD_QUESTION')?></a></p> <table> <?php foreach($rows as $row) { $link = JRoute::_('index.php?option='.$option.'&id='.$row->id.'&task=showlist'); echo '<tr><td><p><a href="' . $link . '">'.$row->name. '</a></td><td>'.$row->desc.'</td></tr>'; } ?> </table> <?php }
Измените выделенный код в функции HTML_questions::showQuestions():
foreach($rows as $row) { $link = JRoute::_('index.php?option='.$option.'&id='.$row->id.'&task=showquestion'); $link_cat = JRoute::_('index.php?option='.$option.'&id_cat='.$row->id_cat.'&task=showlist'); ?>
Измените также выделенный код в функции HTML_questions::showQuestion():
function showQuestion($row, $option, $row_cat) { $link_cat = JRoute::_('index.php?option='.$option.'&id_cat='.$row->id_cat.'&task=showlist');
Теперь компонент будет генерировать SEF-ссылки по шаблону, установленному в функции MyQuestionsBuildRoute().
Декодирование SEF-ссылок
Если вы сейчас попытаетесь щелкнуть на одной из SEF-ссылок, то получите сообщение:
"Fatal error: Call to undefined function myquestionsParseRoute() in Y:\home\localhost\www\joomla\includes\router.php on line …".
Напишем функцию для декодирования SEF-ссылок.
Откройте файл /components/com_myquestions/router.php и добавьте следующую функцию:
function MyQuestionsParseRoute ($segments) { $vars = array(); $vars['task'] = @$segments[0]; $vars['id'] = @$segments[1]; return $vars; }
Как видите, в функции MyQuestionsParseRoute() мы считали переменные task и id из массива $segments в том же порядке, в котором мы их записывали в одноименный массив в функции MyQuestionsBuildRoute().
Знаки "@" при получении элементов массива $segments используются для подавления вывода сообщений об обращении к несуществующим элементам массива, т.к. не все наши SEF-ссылки будут содержать id.
Теперь щелкните по какой-либо ссылке во фронтенде и обратите внимание на строку статуса в браузере. Вы должны увидеть URL вида: http://localhost/joomla/component/myquestions/showlist или http://localhost/joomla/component/myquestions/showquestion/1
Ключевые термины
Краткие итоги
SEF-ссылки в Joomla создаются с помощью метода JRoute::_(), который переводит внутреннюю ссылку, генерируемую Joomla, в SEF-ссылку. Чтобы компонент работал с SEF-ссылками, сгенерированными по собственному шаблону, необходимо создать в корневой папке его фронтенда файл router.php, в котором должны находиться функция для генерации SEF-ссылок и функция для их декодирования. Эти функции осуществляют взаимно обратные операции: первая из них из массива элементов HTTP-запроса создает массив сегментов SEF-ссылки, а вторая из массива сегментов SEF-ссылки создает массив переменных HTTP-запроса.
Так как SEF-ссылки не позволяют задать названия переменных запроса, то единственный способ определить, к какой переменной относится то или иное значение сегмента, - это использовать шаблон, который задает последовательность сегментов. Шаблон неявно задается в коде каждой из функций в файле router.php.
Для работы с документом и с данными пользователя в Joomla существуют соответственно классы JDocument и JUser.
Вопросы
- Какой метод переводит внутреннюю ссылку, генерируемую Joomla, в SEF-ссылку?
- Каким образом компоненты работают с SEF-ссылками?
- Для чего служат функции генерации и декодирования SEF-ссылок?
- Что такое шаблон SEF-ссылок и как он задается?
- Какие классы используются для работы с документом и с данными пользователя?
Упражнения
Адаптируйте код из раздела "Практика" для своего варианта (см. список вариантов в "Варианты заданий для лабораторных работ" ).