diff --git a/app/configs/options.json b/app/configs/options.json
new file mode 100644
index 0000000..5a4d599
--- /dev/null
+++ b/app/configs/options.json
@@ -0,0 +1,204 @@
+{
+ "basic.site.title": {
+ "namespace": "basic.site.title",
+ "public": true,
+ "title": "Site title",
+ "desc": "The site title",
+ "type": "string",
+ "default": "Theme Sakura"
+ },
+ "basic.site.logo": {
+ "namespace": "basic.site.logo",
+ "public": true,
+ "title": "Site logo",
+ "desc": "The site's Logo image, will display on navigation bar.",
+ "type": "mediaPicker",
+ "default": [
+ {
+ "id": 0,
+ "url": "https://v3.vuejs.org/logo.png"
+ }
+ ],
+ "binds": {
+ "title": "Select image for site logo.",
+ "button": "Use this image",
+ "type": "image",
+ "multiple": false
+ }
+ },
+ "social.github": {
+ "namespace": "social.github",
+ "public": true,
+ "title": "Github username",
+ "desc": "Your Github username",
+ "type": "string",
+ "default": ""
+ },
+ "thirdParty.reCaptcha.enable": {
+ "namespace": "thirdParty.reCaptcha.enable",
+ "public": true,
+ "title": "Enable reCAPTCHA",
+ "desc": "Use reCAPTCHA for anti-spam check.",
+ "type": "switcher",
+ "default": false,
+ "binds": {
+ "positiveLabel": "Enabled",
+ "negativeLabel": "Disabled",
+ "disabled": false
+ }
+ },
+ "thirdParty.reCaptcha.version": {
+ "namespace": "thirdParty.reCaptcha.version",
+ "public": true,
+ "title": "reCAPTCHA version",
+ "desc": "Register your reCAPTCHA app 'here', and choose a version.",
+ "type": "choose",
+ "default": null,
+ "binds": {
+ "options": [
+ {
+ "label": "reCAPTCHA version 3",
+ "disabled": false
+ },
+ {
+ "label": "reCAPTCHA version 2",
+ "disabled": false
+ }
+ ]
+ }
+ },
+ "thirdParty.reCaptcha.siteKey": {
+ "namespace": "thirdParty.reCaptcha.siteKey",
+ "public": true,
+ "title": "reCAPTCHA site key",
+ "type": "string",
+ "default": ""
+ },
+ "thirdParty.reCaptcha.secretKey": {
+ "namespace": "thirdParty.reCaptcha.secretKey",
+ "public": false,
+ "title": "reCAPTCHA secret key",
+ "type": "string",
+ "default": ""
+ },
+ "other.hello": {
+ "namespace": "other.hello",
+ "public": true,
+ "title": "Hello world",
+ "type": "string",
+ "default": "world"
+ },
+ "demo.string": {
+ "namespace": "demo.string",
+ "public": true,
+ "title": "String",
+ "desc": "One line string input.",
+ "type": "string",
+ "default": "Hello world!"
+ },
+ "demo.longString": {
+ "namespace": "demo.longString",
+ "public": true,
+ "title": "Long string",
+ "desc": "Textarea for long string input.",
+ "type": "longString",
+ "default": "\"It is the unknown we fear when we look upon death and darkness, nothing more.\"\n-- Albus Dumbledore"
+ },
+ "demo.switcher": {
+ "namespace": "demo.switcher",
+ "public": true,
+ "title": "Switcher",
+ "type": "switcher",
+ "desc": "True/False switcher.",
+ "default": true,
+ "binds": {
+ "positiveLabel": "current on",
+ "negativeLabel": "current off",
+ "disabled": false
+ }
+ },
+ "demo.choose": {
+ "namespace": "demo.choose",
+ "public": true,
+ "title": "Choose",
+ "desc": "Choose one from options.",
+ "type": "choose",
+ "default": null,
+ "binds": {
+ "options": [
+ {
+ "label": "op 1",
+ "disabled": false
+ },
+ {
+ "label": "op 2",
+ "disabled": false
+ },
+ {
+ "label": "op 3",
+ "disabled": false
+ },
+ {
+ "label": "op 4",
+ "disabled": true
+ }
+ ]
+ }
+ },
+ "demo.selection": {
+ "namespace": "demo.selection",
+ "public": true,
+ "title": "Selection",
+ "desc": "Selection multiple items from options. max: {0: no limit, >0: limit}",
+ "type": "selection",
+ "default": [
+ true,
+ false,
+ true
+ ],
+ "binds": {
+ "options": [
+ {
+ "label": "op 1",
+ "disabled": false
+ },
+ {
+ "label": "op 2",
+ "disabled": false
+ },
+ {
+ "label": "op 3",
+ "disabled": false
+ },
+ {
+ "label": "op 4",
+ "disabled": true
+ }
+ ],
+ "max": 2
+ }
+ },
+ "demo.mediaPicker": {
+ "namespace": "demo.mediaPicker",
+ "public": true,
+ "title": "Media picker",
+ "desc": "type=\"image\"|\"video\"|\"audio?\"
, the object must include id, id=0 for remote media.",
+ "type": "mediaPicker",
+ "default": [
+ {
+ "id": 0,
+ "url": "https://view.moezx.cc/images/2021/07/02/d5ab73174d18652d890e2f4d1b9bef8f.gif"
+ },
+ {
+ "id": 0,
+ "url": "https://view.moezx.cc/images/2021/07/02/a90553bf5b67770e87a89b2ce204eaa7.gif"
+ }
+ ],
+ "binds": {
+ "title": "Select Media",
+ "button": "Use this media",
+ "type": "image",
+ "multiple": true
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/controllers/init-state-controller.php b/app/controllers/init-state-controller.php
index 3f7e976..fdce521 100644
--- a/app/controllers/init-state-controller.php
+++ b/app/controllers/init-state-controller.php
@@ -31,9 +31,8 @@ class InitStateController extends BaseController
'menus' => (new MenuController)->get_menus(),
// 'rewrite_rules' => (new \WP_Rewrite())->rewrite_rules(),
'index' => (new WP_Rewrite())->index,
- 'config' => (new ConfigurationController)->public_options(),
- 'recaptcha_site_key' => '6LfHEoEbAAAAAI5p_XBlr1WxEvrsOSNQFCQNcT79', // v2 secret key: 6LfHEoEbAAAAAIh0w2I9PCcVoa0j71mO6t7fipsj
- // 'recaptcha_site_key' => '6LdKhX8bAAAAAF5HJprXtKvg3nfBJMfgd2o007PN' // v3 secret key: 6LdKhX8bAAAAAA010EXlQ32FWoYD1J2sLb8SaYLR
+ 'config' => (new OptionController)->get_public_display_options(),
+ // 'recaptcha_site_key' => sakura_options('thirdParty.reCaptcha.siteKey', ''), // use thirdParty.reCaptcha.siteKey
);
}
diff --git a/app/controllers/configuration-controller.php b/app/controllers/option-controller.php
similarity index 59%
rename from app/controllers/configuration-controller.php
rename to app/controllers/option-controller.php
index 285e503..1cf9374 100644
--- a/app/controllers/configuration-controller.php
+++ b/app/controllers/option-controller.php
@@ -5,24 +5,10 @@ namespace Sakura\Controllers;
use WP_REST_Server;
use WP_REST_Request;
use WP_Error;
-use Sakura\Lib\Exception;
use Sakura\Models\OptionModel;
-class ConfigurationController extends BaseController
+class OptionController extends BaseController
{
- public function public_options()
- {
- $keys = [
- // key => default value
- 'title' => 'Theme Sakura',
- ];
- $res = [];
- foreach ($keys as $key => $default) {
- $res[$key] = $this->sakura_options($key, $default);
- }
- return $res;
- }
-
/**
* Constructor.
*
@@ -49,8 +35,8 @@ class ConfigurationController extends BaseController
array(
array(
'methods' => WP_REST_Server::READABLE,
- 'callback' => array($this, 'get_config'),
- 'permission_callback' => array($this, 'get_config_permissions_check'),
+ 'callback' => array($this, 'get_public_config'),
+ 'permission_callback' => array($this, 'get_public_config_permissions_check'),
// 'args' => $this->get_collection_params(),
),
array(
@@ -64,6 +50,16 @@ class ConfigurationController extends BaseController
);
}
+ public function get_public_config(WP_REST_Request $request)
+ {
+ return $this->get_public_display_options();
+ }
+
+ public function get_public_config_permissions_check(WP_REST_Request $request)
+ {
+ return true;
+ }
+
public function get_config(WP_REST_Request $request)
{
$config = (array) OptionModel::get($this->rest_base);
@@ -85,13 +81,30 @@ class ConfigurationController extends BaseController
public function update_config(WP_REST_Request $request)
{
- $original = (array) $this->get_config($request);
+ $db = (array) $this->get_config($request);
+ $cache = $db;
$json = (array) self::json_validate($request->get_body());
- if (empty(array_diff($original, $json))) {
- return $original;
+ $hasNoDiff = true;
+
+ foreach ($json as $key => $value) {
+ if (array_key_exists($key, $cache)) {
+ $nv = json_encode($value);
+ $ov = json_encode($cache[$key]);
+ if ($hasNoDiff) $hasNoDiff = $nv === $ov;
+ } else {
+ if ($hasNoDiff) $hasNoDiff = false;
+ }
+ $db[$key] = $value;
}
- $config = OptionModel::update($this->rest_base, $json);
+ if ($hasNoDiff) {
+ return [
+ 'code' => 'save_config_succeed',
+ 'message' => __('Configurations already up to date.', self::$text_domain),
+ ];
+ }
+
+ $config = OptionModel::update($this->rest_base, $db);
if (!$config) {
return new WP_Error(
'save_config_failure',
@@ -99,7 +112,10 @@ class ConfigurationController extends BaseController
array('status' => 500)
);
} else {
- return $this->get_config($request);
+ return [
+ 'code' => 'save_config_succeed',
+ 'message' => __('Configurations saved successfully.', self::$text_domain),
+ ];
}
}
@@ -108,10 +124,10 @@ class ConfigurationController extends BaseController
return true;
}
- public function inite_theme()
- {
- $config = OptionModel::create($this->rest_base, (array)[]);
- }
+ // public function inite_theme()
+ // {
+ // $config = OptionModel::create($this->rest_base, (array)[]);
+ // }
public static function json_validate(string $string)
{
@@ -150,4 +166,38 @@ class ConfigurationController extends BaseController
// sprintf(__("No existing database saving value or default value for option '%s'.", self::$text_domain), $namespace)
// );
}
+
+ public static function get_option_json()
+ {
+ $options = file_get_contents(__DIR__ . "/../configs/options.json");
+ return json_decode($options, true);
+ }
+
+ public function get_public_display_options()
+ {
+ $output = [];
+ $defaults = (array) self::get_option_json();
+ // return $defaults;
+ foreach ($defaults as $key => $value) {
+ if ($value['public']) {
+ $output[$value['namespace']] = $this->sakura_options($value['namespace'], $value['default']);
+ }
+ }
+ return $output;
+ }
+
+ /**
+ * Use in admin page only
+ * @return array
+ */
+ public function get_all_options()
+ {
+ $output = [];
+ $defaults = (array) self::get_option_json();
+ // return $defaults;
+ foreach ($defaults as $key => $value) {
+ $output[$value['namespace']] = $this->sakura_options($value['namespace'], $value['default']);
+ }
+ return $output;
+ }
}
diff --git a/app/functions.php b/app/functions.php
index 3936f29..6167c2f 100644
--- a/app/functions.php
+++ b/app/functions.php
@@ -24,6 +24,6 @@ new Sakura\Routers\PagesRouter();
function sakura_options(string $namespace, $default)
{
- $CF = new Sakura\Controllers\ConfigurationController();
+ $CF = new Sakura\Controllers\OptionController();
return $CF->sakura_options($namespace, $default);
}
diff --git a/app/helpers/admin-page-helper.php b/app/helpers/admin-page-helper.php
index daac685..7e540e8 100644
--- a/app/helpers/admin-page-helper.php
+++ b/app/helpers/admin-page-helper.php
@@ -4,6 +4,7 @@ namespace Sakura\Helpers;
use Sakura\Helpers\ViteHelper;
use Sakura\Controllers\InitStateController;
+use Sakura\Controllers\OptionController;
class AdminPageHelper extends ViteHelper
{
@@ -46,7 +47,11 @@ class AdminPageHelper extends ViteHelper
wp_enqueue_script('[type:module]dev-main', self::$development_host . '/src/admin/main.ts', array(), null, true);
+ wp_localize_script('[type:module]dev-main', 'AdminColors', $this->get_admin_color_css());
+
wp_localize_script('[type:module]dev-main', 'InitState', (new InitStateController())->get_initial_state());
+
+ wp_localize_script('[type:module]dev-main', 'SakuraOptions', ['data' => (new OptionController())->get_all_options()]);
}
public function enqueue_production_scripts()
@@ -56,9 +61,13 @@ class AdminPageHelper extends ViteHelper
$manifest = self::get_manifest_file('admin');
//
- wp_enqueue_script('[type:module]chunk-vendors.js', $assets_base_path . $manifest[$entry_key]['file'], array(), null, false);
+ wp_enqueue_script('[type:module]chunk-entrance.js', $assets_base_path . $manifest[$entry_key]['file'], array(), null, false);
- wp_localize_script('[type:module]chunk-vendors.js', 'InitState', (new InitStateController())->get_initial_state());
+ wp_localize_script('[type:module]chunk-entrance.js', 'AdminColors', $this->get_admin_color_css());
+
+ wp_localize_script('[type:module]chunk-entrance.js', 'InitState', (new InitStateController())->get_initial_state());
+
+ wp_localize_script('[type:module]chunk-entrance.js', 'SakuraOptions', (new OptionController())->get_all_options());
//
foreach ($manifest[$entry_key]['imports'] as $index => $import) {
@@ -85,4 +94,26 @@ class AdminPageHelper extends ViteHelper
wp_enqueue_style('fontawesome-free', 'https://cdn.jsdelivr.net/npm/@fortawesome/fontawesome-free@5.15.3/css/all.min.css');
}
+
+ public function get_admin_color_css()
+ {
+ // {"name":"Default","url":false,"colors":["#1d2327","#2c3338","#2271b1","#72aee6"],"icon_colors":{"base":"#a7aaad","focus":"#72aee6","current":"#fff"}}
+ global $_wp_admin_css_colors;
+ $theme = (array) $_wp_admin_css_colors[get_user_option('admin_color')];
+ // $scheme = [
+ // 'dark-primary' => $theme['colors'][0],
+ // 'dark-secondary' => $theme['colors'][1],
+ // 'light-primary' => $theme['colors'][2],
+ // 'light-secondary' => $theme['colors'][3],
+ // 'icon-base' => $theme['icon_colors']['base'],
+ // 'icon-focus' => $theme['icon_colors']['focus'],
+ // 'icon-current' => $theme['icon_colors']['current'],
+ // ];
+ return $theme;
+ // $css = '';
+ // foreach ($scheme as $key => $value) {
+ // $css .= "--{$key}:{$value};";
+ // }
+ // return $css;
+ }
}
diff --git a/app/helpers/setup-helper.php b/app/helpers/setup-helper.php
index c7db468..cd66d39 100644
--- a/app/helpers/setup-helper.php
+++ b/app/helpers/setup-helper.php
@@ -2,7 +2,7 @@
namespace Sakura\Helpers;
-use Sakura\Controllers\ConfigurationController;
+use Sakura\Controllers\OptionController;
class SetupHelper
{
@@ -22,7 +22,7 @@ class SetupHelper
// count post views
add_action('get_header', [$this, 'set_post_views']);
// Inite config options
- add_action('after_switch_theme', [new ConfigurationController(), 'inite_theme'], 1, 2);
+ add_action('after_switch_theme', [new OptionController(), 'inite_theme'], 1, 2);
}
public function setup()
diff --git a/app/helpers/vite-helper.php b/app/helpers/vite-helper.php
index 418ee62..7c0070f 100644
--- a/app/helpers/vite-helper.php
+++ b/app/helpers/vite-helper.php
@@ -67,12 +67,7 @@ class ViteHelper extends BaseClass
wp_enqueue_style('normalize.css', 'https://cdn.jsdelivr.net/npm/normalize.css/normalize.css');
- // TODO: don't use vue.js as handler
- // wp_enqueue_script('vue.js', 'https://unpkg.com/vue@next', array(), false, false);
-
- // wp_localize_script('vue.js', 'InitState', (new InitStateController())->get_initial_state());
-
- wp_enqueue_script('recaptcha', 'https://www.recaptcha.net/recaptcha/api.js?render=6LdKhX8bAAAAAF5HJprXtKvg3nfBJMfgd2o007PN', array(), false, true);
+ wp_enqueue_script('recaptcha', 'https://www.recaptcha.net/recaptcha/api.js', array(), false, true);
}
public static function script_tag_filter($tag, $handle, $src)
diff --git a/app/routers/api-router.php b/app/routers/api-router.php
index 9dc322f..69f6e9b 100644
--- a/app/routers/api-router.php
+++ b/app/routers/api-router.php
@@ -4,7 +4,7 @@ namespace Sakura\Routers;
use WP_REST_Controller;
use WP_REST_Server;
-use Sakura\Controllers\ConfigurationController;
+use Sakura\Controllers\OptionController;
use Sakura\Controllers\InitStateController;
use Sakura\Controllers\MenuController;
use Sakura\Controllers\PostController;
@@ -33,7 +33,9 @@ class ApiRouter extends WP_REST_Controller
*/
public function register_rest_routes()
{
- add_action('rest_api_init', [new ConfigurationController(), 'register_routes']);
+ // add options support
+ add_action('rest_api_init', [new OptionController(), 'register_routes']);
+
add_action('rest_api_init', function () {
// theme's initial states
register_rest_route(
diff --git a/app/utils/tools.php b/app/utils/tools.php
index cad2eb6..f196149 100644
--- a/app/utils/tools.php
+++ b/app/utils/tools.php
@@ -2,6 +2,8 @@
namespace Sakura\Utils;
+use Rogervila\ArrayDiffMultidimensional;
+
class Tools
{
public static function echo_interceptor(callable $callback, ...$args)
@@ -13,15 +15,46 @@ class Tools
return $output;
}
- // public function get_text_from_dom($node, $text) {
- // if (!is_null($node->childNodes)) {
- // foreach ($node->childNodes as $node) {
- // $text = get_text_from_dom($node, $text);
- // }
- // }
- // else {
- // return $text . $node->textContent . ' ';
- // }
- // return $text;
- // }
+ public static function get_text_from_dom($node, $text)
+ {
+ if (!is_null($node->childNodes)) {
+ foreach ($node->childNodes as $node) {
+ $text = self::get_text_from_dom($node, $text);
+ }
+ } else {
+ return $text . $node->textContent . ' ';
+ }
+ return $text;
+ }
+
+ /**
+ * https://stackoverflow.com/a/3877494/8083009
+ *
+ * @param array $aArray1
+ * @param array $aArray2
+ *
+ * @return array
+ */
+ public static function array_recursive_diff(array $aArray1, array $aArray2)
+ {
+ $aReturn = array();
+
+ foreach ($aArray1 as $mKey => $mValue) {
+ if (array_key_exists($mKey, $aArray2)) {
+ if (is_array($mValue)) {
+ $aRecursiveDiff = self::array_recursive_diff($mValue, $aArray2[$mKey]);
+ if (count($aRecursiveDiff)) {
+ $aReturn[$mKey] = $aRecursiveDiff;
+ }
+ } else {
+ if ($mValue != $aArray2[$mKey]) {
+ $aReturn[$mKey] = $mValue;
+ }
+ }
+ } else {
+ $aReturn[$mKey] = $mValue;
+ }
+ }
+ return $aReturn;
+ }
}
diff --git a/app/views/helpers/admin-page-helper.twig b/app/views/helpers/admin-page-helper.twig
index 8cd3b65..26f73dc 100644
--- a/app/views/helpers/admin-page-helper.twig
+++ b/app/views/helpers/admin-page-helper.twig
@@ -1,5 +1,5 @@
{% block admin_app %}
-