wip from ATB

This commit is contained in:
Joey Kimsey
2025-01-04 17:21:14 -05:00
parent 87e4f90bab
commit 32a9711ade
60 changed files with 556 additions and 342 deletions

View File

@ -18,6 +18,8 @@ use TheTempusProject\Hermes\Functions\Redirect;
use TheTempusProject\Bedrock\Functions\Session; use TheTempusProject\Bedrock\Functions\Session;
use TheTempusProject\Bedrock\Classes\Config; use TheTempusProject\Bedrock\Classes\Config;
use TheTempusProject\Models\Token; use TheTempusProject\Models\Token;
use TheTempusProject\Canary\Bin\Canary as Debug;
use TheTempusProject\Houdini\Classes\Views;
class ApiController extends Controller { class ApiController extends Controller {
protected static $canAccessApplicationApi = false; protected static $canAccessApplicationApi = false;
@ -26,16 +28,15 @@ class ApiController extends Controller {
protected static $authToken; protected static $authToken;
public function __construct( $secure = true ) { public function __construct( $secure = true ) {
header('Content-Type: application/json; charset=utf-8');
parent::__construct(); parent::__construct();
$this->verifyApiRequest(); Template::setTemplate( 'api' );
if ( $secure && ! $this->canUseApi() ) {
Session::flash( 'error', 'You do not have permission to view this page.' );
return Redirect::home();
}
Template::noFollow(); Template::noFollow();
Template::noIndex(); Template::noIndex();
Template::addHeader( 'Content-Type: application/json; charset=utf-8' ); $res = $this->verifyApiRequest();
Template::setTemplate( 'api' ); if ( $secure && ! $this->canUseApi() ) {
exit( $res );
}
} }
protected function canUseApi() { protected function canUseApi() {
@ -72,16 +73,16 @@ class ApiController extends Controller {
} else { } else {
$secret = $this->getSecretToken(); $secret = $this->getSecretToken();
if ( empty( $secret ) ) { if ( empty( $secret ) ) {
return; return Views::simpleView( 'api.response', ['response' => json_encode( [ 'error' => 'invalid secret' ], true )]);
} }
$token = $tokens->findBySecret( $secret ); $token = $tokens->findBySecret( $secret );
} }
if ( empty( $token ) ) { if ( empty( $token ) ) {
return; return Views::simpleView( 'api.response', ['response' => json_encode( [ 'error' => 'invalid token' ], true )]);
} }
self::$authToken = $token; self::$authToken = $token;
if ( $token->expiresAt <= time() && empty( $secret ) ) { if ( $token->expiresAt <= time() && empty( $secret ) ) {
return; return Views::simpleView( 'api.response', ['response' => json_encode( [ 'error' => 'token expired' ], true )]);
} }
if ( $token->expiresAt <= time() ) { if ( $token->expiresAt <= time() ) {
self::$canAccessAuthenticationApi = true; self::$canAccessAuthenticationApi = true;

View File

@ -114,6 +114,7 @@ class Forms extends Check {
self::addHandler( 'install', __CLASS__, 'install' ); self::addHandler( 'install', __CLASS__, 'install' );
self::addHandler( 'adminCreateToken', __CLASS__, 'adminCreateToken' ); self::addHandler( 'adminCreateToken', __CLASS__, 'adminCreateToken' );
self::addHandler( 'apiLogin', __CLASS__, 'apiLogin' ); self::addHandler( 'apiLogin', __CLASS__, 'apiLogin' );
self::addHandler( 'updatePreference', __CLASS__, 'updatePreference' );
self::addHandler( 'installStart', __CLASS__, 'install', [ 'start' ] ); self::addHandler( 'installStart', __CLASS__, 'install', [ 'start' ] );
self::addHandler( 'installAgreement', __CLASS__, 'install', [ 'agreement' ] ); self::addHandler( 'installAgreement', __CLASS__, 'install', [ 'agreement' ] );
self::addHandler( 'installCheck', __CLASS__, 'install', [ 'check' ] ); self::addHandler( 'installCheck', __CLASS__, 'install', [ 'check' ] );
@ -650,4 +651,16 @@ class Forms extends Check {
} }
return true; return true;
} }
public static function updatePreference() {
if ( !Input::exists( 'prefName' ) ) {
self::addUserError( 'You must specify a name' );
return false;
}
if ( !Input::exists( 'prefValue' ) ) {
self::addUserError( 'You must specify a value' );
return false;
}
return true;
}
} }

View File

@ -13,6 +13,7 @@ namespace TheTempusProject\Classes;
use TheTempusProject\Houdini\Classes\Issues; use TheTempusProject\Houdini\Classes\Issues;
use TheTempusProject\Houdini\Classes\Forms; use TheTempusProject\Houdini\Classes\Forms;
use TheTempusProject\Houdini\Classes\Template;
use TheTempusProject\Canary\Bin\Canary as Debug; use TheTempusProject\Canary\Bin\Canary as Debug;
use TheTempusProject\Bedrock\Functions\Check; use TheTempusProject\Bedrock\Functions\Check;
use TheTempusProject\Bedrock\Functions\Upload; use TheTempusProject\Bedrock\Functions\Upload;
@ -186,17 +187,92 @@ class Preferences {
} }
public function getFormHtml( $populated = [] ) { public function getFormHtml( $populated = [] ) {
// dv( self::$preferences );
$form = ''; $form = '';
// Added so i can force some sort of ordering
$inputTypes = [
'file' => [],
'select' => [],
'timezone' => [],
'checkbox' => [],
'switch' => [],
];
foreach ( self::$preferences as $name => $details ) { foreach ( self::$preferences as $name => $details ) {
$tempPrefsArray = $this->normalizePreferenceArray( $name, $details ); $tempPrefsArray = $this->normalizePreferenceArray( $name, $details );
if ( isset( $populated[ $name ] ) ) { if ( isset( $populated[ $name ] ) ) {
$tempPrefsArray['default'] = $populated[$name]; $tempPrefsArray['value'] = $populated[$name];
} else {
$tempPrefsArray['value'] = $tempPrefsArray['default'];
} }
$form .= Forms::getFormFieldHtml( $name, $tempPrefsArray['pretty'], $tempPrefsArray['type'], $tempPrefsArray['default'], $tempPrefsArray['options'] ); // $form .= Forms::getFormFieldHtml( $name, $tempPrefsArray['pretty'], $tempPrefsArray['type'], $tempPrefsArray['default'], $tempPrefsArray['options'] );
if ( $tempPrefsArray['type'] == 'checkbox' ) {
$tempPrefsArray['type'] = 'switch';
}
$inputTypes[ $tempPrefsArray['type'] ][] = self::getFormFieldHtml( $name, $tempPrefsArray['pretty'], $tempPrefsArray['type'], $tempPrefsArray['value'], $tempPrefsArray['options'] );
}
foreach ( $inputTypes as $skip => $items ) {
$form .= implode( ' ', $items );
} }
return $form; return $form;
} }
public static function getFormFieldHtml( $fieldname, $fieldTitle, $type, $defaultValue = '', $options = null ) {
$html = '';
switch ( $type ) {
case 'radio':
case 'bool':
case 'boolean':
$fieldHtml = Forms::getRadioHtml( $fieldname, [ 'true', 'false' ], $defaultValue );
break;
case 'select':
$fieldHtml = Forms::getSelectHtml( $fieldname, $options, $defaultValue );
break;
case 'customSelect':
if ( empty( $options ) ) {
$options = '{' . $fieldname . '-options}';
}
$fieldHtml = Forms::getSelectHtml( $fieldname, $options, $defaultValue );
break;
case 'block':
$fieldHtml = Forms::getTextBlockHtml( $fieldname, $defaultValue );
break;
case 'text':
case 'url':
$fieldHtml = Forms::getTextHtml( $fieldname, $defaultValue );
break;
case 'checkbox':
$fieldHtml = Forms::getCheckboxHtml( $fieldname, $defaultValue );
break;
case 'switch':
$fieldHtml = Forms::getSwitchHtml( $fieldname, $defaultValue );
break;
case 'timezone':
$fieldHtml = Forms::getTimezoneHtml( $defaultValue );
break;
case 'file':
$fieldHtml = Forms::getFileHtml( $fieldname );
break;
default:
Debug::error( "unknown field type: $type" );
break;
}
$html .= '<div class="mb-3 row">';
$html .= '<label for="' . $fieldname . '" class="col-lg-6 col-form-label text-end">' . $fieldTitle . '</label>';
$html .= '<div class="col-lg-6">';
$html .= $fieldHtml;
$html .= '</div>';
if ( 'file' === $type ) {
$html .= '<div class="mb-3 row">';
$html .= '<h4 class="col-lg-6 col-form-label text-end">Current Image</h4>';
$html .= '<div class="col-lg-6">';
$html .= '<img alt="User Avatar" src="{ROOT_URL}' . $defaultValue . '" class="img-circle img-fluid p-2 avatar-125">';
$html .= '</div>';
}
$html .= '</div>';
return Template::parse( $html );
}
public function convertFormToArray( $fillMissing = true, $defaultsOnly = true ) { public function convertFormToArray( $fillMissing = true, $defaultsOnly = true ) {
$prefsArray = []; $prefsArray = [];
foreach ( self::$preferences as $name => $details ) { foreach ( self::$preferences as $name => $details ) {

View File

@ -38,7 +38,7 @@ if ( ! defined( 'CONFIG_DIRECTORY' ) ) {
# Tempus Debugger # Tempus Debugger
define( 'CANARY_SECURE_HASH', 'd73ed7591a30f0ca7d686a0e780f0d05' ); define( 'CANARY_SECURE_HASH', 'd73ed7591a30f0ca7d686a0e780f0d05' );
# Tempus Project Core # Tempus Project Core
define( 'APP_NAME', 'All The Bookmarks'); define( 'APP_NAME', 'The Tempus Project');
define( 'TP_DEFAULT_LOGO', 'images/logo.png'); define( 'TP_DEFAULT_LOGO', 'images/logo.png');
// Check // Check
define( 'MINIMUM_PHP_VERSION', 8.1); define( 'MINIMUM_PHP_VERSION', 8.1);

View File

@ -32,12 +32,12 @@ class Home extends AdminController {
} }
public function index() { public function index() {
Components::set( 'commentDash', '' );
if ( class_exists( 'TheTempusProject\Plugins\Comments' ) ) { if ( class_exists( 'TheTempusProject\Plugins\Comments' ) ) {
$plugin = new CommentPlugin; $plugin = new CommentPlugin;
if ( ! $plugin->checkEnabled() ) { if ( ! $plugin->checkEnabled() ) {
Debug::info( 'Comments Plugin is disabled in the control panel.' ); Debug::info( 'Comments Plugin is disabled in the control panel.' );
Components::set( 'commentDash', '' );
} else { } else {
$comments = new Comments; $comments = new Comments;
$commentList = Views::simpleView( 'comments.admin.dashboard', $comments->recent( 'all', 5 ) ); $commentList = Views::simpleView( 'comments.admin.dashboard', $comments->recent( 'all', 5 ) );

View File

@ -37,9 +37,14 @@ class Routes extends AdminController {
public function create() { public function create() {
if ( Input::exists( 'redirect_type' ) ) { if ( Input::exists( 'redirect_type' ) ) {
return Views::view( 'admin.routes.create' );
}
if ( !TTPForms::check( 'createRoute' ) ) { if ( !TTPForms::check( 'createRoute' ) ) {
Issues::add( 'error', [ 'There was an error with your route.' => Check::userErrors() ] ); Issues::add( 'error', [ 'There was an error with your route.' => Check::userErrors() ] );
return Views::view( 'admin.routes.create' );
} }
if ( self::$routes->create( if ( self::$routes->create(
Input::post( 'original_url' ), Input::post( 'original_url' ),
Input::post( 'forwarded_url' ), Input::post( 'forwarded_url' ),
@ -49,7 +54,8 @@ class Routes extends AdminController {
Session::flash( 'success', 'Route Created' ); Session::flash( 'success', 'Route Created' );
Redirect::to( 'admin/routes' ); Redirect::to( 'admin/routes' );
} }
}
Issues::add( 'error', 'There was an unknown error saving your redirect.' );
Views::view( 'admin.routes.create' ); Views::view( 'admin.routes.create' );
} }

View File

@ -27,15 +27,14 @@ class Login extends ApiController {
parent::__construct( false ); parent::__construct( false );
self::$tokens = new Token; self::$tokens = new Token;
self::$user = new User; self::$user = new User;
// Template::addHeader( 'Access-Control-Allow-Origin: *' ); Template::addHeader( 'Access-Control-Allow-Origin: *' );
// Template::addHeader( 'Content-Type: application/json; charset=utf-8' ); Template::addHeader( 'Content-Type: application/json; charset=utf-8' );
} }
public function index() { public function index() {
header('Access-Control-Allow-Origin: *');
if ( ! Forms::check( 'apiLogin' ) ) { if ( ! Forms::check( 'apiLogin' ) ) {
$responseType = 'error'; $responseType = 'error';
$response = 'malformed input1'; $response = 'malformed input';
return Views::view( 'api.response', ['response' => json_encode( [ $responseType => $response ], true )]); return Views::view( 'api.response', ['response' => json_encode( [ $responseType => $response ], true )]);
} }
$user = self::$user->authorize( Input::post( 'username' ), Input::post( 'password' ) ); $user = self::$user->authorize( Input::post( 'username' ), Input::post( 'password' ) );
@ -45,7 +44,7 @@ class Login extends ApiController {
return Views::view( 'api.response', ['response' => json_encode( [ $responseType => $response ], true )]); return Views::view( 'api.response', ['response' => json_encode( [ $responseType => $response ], true )]);
} }
$responseType = 'token'; $responseType = 'token';
$token = self::$tokens->findOrCreateUserToken( $user->ID ); $token = self::$tokens->findOrCreateUserToken( $user->ID, true );
return Views::view( 'api.response', ['response' => json_encode( [ $responseType => $token ], true )]); return Views::view( 'api.response', ['response' => json_encode( [ $responseType => $token ], true )]);
} }
} }

View File

@ -27,13 +27,13 @@ use TheTempusProject\TheTempusProject as App;
class Home extends Controller { class Home extends Controller {
public function index() { public function index() {
self::$title = '{SITENAME}'; self::$title = '{SITENAME}';
self::$pageDescription = 'This is the homepage of your new Tempus Project Installation. Thank you for installing. find more info at https://thetempusproject.com'; self::$pageDescription = '{SITENAME} is here to provide you a better, faster, and easier - way to create and manage your own web applications.';
Views::view( 'index' ); Views::view( 'index' );
} }
public function login() { public function login() {
self::$title = 'Portal - {SITENAME}'; self::$title = 'Portal - {SITENAME}';
self::$pageDescription = 'Please log in to use {SITENAME} member features.'; self::$pageDescription = 'Please log in to access all of the great features {SITENAME} has to offer.';
if ( App::$isLoggedIn ) { if ( App::$isLoggedIn ) {
return Issues::add( 'notice', 'You are already logged in. Please <a href="' . Routes::getAddress() . 'home/logout">click here</a> to log out.' ); return Issues::add( 'notice', 'You are already logged in. Please <a href="' . Routes::getAddress() . 'home/logout">click here</a> to log out.' );
} }
@ -69,7 +69,7 @@ class Home extends Controller {
public function profile( $id = null ) { public function profile( $id = null ) {
self::$title = 'User Profile - {SITENAME}'; self::$title = 'User Profile - {SITENAME}';
self::$pageDescription = 'User Profiles for {SITENAME}'; self::$pageDescription = 'User Profile - {SITENAME}';
if ( !App::$isLoggedIn ) { if ( !App::$isLoggedIn ) {
return Issues::add( 'notice', 'You must be logged in to view this page.' ); return Issues::add( 'notice', 'You must be logged in to view this page.' );
} }
@ -89,38 +89,22 @@ class Home extends Controller {
Views::view( 'termsPage' ); Views::view( 'termsPage' );
} }
public function hashtag( $id = null ) {
self::$title = 'HashTag - {SITENAME}';
self::$pageDescription = 'HashTags for {SITENAME}';
if ( !App::$isLoggedIn ) {
return Issues::add( 'notice', 'You must be logged in to view this page.' );
}
// this should look up comments and blog posts with the hashtag in them
Views::view( 'hashtags' );
}
public function about() { public function about() {
self::$title = 'About - {SITENAME}'; self::$title = 'About - {SITENAME}';
self::$pageDescription = '{SITENAME} Terms and Conditions of use. Please use {SITENAME} safely.'; self::$pageDescription = '{SITENAME} was started by a developer with years of industry experience which has lead to a refined no-nonsense tool for everyone. Find out more about us here.';
Views::view( 'about' ); Views::view( 'about' );
} }
public function contact() {
self::$title = 'Contact Us - {SITENAME}';
self::$pageDescription = '{SITENAME} Terms and Conditions of use. Please use {SITENAME} safely.';
Views::view( 'contact' );
}
public function privacy() { public function privacy() {
self::$title = 'Privacy Policy - {SITENAME}'; self::$title = 'Privacy Policy - {SITENAME}';
self::$pageDescription = '{SITENAME} Terms and Conditions of use. Please use {SITENAME} safely.'; self::$pageDescription = 'At {SITENAME} you privacy is very important to us. On this page you can find a detailed outline of all the information we collect and how its used.';
Components::set( 'PRIVACY', Views::simpleView( 'privacy' ) ); Components::set( 'PRIVACY', Views::simpleView( 'privacy' ) );
Views::raw( '<div class="col-lg-8 mx-auto">{PRIVACY}</div>' ); Views::raw( '<div class="col-lg-8 mx-auto">{PRIVACY}</div>' );
} }
public function faq() { public function faq() {
self::$title = 'Frequently Asked Questions - {SITENAME}'; self::$title = 'Frequently Asked Questions - {SITENAME}';
self::$pageDescription = '{SITENAME} Terms and Conditions of use. Please use {SITENAME} safely.'; self::$pageDescription = 'Many times, we aren\'t the first to ask why or how something works. Here you will find a list of {SITENAME} commonly asked questions and our best answers.' ;
Views::view( 'faq' ); Views::view( 'faq' );
} }
} }

View File

@ -27,6 +27,7 @@ use TheTempusProject\Classes\Forms;
class Register extends Controller { class Register extends Controller {
public function confirm( $code = null ) { public function confirm( $code = null ) {
Template::noIndex();
self::$title = 'Confirm Email'; self::$title = 'Confirm Email';
if ( !isset( $code ) && !Input::exists( 'confirmationCode' ) ) { if ( !isset( $code ) && !Input::exists( 'confirmationCode' ) ) {
return Views::view( 'confirmation' ); return Views::view( 'confirmation' );
@ -43,8 +44,8 @@ class Register extends Controller {
} }
public function index() { public function index() {
self::$title = 'Register'; self::$title = '{SITENAME} Sign Up';
self::$pageDescription = 'Many features of the site are disabled or even hidden from unregistered users. On this page you can sign up for an account to access all the app has to offer.'; 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.';
Components::set( 'TERMS', Views::simpleView( 'terms' ) ); Components::set( 'TERMS', Views::simpleView( 'terms' ) );
if ( App::$isLoggedIn ) { if ( App::$isLoggedIn ) {
return Issues::add( 'notice', 'You are currently logged in.' ); return Issues::add( 'notice', 'You are currently logged in.' );
@ -94,6 +95,7 @@ class Register extends Controller {
public function resend() { public function resend() {
self::$title = 'Resend Confirmation'; self::$title = 'Resend Confirmation';
Template::noIndex();
if ( !App::$isLoggedIn ) { if ( !App::$isLoggedIn ) {
return Issues::add( 'notice', 'Please log in to resend your confirmation email.' ); return Issues::add( 'notice', 'Please log in to resend your confirmation email.' );
} }
@ -110,6 +112,7 @@ class Register extends Controller {
public function reset( $code = null ) { public function reset( $code = null ) {
self::$title = 'Password Reset'; self::$title = 'Password Reset';
Template::noIndex();
if ( !isset( $code ) && !Input::exists( 'resetCode' ) ) { if ( !isset( $code ) && !Input::exists( 'resetCode' ) ) {
Issues::add( 'info', 'Please provide a reset code.' ); Issues::add( 'info', 'Please provide a reset code.' );
return Views::view( 'password_reset_code' ); return Views::view( 'password_reset_code' );

View File

@ -36,12 +36,12 @@ class Usercp extends Controller {
Redirect::home(); Redirect::home();
} }
Template::noIndex(); Template::noIndex();
$menu = Views::simpleView( 'nav.usercp', App::$userCPlinks );
Navigation::activePageSelect( $menu, null, true, true );
} }
public function email() { public function email() {
self::$title = 'Email Settings'; self::$title = 'Email Settings';
$menu = Views::simpleView( 'nav.usercp', App::$userCPlinks );
Navigation::activePageSelect( $menu, null, true, true );
if ( App::$activeUser->confirmed != '1' ) { if ( App::$activeUser->confirmed != '1' ) {
return Issues::add( 'notice', 'You need to confirm your email address before you can make modifications. If you would like to resend that confirmation link, please <a href="/register/resend">click here</a>', true ); return Issues::add( 'notice', 'You need to confirm your email address before you can make modifications. If you would like to resend that confirmation link, please <a href="/register/resend">click here</a>', true );
} }
@ -68,11 +68,15 @@ class Usercp extends Controller {
public function index() { public function index() {
self::$title = 'User Control Panel'; self::$title = 'User Control Panel';
$menu = Views::simpleView( 'nav.usercp', App::$userCPlinks );
Navigation::activePageSelect( $menu, null, true, true );
Views::view( 'profile', App::$activeUser ); Views::view( 'profile', App::$activeUser );
} }
public function password() { public function password() {
self::$title = 'Password Settings'; self::$title = 'Password Settings';
$menu = Views::simpleView( 'nav.usercp', App::$userCPlinks );
Navigation::activePageSelect( $menu, null, true, true );
if ( !Input::exists() ) { if ( !Input::exists() ) {
return Views::view( 'user_cp.password_change' ); return Views::view( 'user_cp.password_change' );
} }
@ -94,11 +98,12 @@ class Usercp extends Controller {
public function settings() { public function settings() {
self::$title = 'Preferences'; self::$title = 'Preferences';
$menu = Views::simpleView( 'nav.usercp', App::$userCPlinks );
Navigation::activePageSelect( $menu, null, true, true );
$prefs = new Preferences; $prefs = new Preferences;
$fields = App::$activePrefs; $fields = App::$activePrefs;
if ( Input::exists( 'submit' ) ) { if ( Input::exists( 'submit' ) ) {
$fields = $prefs->convertFormToArray( true, false ); $fields = $prefs->convertFormToArray( true, false );
// dv( $fields );
// @TODO now i may need to rework the form checker to work with this.... // @TODO now i may need to rework the form checker to work with this....
// if (!Forms::check('userPrefs')) { // if (!Forms::check('userPrefs')) {
// Issues::add( 'error', [ 'There was an error with your request.' => Check::userErrors() ] ); // Issues::add( 'error', [ 'There was an error with your request.' => Check::userErrors() ] );
@ -110,4 +115,37 @@ class Usercp extends Controller {
Components::set( 'PREFERENCES_FORM', $prefs->getFormHtml( $fields ) ); Components::set( 'PREFERENCES_FORM', $prefs->getFormHtml( $fields ) );
Views::view( 'user_cp.settings', App::$activeUser ); Views::view( 'user_cp.settings', App::$activeUser );
} }
public function updatePref() {
Template::setTemplate( 'api' );
if ( ! App::$isLoggedIn ) {
return Views::view( 'api.response', ['response' => json_encode( [ 'error' => 'Not Logged In' ], true )]);
}
if ( ! Forms::check( 'updatePreference' ) ) {
return Views::view( 'api.response', ['response' => json_encode( [ 'error' => Check::userErrors() ], true )]);
}
$name = Input::post( 'prefName' );
$value = Input::post('prefValue' );
if ( 'false' === $value ) {
$value = false;
} elseif ( 'true' === $value ) {
$value = true;
}
if ( empty( Preferences::get( $name ) ) ) {
return Views::view( 'api.response', ['response' => json_encode( [ 'error' => 'Unknown Preference' ], true )]);
}
$prefs = new Preferences;
$fields1 = $prefs->convertFormToArray( true, false );
$fields3 = $fields1;
if ( isset( $fields1[ $name ] ) ) {
$fields3[ $name ] = $value;
}
$result = self::$user->updatePrefs( $fields3, App::$activeUser->ID );
return Views::view( 'api.response', ['response' => json_encode( $result, true )]);
}
} }

View File

@ -21,9 +21,14 @@
.context-second-bg { .context-second-bg {
background-color: #1e1e1e; background-color: #1e1e1e;
} }
.context-third-bg {
background-color: #3a3a3a;
}
.bg-default { .bg-default {
background-color: #2c2c2c; background-color: #2c2c2c;
} }
.bg-none,.bg-warning { .bg-none,.bg-warning {
color: #000 !important; color: #000 !important;
} }
@ -135,3 +140,14 @@ body {
.text-shadow-3 { .text-shadow-3 {
text-shadow: 0 .5rem 1.5rem rgba(255, 255, 255, .25); text-shadow: 0 .5rem 1.5rem rgba(255, 255, 255, .25);
} }
.form-control {
background-color: #1f1f1f;
color: #e0e0e0;
}
.form-control:focus {
color: #e0e0e0;
border-color: #1e90ff;
background-color: #1f1f1f;
box-shadow: 0 0 0 .25rem rgba(30, 144, 255, .5);
}

View File

@ -92,8 +92,10 @@ html {
pre { pre {
white-space: pre-wrap; white-space: pre-wrap;
} }
body { body {
background-image: linear-gradient(180deg, #eee, #fff 100px, #fff); background-color: #e4e4e4;
/* background-image: linear-gradient(180deg, #eee, #fff 100px, #fff); */
} }
@media ( min-width: 768px ) { @media ( min-width: 768px ) {
.main { .main {

Binary file not shown.

Before

Width:  |  Height:  |  Size: 26 KiB

After

Width:  |  Height:  |  Size: 45 KiB

View File

@ -102,7 +102,10 @@ document.addEventListener('DOMContentLoaded', function () {
// Check if dark mode is saved in localStorage // Check if dark mode is saved in localStorage
if (localStorage.getItem('darkMode') === 'enabled') { if (localStorage.getItem('darkMode') === 'enabled') {
darkModeStylesheet.disabled = false; darkModeStylesheet.disabled = false;
if ( toggleButton ) {
toggleButton.checked = true; toggleButton.checked = true;
}
if ( enableButton ) { if ( enableButton ) {
enableButton.innerText = 'Disable Now'; enableButton.innerText = 'Disable Now';
@ -131,11 +134,14 @@ document.addEventListener('DOMContentLoaded', function () {
}); });
} }
if ( toggleButton ) {
toggleButton.addEventListener('click', function () { toggleButton.addEventListener('click', function () {
if (darkModeStylesheet.disabled) { if (darkModeStylesheet.disabled) {
toggleDarkModePref( true );
darkModeStylesheet.disabled = false; darkModeStylesheet.disabled = false;
localStorage.setItem('darkMode', 'enabled'); localStorage.setItem('darkMode', 'enabled');
} else { } else {
toggleDarkModePref( false );
darkModeStylesheet.disabled = true; darkModeStylesheet.disabled = true;
localStorage.setItem('darkMode', 'disabled'); localStorage.setItem('darkMode', 'disabled');
} }
@ -150,4 +156,14 @@ document.addEventListener('DOMContentLoaded', function () {
} }
}); });
}); });
}
function toggleDarkModePref( value ) {
var fields = {};
fields.prefName = 'darkMode';
fields.prefValue = value;
$.post( '/usercp/updatePref', fields ).done(function(response) {
// alert('Timer updated successfully!');
});
}
}); });

View File

@ -72,7 +72,7 @@ class Routes extends DatabaseModel {
return false; return false;
} }
if ( !Check::simpleName( $nickname ) ) { if ( !Check::simpleName( $nickname ) ) {
Debug::warn( 'Invalid route nickname: ' . $name ); Debug::warn( 'Invalid route nickname: ' . $nickname );
return false; return false;
} }
if ( 'external' == $type && !Check::url( $forwarded_url ) ) { if ( 'external' == $type && !Check::url( $forwarded_url ) ) {

View File

@ -94,10 +94,15 @@ class Token extends DatabaseModel {
return false; return false;
} }
public function findOrCreateUserToken( $user_id ) { public function findOrCreateUserToken( $user_id, $refresh = false ) {
$test = $this->findUserToken( $user_id ); $test = $this->findUserToken( $user_id );
if ( ! empty( $test ) ) { if ( ! empty( $test ) ) {
return $test->token; if ( ! empty( $refresh ) ) {
$token = $this->refresh( $test->ID, 'user' );
} else {
$token = $test->token;
}
return $token;
} }
$expiration = Config::getValue( 'api/UserAccessTokenExpiration' ); $expiration = Config::getValue( 'api/UserAccessTokenExpiration' );

View File

@ -121,6 +121,11 @@ class User extends DatabaseModel {
'50', '50',
], ],
], ],
'darkMode' => [
'pretty' => 'Enable Dark-Mode viewing',
'type' => 'checkbox',
'default' => 'false',
],
]; ];
protected static $avatars; protected static $avatars;
protected static $preferences; protected static $preferences;

View File

@ -46,7 +46,7 @@ class Blog extends AdminController {
return $this->index(); return $this->index();
} }
$result = self::$posts->newPost( Input::post( 'title' ), Input::post( 'blogPost' ), Input::post( 'submit' ) ); $result = self::$posts->newPost( Input::post( 'title' ), Input::post( 'blogPost' ), Input::post( 'slug' ), Input::post( 'submit' ) );
if ( $result ) { if ( $result ) {
Issues::add( 'success', 'Your post has been created.' ); Issues::add( 'success', 'Your post has been created.' );
return $this->index(); return $this->index();
@ -67,7 +67,7 @@ class Blog extends AdminController {
Issues::add( 'error', [ 'There was an error with your form.' => Check::userErrors() ] ); Issues::add( 'error', [ 'There was an error with your form.' => Check::userErrors() ] );
return $this->index(); return $this->index();
} }
if ( self::$posts->updatePost( $data, Input::post( 'title' ), Input::post( 'blogPost' ), Input::post( 'submit' ) ) === true ) { if ( self::$posts->updatePost( $data, Input::post( 'title' ), Input::post( 'blogPost' ), Input::post( 'slug' ), Input::post( 'submit' ) ) === true ) {
Issues::add( 'success', 'Post Updated.' ); Issues::add( 'success', 'Post Updated.' );
return $this->index(); return $this->index();
} }

View File

@ -62,12 +62,17 @@ class Blog extends Controller {
return $this->index(); return $this->index();
} }
if ( class_exists( 'TheTempusProject\Plugins\Comments' ) ) {
$plugin = new Comments; $plugin = new Comments;
if ( ! $plugin->checkEnabled() ) { if ( ! $plugin->checkEnabled() ) {
Issues::add( 'error', 'Comments are disabled.' ); Issues::add( 'error', 'Comments are disabled.' );
return $this->index(); return $this->index();
} }
$comments = new CommentsModel; $comments = new CommentsModel;
} else {
Debug::info( 'error', 'Comments plugin missing.' );
return $this->index();
}
switch ( $sub ) { switch ( $sub ) {
case 'post': case 'post':
@ -99,9 +104,12 @@ class Blog extends Controller {
return $this->index(); return $this->index();
} }
$post = self::$posts->findById( $id ); $post = self::$posts->findById( $id );
if ( empty( $post ) ) {
$post = self::$posts->findBySlug( $id );
if ( empty( $post ) ) { if ( empty( $post ) ) {
return $this->index(); return $this->index();
} }
}
Debug::log( 'Controller initiated: ' . __METHOD__ . '.' ); Debug::log( 'Controller initiated: ' . __METHOD__ . '.' );
self::$title = 'Blog Post'; self::$title = 'Blog Post';
@ -111,13 +119,13 @@ class Blog extends Controller {
Components::set( 'CONTENT_ID', $id ); Components::set( 'CONTENT_ID', $id );
Components::set( 'COMMENT_TYPE', self::$posts->tableName ); Components::set( 'COMMENT_TYPE', self::$posts->tableName );
$plugin = new Comments;
if ( ! $plugin->checkEnabled() ) {
Components::set( 'NEWCOMMENT', '' ); Components::set( 'NEWCOMMENT', '' );
Components::set( 'count', '0' ); Components::set( 'count', '0' );
Components::set( 'COMMENTS', '' ); Components::set( 'COMMENTS', '' );
} else {
if ( class_exists( 'TheTempusProject\Plugins\Comments' ) ) {
$plugin = new Comments;
if ( $plugin->checkEnabled() ) {
$comments = new CommentsModel; $comments = new CommentsModel;
if ( App::$isLoggedIn ) { if ( App::$isLoggedIn ) {
Components::set( 'NEWCOMMENT', Views::simpleView( 'comments.create' ) ); Components::set( 'NEWCOMMENT', Views::simpleView( 'comments.create' ) );
@ -127,8 +135,7 @@ class Blog extends Controller {
Components::set( 'count', $comments->count( self::$posts->tableName, $post->ID ) ); Components::set( 'count', $comments->count( self::$posts->tableName, $post->ID ) );
Components::set( 'COMMENTS', Views::simpleView( 'comments.list', $comments->display( 10, self::$posts->tableName, $post->ID ) ) ); Components::set( 'COMMENTS', Views::simpleView( 'comments.list', $comments->display( 10, self::$posts->tableName, $post->ID ) ) );
} }
}
$post = self::$posts->findById( $id );
self::$title .= ' - ' . $post->title; self::$title .= ' - ' . $post->title;
self::$pageDescription = strip_tags( $post->contentSummaryNoLink ); self::$pageDescription = strip_tags( $post->contentSummaryNoLink );

View File

@ -32,6 +32,7 @@ class Posts extends DatabaseModel {
[ 'edited', 'int', '10' ], [ 'edited', 'int', '10' ],
[ 'draft', 'int', '1' ], [ 'draft', 'int', '1' ],
[ 'title', 'varchar', '86' ], [ 'title', 'varchar', '86' ],
[ 'slug', 'varchar', '64' ],
[ 'content', 'text', '' ], [ 'content', 'text', '' ],
]; ];
@ -45,7 +46,7 @@ class Posts extends DatabaseModel {
} }
} }
public function newPost( $title, $post, $draft ) { public function newPost( $title, $post, $slug, $draft ) {
if ( !Check::dataTitle( $title ) ) { if ( !Check::dataTitle( $title ) ) {
Debug::info( 'modelBlog: illegal title.' ); Debug::info( 'modelBlog: illegal title.' );
@ -59,6 +60,7 @@ class Posts extends DatabaseModel {
$fields = [ $fields = [
'author' => App::$activeUser->ID, 'author' => App::$activeUser->ID,
'draft' => $draft, 'draft' => $draft,
'slug' => $slug,
'created' => time(), 'created' => time(),
'edited' => time(), 'edited' => time(),
'content' => Sanitize::rich( $post ), 'content' => Sanitize::rich( $post ),
@ -73,7 +75,7 @@ class Posts extends DatabaseModel {
return true; return true;
} }
public function updatePost( $id, $title, $content, $draft ) { public function updatePost( $id, $title, $content, $slug, $draft ) {
if ( empty( self::$log ) ) { if ( empty( self::$log ) ) {
self::$log = new Log; self::$log = new Log;
} }
@ -94,6 +96,7 @@ class Posts extends DatabaseModel {
} }
$fields = [ $fields = [
'draft' => $draft, 'draft' => $draft,
'slug' => $slug,
'edited' => time(), 'edited' => time(),
'content' => Sanitize::rich( $content ), 'content' => Sanitize::rich( $content ),
'title' => $title, 'title' => $title,
@ -131,39 +134,47 @@ class Posts extends DatabaseModel {
} }
$draft = ''; $draft = '';
$authorName = self::$user->getUsername( $instance->author ); $authorName = self::$user->getUsername( $instance->author );
$cleanPost = Sanitize::contentShort( $instance->content );
$postSpace = explode( ' ', $cleanPost ); // Summarize
$postLine = explode( "\n", $cleanPost ); if ( ! empty( $instance->slug ) ) {
// summary by words: 100 $identifier = $instance->slug;
$spaceSummary = implode( ' ', array_splice( $postSpace, 0, 100 ) ); } else {
// summary by lines: 5 $identifier = $instance->ID;
$lineSummary = implode( "\n", array_splice( $postLine, 0, 5 ) ); }
if ( strlen( $spaceSummary ) < strlen( $lineSummary ) ) {
$contentSummary = $spaceSummary; $cleanPost = Sanitize::contentShort( $instance->content );
if ( count( $postSpace, 1 ) <= 100 ) { // By Word
$contentSummaryNoLink = $contentSummary; $wordsArray = explode( ' ', $cleanPost );
$contentSummary .= '... <a href="{ROOT_URL}blog/post/' . $instance->ID . '">Read More</a>'; $wordSummary = implode( ' ', array_splice( $wordsArray, 0, 100 ) );
} // By Line
$linesArray = explode( "\n", $cleanPost );
$lineSummary = implode( "\n", array_splice( $linesArray, 0, 5 ) );
if ( strlen( $wordSummary ) < strlen( $lineSummary ) ) {
$contentSummaryNoLink = $wordSummary;
$contentSummary = $wordSummary . '... <a href="{ROOT_URL}blog/post/' . $identifier . '" class="text-decoration-none">Read More</a>';
} else { } else {
// @todo: need to refine this after testing
$contentSummaryNoLink = $lineSummary; $contentSummaryNoLink = $lineSummary;
$contentSummary = $lineSummary . '... <a href="{ROOT_URL}blog/post/' . $instance->ID . '">Read More</a>'; $contentSummary = $lineSummary . '... <a href="{ROOT_URL}blog/post/' . $identifier . '" class="text-decoration-none">Read More</a>';
}
$instance->contentSummaryNoLink = $contentSummaryNoLink;
$instance->contentSummary = $contentSummary;
if ( isset( $params['stripHtml'] ) && $params['stripHtml'] === true ) {
$instance->contentSummary = strip_tags( $instance->content );
} }
if ( $instance->draft != '0' ) { if ( $instance->draft != '0' ) {
$draft = ' <b>Draft</b>'; $draft = ' <b>Draft</b>';
} }
$instance->isDraft = $draft; $instance->isDraft = $draft;
$instance->authorName = $authorName; $instance->authorName = $authorName;
$instance->contentSummaryNoLink = $contentSummaryNoLink;
$instance->contentSummary = $contentSummary;
if ( isset( $params['stripHtml'] ) && $params['stripHtml'] === true ) {
$instance->contentSummary = strip_tags( $instance->content );
}
if ( self::$comments !== false ) { if ( self::$comments !== false ) {
$instance->commentCount = self::$comments->count( 'blog', $instance->ID ); $instance->commentCount = self::$comments->count( 'blog', $instance->ID );
} }
$instance->content = Filters::applyOne( 'mentions.0', $instance->content, true ); $instance->content = Filters::applyOne( 'mentions.0', $instance->content, true );
$instance->content = Filters::applyOne( 'hashtags.0', $instance->content, true ); $instance->content = Filters::applyOne( 'hashtags.0', $instance->content, true );
$out[] = $instance; $out[] = $instance;
if ( !empty( $end ) ) { if ( !empty( $end ) ) {
$out = $out[0]; $out = $out[0];
@ -291,6 +302,22 @@ class Posts extends DatabaseModel {
return $this->filter( $postData->results() ); return $this->filter( $postData->results() );
} }
public function findBySlug( $slug, $includeDraft = false ) {
$whereClause = [];
if ( $includeDraft !== true ) {
$whereClause = ['draft', '=', '0', 'AND'];
}
$whereClause = array_merge( $whereClause, ['slug', '=', $slug] );
$postData = self::$db->get( $this->tableName, $whereClause );
if ( !$postData->count() ) {
Debug::info( 'No Blog posts found.' );
return false;
}
return $this->filter( $postData->first() );
}
public function byMonth( $month, $year = 0, $includeDraft = false ) { public function byMonth( $month, $year = 0, $includeDraft = false ) {
if ( 0 === $year ) { if ( 0 === $year ) {
$year = date( 'Y' ); $year = date( 'Y' );

View File

@ -12,6 +12,14 @@
</div> </div>
</div> </div>
<!-- Slug -->
<div class="mb-3 row">
<label for="slug" class="col-lg-3 col-form-label text-end">URL Slug (for pretty linking):</label>
<div class="col-lg-6">
<input type="text" class="form-control" name="slug" id="slug" required>
</div>
</div>
<!-- form buttons --> <!-- form buttons -->
<div class="mb-3 row"> <div class="mb-3 row">
<div class="offset-3 col-lg-6"> <div class="offset-3 col-lg-6">

View File

@ -12,6 +12,14 @@
</div> </div>
</div> </div>
<!-- Slug -->
<div class="mb-3 row">
<label for="slug" class="col-lg-3 col-form-label text-end">URL Slug (for pretty linking):</label>
<div class="col-lg-6">
<input type="text" class="form-control" name="slug" id="slug" value="{slug}" required>
</div>
</div>
<!-- form buttons --> <!-- form buttons -->
<div class="mb-3 row"> <div class="mb-3 row">
<div class="offset-3 col-lg-6"> <div class="offset-3 col-lg-6">

View File

@ -1,7 +1,7 @@
{LOOP} {LOOP}
<article class="blog-post"> <article class="blog-post">
<h2 class="blog-post-title mb-1">{title}</h2> <h2 class="blog-post-title mb-1">{title}</h2>
<p class="blog-post-meta">{DTC date}{created}{/DTC} by <a href="{ROOT_URL}home/profile/{author}">{authorName}</a></p> <p class="blog-post-meta">{DTC date}{created}{/DTC} by <a href="{ROOT_URL}home/profile/{author}" class="text-decoration-none">{authorName}</a></p>
<div class="well"> <div class="well">
{contentSummary} {contentSummary}
</div> </div>

View File

@ -3,7 +3,7 @@
<div class="blog-post"> <div class="blog-post">
<h2 class="blog-post-title">{title}</h2> <h2 class="blog-post-title">{title}</h2>
<hr> <hr>
<p class="blog-post-meta">{DTC date}{created}{/DTC} by <a href="{ROOT_URL}home/profile/{author}">{authorName}</a></p> <p class="blog-post-meta">{DTC date}{created}{/DTC} by <a href="{ROOT_URL}home/profile/{author}" class="text-decoration-none">{authorName}</a></p>
{content} {content}
{ADMIN} {ADMIN}
<hr> <hr>

View File

@ -5,7 +5,7 @@
<div class="card-body"> <div class="card-body">
<ol class="list-unstyled"> <ol class="list-unstyled">
{LOOP} {LOOP}
<li><a href="{ROOT_URL}blog/post/{ID}">{title}</a></li> <li><a href="{ROOT_URL}blog/post/{ID}" class="text-decoration-none">{title}</a></li>
{/LOOP} {/LOOP}
{ALT} {ALT}
<li>No Posts to show</li> <li>No Posts to show</li>
@ -13,6 +13,6 @@
</ol> </ol>
</div> </div>
<div class="card-footer"> <div class="card-footer">
<a href="{ROOT_URL}blog">View All</a> <a href="{ROOT_URL}blog" class="text-decoration-none">View All</a>
</div> </div>
</div> </div>

View File

@ -2,7 +2,7 @@
<h4 class="fst-italic">Archives</h4> <h4 class="fst-italic">Archives</h4>
<ul class="list-unstyled mb-0"> <ul class="list-unstyled mb-0">
{LOOP} {LOOP}
<li>({count}) <a href="{ROOT_URL}blog/month/{month}/{year}">{monthText} {year}</a></li> <li>({count}) <a href="{ROOT_URL}blog/month/{month}/{year}" class="text-decoration-none">{monthText} {year}</a></li>
{/LOOP} {/LOOP}
{ALT} {ALT}
<li>None To Show</li> <li>None To Show</li>

View File

@ -1,5 +1,6 @@
<div class="col-8 mx-auto p-4 rounded shadow-sm mb-5 context-main-bg mt-4 container"> <div class="col-8 mx-auto p-4 rounded shadow-sm mb-5 context-main-bg mt-4 container">
<h2 class="text-center mb-4">Bug Report</h2> <h2 class="text-center mb-4">Bug Report</h2>
<hr>
<p>Thank you for visiting our bug reporting page. We value our users' input highly and in an effort to better serve your needs, please fill out the form below to help us address this issue.</p> <p>Thank you for visiting our bug reporting page. We value our users' input highly and in an effort to better serve your needs, please fill out the form below to help us address this issue.</p>
<p>We read each and every bug report submitted, and by submitting this form you allow us to send you a follow-up email.</p> <p>We read each and every bug report submitted, and by submitting this form you allow us to send you a follow-up email.</p>
<form action="" method="post"> <form action="" method="post">

View File

@ -49,13 +49,13 @@ class Comments extends AdminController {
$this->index(); $this->index();
} }
public function viewComments( $contentIID = null ) { public function viewComments( $contentID = null ) {
if ( empty( $contentIID ) ) { if ( empty( $contentID ) ) {
Issues::add( 'error', 'Content ID not found.' ); Issues::add( 'error', 'Content ID not found.' );
return $this->index(); return $this->index();
} }
$contentData = self::$comments->findById( $data ); $contentData = self::$comments->findById( $data );
if ( empty( $contentIID ) ) { if ( empty( $contentID ) ) {
return Views::view( 'comments.list', $commentData ); return Views::view( 'comments.list', $commentData );
} }
Issues::add( 'error', 'Comment not found.' ); Issues::add( 'error', 'Comment not found.' );

View File

@ -28,7 +28,7 @@
</li> </li>
{/LOOP} {/LOOP}
{ALT} {ALT}
<li class="list-group-item"> <li class="list-group-item context-second-bg context-main mb-2">
<div class="text-center"> <div class="text-center">
<p class="mb-0">Be the first to comment.</p> <p class="mb-0">Be the first to comment.</p>
</div> </div>

View File

@ -115,7 +115,7 @@ class Notification extends DatabaseModel {
]; ];
if ( !self::$db->update( $this->tableName, $id, $fields ) ) { if ( !self::$db->update( $this->tableName, $id, $fields ) ) {
new CustomException( 'notificationDelete' ); new CustomException( 'notificationDelete' );
Debug::error( "Bookmarks: $id not updated" ); Debug::error( "Notifications: $id not updated" );
return false; return false;
} }
return true; return true;

View File

@ -3,13 +3,13 @@
<a <a
href="#" href="#"
class="d-flex align-items-center text-white text-decoration-none dropdown-toggle" class="d-flex align-items-center text-white text-decoration-none dropdown-toggle"
id="notiificationsDropdown" id="notificationsDropdown"
data-bs-toggle="dropdown" data-bs-toggle="dropdown"
aria-haspopup="true" aria-haspopup="true"
aria-expanded="false"> aria-expanded="false">
<i class="fa fa-fw fa-bell"></i><span class="ms-2">{NBADGE}</span> <i class="fa fa-fw fa-bell"></i><span class="ms-2">{NBADGE}</span>
</a> </a>
<ul class="dropdown-menu dropdown-menu-dark dropdown-menu-end text-small shadow" aria-labelledby="notiificationsDropdown"> <ul class="dropdown-menu dropdown-menu-dark dropdown-menu-end text-small shadow" aria-labelledby="notificationsDropdown">
{LOOP} {LOOP}
<!-- Notification Item --> <!-- Notification Item -->
<li> <li>

View File

@ -1,11 +1,11 @@
<div class="col-md-5 offset-md-1 mb-3"> <div class="col-md-5 offset-md-1 mb-3 text-center">
<h5>Subscribe to our newsletter</h5> <h5 class="">Subscribe</h5>
<div class="d-flex flex-column flex-sm-row w-100 gap-2"> <div class="d-flex flex-column flex-sm-row gap-2 justify-content-center mx-auto">
<form action="{ROOT_URL}subscribe/home" method="post" class="form-horizontal"></form> <form action="{ROOT_URL}subscribe/home" method="post" class="form-horizontal">
<label for="newsletter1" class="visually-hidden">Email address</label> <label for="email" class="visually-hidden">Email address</label>
<input name="email" id="email" type="email" class="form-control" placeholder="Email address" autocomplete="email"> <input name="email" id="email" type="email" class="form-control my-2" placeholder="Email address" autocomplete="email">
<input type="hidden" name="token" value="{TOKEN}"> <input type="hidden" name="token" value="{TOKEN}">
<button class="btn btn-primary" name="submit" value="submit" type="submit">Subscribe</button> <button class="btn btn-primary my-2 w-100" name="submit" value="submit" type="submit">Subscribe</button>
</form> </form>
</div> </div>
</div> </div>

View File

@ -41,8 +41,6 @@ class DefaultLoader extends Loader {
Components::set( 'JQUERY_CDN', self::JQUERY_CDN ); Components::set( 'JQUERY_CDN', self::JQUERY_CDN );
Components::set( 'FONT_AWESOME_URL', self::FONT_AWESOME_URL ); Components::set( 'FONT_AWESOME_URL', self::FONT_AWESOME_URL );
} }
$this->addCss( '<link rel="stylesheet" href="{ROOT_URL}app/css/main.css">' );
$this->addCss( '<link rel="stylesheet" href="{ROOT_URL}app/css/main-dark.css" id="dark-mode-stylesheet" disabled>' );
$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/main.js"></script>' );
Components::setIfNull( 'LOGO', Config::getValue( 'main/logo' ) ?? TP_DEFAULT_LOGO ); Components::setIfNull( 'LOGO', Config::getValue( 'main/logo' ) ?? TP_DEFAULT_LOGO );
Components::setIfNull( 'COPY', Views::simpleView( 'footer.copy') ); Components::setIfNull( 'COPY', Views::simpleView( 'footer.copy') );
@ -55,12 +53,21 @@ class DefaultLoader extends Loader {
* Top-Nav * Top-Nav
*/ */
if ( App::$isLoggedIn ) { if ( App::$isLoggedIn ) {
if ( ! empty( App::$activePrefs['darkMode'] ) ) {
$this->addCss( '<link rel="stylesheet" href="{ROOT_URL}app/css/main.css">' );
$this->addCss( '<link rel="stylesheet" href="{ROOT_URL}app/css/main-dark.css" id="dark-mode-stylesheet">' );
} else {
$this->addCss( '<link rel="stylesheet" href="{ROOT_URL}app/css/main.css">' );
$this->addCss( '<link rel="stylesheet" href="{ROOT_URL}app/css/main-dark.css" id="dark-mode-stylesheet" disabled>' );
}
Components::set( 'topNavRightDropdown', Template::parse( App::$topNavRightDropdown ) ); Components::set( 'topNavRightDropdown', Template::parse( App::$topNavRightDropdown ) );
Components::set( 'STATUS', Views::simpleView( 'nav.statusLoggedIn' ) ); Components::set( 'STATUS', Views::simpleView( 'nav.statusLoggedIn' ) );
Components::set( 'USERNAME', \ucfirst( App::$activeUser->username ) ); Components::set( 'USERNAME', \ucfirst( App::$activeUser->username ) );
Components::set( 'AVATAR', App::$activeUser->avatar ); Components::set( 'AVATAR', App::$activeUser->avatar );
} else { } else {
Components::set( 'STATUS', Views::simpleView( 'nav.statusLoggedOut' ) ); Components::set( 'STATUS', Views::simpleView( 'nav.statusLoggedOut' ) );
$this->addCss( '<link rel="stylesheet" href="{ROOT_URL}app/css/main.css">' );
$this->addCss( '<link rel="stylesheet" href="{ROOT_URL}app/css/main-dark.css" id="dark-mode-stylesheet" disabled>' );
} }
Components::set( 'topNavRight', Template::parse( App::$topNavRight . '{STATUS}' ) ); Components::set( 'topNavRight', Template::parse( App::$topNavRight . '{STATUS}' ) );
Components::set( 'topNavLeft', Views::simpleView( 'nav.main', Navigation::getMenuLinks( App::MAIN_MENU_NAME ) ) ); Components::set( 'topNavLeft', Views::simpleView( 'nav.main', Navigation::getMenuLinks( App::MAIN_MENU_NAME ) ) );

View File

@ -1,13 +1,13 @@
<div class="col-8 mx-auto p-4 rounded shadow-sm context-main-bg my-4"> <div class="col-8 mx-auto p-4 rounded shadow-sm context-main-bg my-4">
<h2 class="text-center text-primary mb-4">Welcome to AllTheBookmarks</h2> <h2 class="text-center mb-4">About {SITENAME}</h2>
<p class="lead"> <p class="lead">
AllTheBookmarks was built out of a need to manage my own bookmarks better. As a web developer, I would not only use URLs for every aspect of my job, but I also needed to switch browsers, operating systems, or devices. For years, I had a version of this tool that I used on my own local network. In 2025, I'm bringing it to anyone who wants it. {SITENAME} was built out of a need to create and manage my own web applications.
</p>
<p class="text-muted">
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>
<p> <p>
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 publicly available is not a 1 to 1 job. There may be bugs or issues encountered while you use the produxct. I can't guarantee a fix for every need in every case immidiately, but I do actively keep track of bugs and work hard to ensure everyone has a great experiience usiing the app. 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!
</p>
<p>
If you encounter any bugs, feel free to report them <a href="/bugreport" class="text-primary text-decoration-underline">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> </p>
<div class="text-center mt-4 pb-4"> <div class="text-center mt-4 pb-4">
{loggedin}<a href="/bugreport" class="btn btn-primary btn-lg px-5">Report a Bug</a>{/loggedin} {loggedin}<a href="/bugreport" class="btn btn-primary btn-lg px-5">Report a Bug</a>{/loggedin}

View File

@ -1,5 +1,5 @@
<div class="context-main-bg context-main p-3"> <div class="context-main-bg context-main p-3">
<legend class="text-center">Edit Route: {nickname}</legend> <legend class="text-center">Create Route</legend>
<hr> <hr>
{ADMIN_BREADCRUMBS} {ADMIN_BREADCRUMBS}
<form method="post"> <form method="post">

View File

@ -14,7 +14,7 @@
<div class="mb-3 row"> <div class="mb-3 row">
<label for="token_type" class="col-lg-5 col-form-label text-end">Token Type</label> <label for="token_type" class="col-lg-5 col-form-label text-end">Token Type</label>
<div class="col-lg-3"> <div class="col-lg-3">
<select id="token_type" name="token_type" class="form-select"> <select id="token_type" name="token_type" class="form-control">
<option value='app' selected>Application</option> <option value='app' selected>Application</option>
<option value='user'>User</option> <option value='user'>User</option>
</select> </select>

View File

@ -14,7 +14,7 @@
<div class="mb-3 row"> <div class="mb-3 row">
<label for="token_type" class="col-lg-5 col-form-label text-end">Token Type</label> <label for="token_type" class="col-lg-5 col-form-label text-end">Token Type</label>
<div class="col-lg-3"> <div class="col-lg-3">
<select id="token_type" name="token_type" class="form-select"> <select id="token_type" name="token_type" class="form-control">
{OPTION=token_type} {OPTION=token_type}
<option value='app' selected>Application</option> <option value='app' selected>Application</option>
<option value='user'>User</option> <option value='user'>User</option>

View File

@ -1,11 +1,11 @@
<div class="container border-top mt-auto"> <div class="container border-top border-white mt-auto">
<footer class="pt-4"> <footer class="pt-4">
<div class="row"> <div class="row">
{FOOTER_LEFT} {FOOTER_LEFT}
{FOOTER_CENTER} {FOOTER_CENTER}
{FOOTER_RIGHT} {FOOTER_RIGHT}
</div> </div>
<div class="d-flex flex-column flex-sm-row justify-content-between py-3 mt-2 border-top"> <div class="d-flex flex-column flex-sm-row justify-content-between py-3 mt-2 border-top border-white">
{COPY} {COPY}
{SOCIAL} {SOCIAL}
</div> </div>

View File

@ -1 +1 @@
<span>© 2024 AllTheBookmarks, Powered by <a href="https://thetempusproject.com" class="text-decoration-none">The Tempus Project</a>.</span> <span>© 2025 {SITENAME}, Powered by <a href="https://thetempusproject.com" class="text-decoration-none">The Tempus Project</a>.</span>

View File

@ -1,53 +1,55 @@
<div class="container"> <div class="container">
<div class="row"> <div id="myCarousel" class="carousel slide m-3" data-bs-ride="carousel">
<div class="col-md-12"> <div class="carousel-indicators">
<div id="carousel-home" class="carousel slide" data-ride="carousel"> <button type="button" data-bs-target="#myCarousel" data-bs-slide-to="0" class="active" aria-current="true" aria-label="Slide 1"></button>
<!-- Indicators --> <button type="button" data-bs-target="#myCarousel" data-bs-slide-to="1" aria-label="Slide 2"></button>
<ol class="carousel-indicators"> <button type="button" data-bs-target="#myCarousel" data-bs-slide-to="2" aria-label="Slide 3"></button>
<li data-bs-target="#carousel-home" data-slide-to="0" class="active"></li> </div>
<li data-bs-target="#carousel-home" data-slide-to="1"></li>
<li data-bs-target="#carousel-home" data-slide-to="2"></li>
</ol>
<!-- Carousel Items -->
<div class="carousel-inner"> <div class="carousel-inner">
<div class="carousel-item active"> <div class="carousel-item active">
<img src="{ROOT_URL}app/images/ttp.png" class="d-block w-100" alt="First slide"> <img src="{ROOT_URL}app/images/ttp.png" class="bd-placeholder-img" alt="First slide">
<div class="carousel-caption d-none d-md-block bg-primary slide-text-bg"> <div class="container">
<h3>Powerful</h3> <div class="carousel-caption text-start bg-dark px-4">
<p>The Tempus Project is built with expansion in mind. From a custom template engine to the simple to use MVC style, The Tempus Project is built to provide a powerful and stable foundation for web applications.</p> <h1>Powerful</h1>
<p>
The Tempus Project is built with expansion in mind. From a custom template engine to the simple to use MVC style, The Tempus Project is built to provide a powerful and stable foundation for web applications.
</p>
</div>
</div> </div>
</div> </div>
<div class="carousel-item"> <div class="carousel-item">
<img src="{ROOT_URL}app/images/ttp-install.png" class="d-block w-100" alt="Second slide"> <img src="{ROOT_URL}app/images/ttp-install.png" class="bd-placeholder-img" alt="Second slide">
<div class="carousel-caption d-none d-md-block bg-primary slide-text-bg"> <div class="container">
<h3>Quick and Simple Installation</h3> <div class="carousel-caption bg-dark px-4">
<p>Built with rapid deployment in mind you can have The Tempus Project installed in just minutes.</p> <h1>Quick and Simple Installation</h1>
<p>
Built with rapid deployment in mind you can have The Tempus Project installed in just minutes.
</p>
</div>
</div> </div>
</div> </div>
<div class="carousel-item"> <div class="carousel-item">
<img src="{ROOT_URL}app/images/ttp-github.png" class="d-block w-100" alt="Third slide"> <img src="{ROOT_URL}app/images/ttp-github.png" class="bd-placeholder-img" alt="Third slide">
<div class="carousel-caption d-none d-md-block bg-primary slide-text-bg"> <div class="container">
<h3>Open Source</h3> <div class="carousel-caption text-end bg-dark px-4">
<p>The Tempus Project is completely open source and only utilizes other open-source components. The Project is provided under the MIT license.</p> <h1>Open Source</h1>
<p>
The Tempus Project is completely open source and only utilizes other open-source components. The Project is provided under the MIT license.
</p>
</div> </div>
</div> </div>
</div> </div>
<!-- Controls -->
<a class="carousel-control-prev" href="#carousel-home" data-slide="prev">
<span class="carousel-control-prev-icon" aria-hidden="true"></span>
<span class="sr-only">Previous</span>
</a>
<a class="carousel-control-next" href="#carousel-home" data-slide="next">
<span class="carousel-control-next-icon" aria-hidden="true"></span>
<span class="sr-only">Next</span>
</a>
</div> </div>
<button class="carousel-control-prev" type="button" data-bs-target="#myCarousel" data-bs-slide="prev">
<span class="carousel-control-prev-icon text-dark" aria-hidden="true"></span>
<span class="visually-hidden">Previous</span>
</button>
<button class="carousel-control-next" type="button" data-bs-target="#myCarousel" data-bs-slide="next">
<span class="carousel-control-next-icon text-dark" aria-hidden="true"></span>
<span class="visually-hidden">Next</span>
</button>
</div> </div>
</div> <div class="row m-3">
<div class="row">
<div class="col-lg-9 col-md-9 col-sm-12 col-centered"> <div class="col-lg-9 col-md-9 col-sm-12 col-centered">
<div class="row"> <div class="row">
<div class="col-lg-12 col-md-12 col-sm-12"> <div class="col-lg-12 col-md-12 col-sm-12">
@ -65,37 +67,9 @@
<li>Bug reports and feedback forms included!</li> <li>Bug reports and feedback forms included!</li>
<li>Drag and drop simple to install</li> <li>Drag and drop simple to install</li>
</ul> </ul>
<p>DISCLAIMER: as of January 1, 2023 this code is not production ready! Please use at your own risk! That being said, I am always trying to improve this system. If you have any suggestions or need to report a bug, you can do so on my <a href="https://github.com/TheTempusProject/TheTempusProject">GitHub</a>.</p> <p>DISCLAIMER: as of January 1, 2025 this code is not production ready! Please use at your own risk! That being said, I am always trying to improve this system. If you have any suggestions or need to report a bug, you can do so on my <a href="https://github.com/TheTempusProject/TheTempusProject">GitHub</a>.</p>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
https://www.bookmarkninja.com/
simple robust versatile
sleek
clean
intuitive
modern
A cleaner app for managing bookmarks to cut down on the clutter
Available accross all devices and systems.
regardless of windows or mac, android or apple, wwe have you covered
bring all your bookmarks at once, simply export from your current browser and use our import tool
stop straining your eyes! Not only can you use our dark mode feature, but we have several styles to choose from to customize your experience
no mobile app necessary
privacy is key! but sharing is ok
by defaults everything is set to private, but usiing the web interface will alow you to share single links or entire lists/folders

View File

@ -1,5 +1,5 @@
<div class="context-main-bg my-3 pb-3 rounded col-10 offset-1">
{installer-nav} {installer-nav}
<div class="container">
<div class="row justify-content-center"> <div class="row justify-content-center">
<div class="col-md-10"> <div class="col-md-10">
<legend class="my-3 text-center">Configure</legend> <legend class="my-3 text-center">Configure</legend>

View File

@ -1,5 +1,5 @@
<div class="context-main-bg my-3 pb-3 rounded col-10 offset-1">
{installer-nav} {installer-nav}
<div class="container">
<div class="row justify-content-center"> <div class="row justify-content-center">
<div class="col-md-10"> <div class="col-md-10">
<p class="my-4">All models are required for proper installation of The Tempus Project. In this step, we will add the database tables required for these models. In the next step, you'll be able to select which plugins you would like installed.</p> <p class="my-4">All models are required for proper installation of The Tempus Project. In this step, we will add the database tables required for these models. In the next step, you'll be able to select which plugins you would like installed.</p>

View File

@ -1,12 +1,12 @@
<ul class="nav nav-tabs justify-content-center" role="tablist"> <div class="nav nav-tabs justify-content-center pt-3 col-10 offset-1" id="nav-tab" role="tablist">
<li class="nav-item {menu-Welcome}"><a href="#" class="nav-link">Welcome</a></li> <button class="nav-link {menu-Welcome}" type="button" role="tab">Welcome</button>
<li class="nav-item {menu-Terms}"><a href="#" class="nav-link">Terms</a></li> <button class="nav-link {menu-Terms}" type="button" role="tab">Terms</button>
<li class="nav-item {menu-Verify}"><a href="#" class="nav-link">Verify</a></li> <button class="nav-link {menu-Verify}" type="button" role="tab">Verify</button>
<li class="nav-item {menu-Configure}"><a href="#" class="nav-link">Configure</a></li> <button class="nav-link {menu-Configure}" type="button" role="tab">Configure</button>
<li class="nav-item {menu-Routing}"><a href="#" class="nav-link">Routing</a></li> <button class="nav-link {menu-Routing}" type="button" role="tab">Routing</button>
<li class="nav-item {menu-Models}"><a href="#" class="nav-link">Models</a></li> <button class="nav-link {menu-Models}" type="button" role="tab">Models</button>
<li class="nav-item {menu-Plugins}"><a href="#" class="nav-link">Plugins</a></li> <button class="nav-link {menu-Plugins}" type="button" role="tab">Plugins</button>
<li class="nav-item {menu-Resources}"><a href="#" class="nav-link">Resources</a></li> <button class="nav-link {menu-Resources}" type="button" role="tab">Resources</button>
<li class="nav-item {menu-User}"><a href="#" class="nav-link">User</a></li> <button class="nav-link {menu-User}" type="button" role="tab">User</button>
<li class="nav-item {menu-Complete}"><a href="#" class="nav-link">Complete</a></li> <button class="nav-link {menu-Complete}" type="button" role="tab">Complete</button>
</ul> </div>

View File

@ -1,5 +1,5 @@
<div class="context-main-bg my-3 pb-3 rounded col-10 offset-1">
{installer-nav} {installer-nav}
<div class="container">
<div class="row justify-content-center"> <div class="row justify-content-center">
<div class="col-md-10"> <div class="col-md-10">
<p class="my-4"> <p class="my-4">

View File

@ -1,5 +1,5 @@
<div class="context-main-bg my-3 pb-3 rounded col-10 offset-1">
{installer-nav} {installer-nav}
<div class="container">
<div class="row justify-content-center"> <div class="row justify-content-center">
<div class="col-md-10"> <div class="col-md-10">
<p class="my-4"> <p class="my-4">

View File

@ -1,5 +1,5 @@
<div class="context-main-bg my-3 pb-3 rounded col-10 offset-1">
{installer-nav} {installer-nav}
<div class="container">
<div class="row justify-content-center"> <div class="row justify-content-center">
<div class="col-md-10"> <div class="col-md-10">
<p class="mt-4">The Tempus Project uses rewrites in htaccess files (Apache), or location directives (Nginx), to automatically route all incoming traffic through the app. In this step, we will help set-up and then test that the required configurations have been made.</p> <p class="mt-4">The Tempus Project uses rewrites in htaccess files (Apache), or location directives (Nginx), to automatically route all incoming traffic through the app. In this step, we will help set-up and then test that the required configurations have been made.</p>

View File

@ -1,13 +0,0 @@
<div class="container">
<div class="row justify-content-center">
<div class="col-md-10 text-center">
{installer-nav}
<h1 class="mt-4">Welcome to The Tempus Project Installer.</h1>
<p>This installer will guide you through the process of installing and configuring The Tempus Project. Do not forget to delete this file once you have completed installation.</p>
<form method="post">
<input type="hidden" name="token" value="{TOKEN}">
<button class="btn btn-lg btn-primary center-block" type="submit" name="submit" value="submit">Begin Installation</button><br>
</form>
</div>
</div>
</div>

View File

@ -1,7 +1,7 @@
<div class="context-main-bg my-3 pb-3 rounded col-10 offset-1">
{installer-nav} {installer-nav}
<div class="container">
<div class="row justify-content-center"> <div class="row justify-content-center">
<div class="col-md-10"> <div class="col-10">
<div class="install-terms col-lg-8 mx-auto mt-4"> <div class="install-terms col-lg-8 mx-auto mt-4">
{TERMS} {TERMS}
</div> </div>

View File

@ -1,5 +1,5 @@
<div class="context-main-bg my-3 pb-3 rounded col-10 offset-1">
{installer-nav} {installer-nav}
<div class="container">
<div class="row justify-content-center"> <div class="row justify-content-center">
<div class="col-md-10 pt-4"> <div class="col-md-10 pt-4">
<form action="" method="post" class="form-horizontal"> <form action="" method="post" class="form-horizontal">

View File

@ -1,5 +1,5 @@
<div class="context-main-bg my-3 pb-3 rounded col-10 offset-1">
{installer-nav} {installer-nav}
<div class="container">
<div class="row justify-content-center"> <div class="row justify-content-center">
<div class="col-md-10"> <div class="col-md-10">
<h2 class="mt-4">Requirements</h2> <h2 class="mt-4">Requirements</h2>

View File

@ -0,0 +1,17 @@
<div class="container">
<div class="row justify-content-center">
<div class="col-md-10 text-center">
<div class="context-main-bg my-3 pb-3 rounded">
{installer-nav}
<h1 class="mt-4">Welcome to The Tempus Project Installer.</h1>
<p>
This installer will guide you through the process of installing and configuring The Tempus Project. Do not forget to delete this file once you have completed installation.
</p>
<form action="" method="post">
<input type="hidden" name="token" value="{TOKEN}">
<button class="btn btn-lg btn-primary center-block" type="submit" name="submit" id="submit" value="submit">Begin Installation</button><br>
</form>
</div>
</div>
</div>
</div>

View File

@ -8,7 +8,7 @@
<div class="card-body p-md-5 mx-md-4"> <div class="card-body p-md-5 mx-md-4">
<div class="text-center"> <div class="text-center">
<img src="{ROOT_URL}{LOGO}" style="width: 185px;" alt="logo"> <img src="{ROOT_URL}{LOGO}" style="width: 185px;" alt="logo">
<h4 class="mt-1 mb-5 pb-1">AllTheBookmarks</h4> <h4 class="mt-1 mb-5 pb-1">{SITENAME}</h4>
</div> </div>
<form action="{ROOT_URL}home/login" method="post"> <form action="{ROOT_URL}home/login" method="post">
<div data-mdb-input-init class="form-outline mb-4"> <div data-mdb-input-init class="form-outline mb-4">
@ -26,7 +26,7 @@
</div> </div>
</div> </div>
<div class="text-center pt-1 mb-5 pb-1"> <div class="text-center pt-1 mb-5 pb-1">
<button name="submit" value="submit" type="submit" data-mdb-button-init data-mdb-ripple-init class="btn btn-primary btn-block fa-lg gradient-custom-2 mb-3"> <button name="submit" value="submit" type="submit" data-mdb-button-init data-mdb-ripple-init class="btn btn-primary btn-block mb-3">
Sign in Sign in
</button> </button>
<a class="text-muted" href="{ROOT_URL}register/recover">Forgot password?</a> <a class="text-muted" href="{ROOT_URL}register/recover">Forgot password?</a>

View File

@ -1,4 +1,3 @@
<div class="dropdown nav-item mx-2"> <div class="dropdown nav-item mx-2">
<a <a
href="#" href="#"
@ -9,50 +8,44 @@
aria-expanded="false"> aria-expanded="false">
<i class="fa fa-user"></i> <i class="fa fa-user"></i>
</a> </a>
<div class="dropdown-menu dropdown-menu-dark dropdown-menu-end text-small shadow" aria-labelledby="userDropdown"> <div class="dropdown-menu dropdown-menu-dark dropdown-menu-end text-small shadow" aria-labelledby="userDropdown" style="min-width: 300px;">
<form method="post" action="{ROOT_URL}home/login" id="signin" class="px-4 py-3"> <form method="post" action="{ROOT_URL}home/login" id="signin" class="px-4 py-3">
<input type="hidden" name="rurl" id="rurl" value="{CURRENT_URL}"> <input type="hidden" name="rurl" id="rurl" value="{CURRENT_URL}">
<input type="hidden" name="token" value="{TOKEN}"> <input type="hidden" name="token" value="{TOKEN}">
<!-- Username --> <!-- Username -->
<div class="form-group"> <div class="form-group mb-3">
<input <input id="username"
id="username"
type="text" type="text"
class="form-control mb-2" class="form-control"
name="username" name="username"
placeholder="Username"> placeholder="Username">
</div> </div>
<!-- Password --> <!-- Password -->
<div class="form-group"> <div class="form-group mb-3">
<input <input id="password"
id="password"
type="password" type="password"
class="form-control mb-2" class="form-control"
name="password" name="password"
placeholder="Password"> placeholder="Password">
</div> </div>
<!-- Remember Me --> <!-- Remember Me -->
<div class="form-check"> <div class="form-check form-switch mb-3">
<input <input class="form-check-input" type="checkbox" role="switch" name="remember" id="remember" value="true">
type="checkbox" <label class="form-check-label" for="remember">Stay Logged In</label>
class="form-check-input mb-2"
id="remember"
name="remember"
value="true">
<label class="form-check-label" for="remember">Remember me</label>
</div> </div>
<!-- Submit Button --> <!-- Submit and Register Buttons -->
<button <div class="d-flex gap-1">
type="submit" <button name="submit" value="submit" type="submit" class="btn btn-primary flex-fill">
class="btn btn-primary btn-block"
name="submit"
value="submit">
Sign in Sign in
</button> </button>
<a href="{ROOT_URL}register" class="btn btn-outline-primary flex-fill">
Create new
</a>
</div>
</form> </form>
</div> </div>
</div> </div>

View File

@ -1,8 +1,9 @@
<div class="context-main-bg p-4 my-5"> <div class="context-main-bg p-4 my-5">
<h1>Privacy Policy</h1> <h1>Privacy Policy</h1>
<hr>
<p>Last updated: December 09, 2024</p> <p>Last updated: December 09, 2024</p>
<p>This Privacy Policy describes Our policies and procedures on the collection, use and disclosure of Your information when You use the Service and tells You about Your privacy rights and how the law protects You.</p> <p>This Privacy Policy describes Our policies and procedures on the collection, use and disclosure of Your information when You use the Service and tells You about Your privacy rights and how the law protects You.</p>
<p>We use Your Personal data to provide and improve the Service. By using the Service, You agree to the collection and use of information in accordance with this Privacy Policy. This Privacy Policy has been created with the help of the <a href="https://www.termsfeed.com/privacy-policy-generator/" target="_blank">Privacy Policy Generator</a>.</p> <p>We use Your Personal data to provide and improve the Service. By using the Service, You agree to the collection and use of information in accordance with this Privacy Policy. This Privacy Policy has been created with the help of the <a href="https://www.termsfeed.com/privacy-policy-generator/" target="_blank" class="text-decoration-none">Privacy Policy Generator</a>.</p>
<h2>Interpretation and Definitions</h2> <h2>Interpretation and Definitions</h2>
<h3>Interpretation</h3> <h3>Interpretation</h3>
<p>The words of which the initial letter is capitalized have meanings defined under the following conditions. The following definitions shall have the same meaning regardless of whether they appear in singular or in plural.</p> <p>The words of which the initial letter is capitalized have meanings defined under the following conditions. The following definitions shall have the same meaning regardless of whether they appear in singular or in plural.</p>
@ -16,10 +17,10 @@
<p><strong>Affiliate</strong> means an entity that controls, is controlled by or is under common control with a party, where &quot;control&quot; means ownership of 50% or more of the shares, equity interest or other securities entitled to vote for election of directors or other managing authority.</p> <p><strong>Affiliate</strong> means an entity that controls, is controlled by or is under common control with a party, where &quot;control&quot; means ownership of 50% or more of the shares, equity interest or other securities entitled to vote for election of directors or other managing authority.</p>
</li> </li>
<li> <li>
<p><strong>Application</strong> refers to All The Bookmarks, the software program provided by the Company.</p> <p><strong>Application</strong> refers to {SITENAME}, the software program provided by the Company.</p>
</li> </li>
<li> <li>
<p><strong>Company</strong> (referred to as either &quot;the Company&quot;, &quot;We&quot;, &quot;Us&quot; or &quot;Our&quot; in this Agreement) refers to All The Bookmarks.</p> <p><strong>Company</strong> (referred to as either &quot;the Company&quot;, &quot;We&quot;, &quot;Us&quot; or &quot;Our&quot; in this Agreement) refers to {SITENAME}.</p>
</li> </li>
<li> <li>
<p><strong>Cookies</strong> are small files that are placed on Your computer, mobile device or any other device by a website, containing the details of Your browsing history on that website among its many uses.</p> <p><strong>Cookies</strong> are small files that are placed on Your computer, mobile device or any other device by a website, containing the details of Your browsing history on that website among its many uses.</p>
@ -46,7 +47,7 @@
<p><strong>Usage Data</strong> refers to data collected automatically, either generated by the use of the Service or from the Service infrastructure itself (for example, the duration of a page visit).</p> <p><strong>Usage Data</strong> refers to data collected automatically, either generated by the use of the Service or from the Service infrastructure itself (for example, the duration of a page visit).</p>
</li> </li>
<li> <li>
<p><strong>Website</strong> refers to All The Bookmarks, accessible from <a href="https://allthebookmarks.com" rel="external nofollow noopener" target="_blank">https://allthebookmarks.com</a></p> <p><strong>Website</strong> refers to {SITENAME}, accessible from <a href="{SITE_URL}" rel="external nofollow noopener" class="text-decoration-none" target="_blank">{SITE_URL}</a></p>
</li> </li>
<li> <li>
<p><strong>You</strong> means the individual accessing or using the Service, or the company, or other legal entity on behalf of which such individual is accessing or using the Service, as applicable.</p> <p><strong>You</strong> means the individual accessing or using the Service, or the company, or other legal entity on behalf of which such individual is accessing or using the Service, as applicable.</p>
@ -89,7 +90,7 @@
<li><strong>Cookies or Browser Cookies.</strong> A cookie is a small file placed on Your Device. You can instruct Your browser to refuse all Cookies or to indicate when a Cookie is being sent. However, if You do not accept Cookies, You may not be able to use some parts of our Service. Unless you have adjusted Your browser setting so that it will refuse Cookies, our Service may use Cookies.</li> <li><strong>Cookies or Browser Cookies.</strong> A cookie is a small file placed on Your Device. You can instruct Your browser to refuse all Cookies or to indicate when a Cookie is being sent. However, if You do not accept Cookies, You may not be able to use some parts of our Service. Unless you have adjusted Your browser setting so that it will refuse Cookies, our Service may use Cookies.</li>
<li><strong>Web Beacons.</strong> Certain sections of our Service and our emails may contain small electronic files known as web beacons (also referred to as clear gifs, pixel tags, and single-pixel gifs) that permit the Company, for example, to count users who have visited those pages or opened an email and for other related website statistics (for example, recording the popularity of a certain section and verifying system and server integrity).</li> <li><strong>Web Beacons.</strong> Certain sections of our Service and our emails may contain small electronic files known as web beacons (also referred to as clear gifs, pixel tags, and single-pixel gifs) that permit the Company, for example, to count users who have visited those pages or opened an email and for other related website statistics (for example, recording the popularity of a certain section and verifying system and server integrity).</li>
</ul> </ul>
<p>Cookies can be &quot;Persistent&quot; or &quot;Session&quot; Cookies. Persistent Cookies remain on Your personal computer or mobile device when You go offline, while Session Cookies are deleted as soon as You close Your web browser. You can learn more about cookies on <a href="https://www.termsfeed.com/blog/cookies/#What_Are_Cookies" target="_blank">TermsFeed website</a> article.</p> <p>Cookies can be &quot;Persistent&quot; or &quot;Session&quot; Cookies. Persistent Cookies remain on Your personal computer or mobile device when You go offline, while Session Cookies are deleted as soon as You close Your web browser. You can learn more about cookies on <a class="text-decoration-none" href="https://www.termsfeed.com/blog/cookies/#What_Are_Cookies" target="_blank">TermsFeed website</a> article.</p>
<p>We use both Session and Persistent Cookies for the purposes set out below:</p> <p>We use both Session and Persistent Cookies for the purposes set out below:</p>
<ul> <ul>
<li> <li>
@ -191,10 +192,10 @@
<p>If you have any questions about this Privacy Policy, You can contact us:</p> <p>If you have any questions about this Privacy Policy, You can contact us:</p>
<ul> <ul>
<li> <li>
<p>By email: webmaster@allthebookmarks.com</p> <p>By email: {DEBUG_EMAIL}</p>
</li> </li>
<li> <li>
<p>By visiting this page on our website: <a href="https://allthebookmarks.com/contact" rel="external nofollow noopener" target="_blank">https://allthebookmarks.com/contact</a></p> <p>By visiting this page on our website: <a href="{SITE_URL}/contact" rel="external nofollow noopener" class="text-decoration-none" target="_blank">{SITE_URL}/contact</a></p>
</li> </li>
</ul> </ul>
<div class="text-center py-3"> <div class="text-center py-3">

View File

@ -46,7 +46,7 @@
<div class=""> <div class="">
<input type="checkbox" class="form-check-input" name="terms" id="terms" value="1" required> <input type="checkbox" class="form-check-input" name="terms" id="terms" value="1" required>
<label for="terms" class="form-check-label"> <label for="terms" class="form-check-label">
I have read and agree to the <a href="/home/terms" class="text-primary">Terms of Service</a> I have read and agree to the <a href="/home/terms" class="text-primary text-decoration-none">Terms of Service</a>
</label> </label>
</div> </div>
<div class="terms mt-2 mx-auto"> <div class="terms mt-2 mx-auto">

23
app/views/start.html Normal file
View File

@ -0,0 +1,23 @@
<div class="col-8 mx-auto p-4 rounded shadow-sm context-main-bg my-4">
<h2 class="text-center mb-4">Getting Started with {SITENAME}</h2>
<p class="lead">
{SITENAME} has been open source for many years now. The hopes and intentions for it were always to give others a leg-up to get started building web-apps like i wish i had as a kid.
There were so many tutorials and ideas, expansions and plans for the project.
Unfortunately no person is given unlimited time to accomplish their dreams and over the years the idea for a huge repository for learning and education has taken a back seat.
</p>
<p>
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>.
</p>
<p class="text-muted">
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>
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!
</p>
<div class="text-center mt-4 pb-4">
{loggedin}<a href="/bugreport" class="btn btn-primary btn-lg px-5">Report a Bug</a>{/loggedin}
<a href="/contact" class="btn btn-outline-secondary btn-lg px-5 ms-3">Contact Us</a>
</div>
</div>

View File

@ -2,7 +2,7 @@
<h3 class="mb-4">Preferences</h3> <h3 class="mb-4">Preferences</h3>
<hr> <hr>
<div class="row justify-content-center"> <div class="row justify-content-center">
<div class="col-md-4"> <div class="col-md-6">
<form action="" method="post" class=""> <form action="" method="post" class="">
<fieldset> <fieldset>
{PREFERENCES_FORM} {PREFERENCES_FORM}

View File

@ -425,7 +425,8 @@ class TheTempusProject extends Bedrock {
"url" => "{ROOT_URL}usercp/password", "url" => "{ROOT_URL}usercp/password",
"name" => "Change Password" "name" => "Change Password"
]; ];
Components::set( 'SITE_URL', Routes::getAddress() );
Components::set( 'DEBUG_EMAIL', DEBUG_EMAIL );
Debug::gend(); Debug::gend();
} }

View File

@ -23,7 +23,7 @@ use TheTempusProject\Hermes\Functions\Route;
use TheTempusProject\Houdini\Classes\Components; use TheTempusProject\Houdini\Classes\Components;
use TheTempusProject\Houdini\Classes\Views; use TheTempusProject\Houdini\Classes\Views;
// I switched to cloudflare which uses a dynamic proxy kinda thing, so any IP address lookups go wonky unless i get the OG IIP // I switched to cloudflare which uses a dynamic proxy kinda thing, so any IP address lookups go wonky unless i get the OG IP
if ( isset( $_SERVER['HTTP_CF_CONNECTING_IP'] ) && filter_var( $_SERVER['HTTP_CF_CONNECTING_IP'], FILTER_VALIDATE_IP ) ) { if ( isset( $_SERVER['HTTP_CF_CONNECTING_IP'] ) && filter_var( $_SERVER['HTTP_CF_CONNECTING_IP'], FILTER_VALIDATE_IP ) ) {
$_SERVER['REMOTE_ADDR'] = $_SERVER['HTTP_CF_CONNECTING_IP']; $_SERVER['REMOTE_ADDR'] = $_SERVER['HTTP_CF_CONNECTING_IP'];
} }
@ -36,21 +36,18 @@ if ( isset( $_SERVER['HTTP_CF_CONNECTING_IP'] ) && filter_var( $_SERVER['HTTP_CF
*/ */
$url = ''; $url = '';
$app = new TheTempusProject();
if ( Input::exists( 'error' ) ) { if ( Input::exists( 'error' ) ) {
switch ( Input::get( 'error' ) ) { switch ( Input::get( 'error' ) ) {
case 'image404': case 'image404':
Redirect::to( 'images/imageNotFound.png' ); Redirect::to( 'images/imageNotFound.png' );
exit; exit;
default:
$app->setUrl( 'error/' . Input::get( 'error' ) );
break;
} }
} elseif ( stripos( Route::getUri(), 'install.php' ) ) { } elseif ( stripos( Route::getUri(), 'install.php' ) ) {
require_once 'install.php'; require_once 'install.php';
} }
$app = new TheTempusProject();
if ( CANARY_ENABLED ) { if ( CANARY_ENABLED ) {
ini_set( 'display_errors', '1' ); ini_set( 'display_errors', '1' );
ini_set( 'display_startup_errors', '1' ); ini_set( 'display_startup_errors', '1' );

View File

@ -156,7 +156,7 @@ class Install extends Controller {
if ( Input::exists( 'submit' ) ) { if ( Input::exists( 'submit' ) ) {
Issues::add( 'error', ['There was an error with the Installation.' => Check::userErrors()] ); Issues::add( 'error', ['There was an error with the Installation.' => Check::userErrors()] );
} }
Views::view( 'install.start' ); Views::view( 'install.welcome' );
} }
/** /**
@ -171,7 +171,7 @@ class Install extends Controller {
Issues::add( 'error', [ 'There was an error with the Installation.' => Check::userErrors() ] ); Issues::add( 'error', [ 'There was an error with the Installation.' => Check::userErrors() ] );
} }
Components::set( 'TERMS', Views::simpleView( 'terms' ) ); Components::set( 'TERMS', Views::simpleView( 'terms' ) );
Views::view( 'install.agreement' ); Views::view( 'install.terms' );
} }
/** /**
@ -185,10 +185,9 @@ class Install extends Controller {
if ( Input::exists( 'submit' ) ) { if ( Input::exists( 'submit' ) ) {
Issues::add( 'error', ['There was an error with the Installation.' => array_merge( Check::userErrors(), Check::systemErrors() )] ); Issues::add( 'error', ['There was an error with the Installation.' => array_merge( Check::userErrors(), Check::systemErrors() )] );
} }
Views::view( 'install.check' ); Views::view( 'install.verify' );
} }
/** /**
* One of the most important steps for installation, is the configuration. In this step, we will define some very core settings * One of the most important steps for installation, is the configuration. In this step, we will define some very core settings
* for the app including the app's name and database credentials. * for the app including the app's name and database credentials.
@ -381,7 +380,7 @@ class Install extends Controller {
} elseif ( Input::exists( 'submit' ) ) { } elseif ( Input::exists( 'submit' ) ) {
Issues::add( 'error', ['There was an error with your form.' => Check::userErrors()] ); Issues::add( 'error', ['There was an error with your form.' => Check::userErrors()] );
} }
Views::view( 'install.adminUser' ); Views::view( 'install.user' );
} }
/** /**