Skip to content

Commit

Permalink
version 2
Browse files Browse the repository at this point in the history
  • Loading branch information
alexy-os committed Nov 14, 2024
1 parent 36b1bc6 commit ec9bd71
Show file tree
Hide file tree
Showing 15 changed files with 422 additions and 34 deletions.
14 changes: 0 additions & 14 deletions www-wp-headless/wp-content/themes/headless-theme/index.php

This file was deleted.

File renamed without changes.
94 changes: 88 additions & 6 deletions www-wp-headless/console/index.php → www/console/index.php
Original file line number Diff line number Diff line change
Expand Up @@ -63,15 +63,95 @@ function check_ip_rate_limit() {

// Save the hash in a transient with attempt limit
set_transient('login_hash_' . $hash, [
'attempts' => 3,
'attempts' => 4,
'ip' => $_SERVER['REMOTE_ADDR']
], 30 * MINUTE_IN_SECONDS);

log_access_attempt('hash_generated', $hash);

// Show the temporary link
$login_url = add_query_arg('login', $hash, home_url('/console'));
wp_die("Your temporary login link: <br><br>" . esc_url($login_url) . "<br><br>Link is valid for 30 minutes and 3 login attempts.");

// New Tailwind styled page
?>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Console Login</title>
<script src="https://cdn.tailwindcss.com"></script>
<script>
tailwind.config = {
darkMode: 'class',
theme: {
extend: {
colors: {
border: "hsl(var(--border))",
input: "hsl(var(--input))",
background: "hsl(var(--background))",
}
}
}
}

// Dark theme check
if (localStorage.theme === 'dark' || (!('theme' in localStorage) && window.matchMedia('(prefers-color-scheme: dark)').matches)) {
document.documentElement.classList.add('dark')
}
</script>
</head>
<body class="h-full bg-gradient-to-br from-gray-50 to-gray-100 dark:from-gray-900 dark:to-gray-800">
<div class="min-h-screen flex items-center justify-center py-12 px-4 sm:px-6 lg:px-8">
<div class="max-w-md w-full space-y-8">
<div class="bg-white dark:bg-gray-800 p-8 rounded-lg shadow-lg border border-gray-200 dark:border-gray-700">
<div class="text-center mb-8">
<h2 class="text-2xl font-semibold text-gray-900 dark:text-gray-100">Console Access</h2>
<p class="mt-2 text-sm text-gray-600 dark:text-gray-400">Generate temporary login link</p>
</div>

<div class="space-y-6">
<button
onclick="window.location.href='<?php echo esc_url($login_url); ?>'"
class="w-full flex justify-center py-2 px-4 border border-transparent rounded-md shadow-sm text-sm font-medium text-white
bg-gradient-to-r from-blue-500 to-blue-600 hover:from-blue-600 hover:to-blue-700
focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500"
>
Get Login Link
</button>

<div class="text-sm text-gray-600 dark:text-gray-400 text-center">
Link will be valid for 30 minutes with 3 login attempts
</div>
</div>
</div>
<!-- Theme switcher -->
<div class="text-center">
<button
onclick="toggleTheme()"
class="text-sm text-gray-600 dark:text-gray-400 hover:text-gray-900 dark:hover:text-gray-100"
>
Toggle theme
</button>
</div>
</div>
</div>
</div>
<script>
function toggleTheme() {
if (document.documentElement.classList.contains('dark')) {
document.documentElement.classList.remove('dark')
localStorage.theme = 'light'
} else {
document.documentElement.classList.add('dark')
localStorage.theme = 'dark'
}
}
</script>
</body>
</html>
<?php
exit;
}

// Check the hash and attempts
Expand Down Expand Up @@ -153,8 +233,7 @@ function check_ip_rate_limit() {
log_access_attempt('login_failed', $hash);
}
}
?>
<!DOCTYPE html>
?><!DOCTYPE html>
<html lang="en" class="h-full">
<head>
<meta charset="UTF-8">
Expand Down Expand Up @@ -245,7 +324,10 @@ class="w-full flex justify-center py-2 px-4 border border-transparent rounded-md
Sign in
</button>
</div>
</form>
<div class="text-sm text-gray-600 dark:text-gray-400 text-center mb-4">
Remaining attempts: <?php echo $hash_data['attempts'] + 1; ?>
</div>
</form>
</div>

<!-- Theme switcher -->
Expand All @@ -272,4 +354,4 @@ function toggleTheme() {
}
</script>
</body>
</html>
</html>
File renamed without changes.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<?php
return array (
'pageName' => 'Settings Page',
'pageDescription' => 'Configure headless WordPress settings',
'site_redirect' => '',
'allowed_urls' =>
array (),
);
22 changes: 22 additions & 0 deletions www/wp-content/themes/headless-theme/admin/init.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<?php
namespace HeadlessTheme\Admin;

use HeadlessTheme\Admin\Pages\SettingsPage;

class AdminInit {
public function __construct() {
$this->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();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<?php
namespace HeadlessTheme\Admin\Interfaces;

interface AdminPageInterface {
public function init(): void;
public function render(): void;
public function save(): void;
public function getPageTitle(): string;
public function getPageDescription(): string;
}
158 changes: 158 additions & 0 deletions www/wp-content/themes/headless-theme/admin/pages/SettingsPage.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
<?php
namespace HeadlessTheme\Admin\Pages;

use HeadlessTheme\Admin\Interfaces\AdminPageInterface;
use HeadlessTheme\Admin\Traits\ConfigurationTrait;

class SettingsPage implements AdminPageInterface {
use ConfigurationTrait;

private string $pageSlug = 'headless-settings';

public function __construct() {
$this->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();
}

?>
<div class="wrap">
<div class="max-w-4xl mx-auto py-8">
<h1 class="text-3xl font-bold mb-6"><?php echo esc_html($this->getPageTitle()); ?></h1>
<p class="text-gray-600 mb-8"><?php echo esc_html($this->getPageDescription()); ?></p>

<form method="post" action="options.php" class="bg-white rounded-xl shadow-xl p-6">
<?php settings_fields($this->pageSlug); ?>

<div class="mb-6">
<label class="block text-sm font-medium text-gray-700 mb-2">
<?php _e('Site Redirect URL', 'headless-theme'); ?>
</label>
<input
type="url"
name="site_redirect"
value="<?php echo esc_attr($site_redirect); ?>"
class="w-full px-3 py-2 !bg-gray-100 border border-gray-300 !rounded-lg"
pattern="https://.*"
>
</div>

<div class="mb-6">
<label class="block text-sm font-medium text-gray-700 mb-2">
<?php _e('Allowed URLs', 'headless-theme'); ?>
</label>
<textarea
name="allowed_urls"
rows="4"
class="w-full px-3 py-2 bg-gray-100 border border-gray-300 rounded-lg"
><?php echo esc_textarea($urls_text); ?></textarea>
<p class="text-sm text-gray-500 mt-1">
<?php _e('Enter one URL per line', 'headless-theme'); ?>
</p>
</div>

<p class="submit"><input type="submit" name="submit" id="submit" class="button button-primary !rounded-lg !py-1 !px-4" value="Save setting"></p>
</form>
</div>
</div>
<?php
}

public function getPageTitle(): string {
$config = $this->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);
}
}
Loading

0 comments on commit ec9bd71

Please sign in to comment.