IT-Service-WordPress / WPF

Шаблон плагина для WordPress (CMS)
GNU General Public License v2.0
0 stars 0 forks source link

Инсталляция, удаление плагина и обновление на примере опций #16

Closed sergey-s-betke closed 10 years ago

sergey-s-betke commented 10 years ago

Опции должны быть описаны отдельно от их валидаторов и интерфейса. Просто потому, что валидаторы и интерфейс нужны только в admin console. А сами опции нужны плагину всегда.

Не стоит мудрить с сериализацией опций в одно значение в wp_options, пусть каждая опция будет отдельным значением, это нормально.

Кроме того, необходимо по умолчанию свойства устанавливать не autoload.

Итак, описывать опции логично в главном файле модуля.

sergey-s-betke commented 10 years ago

Напрашивается описание опций так же через компоненты плагина. Возможно - через некий агрегатор компонентов - опций.

sergey-s-betke commented 10 years ago

Доступ к опциям - через методы \WPF\Plugin\Base. При этом доступны должны быть только те опции, которые определены в компонентах, описывающих опции.

То есть плагин должен будет содержать коллекцию опций, которую он будет собирать путём анализа своих компонентов на предмет реализации интерфейса опции.

Для всех компонентов тянуть в базовые интерфейсы и классы методы удаления, деактивации и активации смысла нет. Вполне (благо при этих операциях производительность не столь важна) можно определять необходимые компоненты путём проверки, реализуют ли они необходимый интерфейс (что-нибудь типа IActivatable, IInstallable, IUpdatable и так далее).

sergey-s-betke commented 10 years ago

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

Но нужно предложить и базовые классы, которые в результате отдают просто целое, вещественное числа, строку, дату, и так далее.

sergey-s-betke commented 10 years ago

С удалением всё более менее понятно. Мы можем зарегистрировать register_uninstall_hook. Правда - необходимо использовать статические методы, динамические методы использовать нельзя.

Но регистрацию опций придётся выполнять при первой активации. Наш Фреймворк должен анализировать необходимость выполнения "инсталляции" или обновления.

sergey-s-betke commented 10 years ago

Обязательное правило - новые версии плагина должны уметь обновлять / удалять версии всех предыдущих версий своих.

sergey-s-betke commented 10 years ago

Пока вижу только следующие решения:

На этом этапе в достаточно вызывать некую процедуру обновления, в которую передавать версию, с которой осуществляем обновление.

По результатам обновления желательно генерировать admin notice со ссылкой на страницу настроек плагина.

sergey-s-betke commented 10 years ago

Итак, для каждого объекта опций мы должны определить функцию обновления! В базовом классе будем проверять наличие опции, добавлять её при отсутствии, проводить санитизацию значения и сохранять значение после санитизации.

Если требуется более сложная логика обновления опции - необходимо переопределять класс.

sergey-s-betke commented 10 years ago

Не хотелось бы тянуть в плагин код с логикой обновлений, если и обновлять то будет нечего - то есть если нет опций, базы данных и прочего.

Итого, опции предлагаю оформлять не как компоненты. Как компоненту предлагаю оформить коллекцию опций. И в этой компоненте уже и реализовать логику обновлений.

Но саму компоненту (видимо - отдельную), отвечающую за механизм обновлений, необходимо регистрировать неявно.

sergey-s-betke commented 10 years ago

Сначала необходимо решить более простую задачу: версионность и механизм обновлений необходимо либо изначально в код плагина включать (в код базового класса), либо же опционально - но автоматически при наличии компонентов, требующих этого механизма.

Как вариант решения:

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

Прямую манипуляцию компонентами допускать не стоит, но через методы, с инкапсуляцией, в достаточной степени безопасно...

sergey-s-betke commented 10 years ago

С одной стороны повесить на компоненты задачи по контролю зависимостей заманчиво. Но в таком случае мы ограничим действительный класс компонента зависимости тем, что знает зависимая компонента. А она должна знать только интерфейс, по сути, не более.

В итоге, предложение следующее:

sergey-s-betke commented 10 years ago

Теперь для инсталлятора необходим алгоритм определения вызова обработчиков install, uninstall. Да и update добавить надо.

Проще всего с uninstall. Есть такой hook. Но для него нужен статический метод. Необходимо выяснить очередность вызова uninstall hook, необходимо, чтобы загрузчик admin side уже отработал, потому как наш uninstall hook только там и определяется. И потребуется задействовать singleton для этих целей.

Сложнее - с install, ещё сложнее - с update.

install будем вызывать следующим образом: при activate проверяем наличие некоего свойства. Имя свойства должно определяться с использованием $plugin->get_namespace(). Если свойства нет - значит ещё только инсталлируем наш плагин. Вызываем install, и только после этого - activate.

В это опцию писать предлагаю версию плагина. И по admin_init читаем это свойство, сравниваем версии плагина, и если актуальная версия плагина изменилась в большую сторону - вызываем update, после чего обновляем опцию на актуальную версию.

sergey-s-betke commented 10 years ago

Операция register_uninstall_hook крайне дорогая, как выясняется. И ясно - почему требует статического метода - она сохраняет информацию о callback в базу данных!

Посему использовать эту функции нужно только однажды - при установке (install), но никак не при каждой загрузке плагина или при его активации!

sergey-s-betke commented 10 years ago

Есть радостные новости! Изучение кода wordpress показало, что при деинсталляции вызывается действие do_action( 'uninstall_' . $plugin_file ).

Но вызывается это действие только в том случае, если зарегистрирован деинсталлятор через register_uninstall_hook.

Посему решение следующее:

sergey-s-betke commented 10 years ago

Описанную выше логику с установкой, удалением и обновлением реализовал и протестировал на примере опций - всё работает, включая удаление опций из базы при удалении плагина.

sergey-s-betke commented 10 years ago

Корректное условие на возможности пользователя по установке и удалению плагинов:

if ( ( ! is_multisite() && is_blog_admin() && current_user_can( 'install_plugins' ) ) || ( is_network_admin() && current_user_can( 'manage_network_plugins' ) && current_user_can( 'install_plugins' ) ) )

sergey-s-betke commented 10 years ago

Целесообразно добавить по аналогии с uninstall_<plugin_basename> action также и install_<plugin_basename>, update_<plugin_basename>. Сделаем это.

sergey-s-betke commented 10 years ago

Реализовал и протестировал.