Опубликован: 02.10.2012 | Доступ: свободный | Студентов: 1915 / 85 | Длительность: 11:48:00
Теги: joomla, mvc, php, xhtml, xml
Лекция 4:

Иерархия пунктов меню. Отправка писем. Классы ядра JEditor, JURI, JError, JDate

< Лекция 3 || Лекция 4: 123 || Лекция 5 >

Ошибки (класс 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) - нерабочие.

Кнопки для отправки уведомлений по электронной почте

Рис. 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).

Сообщение об отправке уведомления по электронной почте

увеличить изображение
Рис. 4.5. Сообщение об отправке уведомления по электронной почте

Если вы используете локальный сервер, то ваше письмо, скорее всего, на самом деле не отправилось. В Денвере срабатывает почтовая заглушка sendmail: все письма просто помещаются в папку <путь к Денверу>/tmp/!sendmail в виде файлов с расширением .eml. Если в вашем случае так и есть, то откройте эту папку и найдите в ней только что "отправленное" письмо (можно ориентироваться по названию файла - это дата и время "отправки"). Откройте файл в почтовой программе и убедитесь, что его содержимое соответствует заданным нами значениям ( рис. 4.6).

Результат отправки уведомления на локальном сервере - файл .eml, открытый в почтовой программе

Рис. 4.6. Результат отправки уведомления на локальном сервере - файл .eml, открытый в почтовой программе

Перейдите к списку вопросов и убедитесь, что значение в столбце "Отправлен ли вопрос эксперту" поменялось с "Нет" на "Да" ( рис. 4.7).

Новое значение в столбце "Отправлен ли вопрос эксперту"

увеличить изображение
Рис. 4.7. Новое значение в столбце "Отправлен ли вопрос эксперту"

Аналогично проверьте, как работает кнопка "Отправить ответ".

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

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

Создавая первый пункт меню для нашего компонента, мы не указали значения левого и правого ключей. Определим их сейчас. Уровень данного пункта равен 1 (его предком является корень дерева с уровнем 0, а потомки могут иметь уровень 2 и более). Родительский узел - это самая первая строка в таблице #__menu, посмотрите значение его правого ключа в поле rgt.

Пусть $right_key - правый ключ родительского узла, $level - уровень родительского узла. Тогда для задания правильных значений левого и правого ключей выполним следующие запросы (не забудьте заменить $right_key и $level на значения из вашей таблицы):

  1. Обновление ключей узлов, стоящих за родительским узлом:
    UPDATE jos_menu SET lft=lft+2, rgt=rgt+2 WHERE lft>$right_key
              
  2. Обновление родительской ветки:
    UPDATE jos_menu SET rgt=rgt+2 WHERE rgt>=$right_key AND lft<$right_key
              
  3. Обновление нового узла:
    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).

Подпункты меню в бэкенде

увеличить изображение
Рис. 4.8. Подпункты меню в бэкенде
< Лекция 3 || Лекция 4: 123 || Лекция 5 >
Кирилл Гусаров
Кирилл Гусаров

В разделе "Первые папки и файлы. Добавление пунктов меню"

предлагается создать две файла:

- myquestions.php;

- admin.myquestions.php

с соответствуюшими адресами:

/components/com_myquestions/myquestions.php;

- /administrator/components/com_myquestions/admin.myquestions.php;

Так вот, при создании файла "admin.myquestions.php" В админке выдает ошибку - "Компонент не найден", а при переименовании его на  "myquestions.php" в последующем шаге, в админке не выводятся кнопки редактирования. 
Проверил кодировку, проверил правильность пути к файлам, пересохранил указанный код. Скажите что я делаю не так или в чем может быть причина?