';
+
+ return Template::parse( $html );
}
public static function getCategoryEditHtml( $category ) {
@@ -47,12 +91,12 @@ class Config extends BedrockConfig {
Debug::warn( "Config category not found: $category" );
return;
}
- $categoryHeader = '
';
}
return $html;
}
@@ -68,4 +112,4 @@ class Config extends BedrockConfig {
}
return $html;
}
-}
+}
\ No newline at end of file
diff --git a/app/classes/controller.php b/app/classes/controller.php
index 63e948b..4bde183 100644
--- a/app/classes/controller.php
+++ b/app/classes/controller.php
@@ -35,7 +35,6 @@ class Controller extends BedrockController {
}
new Template;
Template::setTemplate( 'default' );
- Components::set( 'TOKEN', Token::generate() );
}
public function __destruct() {
diff --git a/app/classes/forms.php b/app/classes/forms.php
index a60b7af..1d6c4cb 100644
--- a/app/classes/forms.php
+++ b/app/classes/forms.php
@@ -112,6 +112,9 @@ class Forms extends Check {
self::addHandler( 'newGroup', __CLASS__, 'newGroup' );
self::addHandler( 'editGroup', __CLASS__, 'editGroup' );
self::addHandler( 'install', __CLASS__, 'install' );
+ self::addHandler( 'adminCreateToken', __CLASS__, 'adminCreateToken' );
+ self::addHandler( 'apiLogin', __CLASS__, 'apiLogin' );
+ self::addHandler( 'updatePreference', __CLASS__, 'updatePreference' );
self::addHandler( 'installStart', __CLASS__, 'install', [ 'start' ] );
self::addHandler( 'installAgreement', __CLASS__, 'install', [ 'agreement' ] );
self::addHandler( 'installCheck', __CLASS__, 'install', [ 'check' ] );
@@ -212,6 +215,10 @@ class Forms extends Check {
* @return {bool}
*/
public static function passwordResetCode() {
+ if ( !Input::exists( 'resetCode' ) ) {
+ self::addUserError( 'Invalid resetCode.' );
+ return false;
+ }
if ( !self::token() ) {
return false;
}
@@ -608,4 +615,52 @@ class Forms extends Check {
}
return true;
}
+
+ public static function adminCreateToken() {
+ if ( !Input::exists( 'name' ) ) {
+ self::addUserError( 'You must specify a name' );
+ return false;
+ }
+ if ( !Input::exists( 'token_type' ) ) {
+ self::addUserError( 'You must specify a token_type' );
+ return false;
+ }
+ return true;
+ }
+
+ public static function adminEditToken() {
+ if ( !Input::exists( 'name' ) ) {
+ self::addUserError( 'You must specify a name' );
+ return false;
+ }
+ if ( !Input::exists( 'token_type' ) ) {
+ self::addUserError( 'You must specify a token_type' );
+ return false;
+ }
+ return true;
+ }
+
+ public static function apiLogin() {
+ if ( !self::checkUsername( Input::post( 'username' ) ) ) {
+ self::addUserError( 'Invalid username.' );
+ return false;
+ }
+ if ( !self::password( Input::post( 'password' ) ) ) {
+ self::addUserError( 'Invalid password.' );
+ return false;
+ }
+ 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;
+ }
}
diff --git a/app/classes/permissions.php b/app/classes/permissions.php
index 56ac7ff..bef0368 100644
--- a/app/classes/permissions.php
+++ b/app/classes/permissions.php
@@ -15,6 +15,7 @@ use TheTempusProject\Canary\Bin\Canary as Debug;
use TheTempusProject\Bedrock\Functions\Check;
use TheTempusProject\Houdini\Classes\Forms;
use TheTempusProject\Bedrock\Functions\Input;
+use TheTempusProject\Houdini\Classes\Template;
class Permissions {
public static $permissions = false;
@@ -237,8 +238,21 @@ class Permissions {
} else {
$checked = false;
}
- $form .= Forms::getFormFieldHtml( $name, $details['pretty'], 'checkbox', $checked );
+ $form .= self::getFieldEditHtml( $name, $checked, $details['pretty'] );
}
return $form;
}
+
+ public static function getFieldEditHtml( $name, $default, $pretty ) {
+ $fieldname = str_ireplace( '/', '-', $name );
+ $fieldHtml = Forms::getSwitchHtml( $fieldname, [ 'true', 'false' ], $default );
+ $html = '';
+ $html .= '
';
+ return Template::parse( $html );
+ }
}
diff --git a/app/classes/plugin.php b/app/classes/plugin.php
index 696dda3..76b1c67 100644
--- a/app/classes/plugin.php
+++ b/app/classes/plugin.php
@@ -309,7 +309,7 @@ class Plugin {
$data = [];
foreach( $this->resourceMatrix as $tableName => $entries ) {
foreach ($ids as $id) {
- $data[] = self::$db->delete( $tableName, $id );
+ $data[] = self::$db->delete( $tableName, [ 'ID', '=', $id ] );
}
}
return $data;
@@ -335,9 +335,14 @@ class Plugin {
}
public function loadFooterNav() {
- if ( !empty( $this->footer_links ) ) {
- foreach( $this->footer_links as $key => $link ) {
- Navigation::addLink( App::FOOTER_MENU_NAME, $link );
+ if ( !empty( $this->contact_footer_links ) ) {
+ foreach( $this->contact_footer_links as $key => $link ) {
+ Navigation::addLink( App::CONTACT_FOOTER_MENU_NAME, $link );
+ }
+ }
+ if ( !empty( $this->info_footer_links ) ) {
+ foreach( $this->info_footer_links as $key => $link ) {
+ Navigation::addLink( App::INFO_FOOTER_MENU_NAME, $link );
}
}
}
diff --git a/app/classes/preferences.php b/app/classes/preferences.php
index 4f86d0c..3bf15fa 100644
--- a/app/classes/preferences.php
+++ b/app/classes/preferences.php
@@ -13,6 +13,7 @@ namespace TheTempusProject\Classes;
use TheTempusProject\Houdini\Classes\Issues;
use TheTempusProject\Houdini\Classes\Forms;
+use TheTempusProject\Houdini\Classes\Template;
use TheTempusProject\Canary\Bin\Canary as Debug;
use TheTempusProject\Bedrock\Functions\Check;
use TheTempusProject\Bedrock\Functions\Upload;
@@ -186,17 +187,92 @@ class Preferences {
}
public function getFormHtml( $populated = [] ) {
+ // dv( self::$preferences );
$form = '';
+ // Added so i can force some sort of ordering
+ $inputTypes = [
+ 'file' => [],
+ 'select' => [],
+ 'timezone' => [],
+ 'checkbox' => [],
+ 'switch' => [],
+ ];
foreach ( self::$preferences as $name => $details ) {
$tempPrefsArray = $this->normalizePreferenceArray( $name, $details );
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;
}
+ 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 .= '
';
+ $html .= '
';
+ $html .= '
';
+ $html .= $fieldHtml;
+ $html .= '
';
+ if ( 'file' === $type ) {
+ $html .= '
';
+ $html .= '
Current Image
';
+ $html .= '
';
+ $html .= '

';
+ $html .= '
';
+ }
+ $html .= '
';
+ return Template::parse( $html );
+ }
+
public function convertFormToArray( $fillMissing = true, $defaultsOnly = true ) {
$prefsArray = [];
foreach ( self::$preferences as $name => $details ) {
@@ -212,12 +288,13 @@ class Preferences {
}
if ( 'file' == $details['type'] ) {
if ( Input::exists( $name ) ) {
- $folder = IMAGE_UPLOAD_DIRECTORY . App::$activeUser->username . DIRECTORY_SEPARATOR;
- if ( !Upload::image( $name, $folder ) ) {
- Issues::add( 'error', [ 'There was an error with your upload.' => Check::systemErrors() ] );
- } else {
+ $folder = UPLOAD_DIRECTORY . App::$activeUser->username . DIRECTORY_SEPARATOR . 'images' . DIRECTORY_SEPARATOR;
+ $upload = Upload::image( $name, $folder );
+ if ( $upload ) {
$route = str_replace( APP_ROOT_DIRECTORY, '', $folder );
$prefsArray[$name] = $route . Upload::last();
+ } else {
+ Issues::add( 'error', [ 'There was an error with your upload.' => Check::userErrors() ] );
}
}
}
diff --git a/app/config/prod/constants.php b/app/config/prod/constants.php
index 32e8dda..2f1863c 100644
--- a/app/config/prod/constants.php
+++ b/app/config/prod/constants.php
@@ -38,6 +38,8 @@ if ( ! defined( 'CONFIG_DIRECTORY' ) ) {
# Tempus Debugger
define( 'CANARY_SECURE_HASH', 'd73ed7591a30f0ca7d686a0e780f0d05' );
# Tempus Project Core
+define( 'APP_NAME', 'The Tempus Project');
+define( 'TP_DEFAULT_LOGO', 'images/logo.png');
// Check
define( 'MINIMUM_PHP_VERSION', 8.1);
// Cookies
diff --git a/app/controllers/admin/admin.php b/app/controllers/admin/admin.php
index 3fa11f4..17d1850 100644
--- a/app/controllers/admin/admin.php
+++ b/app/controllers/admin/admin.php
@@ -39,7 +39,7 @@ class Admin extends AdminController {
}
public function index() {
- return Views::view( 'admin.logs.admin_list', self::$log->listPaginated( 'admin' ) );
+ return Views::view( 'admin.logs.admin_list', self::$log->list( 'admin' ) );
}
public function view( $id = null ) {
diff --git a/app/controllers/admin/composer.php b/app/controllers/admin/composer.php
index 0f14bb2..7eb1323 100644
--- a/app/controllers/admin/composer.php
+++ b/app/controllers/admin/composer.php
@@ -59,6 +59,6 @@ class Composer extends AdminController {
$out[] = (object) $versionsInstalled[ $name ];
}
- Views::view( 'admin.modules.composer.dependencies', $out );
+ Views::view( 'admin.modules.dependencies', $out );
}
}
diff --git a/app/controllers/admin/errors.php b/app/controllers/admin/errors.php
index d0bf4b3..da8cf23 100644
--- a/app/controllers/admin/errors.php
+++ b/app/controllers/admin/errors.php
@@ -39,7 +39,7 @@ class Errors extends AdminController {
}
public function index() {
- return Views::view( 'admin.logs.error_list', self::$log->listPaginated( 'error' ) );
+ return Views::view( 'admin.logs.error_list', self::$log->list( 'error' ) );
}
public function view( $id = null ) {
diff --git a/app/controllers/admin/home.php b/app/controllers/admin/home.php
index 5017d89..c4a7704 100644
--- a/app/controllers/admin/home.php
+++ b/app/controllers/admin/home.php
@@ -15,8 +15,11 @@ use TheTempusProject\Houdini\Classes\Views;
use TheTempusProject\Houdini\Classes\Components;
use TheTempusProject\Classes\AdminController;
use TheTempusProject\Models\User;
-use TheTempusProject\Plugins\Comments;
-use TheTempusProject\Plugins\Blog;
+use TheTempusProject\Models\Comments;
+use TheTempusProject\Models\Posts;
+use TheTempusProject\Plugins\Comments as CommentPlugin;
+use TheTempusProject\Plugins\Blog as BlogPlugin;
+use TheTempusProject\Canary\Bin\Canary as Debug;
class Home extends AdminController {
public static $user;
@@ -30,17 +33,29 @@ class Home extends AdminController {
public function index() {
if ( class_exists( 'TheTempusProject\Plugins\Comments' ) ) {
- $comments = new Comments;
- self::$comments = $comments->getModel();
- $comments = Views::simpleView( 'comments.admin.dashboard', self::$comments->recent( 'all', 5 ) );
- Components::set( 'commentDash', $comments );
+ $plugin = new CommentPlugin;
+
+ if ( ! $plugin->checkEnabled() ) {
+ Debug::info( 'Comments Plugin is disabled in the control panel.' );
+ Components::set( 'commentDash', '' );
+ } else {
+ $comments = new Comments;
+ $commentList = Views::simpleView( 'comments.admin.dashboard', $comments->recent( 'all', 5 ) );
+ Components::set( 'commentDash', $commentList );
+ }
}
if ( class_exists( 'TheTempusProject\Plugins\Blog' ) ) {
- $blog = new Blog;
- self::$posts = $blog->posts;
- $posts = Views::simpleView( 'blog.admin.dashboard', self::$posts->recent( 5 ) );
- Components::set( 'blogDash', $posts );
+ $plugin = new BlogPlugin;
+
+ if ( ! $plugin->checkEnabled() ) {
+ Debug::info( 'Blog Plugin is disabled in the control panel.' );
+ Components::set( 'blogDash', '' );
+ } else {
+ $posts = new Posts;
+ $postsList = Views::simpleView( 'blog.admin.dashboard', $posts->recent( 5 ) );
+ Components::set( 'blogDash', $postsList );
+ }
}
self::$user = new User;
diff --git a/app/controllers/admin/logins.php b/app/controllers/admin/logins.php
index 02356f6..174639e 100644
--- a/app/controllers/admin/logins.php
+++ b/app/controllers/admin/logins.php
@@ -39,7 +39,7 @@ class Logins extends AdminController {
}
public function index() {
- return Views::view( 'admin.logs.login_list', self::$log->listPaginated( 'login' ) );
+ return Views::view( 'admin.logs.login_list', self::$log->list( 'login' ) );
}
public function view( $id = null ) {
diff --git a/app/controllers/admin/logs.php b/app/controllers/admin/logs.php
index 6c72abb..1729f1e 100644
--- a/app/controllers/admin/logs.php
+++ b/app/controllers/admin/logs.php
@@ -26,8 +26,8 @@ class Logs extends AdminController {
}
public function index( $data = null ) {
- Views::view( 'admin.logs.error_list', self::$log->listPaginated( 'error' ) );
- Views::view( 'admin.logs.admin_list', self::$log->listPaginated( 'admin' ) );
- Views::view( 'admin.logs.login_list', self::$log->listPaginated( 'login' ) );
+ Views::view( 'admin.logs.error_list', self::$log->list( 'error' ) );
+ Views::view( 'admin.logs.admin_list', self::$log->list( 'admin' ) );
+ Views::view( 'admin.logs.login_list', self::$log->list( 'login' ) );
}
}
diff --git a/app/controllers/admin/plugins.php b/app/controllers/admin/plugins.php
index 18e5836..ea152e8 100644
--- a/app/controllers/admin/plugins.php
+++ b/app/controllers/admin/plugins.php
@@ -39,7 +39,12 @@ class Plugins extends AdminController {
}
public function disable( $name = null ) {
+ if ( empty( $name ) ) {
+ Session::flash( 'error', 'Unknown Plugin.' );
+ Redirect::to( 'admin/plugins' );
+ }
Components::set( 'PLUGIN', $name );
+ self::$title = 'Admin - Disable ' . $name;
if ( !Input::exists( 'installHash' ) ) {
return Views::view( 'admin.modules.plugins.disable' );
}
@@ -52,7 +57,12 @@ class Plugins extends AdminController {
}
public function enable( $name = null ) {
+ if ( empty( $name ) ) {
+ Session::flash( 'error', 'Unknown Plugin.' );
+ Redirect::to( 'admin/plugins' );
+ }
Components::set( 'PLUGIN', $name );
+ self::$title = 'Admin - Enable ' . $name;
if ( !Input::exists( 'installHash' ) ) {
return Views::view( 'admin.modules.plugins.enable' );
}
@@ -71,6 +81,7 @@ class Plugins extends AdminController {
}
$name = strtolower( $name );
Components::set( 'PLUGIN', $name );
+ self::$title = 'Admin - Install ' . $name;
if ( ! Input::exists( 'installHash' ) ) {
return Views::view( 'admin.modules.plugins.install' );
}
@@ -95,6 +106,7 @@ class Plugins extends AdminController {
}
$name = strtolower($name);
Components::set( 'PLUGIN', $name );
+ self::$title = 'Admin - Uninstall ' . $name;
if ( !Input::exists( 'uninstallHash' ) ) {
return Views::view( 'admin.modules.plugins.uninstall' );
diff --git a/app/controllers/admin/routes.php b/app/controllers/admin/routes.php
index e21307b..0f65ccc 100644
--- a/app/controllers/admin/routes.php
+++ b/app/controllers/admin/routes.php
@@ -37,19 +37,25 @@ class Routes extends AdminController {
public function create() {
if ( Input::exists( 'redirect_type' ) ) {
- if ( !TTPForms::check( 'createRoute' ) ) {
- Issues::add( 'error', [ 'There was an error with your route.' => Check::userErrors() ] );
- }
- if ( self::$routes->create(
- Input::post( 'original_url' ),
- Input::post( 'forwarded_url' ),
- Input::post( 'nickname' ),
- Input::post( 'redirect_type' )
- ) ) {
- Session::flash( 'success', 'Route Created' );
- Redirect::to( 'admin/routes' );
- }
+ return Views::view( 'admin.routes.create' );
}
+
+ if ( !TTPForms::check( 'createRoute' ) ) {
+ Issues::add( 'error', [ 'There was an error with your route.' => Check::userErrors() ] );
+ return Views::view( 'admin.routes.create' );
+ }
+
+ if ( self::$routes->create(
+ Input::post( 'original_url' ),
+ Input::post( 'forwarded_url' ),
+ Input::post( 'nickname' ),
+ Input::post( 'redirect_type' )
+ ) ) {
+ Session::flash( 'success', 'Route Created' );
+ Redirect::to( 'admin/routes' );
+ }
+
+ Issues::add( 'error', 'There was an unknown error saving your redirect.' );
Views::view( 'admin.routes.create' );
}
diff --git a/app/controllers/admin/contact.php b/app/controllers/admin/send_mail.php
similarity index 93%
rename from app/controllers/admin/contact.php
rename to app/controllers/admin/send_mail.php
index a71f3c6..e89a3dd 100644
--- a/app/controllers/admin/contact.php
+++ b/app/controllers/admin/send_mail.php
@@ -1,8 +1,8 @@
@@ -19,13 +19,13 @@ use TheTempusProject\Houdini\Classes\Views;
use TheTempusProject\Models\User;
use TheTempusProject\Models\Subscribe;
-class Contact extends AdminController {
+class SendMail extends AdminController {
public static $user;
public static $subscribe;
public function __construct() {
parent::__construct();
- self::$title = 'Admin - Contact';
+ self::$title = 'Admin - Send Mail';
self::$user = new User;
self::$subscribe = new Subscribe;
}
diff --git a/app/controllers/admin/tokens.php b/app/controllers/admin/tokens.php
new file mode 100644
index 0000000..5655b22
--- /dev/null
+++ b/app/controllers/admin/tokens.php
@@ -0,0 +1,90 @@
+
+ * @link https://TheTempusProject.com
+ * @license https://opensource.org/licenses/MIT [MIT LICENSE]
+ */
+namespace TheTempusProject\Controllers\Admin;
+
+use TheTempusProject\Classes\Forms as TTPForms;
+use TheTempusProject\Houdini\Classes\Views;
+use TheTempusProject\Houdini\Classes\Issues;
+use TheTempusProject\Houdini\Classes\Navigation;
+use TheTempusProject\Houdini\Classes\Components;
+use TheTempusProject\Houdini\Classes\Forms;
+use TheTempusProject\Classes\AdminController;
+use TheTempusProject\Models\Token;
+use TheTempusProject\Bedrock\Functions\Input;
+use TheTempusProject\Bedrock\Functions\Check;
+use TheTempusProject\Hermes\Functions\Redirect;
+use TheTempusProject\Bedrock\Functions\Session;
+
+class Tokens extends AdminController {
+ public static $token;
+
+ public function __construct() {
+ parent::__construct();
+ self::$title = 'Admin - Tokens';
+ self::$token = new Token;
+ $view = Navigation::activePageSelect( 'nav.admin', '/admin/tokens' );
+ Components::set( 'ADMINNAV', $view );
+ }
+
+ public function create() {
+ if ( Input::exists( 'submit' ) ) {
+ if ( !TTPForms::check( 'adminCreateToken' ) ) {
+ Issues::add( 'error', [ 'There was an error with your token.' => Check::userErrors() ] );
+ }
+ if ( self::$token->create(
+ Input::post( 'name' ),
+ Input::post( 'notes' ),
+ Input::post( 'token_type' )
+ ) ) {
+ Session::flash( 'success', 'Token Created' );
+ Redirect::to( 'admin/tokens' );
+ }
+ }
+ Views::view( 'admin.tokens.create' );
+ }
+
+ public function delete( $id = null ) {
+ if ( self::$token->delete( [ $id ] ) ) {
+ Session::flash( 'success', 'Token deleted.' );
+ }
+ Redirect::to( 'admin/tokens' );
+ }
+
+ public function edit( $id = null ) {
+ $token = self::$token->findById( $id );
+ if ( Input::exists( 'submit' ) ) {
+ if ( !TTPForms::check( 'adminEditToken' ) ) {
+ Issues::add( 'error', [ 'There was an error with your token.' => Check::userErrors() ] );
+ } else {
+ if ( self::$token->update(
+ $id,
+ Input::post( 'name' ),
+ Input::post( 'notes' ),
+ Input::post( 'token_type' )
+ ) ) {
+ Session::flash( 'success', 'Token Updated' );
+ Redirect::to( 'admin/tokens' );
+ }
+ }
+ }
+ Forms::selectOption( $token->token_type );
+ return Views::view( 'admin.tokens.edit', $token );
+ }
+
+ public function index() {
+ return Views::view( 'admin.tokens.list', self::$token->listPaginated() );
+ }
+
+ public function view( $id = null ) {
+ return Views::view( 'admin.tokens.view', self::$token->findById( $id ) );
+ }
+}
diff --git a/app/controllers/admin/users.php b/app/controllers/admin/users.php
index c2e4055..0cff516 100644
--- a/app/controllers/admin/users.php
+++ b/app/controllers/admin/users.php
@@ -26,6 +26,7 @@ use TheTempusProject\Classes\AdminController;
use TheTempusProject\Models\User;
use TheTempusProject\Models\Group;
use TheTempusProject\TheTempusProject as App;
+use TheTempusProject\Houdini\Classes\Template;
class Users extends AdminController {
public static $user;
@@ -63,8 +64,11 @@ class Users extends AdminController {
}
}
}
-
- $select = Forms::getFormFieldHtml( 'groupSelect', 'User Group', 'select', Config::getValue( 'group/defaultGroup' ), self::$group->listGroupsSimple() );
+ $select = Forms::getSelectHtml(
+ 'groupSelect',
+ self::$group->listGroupsSimple(),
+ Config::getValue( 'group/defaultGroup' ),
+ );
Components::set( 'groupSelect', $select );
Views::view( 'admin.users.create' );
}
@@ -132,9 +136,15 @@ class Users extends AdminController {
$userGroup = $userData->userGroup;
}
Forms::selectRadio( 'confirmed', $userData->confirmed );
- $avatar = Forms::getFormFieldHtml( 'avatar', 'User Avatar', 'file', $avatarLocation );
- $select = Forms::getFormFieldHtml( 'groupSelect', 'User Group', 'select', $userGroup, self::$group->listGroupsSimple() );
+
+ $avatar = $this->getAvatar( 'avatar', $avatarLocation );
Components::set( 'AvatarSettings', $avatar );
+
+ $select = Forms::getSelectHtml(
+ 'groupSelect',
+ self::$group->listGroupsSimple(),
+ $userGroup,
+ );
Components::set( 'groupSelect', $select );
Views::view( 'admin.users.edit', $userData );
}
@@ -153,4 +163,28 @@ class Users extends AdminController {
}
$this->index();
}
+
+ private function getAvatar( $name, $value ) {
+ $fieldname = str_ireplace( '/', '-', $name );
+
+ $html = '';
+ $fieldHtml = '';
+ $fieldHtml = Forms::getFileHtml( $fieldname );
+
+ $html .= '
';
+ $html .= '
';
+ $html .= '
';
+ $html .= ' ' . $fieldHtml;
+ $html .= '
';
+ $html .= '
';
+
+ $html .= '
';
+ $html .= '
Current Image
';
+ $html .= '
';
+ $html .= '

';
+ $html .= '
';
+ $html .= '
';
+
+ return Template::parse( $html );
+ }
}
diff --git a/app/controllers/api/auth.php b/app/controllers/api/auth.php
new file mode 100644
index 0000000..a0de860
--- /dev/null
+++ b/app/controllers/api/auth.php
@@ -0,0 +1,38 @@
+
+ * @link https://TheTempusProject.com
+ * @license https://opensource.org/licenses/MIT [MIT LICENSE]
+ */
+namespace TheTempusProject\Controllers\Api;
+
+use TheTempusProject\Models\User;
+use TheTempusProject\Classes\ApiController;
+use TheTempusProject\Houdini\Classes\Views;
+use TheTempusProject\Models\Token;
+
+class Auth extends ApiController {
+ public static $tokens;
+
+ public function __construct() {
+ parent::__construct();
+ self::$tokens = new Token;
+ }
+
+ public function refresh() {
+ $token = self::$tokens->refresh( self::$authToken->ID );
+ if ( empty( $token ) ) {
+ $responseType = 'error';
+ $response = 'IRDK';
+ } else {
+ $responseType = 'token';
+ $response = $token;
+ }
+ Views::view( 'api.response', ['response' => json_encode( [ $responseType => $response ], true )]);
+ }
+}
\ No newline at end of file
diff --git a/app/controllers/api/login.php b/app/controllers/api/login.php
new file mode 100644
index 0000000..74536eb
--- /dev/null
+++ b/app/controllers/api/login.php
@@ -0,0 +1,50 @@
+
+ * @link https://TheTempusProject.com
+ * @license https://opensource.org/licenses/MIT [MIT LICENSE]
+ */
+namespace TheTempusProject\Controllers\Api;
+
+use TheTempusProject\Classes\ApiController;
+use TheTempusProject\Houdini\Classes\Views;
+use TheTempusProject\Models\Token;
+use TheTempusProject\Models\User;
+use TheTempusProject\Houdini\Classes\Template;
+use TheTempusProject\Classes\Forms;
+use TheTempusProject\Bedrock\Functions\Input;
+
+class Login extends ApiController {
+ public static $tokens;
+ public static $user;
+
+ public function __construct() {
+ parent::__construct( false );
+ self::$tokens = new Token;
+ self::$user = new User;
+ Template::addHeader( 'Access-Control-Allow-Origin: *' );
+ Template::addHeader( 'Content-Type: application/json; charset=utf-8' );
+ }
+
+ public function index() {
+ if ( ! Forms::check( 'apiLogin' ) ) {
+ $responseType = 'error';
+ $response = 'malformed input';
+ return Views::view( 'api.response', ['response' => json_encode( [ $responseType => $response ], true )]);
+ }
+ $user = self::$user->authorize( Input::post( 'username' ), Input::post( 'password' ) );
+ if ( ! $user ) {
+ $responseType = 'error';
+ $response = 'bad credentials';
+ return Views::view( 'api.response', ['response' => json_encode( [ $responseType => $response ], true )]);
+ }
+ $responseType = 'token';
+ $token = self::$tokens->findOrCreateUserToken( $user->ID, true );
+ return Views::view( 'api.response', ['response' => json_encode( [ $responseType => $token ], true )]);
+ }
+}
\ No newline at end of file
diff --git a/app/controllers/home.php b/app/controllers/home.php
index 02c14c2..75c36c0 100644
--- a/app/controllers/home.php
+++ b/app/controllers/home.php
@@ -52,7 +52,7 @@ class Home extends Controller {
public function login() {
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 ) {
return Issues::add( 'notice', 'You are already logged in. Please
click here to log out.' );
}
@@ -88,7 +88,7 @@ class Home extends Controller {
public function profile( $id = null ) {
self::$title = 'User Profile - {SITENAME}';
- self::$pageDescription = 'User Profiles for {SITENAME}';
+ self::$pageDescription = 'User Profile - {SITENAME}';
if ( !App::$isLoggedIn ) {
return Issues::add( 'notice', 'You must be logged in to view this page.' );
}
@@ -105,17 +105,13 @@ class Home extends Controller {
self::$title = 'Terms and Conditions - {SITENAME}';
self::$pageDescription = '{SITENAME} Terms and Conditions of use. Please use {SITENAME} safely.';
Components::set( 'TERMS', Views::simpleView( 'terms' ) );
- Views::raw( '
{TERMS}
' );
+ 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() {
+ self::$title = 'About - {SITENAME}';
+ 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' );
}
public function about() {
diff --git a/app/controllers/register.php b/app/controllers/register.php
index 9f5f41b..275fdc2 100644
--- a/app/controllers/register.php
+++ b/app/controllers/register.php
@@ -27,24 +27,25 @@ use TheTempusProject\Classes\Forms;
class Register extends Controller {
public function confirm( $code = null ) {
+ Template::noIndex();
self::$title = 'Confirm Email';
if ( !isset( $code ) && !Input::exists( 'confirmationCode' ) ) {
- return Views::view( 'email.confirmation' );
+ return Views::view( 'confirmation' );
}
if ( Forms::check( 'emailConfirmation' ) ) {
$code = Input::post( 'confirmationCode' );
}
if ( !self::$user->confirm( $code ) ) {
Issues::add( 'error', 'There was an error confirming your account, please try again.' );
- return Views::view( 'email.confirmation' );
+ return Views::view( 'confirmation' );
}
Session::flash( 'success', 'You have successfully confirmed your email address.' );
Redirect::to( 'home/index' );
}
public function index() {
- self::$title = 'Register';
- 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::$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.';
Components::set( 'TERMS', Views::simpleView( 'terms' ) );
if ( App::$isLoggedIn ) {
return Issues::add( 'notice', 'You are currently logged in.' );
@@ -94,43 +95,45 @@ class Register extends Controller {
public function resend() {
self::$title = 'Resend Confirmation';
+ Template::noIndex();
if ( !App::$isLoggedIn ) {
return Issues::add( 'notice', 'Please log in to resend your confirmation email.' );
}
- if ( App::$activeUser->data()->confirmed == '1' ) {
+ if ( App::$activeUser->confirmed == '1' ) {
return Issues::add( 'notice', 'Your account has already been confirmed.' );
}
if ( !Forms::check( 'confirmationResend' ) ) {
- return Views::view( 'email.confirmation_resend' );
+ return Views::view( 'confirmation_resend' );
}
- Email::send( App::$activeUser->data()->email, 'confirmation', App::$activeUser->data()->confirmationCode, [ 'template' => true ] );
+ Email::send( App::$activeUser->email, 'confirmation', App::$activeUser->confirmationCode, [ 'template' => true ] );
Session::flash( 'success', 'Your confirmation email has been sent to the email for your account.' );
Redirect::to( 'home/index' );
}
public function reset( $code = null ) {
self::$title = 'Password Reset';
+ Template::noIndex();
if ( !isset( $code ) && !Input::exists( 'resetCode' ) ) {
- Issues::add( 'error', 'No reset code provided.' );
+ Issues::add( 'info', 'Please provide a reset code.' );
return Views::view( 'password_reset_code' );
}
if ( Input::exists( 'resetCode' ) ) {
- if ( Forms::check( 'password_reset_code' ) ) {
+ if ( Forms::check( 'passwordResetCode' ) ) {
$code = Input::post( 'resetCode' );
}
}
- if ( !self::$user->checkCode( $code ) ) {
+ if ( ! self::$user->checkCode( $code ) ) {
Issues::add( 'error', 'There was an error with your reset code. Please try again.' );
return Views::view( 'password_reset_code' );
}
- if ( !Input::exists() ) {
+ Components::set( 'resetCode', $code );
+ if ( ! Input::exists('password') ) {
return Views::view( 'password_reset' );
}
- if ( !Forms::check( 'passwordReset' ) ) {
+ if ( ! Forms::check( 'passwordReset' ) ) {
Issues::add( 'error', [ 'There was an error with your request.' => Check::userErrors() ] );
return Views::view( 'password_reset' );
}
- Components::set( 'resetCode', $code );
self::$user->changePassword( $code, Input::post( 'password' ) );
Email::send( self::$user->data()->email, 'passwordChange', null, [ 'template' => true ] );
Session::flash( 'success', 'Your Password has been changed, please use your new password to log in.' );
diff --git a/app/controllers/usercp.php b/app/controllers/usercp.php
index bdeba21..39a5444 100644
--- a/app/controllers/usercp.php
+++ b/app/controllers/usercp.php
@@ -36,13 +36,14 @@ class Usercp extends Controller {
Redirect::home();
}
Template::noIndex();
- Navigation::activePageSelect( 'nav.usercp', null, true );
}
public function email() {
self::$title = 'Email Settings';
+ $menu = Views::simpleView( 'nav.usercp', App::$userCPlinks );
+ Navigation::activePageSelect( $menu, null, true, true );
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
click here', 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
click here', true );
}
if ( !Input::exists() ) {
return Views::view( 'user_cp.email_change' );
@@ -67,11 +68,15 @@ class Usercp extends Controller {
public function index() {
self::$title = 'User Control Panel';
+ $menu = Views::simpleView( 'nav.usercp', App::$userCPlinks );
+ Navigation::activePageSelect( $menu, null, true, true );
Views::view( 'profile', App::$activeUser );
}
public function password() {
self::$title = 'Password Settings';
+ $menu = Views::simpleView( 'nav.usercp', App::$userCPlinks );
+ Navigation::activePageSelect( $menu, null, true, true );
if ( !Input::exists() ) {
return Views::view( 'user_cp.password_change' );
}
@@ -93,6 +98,8 @@ class Usercp extends Controller {
public function settings() {
self::$title = 'Preferences';
+ $menu = Views::simpleView( 'nav.usercp', App::$userCPlinks );
+ Navigation::activePageSelect( $menu, null, true, true );
$prefs = new Preferences;
$fields = App::$activePrefs;
if ( Input::exists( 'submit' ) ) {
@@ -108,4 +115,37 @@ class Usercp extends Controller {
Components::set( 'PREFERENCES_FORM', $prefs->getFormHtml( $fields ) );
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 )]);
+ }
}
diff --git a/app/css/main-dark.css b/app/css/main-dark.css
new file mode 100644
index 0000000..ce195ba
--- /dev/null
+++ b/app/css/main-dark.css
@@ -0,0 +1,150 @@
+/**
+ * app/css/main-dark.css
+ *
+ * This file provides dark mode styles to override existing Bootstrap 5 base styles.
+ *
+ * @version 3.0-dark
+ * @author Joey Kimsey
+ * @link https://TheTempusProject.com
+ * @license https://opensource.org/licenses/MIT [MIT LICENSE]
+ */
+
+.context-main {
+ color: #fff;
+}
+.context-second {
+ color: #1e1e1e;
+}
+.context-main-bg {
+ background-color: #2c2c2c;
+}
+.context-second-bg {
+ background-color: #1e1e1e;
+}
+.bg-default {
+ background-color: #2c2c2c;
+}
+.bg-none,.bg-warning {
+ color: #000 !important;
+}
+.context-other {
+ color: #000;
+}
+.accordion-button:not(.collapsed) {
+ color: #f5f5f5;
+ background-color: var(--bs-accordion-dark-active-bg);
+}
+body {
+ background-image: linear-gradient(180deg, #2c2c2c, #1e1e1e 100px, #1e1e1e);
+ color: #f5f5f5;
+}
+
+/**
+ * Install Terms
+ */
+.install-terms {
+ border: 1px solid #555;
+ background: #3a3a3a;
+}
+.install-terms p,
+.install-terms li {
+ color: #dcdcdc;
+}
+.install-terms h3 {
+ color: #ffffff;
+}
+.install-terms h4 {
+ color: #eaeaea;
+}
+.install-terms strong {
+ color: #ffffff;
+}
+.context-main {
+ color: #ffffff;
+}
+.context-other {
+ color: #ffffff;
+}
+
+/**
+ * Terms Page
+ */
+.terms-page {
+ border: 1px solid #555;
+ background: #3a3a3a;
+}
+.terms-page p,
+.terms-page li {
+ color: #dcdcdc;
+}
+.terms-page h3 {
+ color: #ffffff;
+}
+.terms-page h4 {
+ color: #eaeaea;
+}
+.terms-page strong {
+ color: #ffffff;
+}
+
+/**
+ * Terms
+ */
+.terms {
+ border: 1px solid #555;
+ background: #3a3a3a;
+}
+.terms p,
+.terms li {
+ color: #dcdcdc;
+}
+.terms h3 {
+ color: #ffffff;
+}
+.terms h4 {
+ color: #eaeaea;
+}
+.terms strong {
+ color: #ffffff;
+}
+
+/**
+ * Form Control
+ */
+.form-control-dark:focus {
+ border-color: #1e90ff;
+ box-shadow: 0 0 0 .25rem rgba(30, 144, 255, .5);
+}
+
+/**
+ * Example Divider
+ */
+.b-example-divider {
+ background-color: rgba(255, 255, 255, .1);
+}
+
+/**
+ * Text Shadows
+ */
+.text-shadow-1 {
+ text-shadow: 0 .125rem .25rem rgba(255, 255, 255, .25);
+}
+.text-shadow-2 {
+ text-shadow: 0 .25rem .5rem rgba(255, 255, 255, .25);
+}
+.text-shadow-3 {
+ 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: #85bd3e; */
+ border-color: #1b947f;
+ background-color: #1f1f1f;
+ /* box-shadow: 0 0 0 .25rem #1b947f; */
+ box-shadow: 0 0 0 .25rem #85bd3e;
+}
\ No newline at end of file
diff --git a/app/css/main.css b/app/css/main.css
index 495c7d6..c05624d 100644
--- a/app/css/main.css
+++ b/app/css/main.css
@@ -8,21 +8,96 @@
* @link https://TheTempusProject.com
* @license https://opensource.org/licenses/MIT [MIT LICENSE]
*/
+
+ .context-other-bg {
+ background-color: #eaeaea;
+}
+
+
+.context-main-bg {
+ background-color: #f7f7f7;
+}
+
+/* Base styles for the switch container */
+.material-switch {
+ position: relative;
+ display: inline-block;
+ width: 50px;
+ height: 25px;
+}
+
+/* Hide the default checkbox */
+.material-switch input {
+ opacity: 0;
+ width: 0;
+ height: 0;
+}
+
+/* Style the label as the switch */
+.material-switch .label-default {
+ position: absolute;
+ cursor: pointer;
+ top: 0;
+ left: 0;
+ right: 0;
+ bottom: 0;
+ background-color: var(--switch-off-bg, #ccc);
+ border-radius: 25px;
+ transition: background-color 0.3s ease-in-out;
+}
+
+/* Style the toggle circle (slider) */
+.material-switch .label-default::before {
+ content: '';
+ position: absolute;
+ height: 20px;
+ width: 20px;
+ border-radius: 50%;
+ background-color: var(--switch-slider-bg, #fff);
+ bottom: 2.5px;
+ left: 5px;
+ transition: transform 0.3s ease-in-out;
+ box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);
+}
+
+/* Change background color when checked */
+.material-switch input:checked + .label-default {
+ background-color: var(--switch-on-bg, #555); /* Bootstrap primary color */
+}
+
+/* Move the slider when checked */
+.material-switch input:checked + .label-default::before {
+ transform: translateX(25px); /* Adjust based on switch width */
+}
+
+
+
+
+
+
+
+
+
+.context-main {
+ color: #000;
+}
+.context-other {
+ color: #fff;
+}
html {
font-family: 'Open Sans', sans-serif;
position: relative;
min-height: 100%;
}
-body {
- margin-top: 100px;
-}
pre {
white-space: pre-wrap;
}
+
+body {
+ background-color: #e4e4e4;
+ /* background-image: linear-gradient(180deg, #eee, #fff 100px, #fff); */
+}
@media ( min-width: 768px ) {
- body {
- margin-top: 75px;
- }
.main {
padding-right: 40px;
padding-left: 40px;
@@ -31,546 +106,9 @@ pre {
padding-right: 225px;
padding-left: 0;
}
- .side-nav {
- right: 0;
- left: auto;
+ .bd-placeholder-img-lg {
+ font-size: 3.5rem;
}
- .side-nav {
- position: fixed;
- top: 51px;
- left: 225px;
- width: 225px;
- margin-left: -225px;
- border: none;
- border-radius: 0;
- overflow-y: auto;
- background-color: #222;
- bottom: 53px;
- overflow-x: hidden;
- padding-bottom: 10px;
- }
- .side-nav>li>a {
- width: 225px;
- }
- .side-nav li a:hover,
- .side-nav li a:focus {
- outline: none;
- background-color: #000 !important;
- }
-}
-
-/**
- * Other
- */
-.custom-nav {
- display: relative;
- float: right;
-}
-.navbar-form-alt {
- margin-top: 10px;
- margin-bottom: 10px;
-}
-.bars {
- display: block;
- width: 60px;
- height: 3px;
- background-color: #333;
- box-shadow: 0 5px 0 #333, 0 10px 0 #333;
-}
-.slide-text-bg {
- opacity: 0.6;
-}
-.avatar-125 {
- height: 125px;
- width: 125px;
-}
-.full {
- width: 100%;
-}
-.gap {
- height: 30px;
- width: 100%;
- clear: both;
- display: block;
-}
-.supportLi h4 {
- font-size: 20px;
- font-weight: lighter;
- line-height: normal;
- margin-bottom: 0 !important;
- padding-bottom: 0;
-}
-.bg-gray {
- background-image: -moz-linear-gradient( center bottom, #BBBBBB 0%, #F0F0F0 100% );
- box-shadow: 0 1px 0 #B4B3B3;
-}
-.payments {
- font-size: 1.5em;
-}
-.UI-buffer {
- padding-top: 35px;
- height: auto;
- border-bottom: 1px solid #CCCCCC;
-}
-.avatar {
- max-width: 33px;
-}
-.UI-page-buffer {
- padding-top: 30px;
- position: relative;
- height: auto;
- border-bottom: 1px solid #CCCCCC;
-}
-.main {
- padding: 20px;
- padding-bottom: 75px;
-}
-.user-row {
- margin-bottom: 14px;
-}
-.user-row:last-child {
- margin-bottom: 0;
-}
-.dropdown-user {
- margin: 13px 0;
- padding: 5px;
- height: 100%;
-}
-.dropdown-user:hover {
- cursor: pointer;
-}
-.table-user-information>tbody>tr {
- border-top: 1px solid rgb( 221, 221, 221 );
-}
-.table-user-information>tbody>tr:first-child {
- border-top: 0;
-}
-.table-user-information>tbody>tr>td {
- border-top: 0;
-}
-.top-pad {
- margin-top: 70px;
-}
-.foot-pad {
- padding-bottom: 0;
- /* padding-bottom: 261px; */
-}
-.dynamic-footer-padding {
- padding-bottom: var(--footer-height);
-}
-.footer-head .navbar-toggle {
- display: inline-block;
- float: none;
-}
-.avatar-round-40 {
- height: 40px;
- width: 40px;
-}
-.sticky-foot-head {
- z-index: 10;
- position: fixed;
- bottom: 51px;
- width: 100%;
- background: #EDEFF1;
- border-bottom: 1px solid #CCCCCC;
- border-top: 1px solid #DDDDDD;
-}
-.sticky-foot {
- background-color: #000;
- position: fixed;
- bottom: 0;
- width: 100%;
-}
-.sticky-copy {
- z-index: 10;
- padding-top: 10px;
- padding-bottom: 10px;
- height: 50px;
- background: #E3E3E3;
- border-bottom: 1px solid #CCCCCC;
- border-top: 1px solid #DDDDDD;
-}
-
-/**
- * Main Carousel
- */
-.main-text {
- padding-bottom: 0px;
- padding-top: 0px;
- top: 10px;
- bottom: auto;
- z-index: 10;
- width: auto;
- color: #FFF;
-}
-.btn-min-block {
- min-width: 170px;
- line-height: 26px;
-}
-.btn-clear {
- color: #FFF;
- background-color: transparent;
- border-color: #FFF;
- margin-right: 15px;
-}
-.btn-clear:hover {
- color: #000;
- background-color: #FFF;
-}
-#carousel-home {
- margin-bottom: 30px;
-}
-.col-centered {
- float: none;
- margin: 0 auto;
-}
-
-/**
- * Top Navigation
- */
-.top-nav {
- padding: 0 15px;
-}
-.top-nav>li {
- display: inline-block;
- float: left;
-}
-.top-nav>li>a {
- padding-top: 15px;
- padding-bottom: 15px;
- line-height: 20px;
- color: #999;
-}
-.top-nav>li>a:hover,
-.top-nav>li>a:focus,
-.top-nav>.open>a,
-.top-nav>.open>a:hover,
-.top-nav>.open>a:focus {
- color: #fff;
- background-color: #000;
-}
-.top-nav>.open>.dropdown-menu {
- float: left;
- position: absolute;
- margin-top: 0;
- border: 1px solid rgba( 0, 0, 0, .15 );
- border-top-left-radius: 0;
- border-top-right-radius: 0;
- background-color: #fff;
- -webkit-box-shadow: 0 6px 12px rgba( 0, 0, 0, .175 );
- box-shadow: 0 6px 12px rgba( 0, 0, 0, .175 );
-}
-.top-nav>.open>.dropdown-menu>li>a {
- white-space: normal;
-}
-
-/**
- * Messages Dropdown
- */
-ul.message-dropdown {
- padding: 0;
- max-height: 250px;
- overflow-x: hidden;
- overflow-y: auto;
-}
-li.message-header {
- margin: 5px 0;
- border-bottom: 1px solid rgba( 0, 0, 0, .15 );
-}
-li.message-preview {
- width: 275px;
- border-bottom: 1px solid rgba( 0, 0, 0, .15 );
-}
-li.message-preview>a {
- padding-top: 15px;
- padding-bottom: 15px;
-}
-li.message-footer {
- margin: 5px 0;
-}
-ul.alert-dropdown {
- width: 200px;
-}
-
-/**
- * Widget
- */
-.widget .list-group {
- margin-bottom: 0;
-}
-.widget .panel-title {
- display: inline
-}
-.widget .label {
- float: right;
-}
-.widget li.list-group-item {
- border-radius: 0;
- border: 0;
- border-top: 1px solid #ddd;
-}
-.widget li.list-group-item:hover {
- background-color: rgba( 86, 61, 124, .1 );
-}
-.widget .mic-info {
- color: #666666;
- font-size: 11px;
-}
-.widget .action {
- margin-top: 5px;
-}
-.widget .comment-text {
- font-size: 12px;
-}
-.widget .btn-block {
- border-top-left-radius: 0px;
- border-top-right-radius: 0px;
-}
-
-/**
- * Signin Form
- */
-.form-signin {
- max-width: 330px;
- padding: 15px;
- margin: 0 auto;
-}
-.form-signin .form-signin-heading,
-.form-signin .checkbox {
- margin-bottom: 10px;
-}
-.form-signin .checkbox {
- font-weight: normal;
-}
-.form-signin .form-control {
- position: relative;
- height: auto;
- -webkit-box-sizing: border-box;
- -moz-box-sizing: border-box;
- box-sizing: border-box;
- padding: 10px;
- font-size: 16px;
-}
-.form-signin .form-control:focus {
- z-index: 2;
-}
-.form-signin input[type="text"] {
- margin-bottom: -1px;
- border-bottom-right-radius: 0;
- border-bottom-left-radius: 0;
-}
-.form-signin input[type="password"] {
- margin-bottom: 10px;
- border-top-left-radius: 0;
- border-top-right-radius: 0;
-}
-
-/**
- * Footer and Copyright
- */
-.copy {
- z-index: 10;
- padding-top: 10px;
- padding-bottom: 10px;
- position: absolute;
- bottom: 0;
- width: 100%;
- height: 50px;
- background: #E3E3E3;
- border-bottom: 1px solid #CCCCCC;
- border-top: 1px solid #DDDDDD;
-}
-.footer-head {
- z-index: 10;
- position: absolute;
- bottom: 51px;
- width: 100%;
- background: #EDEFF1;
- border-bottom: 1px solid #CCCCCC;
- border-top: 1px solid #DDDDDD;
-}
-.footer-head p {
- margin: 0;
-}
-.footer-head img {
- max-width: 100%;
-}
-.footer-head h3 {
- border-bottom: 1px solid #BAC1C8;
- color: #54697E;
- font-size: 18px;
- font-weight: 600;
- line-height: 27px;
- padding: 5px 0 10px;
- text-transform: uppercase;
-}
-.footer-head ul {
- font-size: 13px;
- list-style-type: none;
- margin-left: 0;
- padding-left: 0;
- margin-top: 15px;
- color: #7F8C8D;
-}
-.footer-head ul li a {
- padding: 0 0 5px 0;
- display: inline-block;
-}
-.footer-head a {
- color: #78828D
-}
-
-/**
- * Side Navigation
- */
-.side-nav>li>ul {
- padding: 0;
-}
-.side-nav>li>ul>li>a {
- display: block;
- padding: 10px 15px 10px 38px;
- text-decoration: none;
- color: #999;
-}
-.side-nav>li>ul>li>a:hover {
- color: #fff;
-}
-.side-nav .active > a {
- color: #fff;
- background-color: #080808;
-}
-.side-nav .active > a:hover {
- color: #fff;
- background-color: #080808;
-}
-
-/**
- * Social
- */
-.social {
- margin-top: 75px;
- bottom: 0;
-}
-.content {
- position: absolute;
-}
-.social span {
- background: none repeat scroll 0 0 #B5B5B5;
- border: 2px solid #B5B5B5;
- -webkit-border-radius: 50%;
- -moz-border-radius: 50%;
- -o-border-radius: 50%;
- -ms-border-radius: 50%;
- border-radius: 50%;
- float: center;
- height: 36px;
- line-height: 36px;
- margin: 0 8px 0 0;
- padding: 0;
- text-align: center;
- width: 41px;
- transition: all 0.5s ease 0s;
- -moz-transition: all 0.5s ease 0s;
- -webkit-transition: all 0.5s ease 0s;
- -ms-transition: all 0.5s ease 0s;
- -o-transition: all 0.5s ease 0s;
-}
-.social span:hover {
- transform: scale( 1.15 ) rotate( 360deg) ;
- -webkit-transform: scale( 1.1 ) rotate( 360deg) ;
- -moz-transform: scale( 1.1 ) rotate( 360deg) ;
- -ms-transform: scale( 1.1 ) rotate( 360deg) ;
- -o-transform: scale( 1.1 ) rotate( 360deg) ;
-}
-.social span a {
- color: #EDEFF1;
-}
-.social span:hover {
- border: 2px solid #2c3e50;
- background: #2c3e50;
-}
-.social span a i {
- font-size: 16px;
- margin: 0 0 0 5px;
- color: #EDEFF1 !important;
-}
-
-/**
- * Newsletter Box
- */
-.newsletter-box input#appendedInputButton {
- background: #FFFFFF;
- display: inline-block;
- float: center;
- height: 30px;
- clear: both;
- width: 100%;
-}
-.newsletter-box .btn {
- border: medium none;
- -webkit-border-radius: 3px;
- -moz-border-radius: 3px;
- -o-border-radius: 3px;
- -ms-border-radius: 3px;
- border-radius: 3px;
- display: inline-block;
- height: 40px;
- padding: 0;
- width: 100%;
- color: #fff;
-}
-.newsletter-box {
- overflow: hidden;
-}
-
-/**
- * Colored Badges
- */
-.badge {
- padding: 1px 9px 2px;
- font-size: 12.025px;
- font-weight: bold;
- white-space: nowrap;
- color: #ffffff;
- background-color: #999999;
- -webkit-border-radius: 9px;
- -moz-border-radius: 9px;
- border-radius: 9px;
-}
-.badge:hover {
- color: #ffffff;
- text-decoration: none;
- cursor: pointer;
-}
-.badge-error {
- background-color: #b94a48;
-}
-.badge-error:hover {
- background-color: #953b39;
-}
-.badge-warning {
- background-color: #f89406;
-}
-.badge-warning:hover {
- background-color: #c67605;
-}
-.badge-success {
- background-color: #468847;
-}
-.badge-success:hover {
- background-color: #356635;
-}
-.badge-info {
- background-color: #3a87ad;
-}
-.badge-info:hover {
- background-color: #2d6987;
-}
-.badge-inverse {
- background-color: #333333;
-}
-.badge-inverse:hover {
- background-color: #1a1a1a;
}
/**
@@ -658,12 +196,119 @@ ul.alert-dropdown {
.terms strong {
color: #000;
}
-
-
-.navbar-header {
- margin-right: 75px;
+.pricing-header {
+ max-width: 700px;
+}
+.pricing-container {
+ max-width: 960px;
+}
+.bd-placeholder-img {
+ font-size: 1.125rem;
+ text-anchor: middle;
+ -webkit-user-select: none;
+ -moz-user-select: none;
+ user-select: none;
+}
+.b-example-vr {
+ flex-shrink: 0;
+ width: 1.5rem;
+ height: 100vh;
+}
+.bi {
+ vertical-align: -.125em;
+ fill: currentColor;
+}
+.form-control-dark {
+ border-color: var(--bs-gray);
+}
+.form-control-dark:focus {
+ border-color: #fff;
+ box-shadow: 0 0 0 .25rem rgba(255, 255, 255, .25);
+}
+.text-small {
+ font-size: 85%;
+}
+.dropdown-toggle {
+ outline: 0;
+}
+.b-example-divider {
+ height: 3rem;
+ background-color: rgba(0, 0, 0, .1);
+ border: solid rgba(0, 0, 0, .15);
+ border-width: 1px 0;
+ box-shadow: inset 0 .5em 1.5em rgba(0, 0, 0, .1), inset 0 .125em .5em rgba(0, 0, 0, .15);
+}
+.b-example-vr {
+ flex-shrink: 0;
+ width: 1.5rem;
+ height: 100vh;
+}
+.nav-scroller {
+ position: relative;
+ z-index: 2;
+ height: 2.75rem;
+ overflow-y: hidden;
+}
+.nav-scroller .nav {
+ display: flex;
+ flex-wrap: nowrap;
+ padding-bottom: 1rem;
+ margin-top: -1px;
+ overflow-x: auto;
+ text-align: center;
+ white-space: nowrap;
+ -webkit-overflow-scrolling: touch;
+}
+.b-example-vr {
+ flex-shrink: 0;
+ width: 1.5rem;
+ height: 100vh;
+}
+.feature-icon {
+ width: 4rem;
+ height: 4rem;
+ border-radius: .75rem;
+}
+.icon-link > .bi {
+ margin-top: .125rem;
+ margin-left: .125rem;
+ fill: currentcolor;
+ transition: transform .25s ease-in-out;
+}
+.icon-link:hover > .bi {
+ transform: translate(.25rem);
+}
+.icon-square {
+ width: 3rem;
+ height: 3rem;
+ border-radius: .75rem;
+}
+.text-shadow-1 {
+ text-shadow: 0 .125rem .25rem rgba(0, 0, 0, .25);
+}
+.text-shadow-2 {
+ text-shadow: 0 .25rem .5rem rgba(0, 0, 0, .25);
+}
+.text-shadow-3 {
+ text-shadow: 0 .5rem 1.5rem rgba(0, 0, 0, .25);
+}
+.card-cover {
+ background-repeat: no-repeat;
+ background-position: center center;
+ background-size: cover;
+}
+.feature-icon-small {
+ width: 3rem;
+ height: 3rem;
}
-.pagination {
- padding-left: 75px;
-}
\ No newline at end of file
+.gradient-custom-2 {
+ /* fallback for old browsers */
+ background: #fccb90;
+
+ /* Chrome 10-25, Safari 5.1-6 */
+ background: -webkit-linear-gradient(to right, #2c2c2c, #1e1e1e, #1e1e1e);
+
+ /* W3C, IE 10+/ Edge, Firefox 16+, Chrome 26+, Opera 12+, Safari 7+ */
+ background: linear-gradient(to right, #2c2c2c, #1e1e1e, #1e1e1e);
+}
diff --git a/app/functions/common.php b/app/functions/common.php
index 2626c38..24e982d 100644
--- a/app/functions/common.php
+++ b/app/functions/common.php
@@ -88,4 +88,32 @@ function iv( $variable ) {
echo '';
echo var_export( $variable, true );
echo '
';
+}
+
+function generateToken(): string {
+ return bin2hex(random_bytes(32)); // Generates a 64-character hexadecimal token
+}
+
+function generateRandomString( $length = 10 ) {
+ $characters = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
+ $charactersLength = strlen( $characters );
+ $randomString = '';
+ for ($i = 0; $i < $length; $i++) {
+ $randomString .= $characters[random_int(0, $charactersLength - 1)];
+ }
+ return $randomString;
+}
+
+function generateUuidV4(): string {
+ // Generate 16 random bytes
+ $data = random_bytes(16);
+
+ // Set the version to 4 -> random (bits 12-15 of time_hi_and_version)
+ $data[6] = chr((ord($data[6]) & 0x0f) | 0x40);
+
+ // Set the variant to RFC 4122 -> (bits 6-7 of clock_seq_hi_and_reserved)
+ $data[8] = chr((ord($data[8]) & 0x3f) | 0x80);
+
+ // Convert to hexadecimal and format as a UUID
+ return vsprintf('%s%s-%s-%s-%s-%s%s%s', str_split(bin2hex($data), 4));
}
\ No newline at end of file
diff --git a/app/images/ttp.png b/app/images/ttp.png
index 2ad7269..ebb70f8 100644
Binary files a/app/images/ttp.png and b/app/images/ttp.png differ
diff --git a/app/js/main.js b/app/js/main.js
index 86fe116..3173ece 100644
--- a/app/js/main.js
+++ b/app/js/main.js
@@ -27,15 +27,6 @@ function checkAll(ele) {
}
}
-function copyAll( ele ) {
- var eleName = '#' + ele;
- var text = $( eleName ).text();
- text = text.replaceAll( "''", "\n" ).trim();
- text = text.substring( 1, text.length - 1 );
- navigator.clipboard.writeText( text );
- console.log( '#' + ele );
-}
-
function insertTag( box, tag ) {
var Field = document.getElementById( box );
var currentPos = cursorPos( Field );
@@ -69,6 +60,26 @@ function getRandomInt(min, max) {
return Math.floor(Math.random() * (maxFloored - minCeiled) + minCeiled);
}
+function copyElementText( id ) {
+ const inputElement = document.getElementById( id );
+ const textToCopy = inputElement.value;
+
+ if (navigator.clipboard && navigator.clipboard.writeText) {
+ navigator.clipboard.writeText(textToCopy)
+ .then(() => alert('Copied to clipboard!'))
+ .catch((err) => console.error('Failed to copy: ', err));
+ } else {
+ // Fallback for older browsers
+ inputElement.select();
+ try {
+ document.execCommand('copy');
+ alert('Copied to clipboard!');
+ } catch (err) {
+ console.error('Failed to copy: ', err);
+ }
+ }
+}
+
$(document).ready(function() {
$('select').each(function() {
var selectedValue = $(this).attr('value');
@@ -83,29 +94,71 @@ $(document).ready(function() {
});
});
-// with the dynamic footer, you need to adjust the content padding to make sure the footer doesn't overlap the content
-window.onload = function () {
- function updateFooterPadding() {
- var footer = document.querySelector('footer');
- var container = document.querySelector('.container-fluid.top-pad');
- if ( ! container ) {
- return;
+document.addEventListener('DOMContentLoaded', function () {
+ const toggleButton = document.getElementById('dark-mode-toggle');
+ const enableButton = document.getElementById('dark-mode-toggle-button');
+ const darkModeStylesheet = document.getElementById('dark-mode-stylesheet');
+
+ // Check if dark mode is saved in localStorage
+ if (localStorage.getItem('darkMode') === 'enabled') {
+ darkModeStylesheet.disabled = false;
+ toggleButton.checked = true;
+
+ if ( enableButton ) {
+ enableButton.innerText = 'Disable Now';
}
- // footer has no height but its children do!
- var footerHeight = Array.from(footer.children).reduce((totalHeight, child) => {
- return totalHeight + child.offsetHeight;
- }, 0);
-
- footerHeight += 20; // Add 20px for padding
-
- // console.error(footerHeight);
-
- container.style.setProperty('--footer-height', footerHeight + 'px');
}
- // Update padding on initial load
- updateFooterPadding();
+ document.querySelectorAll('.table-striped').forEach((table) => {
+ if (localStorage.getItem('darkMode') === 'enabled') {
+ table.classList.add('table-dark');
+ } else {
+ table.classList.add('table-light')
+ }
+ });
- // Update padding on window resize
- window.addEventListener('resize', updateFooterPadding);
-};
\ No newline at end of file
+ if ( enableButton ) {
+ enableButton.addEventListener('click', function () {
+ if (darkModeStylesheet.disabled) {
+ darkModeStylesheet.disabled = false;
+ localStorage.setItem('darkMode', 'enabled');
+ enableButton.innerText = 'Disable Now';
+ } else {
+ darkModeStylesheet.disabled = true;
+ localStorage.setItem('darkMode', 'disabled');
+ enableButton.innerText = 'Enable Now';
+ }
+ });
+ }
+
+ toggleButton.addEventListener('click', function () {
+ if (darkModeStylesheet.disabled) {
+ toggleDarkModePref( true );
+ darkModeStylesheet.disabled = false;
+ localStorage.setItem('darkMode', 'enabled');
+ } else {
+ toggleDarkModePref( false );
+ darkModeStylesheet.disabled = true;
+ localStorage.setItem('darkMode', 'disabled');
+ }
+
+ document.querySelectorAll('.table-striped').forEach((table) => {
+ if (localStorage.getItem('darkMode') === 'enabled') {
+ table.classList.add('table-dark');
+ table.classList.remove('table-light');
+ } else {
+ table.classList.add('table-light');
+ table.classList.remove('table-dark');
+ }
+ });
+ });
+
+ function toggleDarkModePref( value ) {
+ var fields = {};
+ fields.prefName = 'darkMode';
+ fields.prefValue = value;
+ $.post( '/usercp/updatePref', fields ).done(function(response) {
+ // alert('Timer updated successfully!');
+ });
+ }
+});
diff --git a/app/models/group.php b/app/models/group.php
index 4a375f6..bbc512b 100644
--- a/app/models/group.php
+++ b/app/models/group.php
@@ -257,7 +257,7 @@ class Group extends DatabaseModel {
if ( $group === false ) {
return false;
}
- $members = self::$db->getPaginated( 'users', [ 'userGroup', '=', $id ] );
+ $members = self::$db->get( 'users', [ 'userGroup', '=', $id ] );
if ( !$members->count() ) {
Debug::info( "list members: Could not find anyone in group: $id" );
return false;
diff --git a/app/models/log.php b/app/models/log.php
index 9131548..efd7b4a 100644
--- a/app/models/log.php
+++ b/app/models/log.php
@@ -87,7 +87,7 @@ class Log extends DatabaseModel {
}
public function list( $filter = null ) {
- $logData = self::$db->getPaginated( $this->tableName, [ 'source', '=', $filter ] );
+ $logData = self::$db->get( $this->tableName, [ 'source', '=', $filter ] );
if ( !$logData->count() ) {
return false;
}
diff --git a/app/models/routes.php b/app/models/routes.php
index 19b26b2..8333692 100644
--- a/app/models/routes.php
+++ b/app/models/routes.php
@@ -72,7 +72,7 @@ class Routes extends DatabaseModel {
return false;
}
if ( !Check::simpleName( $nickname ) ) {
- Debug::warn( 'Invalid route nickname: ' . $name );
+ Debug::warn( 'Invalid route nickname: ' . $nickname );
return false;
}
if ( 'external' == $type && !Check::url( $forwarded_url ) ) {
@@ -128,7 +128,7 @@ class Routes extends DatabaseModel {
}
$routeData = self::$db->get( $this->tableName, [ 'nickname', '=', $name ] );
if ( !$routeData->count() ) {
- Debug::warn( "Could not find a group named: $name" );
+ Debug::info( "Routes:findByName: Could not find a route named: $name" );
return false;
}
return $this->filter( $routeData->first() );
@@ -137,7 +137,7 @@ class Routes extends DatabaseModel {
public function findByOriginalUrl( $url ) {
$routeData = self::$db->get( $this->tableName, [ 'original_url', '=', $url ] );
if ( !$routeData->count() ) {
- Debug::warn( "Could not find route by original url: $url" );
+ Debug::info( "Routes:findByOriginalUrl: Could not find route by original url: $url" );
return false;
}
return $this->filter( $routeData->first() );
@@ -145,12 +145,12 @@ class Routes extends DatabaseModel {
public function findByforwardedUrl( $url ) {
if ( !Check::url( $url ) ) {
- Debug::warn( "Invalid forwarded_url: $url" );
+ Debug::warn( "Routes:findByforwardedUrl: Invalid forwarded_url: $url" );
return false;
}
$routeData = self::$db->get( $this->tableName, [ 'forwarded_url', '=', $url ] );
if ( !$routeData->count() ) {
- Debug::warn( "Could not find route by forwarded url: $url" );
+ Debug::info( "Routes:findByforwardedUrl: Could not find route by forwarded url: $url" );
return false;
}
return $this->filter( $routeData->first() );
diff --git a/app/models/sessions.php b/app/models/sessions.php
index 6a99070..9edde85 100644
--- a/app/models/sessions.php
+++ b/app/models/sessions.php
@@ -20,6 +20,7 @@ use TheTempusProject\Canary\Bin\Canary as Debug;
use TheTempusProject\Bedrock\Functions\Session;
use TheTempusProject\Bedrock\Functions\Cookie;
use TheTempusProject\Classes\DatabaseModel;
+use TheTempusProject\Classes\Config;
use TheTempusProject\TheTempusProject as App;
class Sessions extends DatabaseModel {
@@ -56,9 +57,11 @@ class Sessions extends DatabaseModel {
$user = new User;
// @todo lets put this on some sort of realistic checking regime other than check everything every time
if ( $sessionID == false ) {
+ Debug::log( 'sessionID false' );
return false;
}
if ( !Check::id( $sessionID ) ) {
+ Debug::log( 'sessionID not id' );
return false;
}
$data = self::$db->get( $this->tableName, [ 'ID', '=', $sessionID ] );
@@ -115,12 +118,12 @@ class Sessions extends DatabaseModel {
public function checkCookie( $cookieToken, $create = false ) {
$user = new User;
if ( $cookieToken === false ) {
+ Debug::info( 'cookieToken false' );
return false;
}
$data = self::$db->get( $this->tableName, [ 'token', '=', $cookieToken ] );
if ( !$data->count() ) {
Debug::info( 'sessions->checkCookie - Session token not found.' );
-
return false;
}
$session = $data->first();
@@ -145,22 +148,6 @@ class Sessions extends DatabaseModel {
return true;
}
- public function checkToken( $apiToken, $create = false ) {
- $user = new User;
- if ( $apiToken === false ) {
- return false;
- }
- $result = $user->findByToken( $apiToken );
- if ( $result === false ) {
- Debug::info( 'sessions->checkToken - could not find user by token.' );
- return false;
- }
- if ( $create ) {
- return $this->newSession( null, false, false, $result->ID );
- }
- return true;
- }
-
/**
* Creates a new session from the data provided. The
* expiration time is optional and will be set to the
@@ -171,9 +158,10 @@ class Sessions extends DatabaseModel {
* @return {bool}
*/
public function newSession( $expire = null, $override = false, $remember = false, $userID = null ) {
- if ( ! isset( $expire ) ) {
+ if ( empty( $expire ) ) {
// default Session Expiration is 24 hours
- $expire = ( time() + ( 3600 * 24 ) );
+ $expireLimit = Config::getValue( 'main/loginTimer' );
+ $expire = ( time() + $expireLimit );
Debug::log( 'Using default expiration time' );
}
$lastPage = App::getUrl();
diff --git a/app/models/token.php b/app/models/token.php
new file mode 100644
index 0000000..fe9198a
--- /dev/null
+++ b/app/models/token.php
@@ -0,0 +1,203 @@
+
+ * @link https://TheTempusProject.com
+ * @license https://opensource.org/licenses/MIT [MIT LICENSE]
+ */
+namespace TheTempusProject\Models;
+
+use TheTempusProject\Bedrock\Functions\Check;
+use TheTempusProject\Canary\Bin\Canary as Debug;
+use TheTempusProject\Classes\DatabaseModel;
+use TheTempusProject\Bedrock\Classes\Config;
+use TheTempusProject\TheTempusProject as App;
+
+class Token extends DatabaseModel {
+ public $tableName = 'tokens';
+ public $modelVersion = '1.0';
+ public $configName = 'api';
+ public $databaseMatrix = [
+ [ 'name', 'varchar', '128' ],
+ [ 'token_type', 'varchar', '8' ],
+ [ 'notes', 'text', '' ],
+ [ 'token', 'varchar', '64' ],
+ [ 'secret', 'varchar', '256' ],
+ [ 'createdAt', 'int', '10' ],
+ [ 'createdBy', 'int', '10' ],
+ [ 'expiresAt', 'int', '10' ],
+ ];
+ public $permissionMatrix = [
+ 'addAppToken' => [
+ 'pretty' => 'Add Application Tokens',
+ 'default' => false,
+ ],
+ 'addAppToken' => [
+ 'pretty' => 'Add Personal Tokens',
+ 'default' => false,
+ ],
+ ];
+ public $configMatrix = [
+ 'apiAccessApp' => [
+ 'type' => 'radio',
+ 'pretty' => 'Enable Api Access for Personal Tokens.',
+ 'default' => true,
+ ],
+ 'apiAccessPersonal' => [
+ 'type' => 'radio',
+ 'pretty' => 'Enable Api Access for Personal Tokens.',
+ 'default' => true,
+ ],
+ 'AppAccessTokenExpiration' => [
+ 'type' => 'text',
+ 'pretty' => 'How long before app tokens expire (in seconds)',
+ 'default' => 2592000,
+ ],
+ 'UserAccessTokenExpiration' => [
+ 'type' => 'text',
+ 'pretty' => 'How long before user tokens expire (in seconds)',
+ 'default' => 604800,
+ ],
+ ];
+
+ public function create( $name, $note, $token_type = 'app' ) {
+ if ( 'app' == $token_type ) {
+ $expiration = Config::getValue( 'api/AppAccessTokenExpiration' );
+ if ( empty( $expiration ) ) {
+ $expiration = $this->configMatrix['AppAccessTokenExpiration']['default'];
+ }
+ } else {
+ $expiration = Config::getValue( 'api/UserAccessTokenExpiration' );
+ if ( empty( $expiration ) ) {
+ $expiration = $this->configMatrix['UserAccessTokenExpiration']['default'];
+ }
+ }
+ $expireTime = time() + $expiration;
+
+ $fields = [
+ 'name' => $name,
+ 'notes' => $note,
+ 'token_type' => $token_type,
+ 'createdBy' => App::$activeUser->ID,
+ 'createdAt' => time(),
+ 'expiresAt' => $expireTime,
+ 'token' => generateToken(),
+ 'secret' => generateRandomString(256),
+ ];
+ if ( self::$db->insert( $this->tableName, $fields ) ) {
+ return true;
+ }
+ return false;
+ }
+
+ public function findOrCreateUserToken( $user_id, $refresh = false ) {
+ $test = $this->findUserToken( $user_id );
+ if ( ! empty( $test ) ) {
+ if ( ! empty( $refresh ) ) {
+ $token = $this->refresh( $test->ID, 'user' );
+ } else {
+ $token = $test->token;
+ }
+ return $token;
+ }
+
+ $expiration = Config::getValue( 'api/UserAccessTokenExpiration' );
+ if ( empty( $expiration ) ) {
+ $expiration = $this->configMatrix['UserAccessTokenExpiration']['default'];
+ }
+ $expireTime = time() + $expiration;
+ $token = generateToken();
+ $fields = [
+ 'name' => 'Browser Token',
+ 'notes' => 'findOrCreateUserToken',
+ 'token_type' => 'user',
+ 'createdBy' => $user_id,
+ 'createdAt' => time(),
+ 'expiresAt' => $expireTime,
+ 'token' => $token,
+ 'secret' => generateRandomString(256),
+ ];
+ if ( self::$db->insert( $this->tableName, $fields ) ) {
+ return $token;
+ }
+ return false;
+ }
+
+ public function update( $id, $name, $note, $token_type = 'app' ) {
+ $fields = [
+ 'name' => $name,
+ 'notes' => $note,
+ 'token_type' => $token_type,
+ ];
+ if ( self::$db->update( $this->tableName, $id, $fields ) ) {
+ return true;
+ }
+ return false;
+ }
+
+ public function refresh( $id, $token_type = 'app' ) {
+ if ( 'app' == $token_type ) {
+ $expiration = Config::getValue( 'api/AppAccessTokenExpiration' );
+ if ( empty( $expiration ) ) {
+ $expiration = $this->configMatrix['AppAccessTokenExpiration']['default'];
+ }
+ } else {
+ $expiration = Config::getValue( 'api/UserAccessTokenExpiration' );
+ if ( empty( $expiration ) ) {
+ $expiration = $this->configMatrix['UserAccessTokenExpiration']['default'];
+ }
+ }
+ $expireTime = time() + $expiration;
+ $token = generateToken();
+
+ $fields = [
+ 'expiresAt' => $expireTime,
+ 'token' => $token,
+ ];
+ if ( self::$db->update( $this->tableName, $id, $fields ) ) {
+ return $token;
+ }
+ return false;
+ }
+
+ public function findByforwardedUrl( $url ) {
+ if ( !Check::url( $url ) ) {
+ Debug::warn( "Invalid forwarded_url: $url" );
+ return false;
+ }
+ $routeData = self::$db->get( $this->tableName, [ 'forwarded_url', '=', $url ] );
+ if ( !$routeData->count() ) {
+ Debug::warn( "Could not find route by forwarded url: $url" );
+ return false;
+ }
+ return $this->filter( $routeData->first() );
+ }
+
+ public function findByToken( $token ) {
+ $data = self::$db->get( $this->tableName, [ 'token', '=', $token ] );
+ if ( ! $data->count() ) {
+ return false;
+ }
+ return $data->first();
+ }
+
+ public function findBySecret( $secret ) {
+ $data = self::$db->get( $this->tableName, [ 'secret', '=', $secret ] );
+ if ( ! $data->count() ) {
+ return false;
+ }
+ return $data->first();
+ }
+
+ public function findUserToken( $user_id ) {
+ $data = self::$db->get( $this->tableName, [ 'createdBy', '=', $user_id, 'AND', 'token_type', '=', 'user' ] );
+ if ( ! $data->count() ) {
+ return false;
+ }
+ return $data->first();
+ }
+}
diff --git a/app/models/user.php b/app/models/user.php
index d31fb99..62012fa 100644
--- a/app/models/user.php
+++ b/app/models/user.php
@@ -43,7 +43,6 @@ class User extends DatabaseModel {
[ 'name', 'varchar', '20' ],
[ 'confirmationCode', 'varchar', '80' ],
[ 'prefs', 'text', '' ],
- [ 'auth_token', 'text', '' ],
];
public $permissionMatrix = [
'uploadImages' => [
@@ -122,6 +121,11 @@ class User extends DatabaseModel {
'50',
],
],
+ 'darkMode' => [
+ 'pretty' => 'Enable Dark-Mode viewing',
+ 'type' => 'checkbox',
+ 'default' => 'false',
+ ],
];
protected static $avatars;
protected static $preferences;
@@ -447,7 +451,7 @@ class User extends DatabaseModel {
*/
public function recent( $limit = null ) {
if ( empty( $limit ) ) {
- $data = self::$db->getpaginated( $this->tableName, '*' );
+ $data = self::$db->get( $this->tableName, '*' );
} else {
$data = self::$db->get( $this->tableName, [ 'ID', '>', '0' ], 'ID', 'DESC', [ 0, $limit ] );
}
@@ -682,6 +686,10 @@ class User extends DatabaseModel {
Debug::error( "User: $id not updated." );
return false;
}
+ if ( $id === App::$activeUser->ID ) {
+ $userData = $this->get( $id );
+ App::$activeUser = $userData;
+ }
return true;
}
@@ -694,33 +702,45 @@ class User extends DatabaseModel {
return $this->data;
}
- public function findByToken( $token ) {
- $data = self::$db->get( $this->tableName, [ 'auth_token', '=', $token ] );
- if ( ! $data->count() ) {
+ public function authorize( $username, $password ) {
+ if ( !isset( self::$log ) ) {
+ self::$log = new Log;
+ }
+ if ( !$this->get( $username ) ) {
+ self::$log->login( 0, "API: User not found: $username" );
return false;
}
- return $data->first();
- }
-
- public function addAccessToken( $id, $length = 64 ) {
- if ( ! Check::id( $id ) ) {
+ // login attempts protection.
+ $timeLimit = ( time() - 3600 );
+ $limit = Config::getValue( 'main/loginLimit' );
+ $user = $this->data();
+ if ( $limit > 0 ) {
+ $limitCheck = self::$db->get(
+ 'logs',
+ [
+ 'source', '=', 'login',
+ 'AND',
+ 'userID', '=', $user->ID,
+ 'AND',
+ 'time', '>=', $timeLimit,
+ 'AND',
+ 'action', '!=', 'pass',
+ ]
+ );
+ if ( $limitCheck->count() >= $limit ) {
+ self::$log->login( $user->ID, 'API: Too many failed attempts.' );
+ return false;
+ }
+ }
+ if ( !Check::password( $password ) ) {
+ self::$log->login( $user->ID, 'API: Invalid Password.' );
return false;
}
- $fields = [ 'auth_token' => $this->generateRandomString( $length ) ];
- if ( !self::$db->update( $this->tableName, $id, $fields ) ) {
- Debug::error( "User: $id not updated." );
+ if ( !Hash::check( $password, $user->password ) ) {
+ self::$log->login( $user->ID, 'API: Wrong Password.' );
return false;
}
- return true;
- }
-
- private function generateRandomString( $length = 10 ) {
- $characters = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
- $charactersLength = strlen( $characters );
- $randomString = '';
- for ($i = 0; $i < $length; $i++) {
- $randomString .= $characters[random_int(0, $charactersLength - 1)];
- }
- return $randomString;
+ self::$log->login( $this->data()->ID, 'API: pass' );
+ return $user;
}
}
diff --git a/app/plugins/blog/controllers/admin/blog.php b/app/plugins/blog/controllers/admin/blog.php
index d786858..adfc451 100644
--- a/app/plugins/blog/controllers/admin/blog.php
+++ b/app/plugins/blog/controllers/admin/blog.php
@@ -20,15 +20,14 @@ use TheTempusProject\Houdini\Classes\Navigation;
use TheTempusProject\Houdini\Classes\Components;
use TheTempusProject\Classes\AdminController;
use TheTempusProject\Classes\Forms;
-use TheTempusProject\Plugins\Blog as BlogPlugin;
+use TheTempusProject\Models\Posts;
class Blog extends AdminController {
public static $posts;
public function __construct() {
parent::__construct();
- $blog = new BlogPlugin;
- self::$posts = $blog->posts;
+ self::$posts = new Posts;
self::$title = 'Admin - Blog';
$view = Navigation::activePageSelect( 'nav.admin', '/admin/blog' );
Components::set( 'ADMINNAV', $view );
diff --git a/app/plugins/blog/controllers/blog.php b/app/plugins/blog/controllers/blog.php
index 3249365..b14100b 100644
--- a/app/plugins/blog/controllers/blog.php
+++ b/app/plugins/blog/controllers/blog.php
@@ -27,17 +27,16 @@ use TheTempusProject\Plugins\Blog as BlogPlugin;
use TheTempusProject\TheTempusProject as App;
use TheTempusProject\Plugins\Comments;
use TheTempusProject\Models\Comments as CommentsModel;
+use TheTempusProject\Models\Posts as PostsModel;
class Blog extends Controller {
protected static $blog;
- protected static $comments;
protected static $posts;
public function __construct() {
parent::__construct();
Template::setTemplate( 'blog' );
- $blog = new BlogPlugin;
- self::$posts = $blog->posts;
+ self::$posts = new PostsModel;
}
public function index() {
@@ -57,39 +56,41 @@ class Blog extends Controller {
public function comments( $sub = null, $data = null ) {
Debug::log( 'Controller initiated: ' . __METHOD__ . '.' );
- if ( empty( self::$comments ) ) {
- self::$comments = new CommentsModel;
- }
- $plugin = new Comments;
+
if ( empty( $sub ) || empty( $data ) ) {
- Session::flash( 'error', 'Whoops, try again.' );
- Redirect::to( 'blog' );
+ Issues::add( 'error', 'There was an issue with your request. Please check the url and try again.' );
+ return $this->index();
}
+
+ $plugin = new Comments;
+ if ( ! $plugin->checkEnabled() ) {
+ Issues::add( 'error', 'Comments are disabled.' );
+ return $this->index();
+ }
+ $comments = new CommentsModel;
+
switch ( $sub ) {
case 'post':
$content = self::$posts->findById( (int) $data );
if ( empty( $content ) ) {
- Session::flash( 'error', 'Unknown Post.' );
- Redirect::to( 'blog' );
+ Issues::add( 'error', 'Unknown Content.' );
+ return $this->index();
}
return $plugin->formPost( self::$posts->tableName, $content, 'blog/post/' );
- return self::$comments->formPost( 'blog', $content, 'blog/post/' );
case 'edit':
- $content = self::$comments->findById( $data );
+ $content = $comments->findById( $data );
if ( empty( $content ) ) {
- Session::flash( 'error', 'Unknown Comment.' );
- Redirect::to( 'blog' );
+ Issues::add( 'error', 'Unknown Comment.' );
+ return $this->index();
}
return $plugin->formEdit( self::$posts->tableName, $content, 'blog/post/' );
- return self::$comments->formEdit( 'blog', $content, 'blog/post/' );
case 'delete':
- $content = self::$comments->findById( $data );
+ $content = $comments->findById( $data );
if ( empty( $content ) ) {
- Session::flash( 'error', 'Unknown Comment.' );
- Redirect::to( 'blog' );
+ Issues::add( 'error', 'Unknown Comment.' );
+ return $this->index();
}
return $plugin->formDelete( self::$posts->tableName, $content, 'blog/post/' );
- return self::$comments->formDelete( 'blog', $content, 'blog/post/' );
}
}
@@ -101,26 +102,34 @@ class Blog extends Controller {
if ( empty( $post ) ) {
return $this->index();
}
- if ( empty( self::$comments ) ) {
- self::$comments = new CommentsModel;
- }
Debug::log( 'Controller initiated: ' . __METHOD__ . '.' );
self::$title = 'Blog Post';
- // I removed this once because i didn't realize.
- // this triggers the comment post controller method when the comment form is submitted on the post viewing page
+
if ( Input::exists( 'contentId' ) ) {
$this->comments( 'post', Input::post( 'contentId' ) );
}
+
Components::set( 'CONTENT_ID', $id );
- Components::set( 'COMMENT_TYPE', 'blog' );
- if ( App::$isLoggedIn ) {
- Components::set( 'NEWCOMMENT', Views::simpleView( 'comments.create' ) );
- } else {
+ Components::set( 'COMMENT_TYPE', self::$posts->tableName );
+
+ $plugin = new Comments;
+ if ( ! $plugin->checkEnabled() ) {
Components::set( 'NEWCOMMENT', '' );
+ Components::set( 'count', '0' );
+ Components::set( 'COMMENTS', '' );
+ } else {
+ $comments = new CommentsModel;
+ if ( App::$isLoggedIn ) {
+ Components::set( 'NEWCOMMENT', Views::simpleView( 'comments.create' ) );
+ } else {
+ Components::set( 'NEWCOMMENT', '' );
+ }
+ 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 ) ) );
}
+
$post = self::$posts->findById( $id );
- Components::set( 'count', self::$comments->count( self::$posts->tableName, $post->ID ) );
- Components::set( 'COMMENTS', Views::simpleView( 'comments.list', self::$comments->display( 10, self::$posts->tableName, $post->ID ) ) );
+
self::$title .= ' - ' . $post->title;
self::$pageDescription = strip_tags( $post->contentSummaryNoLink );
Views::view( 'blog.post', $post );
diff --git a/app/plugins/blog/models/posts.php b/app/plugins/blog/models/posts.php
index 5f8873d..7a4e158 100644
--- a/app/plugins/blog/models/posts.php
+++ b/app/plugins/blog/models/posts.php
@@ -131,34 +131,34 @@ class Posts extends DatabaseModel {
}
$draft = '';
$authorName = self::$user->getUsername( $instance->author );
+
$cleanPost = Sanitize::contentShort( $instance->content );
- $postSpace = explode( ' ', $cleanPost );
+ $wordsArray = explode( ' ', $cleanPost );
$postLine = explode( "\n", $cleanPost );
- // summary by words: 100
- $spaceSummary = implode( ' ', array_splice( $postSpace, 0, 100 ) );
- // summary by lines: 5
+ $spaceSummary = implode( ' ', array_splice( $wordsArray, 0, 100 ) );
$lineSummary = implode( "\n", array_splice( $postLine, 0, 5 ) );
if ( strlen( $spaceSummary ) < strlen( $lineSummary ) ) {
+ $contentSummaryNoLink = $spaceSummary;
$contentSummary = $spaceSummary;
- if ( count( $postSpace, 1 ) <= 100 ) {
+ if ( count( $wordsArray, 1 ) >= 100 ) {
$contentSummaryNoLink = $contentSummary;
- $contentSummary .= '... Read More';
+ $contentSummary .= '... Read More';
}
} else {
- // @todo: need to refine this after testing
$contentSummaryNoLink = $lineSummary;
- $contentSummary = $lineSummary . '... Read More';
+ $contentSummary = $lineSummary . '... Read More';
+ }
+ $instance->contentSummaryNoLink = $contentSummaryNoLink;
+ $instance->contentSummary = $contentSummary;
+
+ if ( isset( $params['stripHtml'] ) && $params['stripHtml'] === true ) {
+ $instance->contentSummary = strip_tags( $instance->content );
}
if ( $instance->draft != '0' ) {
$draft = ' Draft';
}
$instance->isDraft = $draft;
$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 ) {
$instance->commentCount = self::$comments->count( 'blog', $instance->ID );
}
@@ -221,9 +221,9 @@ class Posts extends DatabaseModel {
$whereClause = '*';
}
if ( empty( $limit ) ) {
- $postData = self::$db->getPaginated( $this->tableName, $whereClause );
+ $postData = self::$db->get( $this->tableName, $whereClause );
} else {
- $postData = self::$db->getPaginated( $this->tableName, $whereClause, 'ID', 'DESC', [0, $limit] );
+ $postData = self::$db->get( $this->tableName, $whereClause, 'ID', 'DESC', [0, $limit] );
}
if ( !$postData->count() ) {
Debug::info( 'No Blog posts found.' );
@@ -239,7 +239,7 @@ class Posts extends DatabaseModel {
} else {
$whereClause = ['draft', '=', '0'];
}
- $postData = self::$db->getPaginated( $this->tableName, $whereClause );
+ $postData = self::$db->get( $this->tableName, $whereClause );
if ( !$postData->count() ) {
Debug::info( 'No Blog posts found.' );
@@ -263,7 +263,7 @@ class Posts extends DatabaseModel {
$firstDayUnix = date( 'U', strtotime( "first day of $year" ) );
$lastDayUnix = date( 'U', strtotime( "last day of $year" ) );
$whereClause = array_merge( $whereClause, ['created', '<=', $lastDayUnix, 'AND', 'created', '>=', $firstDayUnix] );
- $postData = self::$db->getPaginated( $this->tableName, $whereClause );
+ $postData = self::$db->get( $this->tableName, $whereClause );
if ( !$postData->count() ) {
Debug::info( 'No Blog posts found.' );
@@ -282,7 +282,7 @@ class Posts extends DatabaseModel {
$whereClause = ['draft', '=', '0', 'AND'];
}
$whereClause = array_merge( $whereClause, ['author' => $ID] );
- $postData = self::$db->getPaginated( $this->tableName, $whereClause );
+ $postData = self::$db->get( $this->tableName, $whereClause );
if ( !$postData->count() ) {
Debug::info( 'No Blog posts found.' );
@@ -311,7 +311,7 @@ class Posts extends DatabaseModel {
$month = date( 'F', $firstDayUnix );
$lastDayUnix = date( 'U', strtotime( "last day of $month $year" ) );
$whereClause = array_merge( $whereClause, ['created', '<=', $lastDayUnix, 'AND', 'created', '>=', $firstDayUnix] );
- $postData = self::$db->getPaginated( $this->tableName, $whereClause );
+ $postData = self::$db->get( $this->tableName, $whereClause );
if ( !$postData->count() ) {
Debug::info( 'No Blog posts found.' );
diff --git a/app/plugins/blog/plugin.php b/app/plugins/blog/plugin.php
index d784824..883d6bd 100644
--- a/app/plugins/blog/plugin.php
+++ b/app/plugins/blog/plugin.php
@@ -12,12 +12,7 @@
*/
namespace TheTempusProject\Plugins;
-use ReflectionClass;
-use TheTempusProject\Classes\Installer;
-use TheTempusProject\Houdini\Classes\Navigation;
use TheTempusProject\Classes\Plugin;
-use TheTempusProject\Models\Posts;
-use TheTempusProject\TheTempusProject as App;
class Blog extends Plugin {
public $pluginName = 'TP Blog';
@@ -28,11 +23,11 @@ class Blog extends Plugin {
public $pluginDescription = 'A simple plugin to add a blog to your installation.';
public $admin_links = [
[
- 'text' => ' Blog',
+ 'text' => ' Blog',
'url' => '{ROOT_URL}admin/blog',
],
];
- public $footer_links = [
+ public $info_footer_links = [
[
'text' => 'Blog',
'url' => '{ROOT_URL}blog/index',
@@ -50,10 +45,4 @@ class Blog extends Plugin {
],
],
];
- public $posts;
-
- public function __construct( $load = false ) {
- $this->posts = new Posts;
- parent::__construct( $load );
- }
}
diff --git a/app/plugins/blog/templates/blog.inc.php b/app/plugins/blog/templates/blog.inc.php
index b4548ac..857006f 100644
--- a/app/plugins/blog/templates/blog.inc.php
+++ b/app/plugins/blog/templates/blog.inc.php
@@ -12,7 +12,7 @@
*/
namespace TheTempusProject\Templates;
-use TheTempusProject\Plugins\Blog;
+use TheTempusProject\Models\Posts;
use TheTempusProject\Houdini\Classes\Components;
use TheTempusProject\Houdini\Classes\Navigation;
use TheTempusProject\Houdini\Classes\Views;
@@ -25,10 +25,11 @@ class BlogLoader extends DefaultLoader {
* needed by this template.
*/
public function __construct() {
- $blog = new Blog;
- $posts = $blog->posts;
+ $posts = new Posts;
Components::set('SIDEBAR', Views::simpleView('blog.sidebar', $posts->recent(5)));
Components::set('SIDEBAR2', Views::simpleView('blog.sidebar2', $posts->archive()));
+ Components::set('SIDEBARABOUT', Views::simpleView('blog.about'));
+ Components::set('BLOGFEATURES', '');
Navigation::setCrumbComponent( 'BLOG_BREADCRUMBS', Input::get( 'url' ) );
Components::set( 'BLOG_TEMPLATE_URL', Template::parse( '{ROOT_URL}app/plugins/comments/' ) );
$this->addCss( '' );
diff --git a/app/plugins/blog/templates/blog.tpl b/app/plugins/blog/templates/blog.tpl
index 0be2524..880a176 100644
--- a/app/plugins/blog/templates/blog.tpl
+++ b/app/plugins/blog/templates/blog.tpl
@@ -10,10 +10,10 @@
* @license https://opensource.org/licenses/MIT [MIT LICENSE]
-->
-
+
-
+
{TITLE}
@@ -28,84 +28,89 @@
{ROBOT}
-
-
-
+
-
+
{TEMPLATE_CSS_INCLUDES}
-