Разработка расширений

Обзор

С приходом ExpressionEngine 1.4 появилась возможность переписывать и изменять внутреннюю работы программы. В ExpressionEngine это то что известно как 'хуки'; маленькие фрагменты кода в более чем 100 местах которые позваляют вызвать сторонние скрипты. Расширения могут изменить целую страницу панели управления, добавить/удалить функциональность, и изменить внешний вид отдельных элементов страницы. Расширения позволяют сторонних разработчиков изменять аспекты ExpressionEngine без хуков в стандартных скриптах. Вы можете считать, что расширение как плагины, но в отличие от плагина, расширение не используется для ваших шаблонов, вместо этого оно позваляет изменить саму систему.

Расширение представляет собой отдельный скрипт который находится в /system/extensions/ директории и включается в Менеджере расширений в панели управления. Расширение может иметь свои собственные настройки и собственные базы данных таблицы, в случае необходимости, но не является необходимой. Если настройки доступны для расширения, языковой файл не требуется, но в отличие от модуля нет панели управления для расширений.

Правила именований

Расширения имеют аналогичные именования как в ExpressionEngine плагинах, так что разработчики поймут смысл быстро. Существует только один файл, необходимый для расширения и внутри этого файла должен быть PHP класс. Название класса используется в имени файла расширения, имя файла должно быть в нижнем регистре. Имя класса содержит префикс 'ext.' и стандартную php приставку '.php'. Итак, если у нас есть вызов класса 'Example_extension', то имя файла расширения будет 'ext.example_extension.php'.

Внутри расширения

Внутри файла расширения должен быть класс, который будет вызываться ExpressionEngine когда потребуется это расширение. Конструктор для класса требует один параметр, который будет получать настройки для расширения и установить класс переменной.

class Example_extension
{
    var $settings        = array();
    
    // -------------------------------
    //   Constructor - Extensions use this for settings
    // -------------------------------
    
    function Example_extension($settings='')
    {
        $this->settings = $settings;
    }
    // END
}
// END CLASS

Кроме переменной $settings, есть ещё пять других переменных которые у вас должны быть. Эти переменные вывода мета-информации о Ваших расширений к менеджеру расширений, чтобы его можно описать ваши расширения, предоставить документацию, а также обновить параметры (если таковые имеются).

class Example_extension
{
    var $settings        = array();
    
    var $name            = 'Example Extension';
    var $version         = '1.0.0';
    var $description     = 'Our example extension';
    var $settings_exist  = 'n';
    var $docs_url        = '';//'http://expressionengine.com';
    
    // -------------------------------
    //   Constructor - Extensions use this for settings
    // -------------------------------
    
    function Example_extension($settings='')
    {
        $this->settings = $settings;
    }
    // END
}
// END CLASS

Если ваше расширение имеет языковой файл, то $name и $description переменные должны быть указаны в конструкторе для вызова языкового файла и переменных используя Языковой ($LANG) класс. Если расширение может быть использовано и на международном уровне, не английскими пользователями это является рекомендуемым действием.

Активация и обновления

Существуют два метода требуется для вашего класса расширения, которые контролируют активацию и обновление вашего расширения. Наиболее важной является функция активации расширения в ExpressionEngine. Чтобы включить расширение, вы просто выполняете запрос в базу данных с разной информацией, как хуки расширения и имя метода в вашего расширения класса для вызова этого хука.

// --------------------------------
//  Activate Extension
// --------------------------------

function activate_extension()
{
    global $DB;
    
    $DB->query($DB->insert_string('exp_extensions',
                                  array(
                                        'extension_id' => '',
                                        'class'        => "Example_extension",
                                        'method'       => "rewrite_admin_home",
                                        'hook'         => "admin_home_page_start",
                                        'settings'     => "",
                                        'priority'     => 10,
                                        'version'      => $this->version,
                                        'enabled'      => "y"
                                      )
                                 )
              );
}
// END

Здесь короткий список того что означают поля в таблице базы данных:

Обновлять расширения очень легко в ExpressionEngine. Пользователь просто загружает новую версию расширения и ExpressionEngine автоматически обновит расширение в следующий раз, когда оно будет вызвано. Все что требуется, это специальная Функция под называнием update_extension(). Программа автоматически сравнивает версии расширений информации в базе данных в отношении версии расширения файла, а Если расширение файла новой версии он вызывает эту функцию.

// --------------------------------
//  Update Extension
// --------------------------------  

function update_extension($current='')
{
    global $DB;
    
    if ($current == '' OR $current == $this->version)
    {
        return FALSE;
    }
    
    if ($current < '1.0.1')
    {
        // Update to next version 1.0.1
    }
    
    if ($current < '1.0.2')
    {
        // Update to next version 1.0.2
    }
    
    $DB->query("UPDATE exp_extensions 
                SET version = '".$DB->escape_str($this->version)."' 
                WHERE class = 'Example_extension'");
}
// END

Отключение

Когда расширение включается впервый раз, то вызывается функция activate_extension() и расширение делает запись в базу данных. Если расширение включается впервые, то запускается фукнкция activate_extension(). Когда расширение отключают, то расширение не удаляет вызовы из базы данных.

Это создает проблему для разработчиков, которые, разрабатывая расширение, часто тестируют код, проверяя все хуки расширения в функции activate_extension (). То, что мы сделали это позволило создание из disable_extension () в классе расширения. Если эта функция существует в классе, она будет вызываться, когда ваше расширение отключают. Это позволит вам выяснить основные данные вашего расширения и начать новые каждый раз.

// --------------------------------
//  Отключение расширения
// --------------------------------

function disable_extension()
{
    global $DB;
    
    $DB->query("DELETE FROM exp_extensions WHERE class = 'Example_extension'");
}
// END

Настройки

Общие настройки форм и обработки

Если вы хотите сделать вашему расширения возможность иметь настройки, то это делается очень легко. Первое, вы должны убедиться что в $settings_exist переменной класса указано 'y'. Второе, вам нужен языковой файл вашего расширения, имя языкового файла имеет имя расширения в нижнем регистре букв, начинается с профикса 'lang.' и оканчивается на '.php'. Созданный языковой файл переносится на директорию /system/language/. И наконец, вам нужен метод в ваш класс расширения, для вызова settings(). Эта функция возвращает массив в специальную форму которая поможет Extensions Manager автоматический создать форму ваших настроек.

// --------------------------------
//  Настройки
// --------------------------------  

function settings()
{
    $settings = array();
    
    $settings['butter']    = "Quite Tasty";
    $settings['buttery']   = array('r', array('yes' => "yes", 'no' => "no"), 'no');
    
    // Complex:
    // [variable_name] => array(type, values, default value)
    // variable_name => short name for setting and used as the key for language file variable
    // type:  t - textarea, r - radio buttons, s - select, ms - multiselect, f - function calls
    // values:  can be array (r, s, ms), string (t), function name (f)
    // default:  name of array member, string, nothing
    //
    // Simple:
    // [variable_name] => 'Butter'
    // Text input, with 'Butter' as the default.
    
    return $settings;
}
// END

Для примера была создана форма настроек с текстовым полем и переменной с именем 'butter' и по умолчанию указано значение "Not Tasty" и  поле радио кнопки  с именем переменнойf 'buttery' с возможными значениями "Yes" или "No" и стандартно "No".

Заметка о массиве второго поля, ключи используются как значения для для этого элемента, а значения будут текстом. Если вы хотите, значения ваших переменных должны быть как языковые переменные вашего расширения языкового файла и Extensions Manager сам автоматический вернёт их для вас.

Создание и обработка формы настроек

Если ваши настройки требуют специальную формукоторую не создать на специальном месте как выше, то ExpressionEngine позваляет вам создать свою форму настроек и функцию обработки в рамках вашего расширения. Сначало, вам нужно иметь метод в вашем классе расширениия, функция называется settings_form(). Эта функция создаёт форму и выводит в Панель управления. Работает эта функция как и любая другая страница созданная для ExpressionEngine панели управления, для создания используйте Display class и свой метод.

function settings_form($current)
{
    global $DSP, $LANG, $IN;
    
    $DSP->crumbline = TRUE;
    
    $DSP->title  = $LANG->line('extension_settings');
    $DSP->crumb  = $DSP->anchor(BASE.AMP.'C=admin'.AMP.'area=utilities', $LANG->line('utilities')).
                               $DSP->crumb_item($DSP->anchor(BASE.AMP.'C=admin'.AMP.'M=utilities'.AMP.'P=extensions_manager', $LANG->line('extensions_manager')));
    $DSP->crumb .= $DSP->crumb_item($this->name);
    
    $DSP->right_crumb($LANG->line('disable_extension'), BASE.AMP.'C=admin'.AMP.'M=utilities'.AMP.'P=toggle_extension_confirm'.AMP.'which=disable'.AMP.'name='.$IN->GBL('name'));
    
    $DSP->body = $DSP->form_open(
                                array(
                                        'action' => 'C=admin'.AMP.'M=utilities'.AMP.'P=save_extension_settings',
                                        'name'   => 'settings_example',
                                        'id'     => 'settings_example'
                                    ),
                                array('name' => get_class($this))
                            );
    
    
    $DSP->body .=   $DSP->table('tableBorder', '0', '', '100%');
    $DSP->body .=   $DSP->tr();
    $DSP->body .=   $DSP->td('tableHeadingAlt', '', '2');
    $DSP->body .=   $this->name;
    $DSP->body .=   $DSP->td_c();
    $DSP->body .=   $DSP->tr_c();
    
    $DSP->body .=   $DSP->tr();
    $DSP->body .=   $DSP->td('tableCellOne', '45%');
    $DSP->body .=   $DSP->qdiv('defaultBold', $LANG->line('log_table'));
    $DSP->body .=   $DSP->td_c();
    
    $DSP->body .=   $DSP->td('tableCellOne');
    $DSP->body .=   $DSP->input_text('log_table', ( ! isset($current['log_table'])) ? '' : $current['log_table']);
    $DSP->body .=   $DSP->td_c();
    $DSP->body .=   $DSP->tr_c();
    
    $DSP->body .=   $DSP->table_c();
    $DSP->body .=   $DSP->qdiv('itemWrapperTop', $DSP->input_submit());
    $DSP->body .=   $DSP->form_c();
}

Наконец, вам необходимо будет иметь метод в классе вашего расширения вызывающий save_settings (). Эта функция будет вызываться, когда ваш settings_form() представляется в форме метода. Используйте его для обработки данных отправки и ввода его в exp_extensions таблицы базы данных. Помните, что данные записаные в базу данных сериализованный массив, поэтому заполните их соответствующим образом.

Вызов расширения

Ниже приводится пример ExpressionEngine хук асширения, который доступен для использования:

// --------------------------------------------
// 'publish_form_start' hook.
//  - Allows complete rewrite of Publish page.
//
	$edata = $EXT->call_extension('publish_form_start', $which, $submission_error, $entry_id);
	if ($EXT->end_script === TRUE) return;
//
// -------------------------------------------

Первый параметр $EXT->call_extension это имя хука, которое позваляет расширению класса знать какое расширение вызывать. Остальные три параметра являются переменными взяты из той функции, которую хук вкладывается внутри. Они предоставляют информацию и данные для начала вызова этого хука, который позваляет выполнять действия или манипулировать данными. Когда расширение вызывается, ExpressionEngine загружает файл расширения, экземпляр класса расширения, и затем вызовы метода подходящие для хука расширения как указано в расширении когда оно будет активировано (смотрите выше об активации).

Когда этот метод вызывается в классе расширения те три другие параметры будут направлены в метод автоматически. Вот что метод может выглядеть следующим образом:

function replace_entry_form($which, $submission_error, $entry_id)
{
    global $DSP, $DB, $IN, $EXT;
    
    $EXT->end_script = TRUE;

    $DSP->body = 'New Entry Form Here';
}

Три параметра для хука расширения отображаются прямо на трех параметров вызываемого метода, и поэтому в ваших расширениях легко можете использовать эти параметры и делать то, что он должен делать. На ExpressionEngine.com Библиотека хуков расширений содержит записи всех хуков расширений и доступных параметров для вас, а также советы как использовать хуки расширений.

Несколько расширений, другие хуки

Когда вызывается хук расширения, ExpressionEngine проверяет базу данных и смотрит, есть ли расширения доступные для хука Если есть расширения, то они обрабатываются в порядке начиная с меньшего номера приоритета вызывающего расширение. Из-за преоритета, расширения могут помешать друг другу, по этому мы предоставили две переменные для помощи с этим.

$EXT->last_call

Здесь будут популярны хуки начинающие использовать несколько расширения и некоторые ждут от вас возвращение данных в хук расширения. Из-за этого, есть переменная доступа из класса Расширения ($EXT) что будет содержать полеченные данные любых расширений для этого хука Скажем, есть хук для форматирования текста и вызывается расширение перед вами. Это расширение будет возвращать форматированый текст, но ваше расширение вызывается с оригинальным текстом информации начала отправки. В таком случае данные возвращаются и доступны другим расширениям в переменной, в ней содержится уже отформатированый текст: $EXT->last_call. Эта переменная вернёт все последние возвращения для этого хука. Если не было расширения раньше, то значение переменной будет FALSE.

$EXT->end_script

Многие хуки расширения существуют специально для контроля страницы или скрипта в панели управления. Они предназначены для просмотра появления формы, или изменение хода работы скрипта для обработки данных формы. В тех случаях, когда вы хотите, чтобы ваше расширение было последним, и вызывов хуков больше небыло. $EXT->end_script существует исключение для этой цели. Если вы укажите значение TRUE, то как только ваше расширение завершит работу, исключение завершит хук.

Наверх страницы