This commit is contained in:
Joey Kimsey
2025-02-25 14:03:12 -05:00
parent c40b29e812
commit 46390c2447
21 changed files with 197 additions and 60 deletions

View File

@ -74,7 +74,7 @@ class Config extends BedrockConfig {
$html .= '<div class="mb-3 row">';
$html .= '<h4 class="col-lg-3 col-form-label text-end">Current Value</h4>';
$html .= '<div class="col-lg-6">';
$html .= '<input type="text" class="form-control" name="'.$name.'Text" value="'.$node['value'] . '">';
$html .= '<input type="text" class="form-control" name="'.$fieldname.'Text" value="'.$node['value'] . '">';
$html .= '</div>';
$html .= '</div>';
$html .= '<div class="mb-3 row">';

View File

@ -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 );

View File

@ -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;

View File

@ -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' ) ),

View File

@ -8,6 +8,7 @@
* @link https://TheTempusProject.com
* @license https://opensource.org/licenses/MIT [MIT LICENSE]
*/
.context-popover {
background-color: #383838;
color: white;

View File

@ -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;

View File

@ -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"]'));

View File

@ -641,7 +641,7 @@ class User extends DatabaseModel {
Debug::error( 'User not created.' );
return false;
}
return true;
return self::$db->lastId();
}
/**

View File

@ -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;
}

View File

@ -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.' );

View File

@ -40,6 +40,7 @@
<!-- Submit Button -->
<div class="text-center">
{TURNSTILE_WIDGET}
<button type="submit" name="submit" value="submit" class="btn btn-primary btn-lg">Submit</button>
</div>
</form>

View File

@ -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 ) {

View File

@ -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 = '<a href="{ROOT_URL}notifications/markRead/'.$message->ID.'" class="btn btn-sm btn-primary"><i class="fa-solid fa-fw fa-envelope-open"></i></a>';
if ( $entity->seenAt == 0 ) {
$entity->unseenBadge = Views::simpleView( 'notifications.unseenBadge' );
$entity->markReadLink = '<a href="{ROOT_URL}notifications/markRead/'.$entity->ID.'" class="btn btn-sm btn-primary"><i class="fa-solid fa-fw fa-envelope-open"></i></a>';
} else {
$message->unseenBadge = '';
$message->markReadLink = '';
$entity->unseenBadge = '';
$entity->markReadLink = '';
}
$out[] = (object) $message;
$out[] = (object) $entity;
if ( !empty( $end ) ) {
$out = $out[0];
break;

View File

@ -0,0 +1,85 @@
<?php
/**
* app/plugins/turnstile/plugin.php
*
* This houses all of the main plugin info and functionality.
*
* @package TP Turnstile
* @version 5.0.1
* @author Joey Kimsey <Joey@thetempusproject.com>
* @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', '<script src="https://challenges.cloudflare.com/turnstile/v0/api.js" async defer></script>' );
}
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;
}
}

View File

@ -0,0 +1 @@
<div class="cf-turnstile" data-sitekey="{TURNSTILE_API_KEY}"></div>

View File

@ -49,6 +49,7 @@ class DefaultLoader extends Loader {
Components::set( 'FONT_AWESOME_URL', self::FONT_AWESOME_URL );
}
$this->addJs( '<script language="JavaScript" crossorigin="anonymous" type="text/javascript" src="{ROOT_URL}app/js/main.js"></script>' );
$this->addJs( '<script language="JavaScript" crossorigin="anonymous" type="text/javascript" src="{ROOT_URL}app/js/sw.js"></script>' );
Components::setIfNull( 'LOGO', Config::getValue( 'main/logo' ) ?? TP_DEFAULT_LOGO );
if ( ! empty( Config::getValue( 'share/enabled' ) ) ) {

View File

@ -19,7 +19,7 @@
<tbody>
{LOOP}
<tr>
<td align="center">{ID}</td>
<td class="text-center">{ID}</td>
<td><a href='{ROOT_URL}admin/users/view/{ID}' class="text-decoration-none">{usernamePretty}</a></td>
<td>{DTC date}{registered}{/DTC}</td>
<td><a href="{ROOT_URL}admin/users/edit/{ID}" class="btn btn-sm btn-warning"><i class="fa fa-fw fa-pencil"></i></a></td>
@ -31,7 +31,7 @@
{/LOOP}
{ALT}
<tr>
<td align="center" colspan="6">
<td class="text-center" colspan="6">
No results to show.
</td>
</tr>

View File

@ -15,6 +15,7 @@
<label for="email" class="col-lg-6 col-form-label text-lg-end">Email:</label>
<div class="col-lg-2">
<input type="email" class="form-control" name="email" id="email" required>
<input type="email" class="d-none" name="userEmail" id="userEmail">
</div>
</div>
@ -42,6 +43,13 @@
</div>
</div>
<!-- Cloudflare Turnstile Widget -->
<div class="mb-3 row">
<div class="col-2 offset-5">
{TURNSTILE_WIDGET}
</div>
</div>
<!-- Terms of Service -->
<div class="mb-3 text-center">
<div class="">

View File

@ -56,8 +56,8 @@
<div id="collapse3" class="accordion-collapse collapse" aria-labelledby="generalHeading3" data-bs-parent="#generalAccordion">
<div class="accordion-body context-main context-other-bg" id="general3">
<span class="text-lead text-primary">
{SITENAME} is open source and available free of charge through <a href="{ROOT_URL}libraries/ttp/git" class="text-decoration-none context-main">GitLab</a> and <a href="{ROOT_URL}libraries/ttp/packagist" class="text-decoration-none context-main">Packagist</a>.
The developer behind the project is <a href="https://joeykimsey.com/" class="text-decoration-none context-main">Joey Kimsey</a> and he can be contacted through his website for development services.
{SITENAME} is open source and available free of charge through <a href="{ROOT_URL}libraries/ttp/git" class="text-decoration-none">GitLab</a> and <a href="{ROOT_URL}libraries/ttp/packagist" class="text-decoration-none">Packagist</a>.
The developer behind the project is <a href="https://joeykimsey.com/" class="text-decoration-none">Joey Kimsey</a> and he can be contacted through his website for development services.
</span>
</div>
</div>

View File

@ -10,9 +10,9 @@
<div class="alert alert-success alert-dismissible w-100 d-none" role="alert" id="ios-install-message">
<div class="d-flex justify-content-between align-items-center">
{SITENAME} is now available as a Progressive-Web-App, tap the share icon and then "Add to Home Screen".
{SITENAME} is now available as a Progressive-Web-App, tap <img src="/images/share-icon.png" width="20"> and then "Add to Home Screen".
</div>
<img src="/images/share-icon.png" class="iimg-fluid">
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
</div>
</div>
</div>

View File

@ -9,13 +9,13 @@
<p class="text-center text-lg-start">
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 <a href="https://joeykimsey.com">JoeyKimsey.com</a>.
In addition to the site here, you can contact the lead developer (me) directly through <a href="https://joeykimsey.com" class="text-white text-decoration-none">JoeyKimsey.com</a>.
</p>
<p class="text-muted text-center text-lg-start">
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.
</p>
<p class="text-center text-lg-start">
If you encounter any bugs, feel free to report them <a href="/bugreport" class="text-decoration-none">here</a>. 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 <a href="/bugreport" class="text-white text-decoration-none">here</a>. Likewise, there are forms for feedback, reviews, suggestions, and a general contact form. Thanks for taking the time to check out the product!
</p>
<div class="text-center mt-4 pb-4">
{loggedin}