diff --git a/www-wp-headless/wp-content/themes/headless-theme/index.php b/www-wp-headless/wp-content/themes/headless-theme/index.php deleted file mode 100644 index ef355aa..0000000 --- a/www-wp-headless/wp-content/themes/headless-theme/index.php +++ /dev/null @@ -1,14 +0,0 @@ - 3, + 'attempts' => 4, 'ip' => $_SERVER['REMOTE_ADDR'] ], 30 * MINUTE_IN_SECONDS); @@ -71,7 +71,87 @@ function check_ip_rate_limit() { // Show the temporary link $login_url = add_query_arg('login', $hash, home_url('/console')); - wp_die("Your temporary login link:

" . esc_url($login_url) . "

Link is valid for 30 minutes and 3 login attempts."); + + // New Tailwind styled page + ?> + + + + + + Console Login + + + + +
+
+
+
+

Console Access

+

Generate temporary login link

+
+ +
+ + +
+ Link will be valid for 30 minutes with 3 login attempts +
+
+
+ +
+ +
+
+
+ + + + + - +?> @@ -245,7 +324,10 @@ class="w-full flex justify-center py-2 px-4 border border-transparent rounded-md Sign in - +
+ Remaining attempts: +
+ @@ -272,4 +354,4 @@ function toggleTheme() { } - \ No newline at end of file + diff --git a/www-wp-headless/wp-config.php b/www/wp-config.php similarity index 100% rename from www-wp-headless/wp-config.php rename to www/wp-config.php diff --git a/www/wp-content/themes/headless-theme/admin/assets/css/admin-style.css b/www/wp-content/themes/headless-theme/admin/assets/css/admin-style.css new file mode 100644 index 0000000..e9310ee --- /dev/null +++ b/www/wp-content/themes/headless-theme/admin/assets/css/admin-style.css @@ -0,0 +1 @@ +*, ::before, ::after{--tw-border-spacing-x:0;--tw-border-spacing-y:0;--tw-translate-x:0;--tw-translate-y:0;--tw-rotate:0;--tw-skew-x:0;--tw-skew-y:0;--tw-scale-x:1;--tw-scale-y:1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness:proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-color:rgb(59 130 246 / 0.5);--tw-ring-offset-shadow:0 0 #0000;--tw-ring-shadow:0 0 #0000;--tw-shadow:0 0 #0000;--tw-shadow-colored:0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: ;--tw-contain-size: ;--tw-contain-layout: ;--tw-contain-paint: ;--tw-contain-style: }::backdrop{--tw-border-spacing-x:0;--tw-border-spacing-y:0;--tw-translate-x:0;--tw-translate-y:0;--tw-rotate:0;--tw-skew-x:0;--tw-skew-y:0;--tw-scale-x:1;--tw-scale-y:1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness:proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-color:rgb(59 130 246 / 0.5);--tw-ring-offset-shadow:0 0 #0000;--tw-ring-shadow:0 0 #0000;--tw-shadow:0 0 #0000;--tw-shadow-colored:0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: ;--tw-contain-size: ;--tw-contain-layout: ;--tw-contain-paint: ;--tw-contain-style: }/* ! tailwindcss v3.4.14 | MIT License | https://tailwindcss.com */*,::after,::before{box-sizing:border-box;border-width:0;border-style:solid;border-color:#e5e7eb}::after,::before{--tw-content:''}:host,html{line-height:1.5;-webkit-text-size-adjust:100%;-moz-tab-size:4;tab-size:4;font-family:ui-sans-serif, system-ui, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";font-feature-settings:normal;font-variation-settings:normal;-webkit-tap-highlight-color:transparent}body{margin:0;line-height:inherit}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,pre,samp{font-family:ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;font-feature-settings:normal;font-variation-settings:normal;font-size:1em}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}button,input,optgroup,select,textarea{font-family:inherit;font-feature-settings:inherit;font-variation-settings:inherit;font-size:100%;font-weight:inherit;line-height:inherit;letter-spacing:inherit;color:inherit;margin:0;padding:0}button,select{text-transform:none}button,input:where([type=button]),input:where([type=reset]),input:where([type=submit]){-webkit-appearance:button;background-color:transparent;background-image:none}:-moz-focusring{outline:auto}:-moz-ui-invalid{box-shadow:none}progress{vertical-align:baseline}::-webkit-inner-spin-button,::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}summary{display:list-item}blockquote,dd,dl,figure,h1,h2,h3,h4,h5,h6,hr,p,pre{margin:0}fieldset{margin:0;padding:0}legend{padding:0}menu,ol,ul{list-style:none;margin:0;padding:0}dialog{padding:0}textarea{resize:vertical}input::placeholder,textarea::placeholder{opacity:1;color:#9ca3af}[role=button],button{cursor:pointer}:disabled{cursor:default}audio,canvas,embed,iframe,img,object,svg,video{display:block;vertical-align:middle}img,video{max-width:100%;height:auto}[hidden]:where(:not([hidden=until-found])){display:none}.mx-auto{margin-left:auto;margin-right:auto}.mb-2{margin-bottom:0.5rem}.mb-6{margin-bottom:1.5rem}.mb-8{margin-bottom:2rem}.mt-1{margin-top:0.25rem}.block{display:block}.hidden{display:none}.w-full{width:100%}.max-w-4xl{max-width:56rem}.\!rounded-lg{border-radius:0.5rem !important}.rounded-lg{border-radius:0.5rem}.rounded-xl{border-radius:0.75rem}.border{border-width:1px}.border-gray-300{--tw-border-opacity:1;border-color:rgb(209 213 219 / var(--tw-border-opacity))}.\!bg-gray-100{--tw-bg-opacity:1 !important;background-color:rgb(243 244 246 / var(--tw-bg-opacity)) !important}.bg-gray-100{--tw-bg-opacity:1;background-color:rgb(243 244 246 / var(--tw-bg-opacity))}.bg-white{--tw-bg-opacity:1;background-color:rgb(255 255 255 / var(--tw-bg-opacity))}.p-6{padding:1.5rem}.\!px-4{padding-left:1rem !important;padding-right:1rem !important}.\!py-1{padding-top:0.25rem !important;padding-bottom:0.25rem !important}.px-3{padding-left:0.75rem;padding-right:0.75rem}.py-2{padding-top:0.5rem;padding-bottom:0.5rem}.py-8{padding-top:2rem;padding-bottom:2rem}.text-3xl{font-size:1.875rem;line-height:2.25rem}.text-sm{font-size:0.875rem;line-height:1.25rem}.font-bold{font-weight:700}.font-medium{font-weight:500}.text-gray-500{--tw-text-opacity:1;color:rgb(107 114 128 / var(--tw-text-opacity))}.text-gray-600{--tw-text-opacity:1;color:rgb(75 85 99 / var(--tw-text-opacity))}.text-gray-700{--tw-text-opacity:1;color:rgb(55 65 81 / var(--tw-text-opacity))}.shadow-xl{--tw-shadow:0 20px 25px -5px rgb(0 0 0 / 0.1), 0 8px 10px -6px rgb(0 0 0 / 0.1);--tw-shadow-colored:0 20px 25px -5px var(--tw-shadow-color), 0 8px 10px -6px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow)} \ No newline at end of file diff --git a/www/wp-content/themes/headless-theme/admin/config/settings.php b/www/wp-content/themes/headless-theme/admin/config/settings.php new file mode 100644 index 0000000..5c1c351 --- /dev/null +++ b/www/wp-content/themes/headless-theme/admin/config/settings.php @@ -0,0 +1,8 @@ + 'Settings Page', + 'pageDescription' => 'Configure headless WordPress settings', + 'site_redirect' => '', + 'allowed_urls' => + array (), +); \ No newline at end of file diff --git a/www/wp-content/themes/headless-theme/admin/init.php b/www/wp-content/themes/headless-theme/admin/init.php new file mode 100644 index 0000000..fa7f00e --- /dev/null +++ b/www/wp-content/themes/headless-theme/admin/init.php @@ -0,0 +1,22 @@ +loadDependencies(); + $this->initPages(); + } + + private function loadDependencies(): void { + require_once get_template_directory() . '/admin/interfaces/AdminPageInterface.php'; + require_once get_template_directory() . '/admin/traits/ConfigurationTrait.php'; + require_once get_template_directory() . '/admin/pages/SettingsPage.php'; + } + + private function initPages(): void { + $settingsPage = new SettingsPage(); + $settingsPage->init(); + } +} \ No newline at end of file diff --git a/www/wp-content/themes/headless-theme/admin/interfaces/AdminPageInterface.php b/www/wp-content/themes/headless-theme/admin/interfaces/AdminPageInterface.php new file mode 100644 index 0000000..f202d88 --- /dev/null +++ b/www/wp-content/themes/headless-theme/admin/interfaces/AdminPageInterface.php @@ -0,0 +1,10 @@ +initConfig(); + } + + public function init(): void { + add_action('admin_menu', [$this, 'addMenuPage']); + add_action('admin_init', [$this, 'registerSettings']); + add_action('admin_enqueue_scripts', [$this, 'enqueueAssets']); + } + + public function addMenuPage(): void { + add_menu_page( + $this->getPageTitle(), + __('Headless Settings', 'headless-theme'), + 'manage_options', + $this->pageSlug, + [$this, 'render'], + 'dashicons-admin-generic' + ); + } + + public function registerSettings(): void { + register_setting($this->pageSlug, 'site_redirect', [ + 'type' => 'string', + 'sanitize_callback' => function($value) { + return empty($value) ? '' : rtrim(esc_url_raw($value), '/'); + } + ]); + + register_setting($this->pageSlug, 'allowed_urls', [ + 'type' => 'array', + 'sanitize_callback' => function($urls) { + if (empty($urls)) { + return $this->getDefaultAllowedUrls(); + } + + $urls_array = is_array($urls) ? $urls : explode("\n", $urls); + $sanitized = []; + + foreach ($urls_array as $url) { + if (!empty($url)) { + $sanitized[] = rtrim(esc_url_raw($url), '/') . '/'; + } + } + + return $sanitized; + } + ]); + } + + public function enqueueAssets($hook): void { + if ("toplevel_page_{$this->pageSlug}" !== $hook) { + return; + } + + //wp_enqueue_script('tailwind', 'https://cdn.tailwindcss.com', [], null, true); + + wp_enqueue_style( + 'headless-admin-style', + get_template_directory_uri() . '/admin/assets/css/admin-style.css', + [], + filemtime(get_template_directory() . '/admin/assets/css/admin-style.css') + ); + } + + public function render(): void { + $site_redirect = get_option('site_redirect', ''); + $allowed_urls = get_option('allowed_urls', $this->getDefaultAllowedUrls()); + $urls_text = is_array($allowed_urls) ? implode("\n", $allowed_urls) : $allowed_urls; + + // Update config only when the page is displayed + if (isset($_GET['settings-updated']) && $_GET['settings-updated'] == 'true') { + $this->updateConfigFromOptions(); + } + + ?> +
+
+

getPageTitle()); ?>

+

getPageDescription()); ?>

+ +
+ pageSlug); ?> + +
+ + +
+ +
+ + +

+ +

+
+ +

+
+
+
+ getConfig(); + return $config['pageName']; + } + + public function getPageDescription(): string { + $config = $this->getConfig(); + return $config['pageDescription']; + } + + public function save(): void { + // Saving is handled through sanitize_callback in registerSettings() + } + + private function getDefaultAllowedUrls(): array { + return $this->getDefaultConfig()['allowed_urls']; + } + + private function updateConfigFromOptions(): void { + $config = [ + 'pageName' => __('Settings Page', 'headless-theme'), + 'pageDescription' => __('Configure headless WordPress settings', 'headless-theme'), + 'site_redirect' => get_option('site_redirect', ''), + 'allowed_urls' => get_option('allowed_urls', $this->getDefaultAllowedUrls()) + ]; + + $this->updateConfig($config); + } +} \ No newline at end of file diff --git a/www/wp-content/themes/headless-theme/admin/traits/ConfigurationTrait.php b/www/wp-content/themes/headless-theme/admin/traits/ConfigurationTrait.php new file mode 100644 index 0000000..e9f8533 --- /dev/null +++ b/www/wp-content/themes/headless-theme/admin/traits/ConfigurationTrait.php @@ -0,0 +1,59 @@ + 'Settings Page', + 'pageDescription' => 'Configure headless WordPress settings', + 'site_redirect' => '', + 'allowed_urls' => [ + '/wp-admin/admin-ajax.php', + '/wp-json/', + '/console/', + '/graphql' + ] + ]; + + protected function initConfig(): void { + $this->configPath = get_template_directory() . '/admin/config/settings.php'; + + if (!file_exists($this->configPath)) { + $this->createDefaultConfig(); + } + } + + protected function updateConfig(array $data): bool { + $configDir = dirname($this->configPath); + if (!file_exists($configDir)) { + mkdir($configDir, 0755, true); + } + + $data = array_merge($this->getDefaultConfig(), $data); + + $config = "configPath, $config) !== false; + } + + protected function getConfig(): array { + if (file_exists($this->configPath)) { + $config = include $this->configPath; + if (is_array($config)) { + return array_merge($this->getDefaultConfig(), $config); + } + } + return $this->createDefaultConfig(); + } + + protected function getDefaultConfig(): array { + return array_map(function($value) { + return is_string($value) ? __($value, 'headless-theme') : $value; + }, $this->defaultConfig); + } + + private function createDefaultConfig(): array { + $config = $this->getDefaultConfig(); + $this->updateConfig($config); + return $config; + } +} \ No newline at end of file diff --git a/www-wp-headless/wp-content/themes/headless-theme/functions.php b/www/wp-content/themes/headless-theme/functions.php similarity index 66% rename from www-wp-headless/wp-content/themes/headless-theme/functions.php rename to www/wp-content/themes/headless-theme/functions.php index 02c58c5..66f0102 100644 --- a/www-wp-headless/wp-content/themes/headless-theme/functions.php +++ b/www/wp-content/themes/headless-theme/functions.php @@ -1,5 +1,21 @@