From fc04b4c5f23d0333efa21f319c74b8410290488b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lucas=20Garc=C3=ADa?= <lucas@codeccoop.org> Date: Wed, 31 Jan 2024 08:38:20 +0100 Subject: [PATCH] feat: plugin scaffolding --- includes/class-base-settings.php | 56 +++++++++------- .../{class-api.php => class-http-client.php} | 14 ++-- includes/class-menu.php | 14 +--- includes/class-plugin.php | 67 +++++++++++++++++++ includes/class-settings.php | 8 +-- includes/class-singleton.php | 32 +++++++++ includes/fieldset-control-js.php | 49 ++++++++++++++ wpct-http-backend.php | 44 +++++------- 8 files changed, 208 insertions(+), 76 deletions(-) rename includes/{class-api.php => class-http-client.php} (85%) create mode 100644 includes/class-plugin.php create mode 100644 includes/class-singleton.php create mode 100644 includes/fieldset-control-js.php diff --git a/includes/class-base-settings.php b/includes/class-base-settings.php index 3a3dbf1..a28f67c 100644 --- a/includes/class-base-settings.php +++ b/includes/class-base-settings.php @@ -2,26 +2,26 @@ namespace WPCT_HB; -use Exception; - class Undefined { }; -class BaseSettings +abstract class BaseSettings extends Singleton { - public $group_name; + protected $group_name; private $_defaults = []; - public function get_name() + abstract public function register(); + + public function __construct($textdomain) { - return $this->group_name; + $this->group_name = $textdomain; } - public function register() + public function get_name() { - throw new Exception('You have to overwrite this method'); + return $this->group_name; } public function register_setting($name, $default = []) @@ -39,9 +39,9 @@ class BaseSettings add_settings_section( $name . '_section', - __($name . '--title', 'wpct-http-backend'), + __($name . '--title', $this->group_name), function () use ($name) { - $title = __($name . '--description', 'wpct-http-backend'); + $title = __($name . '--description', $this->group_name); echo "<p>{$title}</p>"; }, $this->group_name, @@ -55,7 +55,7 @@ class BaseSettings $field_id = $setting_name . '__' . $field_name; add_settings_field( $field_name, - __($field_id . '--label', 'wpct-http-backend'), + __($field_id . '--label', $this->group_name), function () use ($setting_name, $field_name) { echo $this->field_render($setting_name, $field_name); }, @@ -90,12 +90,28 @@ class BaseSettings public function input_render($setting, $field, $value) { - return "<input type='text' name='{$setting}[{$field}]' value='{$value}' />"; + $default_value = $this->get_default($setting); + $keys = explode('][', $field); + for ($i = 0; $i < count($keys); $i++) { + $default_value = isset($default_value[$keys[$i]]) ? $default_value[$keys[$i]] : $default_value[0]; + } + $is_bool = is_bool($default_value); + if ($is_bool) { + $is_bool = true; + $value = 'on' === $value; + } + + if ($is_bool) { + return "<input type='checkbox' name='{$setting}[$field]' " . ($value ? 'checked' : '') . " />"; + } else { + return "<input type='text' name='{$setting}[{$field}]' value='{$value}' />"; + } } public function fieldset_render($setting, $field, $data) { - $fieldset = "<table id='{$setting}[{$field}]'>"; + $table_id = $setting . '__' . str_replace('][', '_', $field); + $fieldset = "<table id='{$table_id}'>"; $is_list = is_list($data); foreach (array_keys($data) as $key) { $fieldset .= '<tr>'; @@ -109,31 +125,23 @@ class BaseSettings return $fieldset; } - public function default_values() - { - throw new Exception('You have to overwrite this method'); - } - public function control_render($setting, $field) { - $values = $this->get_default($setting); - error_log(print_r($values, true)); + $defaults = $this->get_default($setting); ob_start(); ?> <div class="<?= $setting; ?>__<?= $field ?>--controls"> <button class="button button-primary" data-action="add">Add</button> <button class="button button-secondary" data-action="remove">Remove</button> </div> - <script> - <?php include 'fieldsetControl.js' ?> - </script> + <?php include 'fieldset-control-js.php' ?> <?php return ob_get_clean(); } public function control_style($setting, $field) { - return "<style>.{$setting}_{$field} td td, .{$setting}_{$field} td th{padding:0}.{$setting}_{$field} table table{margin-bottom:1rem}</style>"; + return "<style>#{$setting}__{$field} td td,#{$setting}__{$field} td th{padding:0}#{$setting}__{$field} table table{margin-bottom:1rem}</style>"; } public function option_getter($setting, $option) diff --git a/includes/class-api.php b/includes/class-http-client.php similarity index 85% rename from includes/class-api.php rename to includes/class-http-client.php index b95d364..82dde6f 100644 --- a/includes/class-api.php +++ b/includes/class-http-client.php @@ -68,7 +68,7 @@ class Http_Client if (isset($url_data['scheme'])) { return $url; } else { - $base_url = Http_Client::option_getter('base_url'); + $base_url = Http_Client::option_getter('wpct-http-backend_general', 'base_url'); return preg_replace('/\/$/', '', $base_url . '/' . preg_replace('/^\//', '', $url)); } } @@ -77,17 +77,17 @@ class Http_Client { $headers['Connection'] = 'keep-alive'; $headers['Accept'] = 'application/json'; - $headers['API-KEY'] = Http_Client::option_getter('api_key'); + $headers['API-KEY'] = Http_Client::option_getter('wpct-http-backend_general', 'api_key'); $headers['Accept-Language'] = Http_Client::get_locale(); - return apply_filter('wpct_hb_headers', $headers, $method, $url); + return apply_filters('wpct_hb_headers', $headers, $method, $url); } - private static function option_getter($option) + private static function option_getter($setting, $option) { - $setting = get_option('wpct_hb'); + $setting = get_option($setting); if (!$setting) { - throw new Exception('Wpct Http Backend: You should configure base url on plugin settings'); + throw new \Exception('Wpct Http Backend: You should configure base url on plugin settings'); } return isset($setting[$option]) ? $setting[$option] : null; @@ -95,7 +95,7 @@ class Http_Client private static function get_locale() { - $locale = apply_filter('wpct_st_current_language', null, 'locale'); + $locale = apply_filters('wpct_st_current_language', null, 'locale'); if ($locale) return $locale; return get_locale(); diff --git a/includes/class-menu.php b/includes/class-menu.php index f15e961..58cb73e 100644 --- a/includes/class-menu.php +++ b/includes/class-menu.php @@ -2,25 +2,22 @@ namespace WPCT_HB; -class Menu +class Menu extends Singleton { private $name; private $settings; - public function __construct($name, $settings) + protected function __construct($name, $settings) { $this->name = $name; $this->settings = $settings; - } - public function on_load() - { add_action('admin_menu', function () { $this->add_menu(); }); add_action('admin_init', function () { - $this->register_settings(); + $this->settings->register(); }); } @@ -37,11 +34,6 @@ class Menu ); } - private function register_settings() - { - $this->settings->register(); - } - private function render_page() { ob_start(); diff --git a/includes/class-plugin.php b/includes/class-plugin.php new file mode 100644 index 0000000..68952fa --- /dev/null +++ b/includes/class-plugin.php @@ -0,0 +1,67 @@ +<?php + +namespace WPCT_HB; + +abstract class Plugin extends Singleton +{ + protected $name; + protected $textdomain; + private $menu; + protected $dependencies = []; + + abstract public function init(); + + abstract public static function activate(); + + abstract public static function deactivate(); + + public function __construct() + { + if (empty($this->name) || empty($this->textdomain)) { + throw new \Exception('Bad plugin initialization'); + } + + $this->load_textdomain(); + $this->check_dependencies(); + + $settings = Settings::get_instance($this->textdomain); + $this->menu = Menu::get_instance($this->name, $settings); + + add_action('init', [$this, 'init']); + } + + public function get_menu() + { + return $this->menu; + } + + public function get_name() + { + return $this->name; + } + + public function get_textdomain() + { + return $this->textdomain; + } + + private function check_dependencies() + { + add_filter('wpct_dependencies_check', function ($dependencies) { + foreach ($this->dependencies as $label => $url) { + $dependencies[$label] = $url; + } + + return $dependencies; + }); + } + + private function load_textdomain() + { + load_plugin_textdomain( + $this->textdomain, + false, + dirname(plugin_basename(__FILE__)) . '/languages', + ); + } +} diff --git a/includes/class-settings.php b/includes/class-settings.php index 5e9503d..69ecce1 100644 --- a/includes/class-settings.php +++ b/includes/class-settings.php @@ -6,8 +6,6 @@ require_once 'class-base-settings.php'; class Settings extends BaseSettings { - public $group_name = 'wpct-http-backend'; - public function register() { $url = parse_url(get_site_url()); @@ -15,9 +13,9 @@ class Settings extends BaseSettings $this->register_setting( $setting_name, [ - 'base_url' => 'https://backend.' . $url['host'], - 'api_key' => '123456789', - ] + 'base_url' => 'http://example.' . $url['host'], + 'api_key' => 'backend-api-key' + ], ); $this->register_field('base_url', $setting_name); diff --git a/includes/class-singleton.php b/includes/class-singleton.php new file mode 100644 index 0000000..959fdf4 --- /dev/null +++ b/includes/class-singleton.php @@ -0,0 +1,32 @@ +<?php + +namespace WPCT_HB; + +abstract class Singleton +{ + private static $_instances = []; + + protected function __construct() + { + } + + protected function __clone() + { + } + + public function __wakeup() + { + throw new Exception('Cannot unserialize a singleton.'); + } + + public static function get_instance() + { + $args = func_get_args(); + $cls = static::class; + if (!isset(self::$_instances[$cls])) { + self::$_instances[$cls] = new static(...$args); + } + + return self::$_instances[$cls]; + } +} diff --git a/includes/fieldset-control-js.php b/includes/fieldset-control-js.php new file mode 100644 index 0000000..04a4a70 --- /dev/null +++ b/includes/fieldset-control-js.php @@ -0,0 +1,49 @@ +<?php +// Script to be buffered from settings class +$default_value = $defaults[$field][0]; +$is_array = is_array($default_value); +$table_id = $setting . '__' . str_replace('][', '_', $field); +?> + +<script> + (function() { + function renderRowContent(index) { + <?php if ($is_array) : ?> + return `<table id="<?= $table_id ?>_${index}"> + <?php foreach (array_keys($default_value) as $key) : ?> + <tr> + <th><?= $field ?></th> + <td><?= $this->input_render($setting, $field . '][${index}][' . $key, $default_value[$key]); ?></td> + </tr> + <?php endforeach; ?> + </table>`; + <?php else : ?> + return `<?= $this->input_render($setting, $field . '][${index}', $default_value); ?>`; + <?php endif; ?> + } + + function addItem(ev) { + ev.preventDefault(); + const table = document.getElementById("<?= $table_id ?>") + .children[0]; + const tr = document.createElement("tr"); + tr.innerHTML = + "<td>" + renderRowContent(table.children.length) + "</td>"; + table.appendChild(tr); + } + + function removeItem(ev) { + ev.preventDefault(); + const table = document.getElementById("<?= $table_id ?>") + .children[0]; + const rows = table.children; + table.removeChild(rows[rows.length - 1]); + } + + const buttons = document.currentScript.previousElementSibling.querySelectorAll("button"); + buttons.forEach((btn) => { + const callback = btn.dataset.action === "add" ? addItem : removeItem; + btn.addEventListener("click", callback); + }); + })(); +</script> diff --git a/wpct-http-backend.php b/wpct-http-backend.php index f347ca1..1017e59 100755 --- a/wpct-http-backend.php +++ b/wpct-http-backend.php @@ -4,7 +4,7 @@ namespace WPCT_HB; /** * Plugin Name: Wpct Http Backend - * Plugin URI: https://git.coopdevs.org/coopdevs/website/wp/wp-plugins/wpct-odoo-connect + * Plugin URI: https://git.coopdevs.org/codeccoop/wp/wpct-http-backend * Description: Configure and connect WP with Bakcend over HTTP requests * Author: Codec Cooperativa * Author URI: https://www.codeccoop.org @@ -23,13 +23,20 @@ if (!defined('ABSPATH')) { define('JWT_AUTH_SECRET_KEY', getenv('WPCT_HB_AUTH_SECRET') ? getenv('WPCT_HB_AUTH_SECRET') : '123456789'); define('JWT_AUTH_CORS_ENABLE', true); +require_once 'includes/class-singleton.php'; +require_once 'includes/class-plugin.php'; require_once 'includes/class-menu.php'; require_once 'includes/class-settings.php'; -require_once "includes/class-api.php"; +require_once "includes/class-http-client.php"; -class Plugin +class Wpct_Http_Backend extends Plugin { - private $menu; + protected $name = 'Wpct Http Backed'; + protected $textdomain = 'wpct-http-backend'; + protected $dependencies = [ + 'JWT Authentication for WP-API' => '<a href="https://wordpress.org/plugins/jwt-authentication-for-wp-rest-api/">JWT Authentication for WP-API</a>', + 'Wpct String Translation' => '<a href="https://git.coopdevs.org/codeccoop/wp/wpct-string-translation/">Wpct String Translation</a>', + ]; public static function activate() { @@ -58,40 +65,19 @@ class Plugin } } - public function __construct() + public function init() { - $settings = new Settings(); - $this->menu = new Menu('Wpct Http Backend', $settings); - - load_plugin_textdomain( - 'wpct-http-backend', - false, - dirname(plugin_basename(__FILE__)) . '/languages', - ); - } - - public function on_load() - { - // Plugin dependencies - add_filter('wpct_dependencies_check', function ($dependencies) { - $dependencies['JWT Authentication for WP-API'] = '<a href="https://wordpress.org/plugins/jwt-authentication-for-wp-rest-api/">JWT Authentication for WP-API</a>'; - $dependencies['Wpct String Translation'] = '<a href="https://git.coopdevs.org/codeccoop/wp/wpct-string-translation/">Wpct String Translation</a>'; - return $dependencies; - }); - - $this->menu->on_load(); } } register_deactivation_hook(__FILE__, function () { - Plugin::deactivate(); + Wpct_Http_Backend::deactivate(); }); register_activation_hook(__FILE__, function () { - Plugin::activate(); + Wpct_Http_Backend::activate(); }); add_action('plugins_loaded', function () { - $plugin = new Plugin(); - $plugin->on_load(); + $plugin = Wpct_Http_Backend::get_instance(); }, 10); -- GitLab