From 46390c244762cf2d60299c74656412e3d53796df Mon Sep 17 00:00:00 2001 From: Joey Kimsey Date: Tue, 25 Feb 2025 14:03:12 -0500 Subject: [PATCH] wip --- app/classes/config.php | 2 +- app/classes/email.php | 2 +- app/classes/forms.php | 6 +- app/controllers/register.php | 27 +++++- app/css/main-dark.css | 1 + app/css/main.css | 40 ++++----- app/js/main.js | 21 +++-- app/models/user.php | 2 +- app/plugins/comments/plugin.php | 2 +- app/plugins/contact/controllers/contact.php | 20 ++++- app/plugins/contact/views/create.html | 1 + .../controllers/admin/notifications.php | 2 - .../notifications/models/notification.php | 20 ++--- app/plugins/turnstile/plugin.php | 85 +++++++++++++++++++ app/plugins/turnstile/views/widget.html | 1 + app/templates/default/default.inc.php | 1 + app/views/admin/users/list.html | 4 +- app/views/auth/register.html | 8 ++ app/views/faq.html | 4 +- app/views/pwa.html | 4 +- app/views/start.html | 4 +- 21 files changed, 197 insertions(+), 60 deletions(-) create mode 100644 app/plugins/turnstile/plugin.php create mode 100644 app/plugins/turnstile/views/widget.html diff --git a/app/classes/config.php b/app/classes/config.php index dff2866..330b247 100644 --- a/app/classes/config.php +++ b/app/classes/config.php @@ -74,7 +74,7 @@ class Config extends BedrockConfig { $html .= '
'; $html .= '

Current Value

'; $html .= '
'; - $html .= ''; + $html .= ''; $html .= '
'; $html .= '
'; $html .= '
'; diff --git a/app/classes/email.php b/app/classes/email.php index 142c6a8..1ac110e 100644 --- a/app/classes/email.php +++ b/app/classes/email.php @@ -164,7 +164,7 @@ class Email { } } $data->MAIL_FOOT = Views::simpleView( 'email.foot' ); - $data->MAIL_TITLE = self::$title; + $data->MAIL_TITLE = Template::parse( self::$title ); $data->MAIL_BODY = Template::parse( self::$message, $data ); $subject = Template::parse( self::$subject, $data ); $body = Views::simpleView( 'email.template', $data ); diff --git a/app/classes/forms.php b/app/classes/forms.php index 28201a2..728adac 100644 --- a/app/classes/forms.php +++ b/app/classes/forms.php @@ -358,6 +358,10 @@ class Forms extends Check { self::addUserError( 'Invalid Email.' ); return false; } + if ( $user->usernameExists( Input::post( 'username' ) ) ) { + self::addUserError( 'A user with that username is already registered.' ); + return false; + } if ( !$user->noEmailExists( Input::post( 'email' ) ) ) { self::addUserError( 'A user with that email is already registered.' ); return false; @@ -374,7 +378,7 @@ class Forms extends Check { self::addUserError( 'You must agree to the terms of service.' ); return false; } - if ( !self::token() ) { + if ( ! self::token() ) { return false; } return true; diff --git a/app/controllers/register.php b/app/controllers/register.php index c04e6ca..f8ecbe3 100644 --- a/app/controllers/register.php +++ b/app/controllers/register.php @@ -25,6 +25,7 @@ use TheTempusProject\TheTempusProject as App; use TheTempusProject\Classes\Controller; use TheTempusProject\Classes\Forms; use TheTempusProject\Bedrock\Classes\Config; +use TheTempusProject\Plugins\Turnstile; class Register extends Controller { public function confirm( $code = null ) { @@ -47,22 +48,40 @@ class Register extends Controller { public function index() { self::$title = '{SITENAME} Sign Up'; self::$pageDescription = 'Many features of {SITENAME} are disabled or hidden from unregistered users. On this page you can sign up for an account to access all the app has to offer.'; - if ( ! Config::getValue( 'main/registrationEnabled' ) ) { return Issues::add( 'notice', 'The site administrator has disable the ability to register a new account.' ); } - + $turnstile = ''; + if ( class_exists( 'TheTempusProject\Plugins\Turnstile' ) ) { + $turnstile = new Turnstile; + if ( ! $turnstile->checkEnabled() ) { + Components::set( 'TURNSTILE_WIDGET', '' ); + $turnstile = ''; + } + } else { + Components::set( 'TURNSTILE_WIDGET', '' ); + } Components::set( 'TERMS', Views::simpleView( 'auth.terms' ) ); if ( App::$isLoggedIn ) { return Issues::add( 'notice', 'You are currently logged in.' ); } - if ( !Input::exists() ) { + if ( ! Input::exists() ) { return Views::view( 'auth.register' ); } - if ( !Forms::check( 'register' ) ) { + if ( Input::exists( 'userEmail' ) ) { + // for the really bad AI / headless bots + Session::flash( 'success', 'Thank you for registering! Please check your email to confirm your account.' ); + Redirect::to( 'home/index' ); + } + if ( ! Forms::check( 'register' ) ) { Issues::add( 'error', [ 'There was an error with your registration.' => Check::userErrors() ] ); return Views::view( 'auth.register' ); } + if ( ! empty( $turnstile ) ) { + if ( empty( $turnstile->verify() ) ) { + return Views::view( 'auth.register' ); + } + } self::$user->create( [ 'username' => Input::post( 'username' ), 'password' => Hash::make( Input::post( 'password' ) ), diff --git a/app/css/main-dark.css b/app/css/main-dark.css index f6306ae..eb01391 100644 --- a/app/css/main-dark.css +++ b/app/css/main-dark.css @@ -8,6 +8,7 @@ * @link https://TheTempusProject.com * @license https://opensource.org/licenses/MIT [MIT LICENSE] */ + .context-popover { background-color: #383838; color: white; diff --git a/app/css/main.css b/app/css/main.css index 86cd59f..19640bb 100644 --- a/app/css/main.css +++ b/app/css/main.css @@ -8,40 +8,40 @@ * @link https://TheTempusProject.com * @license https://opensource.org/licenses/MIT [MIT LICENSE] */ -.facebook { + .facebook { border-color: #1877F2 !important; /* Facebook Blue */ color: #1877F2 !important; -} - -.x-black { + } + + .x-black { border-color: #000000 !important; /* X (formerly Twitter) Black */ color: #000000 !important; -} - -.reddit { + } + + .reddit { border-color: #FF4500 !important; /* Reddit Orange */ color: #FF4500 !important; -} - -.opera { + } + + .opera { border-color: #FF1B2D !important; /* Opera Red */ color: #FF1B2D !important; -} - -.firefox { + } + + .firefox { border-color: #FF7139 !important; /* Firefox Orange */ color: #FF7139 !important; -} - -.edge { + } + + .edge { border-color: #0078D7 !important; /* Microsoft Edge Blue */ color: #0078D7 !important; -} - -.safari { + } + + .safari { border-color: #0B78E3 !important; /* Safari Blue */ color: #0B78E3 !important; -} + } .context-main-border { border-color: #1e1e1e!important; diff --git a/app/js/main.js b/app/js/main.js index 95869a2..42bc568 100644 --- a/app/js/main.js +++ b/app/js/main.js @@ -25,26 +25,28 @@ if ( ! localStorage.getItem("pwaInstallDismissed") ) { deferredPrompt = event; installPrompt.classList.remove("d-none"); installPrompt.classList.add("d-block"); // Show the alert - chromeMessage.classList.remove("d-none"); - chromeMessage.classList.add("d-block"); // Show the prompt + if ( chromeMessage ) { + chromeMessage.classList.remove("d-none"); + chromeMessage.classList.add("d-block"); // Show the prompt + } }); if ( isIos() && ! isInStandaloneMode() ) { installPrompt.classList.remove("d-none"); installPrompt.classList.add("d-block"); // Show the alert - iosMessage.classList.remove("d-none"); - iosMessage.classList.add("d-block"); // Show the prompt + + if ( iosMessage ) { + iosMessage.classList.remove("d-none"); + iosMessage.classList.add("d-block"); // Show the prompt + } } } // ios REQUIRES a service worker -if ( 'serviceWorker' in navigator ) { - navigator.serviceWorker.register('app/js/sw.js') +if ('serviceWorker' in navigator) { + navigator.serviceWorker.register('/sw.js') .then(() => console.log('Service Worker Registered')); } -// self.addEventListener('install', () => self.skipWaiting()); -// self.addEventListener('activate', () => self.clients.claim()); -// self.addEventListener('fetch', () => {}); // No file interception // Handle Install Button Click if ( installButton ) { @@ -299,6 +301,7 @@ document.querySelectorAll('[data-bs-toggle="collapse"]').forEach(button => { }); + // this should load all popovers document.addEventListener("DOMContentLoaded", function () { var popoverTriggerList = [].slice.call(document.querySelectorAll('[data-bs-toggle="popover"]')); diff --git a/app/models/user.php b/app/models/user.php index 0bbf75a..7d07d12 100644 --- a/app/models/user.php +++ b/app/models/user.php @@ -641,7 +641,7 @@ class User extends DatabaseModel { Debug::error( 'User not created.' ); return false; } - return true; + return self::$db->lastId(); } /** diff --git a/app/plugins/comments/plugin.php b/app/plugins/comments/plugin.php index 61fb325..752c021 100644 --- a/app/plugins/comments/plugin.php +++ b/app/plugins/comments/plugin.php @@ -65,7 +65,7 @@ class Comments extends Plugin { public function __construct( $load = false ) { if ( !empty(App::$activePerms) ) { - App::$isMod = !empty(App::$activePerms['modAccess']); + App::$isMod = ! empty( App::$activePerms['modAccess'] ); } else { App::$isMod = false; } diff --git a/app/plugins/contact/controllers/contact.php b/app/plugins/contact/controllers/contact.php index 7cb5969..68050a6 100644 --- a/app/plugins/contact/controllers/contact.php +++ b/app/plugins/contact/controllers/contact.php @@ -21,6 +21,7 @@ use TheTempusProject\Bedrock\Functions\Input; use TheTempusProject\Bedrock\Functions\Session; use TheTempusProject\Hermes\Functions\Redirect; use TheTempusProject\Models\Contact as ContactModel; +use TheTempusProject\Plugins\Turnstile; class Contact extends Controller { protected static $contact; @@ -29,13 +30,28 @@ class Contact extends Controller { self::$contact = new ContactModel; self::$title = 'Contact - {SITENAME}'; self::$pageDescription = 'At {SITENAME}, we value our users\' input. You can provide any contact or suggestions using this form.'; - if ( !Input::exists() ) { + $turnstile = ''; + if ( class_exists( 'TheTempusProject\Plugins\Turnstile' ) ) { + $turnstile = new Turnstile; + if ( ! $turnstile->checkEnabled() ) { + Components::set( 'TURNSTILE_WIDGET', '' ); + $turnstile = ''; + } + } else { + Components::set( 'TURNSTILE_WIDGET', '' ); + } + if ( ! Input::exists() ) { return Views::view( 'contact.create' ); } - if ( !Forms::check( 'contact' ) ) { + if ( ! Forms::check( 'contact' ) ) { Issues::add( 'error', [ 'There was an error with your form, please check your submission and try again.' => Check::userErrors() ] ); return Views::view( 'contact.create' ); } + if ( ! empty( $turnstile ) ) { + if ( empty( $turnstile->verify() ) ) { + return Views::view( 'contact.create' ); + } + } $result = self::$contact->create( Input::post( 'name' ), Input::post( 'contactEmail' ), Input::post( 'entry' ) ); if ( $result ) { Session::flash( 'success', 'Thank you! Your contact has been received.' ); diff --git a/app/plugins/contact/views/create.html b/app/plugins/contact/views/create.html index c4783ba..1d17811 100644 --- a/app/plugins/contact/views/create.html +++ b/app/plugins/contact/views/create.html @@ -40,6 +40,7 @@
+ {TURNSTILE_WIDGET}
diff --git a/app/plugins/notifications/controllers/admin/notifications.php b/app/plugins/notifications/controllers/admin/notifications.php index ac935f1..b14fd0a 100644 --- a/app/plugins/notifications/controllers/admin/notifications.php +++ b/app/plugins/notifications/controllers/admin/notifications.php @@ -35,8 +35,6 @@ class Notifications extends AdminController { self::$notifications = new NotificationsModel; self::$user = new User; self::$group = new Group; - $view = Navigation::activePageSelect( 'nav.admin', '/admin/Notifications' ); - Components::set( 'ADMINNAV', $view ); } public function index( $data = null ) { diff --git a/app/plugins/notifications/models/notification.php b/app/plugins/notifications/models/notification.php index d9f939c..db8fc6f 100644 --- a/app/plugins/notifications/models/notification.php +++ b/app/plugins/notifications/models/notification.php @@ -121,21 +121,21 @@ class Notification extends DatabaseModel { return true; } - public function filter( $messageArray, $filters = [] ) { + public function filter( $entities, $filters = [] ) { $out = []; - foreach ( $messageArray as $message ) { - if ( !is_object( $message ) ) { - $message = $messageArray; + foreach ( $entities as $entity ) { + if ( !is_object( $entity ) ) { + $entity = $entities; $end = true; } - if ( $message->seenAt == 0 ) { - $message->unseenBadge = Views::simpleView( 'notifications.unseenBadge' ); - $message->markReadLink = ''; + if ( $entity->seenAt == 0 ) { + $entity->unseenBadge = Views::simpleView( 'notifications.unseenBadge' ); + $entity->markReadLink = ''; } else { - $message->unseenBadge = ''; - $message->markReadLink = ''; + $entity->unseenBadge = ''; + $entity->markReadLink = ''; } - $out[] = (object) $message; + $out[] = (object) $entity; if ( !empty( $end ) ) { $out = $out[0]; break; diff --git a/app/plugins/turnstile/plugin.php b/app/plugins/turnstile/plugin.php new file mode 100644 index 0000000..d2a6e4d --- /dev/null +++ b/app/plugins/turnstile/plugin.php @@ -0,0 +1,85 @@ + + * @link https://TheTempusProject.com + * @license https://opensource.org/licenses/MIT [MIT LICENSE] + */ +namespace TheTempusProject\Plugins; + +use TheTempusProject\TheTempusProject as App; +use TheTempusProject\Classes\Plugin; +use TheTempusProject\Models\Notification; +use TheTempusProject\Houdini\Classes\Components; +use TheTempusProject\Houdini\Classes\Views; +use TheTempusProject\Bedrock\Classes\Config; +use TheTempusProject\Bedrock\Functions\Input; +use TheTempusProject\Houdini\Classes\Issues; + +class Turnstile extends Plugin { + private static $loaded = false; + public $pluginName = 'TP Turnstile'; + public $pluginAuthor = 'JoeyK'; + public $pluginWebsite = 'https://TheTempusProject.com'; + public $modelVersion = '1.0'; + public $pluginVersion = '3.0'; + public $pluginDescription = 'A simple plugin which adds a site wide cloudflare turnstile integration.'; + public $configName = 'turnstile'; + public $configMatrix = [ + 'secretKey' => [ + 'type' => 'text', + 'pretty' => 'Turnstile Secret Key', + 'default' => 'xxxxxxxxxxxxx', + ], + 'apiKey' => [ + 'type' => 'text', + 'pretty' => 'Turnstile API Key', + 'default' => 'xxxxxxxxxxxxxx', + ], + ]; + + public function __construct( $load = false ) { + parent::__construct( $load ); + if ( ! self::$loaded ) { + if ( $this->checkEnabled() ) { + Components::set( 'TURNSTILE_API_KEY', Config::getValue( 'turnstile/apiKey' ) ); + Components::set( 'TURNSTILE_WIDGET', Views::simpleView( 'turnstile.widget') ); + Components::append( 'TEMPLATE_JS_INCLUDES', '' ); + } + self::$loaded = true; + } + } + + public function verify() { + if ( ! Input::exists('cf-turnstile-response') ) { + Issues::add( 'notice', 'Turnstile verification failed. Please try again.' ); + return false; + } + $verify_url = "https://challenges.cloudflare.com/turnstile/v0/siteverify"; + $data = [ + "secret" => Config::getValue( 'turnstile/secretKey' ), + "response" => Input::post('cf-turnstile-response'), + "remoteip" => $_SERVER["REMOTE_ADDR"] // Optional, helps detect abuse + ]; + $options = [ + "http" => [ + "header" => "Content-Type: application/x-www-form-urlencoded", + "method" => "POST", + "content" => http_build_query($data) + ] + ]; + $context = stream_context_create($options); + $response = file_get_contents($verify_url, false, $context); + $result = json_decode($response, true); + if ( ! $result["success"]) { + Issues::add( 'notice', 'Turnstile verification failed. Please try again. If the issue persists, please contact the site administrator.' ); + return false; + } + return true; + } +} diff --git a/app/plugins/turnstile/views/widget.html b/app/plugins/turnstile/views/widget.html new file mode 100644 index 0000000..b129fc9 --- /dev/null +++ b/app/plugins/turnstile/views/widget.html @@ -0,0 +1 @@ +
\ No newline at end of file diff --git a/app/templates/default/default.inc.php b/app/templates/default/default.inc.php index 6747556..d84b00d 100644 --- a/app/templates/default/default.inc.php +++ b/app/templates/default/default.inc.php @@ -49,6 +49,7 @@ class DefaultLoader extends Loader { Components::set( 'FONT_AWESOME_URL', self::FONT_AWESOME_URL ); } $this->addJs( '' ); + $this->addJs( '' ); Components::setIfNull( 'LOGO', Config::getValue( 'main/logo' ) ?? TP_DEFAULT_LOGO ); if ( ! empty( Config::getValue( 'share/enabled' ) ) ) { diff --git a/app/views/admin/users/list.html b/app/views/admin/users/list.html index 79110bd..430afe2 100644 --- a/app/views/admin/users/list.html +++ b/app/views/admin/users/list.html @@ -19,7 +19,7 @@ {LOOP} - {ID} + {ID} {usernamePretty} {DTC date}{registered}{/DTC} @@ -31,7 +31,7 @@ {/LOOP} {ALT} - + No results to show. diff --git a/app/views/auth/register.html b/app/views/auth/register.html index 23281cd..79a407f 100644 --- a/app/views/auth/register.html +++ b/app/views/auth/register.html @@ -15,6 +15,7 @@
+
@@ -42,6 +43,13 @@ + +
+
+ {TURNSTILE_WIDGET} +
+
+
diff --git a/app/views/faq.html b/app/views/faq.html index b914758..956f2b6 100644 --- a/app/views/faq.html +++ b/app/views/faq.html @@ -56,8 +56,8 @@
- {SITENAME} is open source and available free of charge through GitLab and Packagist. - The developer behind the project is Joey Kimsey and he can be contacted through his website for development services. + {SITENAME} is open source and available free of charge through GitLab and Packagist. + The developer behind the project is Joey Kimsey and he can be contacted through his website for development services.
diff --git a/app/views/pwa.html b/app/views/pwa.html index 45bfae8..360088a 100644 --- a/app/views/pwa.html +++ b/app/views/pwa.html @@ -10,9 +10,9 @@
\ No newline at end of file diff --git a/app/views/start.html b/app/views/start.html index 3afa8a8..4ff8e7b 100644 --- a/app/views/start.html +++ b/app/views/start.html @@ -9,13 +9,13 @@

At this time, the best recommendation available is to contact us for more information. The site here is actively maintained so feel free to utilize any of our available resources for contact. - In addition to the site here, you can contact the lead developer (me) directly through JoeyKimsey.com. + In addition to the site here, you can contact the lead developer (me) directly through JoeyKimsey.com.

Right now, this entire system was built and managed by myself. As stated, I have used my own version of this for years, but translating it to a publicly available product is not a 1-to-1 job. There may be bugs or issues encountered while you use the product. I can't guarantee a fix for every need in every case immediately, but I do actively keep track of bugs and work hard to ensure everyone has a great experience using the app.

- If you encounter any bugs, feel free to report them here. Likewise, there are forms for feedback, reviews, suggestions, and a general contact form. Thanks for taking the time to check out the product! + If you encounter any bugs, feel free to report them here. Likewise, there are forms for feedback, reviews, suggestions, and a general contact form. Thanks for taking the time to check out the product!

{loggedin}