В разделе "Первые папки и файлы. Добавление пунктов меню" предлагается создать две файла: - myquestions.php; - admin.myquestions.php с соответствуюшими адресами: - /components/com_myquestions/myquestions.php; - /administrator/components/com_myquestions/admin.myquestions.php; Так вот, при создании файла "admin.myquestions.php" В админке выдает ошибку - "Компонент не найден", а при переименовании его на "myquestions.php" в последующем шаге, в админке не выводятся кнопки редактирования. |
Иерархия пунктов меню. Отправка писем. Классы ядра JEditor, JURI, JError, JDate
Ошибки (класс JError)
Методы raiseError(), raiseNotice() и raiseWarning() класса JError вызываются при возникновении каких-либо ошибок. Все они возвращают объект JException, содержащий сведения об ошибке, например, в каких файлах она возникла. raiseError() добавляет заданное сообщение в очередь сообщений и останавливает выполнение расширения, а остальные два метода только добавляют сообщение в очередь, но работа расширения продолжается.
JException raiseError(string $code, string $msg, mixed $info = null) JException raiseWarning(string $code, string $msg, mixed $info = null) JException raiseNotice(string $code, string $msg, mixed $info = null)
где
$code | - внутренний код ошибки, задается по усмотрению программиста; |
$msg | - сообщение об ошибке для пользователя; |
$info | - дополнительная информация об ошибке для программиста. |
Пример:
if ($this->published == 0) JError::raiseError (404, "Нет такой страницы");
Перечисленные методы создавались в то время, когда Joomla работала с PHP4, где отсутствовал механизм обработки исключений. Если на хостинге стоит PHP5, предпочтительнее использовать класс Exception.
Даты (класс JDate)
JDate - класс для работы с датами. Для создания объекта JDate используется метод getDate() класса JFactory:
JDate JFactory::getDate(mixed $time = 'now', mixed $tzOffset = null)
где
$time | - дата и время в формате, годном для функции PHP strtotime(); |
$tzOffset | - временная зона. Может задаваться числом от -12 до 14 (смещение относительно всемирного координированного времени - UTC) или строкой из числа временных зон, поддерживаемых PHP (их список можно найти на странице http://www.php.net/manual/ru/timezones.php). |
В отличие от многих других методов JFactory, getDate() не возвращает глобальный объект, а создает новый. Поэтому перед вызовом этого метода не нужно ставить амперсанд:
$now = JFactory::getDate(); // текущие дата и время $some_date = JFactory::getDate('2012-01-01 00:00:00','Europe/Moscow');
Получение даты в каком-либо формате
string format(string $format, bool $local = false, bool $translate = true)
где
$format | - формат; |
$local | - возвращать дату в локальной временной зоне или в GMT; |
$translate | - переводить ли текст. |
Чтобы не задавать формат даты вручную, можно использовать один из определенных в Joomla форматов: DATE_FORMAT_LC, DATE_FORMAT_LC1 и т.д. Их список находится в языковом файле language/<ln-LN>/<ln-LN>.ini, где <ln-LN> - код языка. Как и для всякого языкового ключа, значение такого формата можно получить с помощью функции JText::_(). Например, выведем дату в формате DATE_FORMAT_LC3:
echo $some_date->format(JText::_('DATE_FORMAT_LC3'));
Получение даты в формате, пригодном для вставки в запрос SQL
string toMySQL(bool $local=false)
Например, для получения материалов, созданных ранее даты $some_date, можно выполнить запрос:
$query = "SELECT * FROM #__content WHERE created < '{$some_date->toMySQL()}'";
Практика
Отправка уведомлений по электронной почте
Сейчас в форме для ответа на вопрос две кнопки на панели инструментов ( рис. 4.4) - нерабочие.
Напишем код для их обработки.
Откройте файл admin.myquestions.php и добавьте в переключатель switch обработку двух задач:
case 'sendToExpert': case 'sendAnswer': send($option,$task); break;
Добавьте также в этот же файл функцию send():
function send($option,$task) { $row_new = save(); $q = $row_new->question; $a = $row_new->answer; $mailer =& JFactory::getMailer(); $mailer->setSender('test@mysite.ru'); if ($task == 'sendToExpert') { $mailer->addRecipient('expert@mysite.ru'); $mailer->setSubject(JText::_('COM_MYQUESTIONS_NEW_QUESTION')); $mailer->setBody(JText::sprintf('COM_MYQUESTIONS_EMAIL_EXPERT_BODY',$q)); } else { $mailer->addRecipient($row_new->email); $mailer->setSubject(JText::_('COM_MYQUESTIONS_NEW_ANSWER')); $mailer->setBody(JText::sprintf('COM_MYQUESTIONS_EMAIL_USER_BODY',$q,$a)); } $mailer->IsHTML(true); if ($mailer->Send() !== true) $message = 'COM_MYQUESTIONS_EMAIL_ERROR'; else { $message = 'COM_MYQUESTIONS_EMAIL_SUCCESS'; $db =& JFactory::getDbo(); if ($task == 'sendToExpert') $query = "UPDATE #__myquestions SET senttoexpert=1 WHERE id={$row_new->id}"; else $query = "UPDATE #__myquestions SET senttoauthor=1 WHERE id={$row_new->id}"; $db->setQuery($query); $db->query(); if ($db->getErrorNum()) { echo $db->stderr(); return false; } } global $app; $app->redirect('index.php?option='.$option.'&task=reply&cid[]='.$row_new->id, JText::_($message)); }Листинг .
В данной функции прежде всего происходит сохранение текущей записи и обновленная запись сохраняется в переменной row_new. Затем в зависимости от того, отправляется это уведомление эксперту или автору вопроса, задаются отправитель, получатель, тема и текст письма. Если уведомление отправляется пользователю, задавшему вопрос, то адрес электронной почты получателя берется из записи row_new.
При задании тела письма используется метод JText::sprintf(), пропускающий заданную ему строку через PHP-функцию sprintf(). В выражении JText::sprintf('COM_MYQUESTIONS_EMAIL_USER_BODY',$q,$a) 'COM_MYQUESTIONS_EMAIL_USER_BODY' - это строка формата, которую мы сейчас зададим в языковом файле, а $q и $a - это аргументы (соответственно текст вопроса и текст ответа).
Тип содержимого письма задается как text/html, так как оно содержит html-теги.
Если письмо успешно отправлено, то в соответствующей вопросу записи в базе данных значение senttoexpert или senttoauthor устанавливается равным 1.
В конце функции происходит перенаправление на текущую страницу с выводом сообщения либо об успешной отправке письма, либо об ошибке.
Добавьте в файл /administrator/language/ru-RU/ru-RU.com_myquestions.ini следующий код:
COM_MYQUESTIONS_EMAIL_SUCCESS="e-mail отправлен" COM_MYQUESTIONS_EMAIL_ERROR="Не удалось отправить e-mail" COM_MYQUESTIONS_NEW_QUESTION="Новый вопрос" COM_MYQUESTIONS_EMAIL_EXPERT_BODY="<p>Добрый день!</p><p>На сайте появился новый вопрос: </p><p><i>%s</i></p>" COM_MYQUESTIONS_NEW_ANSWER="Ответ на ваш вопрос" COM_MYQUESTIONS_EMAIL_USER_BODY="<p>Добрый день!</p><p>На сайте появился ответ на ваш вопрос: </p><p><i>%s</i></p><p>Ответ был таким:</p><p><i>%s</i></p>"
Как видите, строки COM_MYQUESTIONS_EMAIL_EXPERT_BODY и COM_MYQUESTIONS_EMAIL_USER_BODY содержат параметры. Так как их тип - строковый (тексты вопроса, ответа), то используется описатель типа %s.
Теперь перейдите в бэкенде на страницу ответа на вопрос и нажмите кнопку "Отправить эксперту". Вы должны увидеть сообщение о том, что письмо отправлено ( рис. 4.5).
Если вы используете локальный сервер, то ваше письмо, скорее всего, на самом деле не отправилось. В Денвере срабатывает почтовая заглушка sendmail: все письма просто помещаются в папку <путь к Денверу>/tmp/!sendmail в виде файлов с расширением .eml. Если в вашем случае так и есть, то откройте эту папку и найдите в ней только что "отправленное" письмо (можно ориентироваться по названию файла - это дата и время "отправки"). Откройте файл в почтовой программе и убедитесь, что его содержимое соответствует заданным нами значениям ( рис. 4.6).
Рис. 4.6. Результат отправки уведомления на локальном сервере - файл .eml, открытый в почтовой программе
Перейдите к списку вопросов и убедитесь, что значение в столбце "Отправлен ли вопрос эксперту" поменялось с "Нет" на "Да" ( рис. 4.7).
Аналогично проверьте, как работает кнопка "Отправить ответ".
Добавление пункта меню
Добавим пункт меню для управления категориями.
Создавая первый пункт меню для нашего компонента, мы не указали значения левого и правого ключей. Определим их сейчас. Уровень данного пункта равен 1 (его предком является корень дерева с уровнем 0, а потомки могут иметь уровень 2 и более). Родительский узел - это самая первая строка в таблице #__menu, посмотрите значение его правого ключа в поле rgt.
Пусть $right_key - правый ключ родительского узла, $level - уровень родительского узла. Тогда для задания правильных значений левого и правого ключей выполним следующие запросы (не забудьте заменить $right_key и $level на значения из вашей таблицы):
- Обновление ключей узлов, стоящих за родительским узлом:
UPDATE jos_menu SET lft=lft+2, rgt=rgt+2 WHERE lft>$right_key
- Обновление родительской ветки:
UPDATE jos_menu SET rgt=rgt+2 WHERE rgt>=$right_key AND lft<$right_key
- Обновление нового узла:
UPDATE jos_menu SET lft=$right_key, rgt=$right_key+1, level=$level+1 WHERE title='com_myquestions_menu'
Пусть теперь $right_key и $level - соответственно правый ключ и уровень этого только что обновленного нами узла, а $parent_id - его id. Создадим два подпункта этого пункта меню (не забудьте заменить 10006 на id вашего компонента из таблицы #__extensions).
Подпункт для управления списком вопросов:
UPDATE jos_menu SET lft=lft+2, rgt=rgt+2 WHERE lft>$right_key; UPDATE jos_menu SET rgt=rgt+2 WHERE rgt>=$right_key AND lft<$right_key; INSERT INTO `jos_menu` (`menutype`, `title`, `alias`, `path`, `link`, `type`, `parent_id`, `level`, `component_id`, `access`, `img`, `params`, `lft`, `rgt`, `client_id`) VALUES ('menu', 'com_myquestions_menu_questions', 'Questions', 'My Questions/Questions', 'index.php?option=com_myquestions', 'component', $parent_id, $level+1, 10006, 1, 'class:component', '', $right_key, $right_key+1, 1);
Обратите внимание, что значения alias и path соответствуют иерархии узлов.
Посмотрите в таблице #__menu новое значение $right_key главного пункта меню для нашего компонента - оно должно было увеличиться на 2. Теперь создадим подпункт для управления списком категорий:
UPDATE jos_menu SET lft=lft+2, rgt=rgt+2 WHERE lft>$right_key; UPDATE jos_menu SET rgt=rgt+2 WHERE rgt>=$right_key AND lft<$right_key; INSERT INTO `jos_menu` (`menutype`, `title`, `alias`, `path`, `link`, `type`, `parent_id`, `level`, `component_id`, `access`, `img`, `params`, `lft`, `rgt`, `client_id`) VALUES ('menu', 'com_myquestions_menu_categories', 'Categories', 'My Questions/Categories', 'index.php?option=com_myquestions&task=showcat', 'component', $parent_id, $level+1, 10006, 1, 'class:component', '', $right_key, $right_key+1, 1);
Наконец, добавьте в файл /administrator/language/ru-RU/ru-RU.com_myquestions.sys.ini код:
COM_MYQUESTIONS_MENU_QUESTIONS="Управление вопросами" COM_MYQUESTIONS_MENU_CATEGORIES="Управление категориями"
Обновите любую страницу в бэкенде и убедитесь, что появились два новых подпункта меню "Моя система "вопрос - ответ"" ( рис. 4.8).