diff --git a/.gitignore b/.gitignore index a32fd2a..0d7e731 100644 --- a/.gitignore +++ b/.gitignore @@ -62,3 +62,5 @@ mail.log vendor/canary/logs/* .env components/* +mailhog.log +uploads/* diff --git a/app/classes/api_controller.php b/app/classes/api_controller.php index 43fd7c7..b23fbe4 100644 --- a/app/classes/api_controller.php +++ b/app/classes/api_controller.php @@ -16,17 +16,122 @@ use TheTempusProject\Houdini\Classes\Template; use TheTempusProject\TheTempusProject as App; use TheTempusProject\Hermes\Functions\Redirect; use TheTempusProject\Bedrock\Functions\Session; +use TheTempusProject\Bedrock\Classes\Config; +use TheTempusProject\Models\Token; +use TheTempusProject\Canary\Bin\Canary as Debug; +use TheTempusProject\Houdini\Classes\Views; class ApiController extends Controller { - public function __construct() { + protected static $canAccessApplicationApi = false; + protected static $canAccessUserApi = false; + protected static $canAccessAuthenticationApi = false; + protected static $authToken; + + public function __construct( $secure = true ) { + header('Content-Type: application/json; charset=utf-8'); parent::__construct(); - if ( ! App::verifyApiRequest() ) { - Session::flash( 'error', 'You do not have permission to view this page.' ); - return Redirect::home(); - } + Template::setTemplate( 'api' ); Template::noFollow(); Template::noIndex(); - Template::addHeader( 'Content-Type: application/json; charset=utf-8' ); - Template::setTemplate( 'api' ); + $res = $this->verifyApiRequest(); + if ( $secure && ! $this->canUseApi() ) { + exit( $res ); + } + } + + protected function canUseApi() { + return ( $this->canUseUserApi() || $this->canUseAppApi() || $this->canUseAuthApi() ); + } + + protected function canUseUserApi() { + $apiEnabled = Config::getValue( 'api/apiAccessApp' ); + if ( empty( $apiEnabled ) ) { + return false; + } + return self::$canAccessUserApi; + } + + protected function canUseAppApi() { + $apiEnabled = Config::getValue( 'api/apiAccessPersonal' ); + if ( empty( $apiEnabled ) ) { + return false; + } + return self::$canAccessApplicationApi; + } + + protected function canUseAuthApi() { + return self::$canAccessAuthenticationApi; + } + + public function verifyApiRequest() { + $tokens = new Token; + $secret = null; + + $bearer_token = $this->getBearerToken(); + if ( ! empty( $bearer_token ) ) { + $token = $tokens->findByToken( $bearer_token ); + } else { + $secret = $this->getSecretToken(); + if ( empty( $secret ) ) { + return Views::simpleView( 'api.response', ['response' => json_encode( [ 'error' => 'invalid secret' ], true )]); + } + $token = $tokens->findBySecret( $secret ); + } + if ( empty( $token ) ) { + return Views::simpleView( 'api.response', ['response' => json_encode( [ 'error' => 'invalid token' ], true )]); + } + self::$authToken = $token; + if ( $token->expiresAt <= time() && empty( $secret ) ) { + return Views::simpleView( 'api.response', ['response' => json_encode( [ 'error' => 'token expired' ], true )]); + } + if ( $token->expiresAt <= time() ) { + self::$canAccessAuthenticationApi = true; + return; + } + if ( $token->token_type == 'app' ) { + self::$canAccessApplicationApi = true; + return; + } + if ( $token->token_type == 'user' ) { + self::$canAccessUserApi = true; + return; + } + return $result; + } + + public function getSecretToken() { + $headers = $this->getAuthorizationHeader(); + if ( ! empty( $headers ) ) { + if ( preg_match( '/Secret\s(\S+)/', $headers, $matches ) ) { + return $matches[1]; + } + } + return null; + } + + public function getBearerToken() { + $headers = $this->getAuthorizationHeader(); + if ( ! empty( $headers ) ) { + if ( preg_match( '/Bearer\s(\S+)/', $headers, $matches ) ) { + return $matches[1]; + } + } + return null; + } + + public function getAuthorizationHeader(){ + $headers = null; + if ( isset( $_SERVER['Authorization'] ) ) { + $headers = trim( $_SERVER["Authorization"] ); + } elseif ( isset( $_SERVER['HTTP_AUTHORIZATION'] ) ) { + $headers = trim( $_SERVER["HTTP_AUTHORIZATION"] ); + } elseif ( function_exists( 'apache_request_headers' ) ) { + $requestHeaders = apache_request_headers(); + $requestHeaders = array_combine(array_map('ucwords', array_keys($requestHeaders)), array_values($requestHeaders)); + if ( isset( $requestHeaders['Authorization'] ) ) { + $headers = trim( $requestHeaders['Authorization'] ); + } + } + return $headers; } } diff --git a/app/classes/config.php b/app/classes/config.php index 4eb8119..b8f99d8 100644 --- a/app/classes/config.php +++ b/app/classes/config.php @@ -12,6 +12,7 @@ namespace TheTempusProject\Classes; 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\Input; @@ -19,7 +20,6 @@ use TheTempusProject\Bedrock\Classes\Config as BedrockConfig; class Config extends BedrockConfig { public static function getFieldEditHtml( $name, $includeProtected = false ) { - // @todo: includeProtected is unused here $node = self::get( $name ); if ( empty( $node ) ) { return; @@ -28,13 +28,57 @@ class Config extends BedrockConfig { return; } $fieldname = str_ireplace( '/', '-', $name ); - $html = Forms::getFormFieldHtml( - $fieldname, - $node['pretty'], - $node['type'], - $node['value'], - ); - return $html; + + $html = ''; + $fieldHtml = ''; + switch ( $node['type'] ) { + case 'radio': + case 'bool': + case 'boolean': + $fieldHtml = Forms::getSwitchHtml( $fieldname, [ 'true', 'false' ], $node['value'] ); + break; + case 'select': + $fieldHtml = Forms::getSelectHtml( $fieldname, $options, $node['value'] ); + break; + case 'block': + $fieldHtml = Forms::getTextBlockHtml( $fieldname, $node['value'] ); + break; + case 'text': + case 'url': + $fieldHtml = Forms::getTextHtml( $fieldname, $node['value'] ); + break; + case 'checkbox': + $fieldHtml = Forms::getCheckboxHtml( $fieldname, $node['value'] ); + break; + case 'timezone': + $fieldHtml = Forms::getTimezoneHtml( $node['value'] ); + break; + case 'file': + $fieldHtml = Forms::getFileHtml( $fieldname ); + break; + case 'customSelect': + if ( empty( $options ) ) { + $options = '{' . $fieldname . '-options}'; + } + $fieldHtml = Forms::getSelectHtml( $fieldname, $options, $node['value'] ); + break; + } + + $html .= '
'; + $html .= ''; + $html .= '
'; + $html .= $fieldHtml; + $html .= '
'; + if ( 'file' === $node['type'] ) { + $html .= '
'; + $html .= '

Current Image

'; + $html .= '
'; + $html .= 'User Avatar'; + $html .= '
'; + } + $html .= '
'; + + 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 = '

'; + $categoryHeader = '

' . ucfirst( $category ) . ':


'; foreach ( self::$config[$category] as $field => $node ) { $html .= self::getFieldEditHtml( $category . '/' . $field ); } if ( !empty( $html ) ) { - $html = $categoryHeader . $html; + $html = $categoryHeader . $html . '
'; } 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 .= '
'; + $html .= ''; + $html .= '
'; + $html .= $fieldHtml; + $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 .= 'User Avatar'; + $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 .= ' User Avatar'; + $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} - -
-
+
+ {topNavRight} +
+
+
+ + + + + + + +
+
{ISSUES} -
-
+
+
{ERROR} {NOTICE} {SUCCESS} + {INFO}
{/ISSUES} -
+ + +
+ {BLOGFEATURES} +
+ +
- -
-
- {BLOG_BREADCRUMBS} +

+ {SITENAME} Blog +

+
+ +
{CONTENT}
- -
- - -
-
-
{FOOT} - {COPY} -
+
- - + + + {TEMPLATE_JS_INCLUDES} diff --git a/app/plugins/blog/views/about.html b/app/plugins/blog/views/about.html new file mode 100644 index 0000000..ccd8d95 --- /dev/null +++ b/app/plugins/blog/views/about.html @@ -0,0 +1,6 @@ +
+

About

+

+ The blog is mostly here to serve ass a simple way to link to long-form content on the site. There won't be any breaking news or tell-all stories here. Just good ole fashioned boring crap no one wants to read. +

+
\ No newline at end of file diff --git a/app/plugins/blog/views/admin/create.html b/app/plugins/blog/views/admin/create.html index 0e31c5d..7ae25f5 100644 --- a/app/plugins/blog/views/admin/create.html +++ b/app/plugins/blog/views/admin/create.html @@ -1,31 +1,47 @@ -
- New Blog Post -
- -
- +
+ Add Blog Post +
+ {ADMIN_BREADCRUMBS} + +
+ +
+ +
+ +
+
+ + +
+
+
+ + + + +
+
+
+ + +
+ +
+ + Max: 2000 characters +
+
+ + + +
+ + +
+ + +
-
-
-
- - - - -
-
-
- -
- -
-
-
-
- - - -
-
- - \ No newline at end of file + +
\ No newline at end of file diff --git a/app/plugins/blog/views/admin/dashboard.html b/app/plugins/blog/views/admin/dashboard.html index 2072462..3b442a3 100644 --- a/app/plugins/blog/views/admin/dashboard.html +++ b/app/plugins/blog/views/admin/dashboard.html @@ -1,5 +1,5 @@ New Posts - +
@@ -14,9 +14,9 @@ - - - + + + {/LOOP} {ALT} diff --git a/app/plugins/blog/views/admin/edit.html b/app/plugins/blog/views/admin/edit.html index 8580de4..986ff11 100644 --- a/app/plugins/blog/views/admin/edit.html +++ b/app/plugins/blog/views/admin/edit.html @@ -1,31 +1,47 @@ - - Edit Blog Post -
- -
- +
+ Edit Blog Post +
+ {ADMIN_BREADCRUMBS} + +
+ +
+ +
+ +
+
+ + +
+
+
+ + + + +
+
+
+ + +
+ +
+ + Max: 2000 characters +
+
+ + + +
+ + +
+ + +
-
-
-
- - - - -
-
-
- -
- -
-
-
-
- - - -
-
- - \ No newline at end of file + +
\ No newline at end of file diff --git a/app/plugins/blog/views/admin/list.html b/app/plugins/blog/views/admin/list.html index 23927e4..62df2fb 100644 --- a/app/plugins/blog/views/admin/list.html +++ b/app/plugins/blog/views/admin/list.html @@ -1,45 +1,48 @@ -Blog Posts -{PAGINATION} -
-
{title} {contentSummary}
- - - - - - - - - - - - - - {LOOP} - - - - - - - - - - - {/LOOP} - {ALT} - - - - {/ALT} - -
TitleAuthorcommentsCreatedUpdated - -
{title}{isDraft}{authorName}{commentCount}{DTC}{created}{/DTC}{DTC}{edited}{/DTC} - -
- No results to show. -
- Create - - \ No newline at end of file +
+ Blog Posts +
+ {ADMIN_BREADCRUMBS} +
+ + + + + + + + + + + + + + + {LOOP} + + + + + + + + + + + {/LOOP} + {ALT} + + + + {/ALT} + +
TitleAuthorcommentsCreatedUpdated + +
{title}{isDraft}{authorName}{commentCount}{DTC}{created}{/DTC}{DTC}{edited}{/DTC} + +
+ No results to show. +
+ Create + +
+
\ No newline at end of file diff --git a/app/plugins/blog/views/admin/view.html b/app/plugins/blog/views/admin/view.html index 005c72c..7f66e37 100644 --- a/app/plugins/blog/views/admin/view.html +++ b/app/plugins/blog/views/admin/view.html @@ -1,11 +1,14 @@ -
-
- -
{content}
- Delete - Edit - View Comments -
-
\ No newline at end of file +
+ Blog Post: {title} +
+ {ADMIN_BREADCRUMBS} + +
{content}
+ {ADMIN} +
+ + + View Comments +
+ {/ADMIN} +
\ No newline at end of file diff --git a/app/plugins/blog/views/largeFeature.html b/app/plugins/blog/views/largeFeature.html new file mode 100644 index 0000000..e533c50 --- /dev/null +++ b/app/plugins/blog/views/largeFeature.html @@ -0,0 +1,7 @@ +
+
+

Title of a longer featured blog post

+

Multiple lines of text that form the lede, informing new readers quickly and efficiently about what’s most interesting in this post’s contents.

+

Continue reading...

+
+
\ No newline at end of file diff --git a/app/plugins/blog/views/list.html b/app/plugins/blog/views/list.html index 19e6dd1..0a42697 100644 --- a/app/plugins/blog/views/list.html +++ b/app/plugins/blog/views/list.html @@ -1,18 +1,15 @@ -{PAGINATION} {LOOP} -
-

{title}

-
+
+

{title}

+
- {contentSummary}
-
+ +
{/LOOP} {ALT} -
+
-
+ {/ALT} diff --git a/app/plugins/blog/views/post.html b/app/plugins/blog/views/post.html index 9a55303..3484817 100644 --- a/app/plugins/blog/views/post.html +++ b/app/plugins/blog/views/post.html @@ -3,12 +3,12 @@

{title}


- + {content} {ADMIN}
- Delete - Edit + +
{/ADMIN}
diff --git a/app/plugins/blog/views/recentWidget.html b/app/plugins/blog/views/recentWidget.html index 2a4caef..e256079 100644 --- a/app/plugins/blog/views/recentWidget.html +++ b/app/plugins/blog/views/recentWidget.html @@ -1,6 +1,6 @@ -
-
-

Recent Posts

+
+
+

Recent Posts

    {LOOP} diff --git a/app/plugins/blog/views/sidebar.html b/app/plugins/blog/views/sidebar.html index 49dc8ff..85eb67e 100644 --- a/app/plugins/blog/views/sidebar.html +++ b/app/plugins/blog/views/sidebar.html @@ -1,18 +1,18 @@ -
    -
    -

    Recent Posts

    +
    +
    +

    Recent Posts

    -
    +
      {LOOP} -
    1. {title}
    2. +
    3. {title}
    4. {/LOOP} {ALT}
    5. No Posts to show
    6. {/ALT}
    - \ No newline at end of file diff --git a/app/plugins/blog/views/sidebar2.html b/app/plugins/blog/views/sidebar2.html index 6ff712d..d64ac19 100644 --- a/app/plugins/blog/views/sidebar2.html +++ b/app/plugins/blog/views/sidebar2.html @@ -1,14 +1,11 @@ -
    -
    -

    Archives

    -
    -
    -
      - {LOOP} -
    1. ({count}) {monthText} {year}
    2. - {/LOOP} - {ALT} - {/ALT} -
    -
    +
    +

    Archives

    +
    \ No newline at end of file diff --git a/app/plugins/blog/views/smallFeature.html b/app/plugins/blog/views/smallFeature.html new file mode 100644 index 0000000..cf2ea73 --- /dev/null +++ b/app/plugins/blog/views/smallFeature.html @@ -0,0 +1,31 @@ + +
    +
    +
    +
    + World +

    Featured post

    +
    Nov 12
    +

    This is a wider card with supporting text below as a natural lead-in to additional content.

    + Continue reading +
    +
    + PlaceholderThumbnail +
    +
    +
    +
    +
    +
    + Design +

    Post title

    +
    Nov 11
    +

    This is a wider card with supporting text below as a natural lead-in to additional content.

    + Continue reading +
    +
    + PlaceholderThumbnail +
    +
    +
    +
    \ No newline at end of file diff --git a/app/plugins/bugreport/controllers/bugreport.php b/app/plugins/bugreport/controllers/bugreport.php index 5da4f78..75c5ad5 100644 --- a/app/plugins/bugreport/controllers/bugreport.php +++ b/app/plugins/bugreport/controllers/bugreport.php @@ -41,7 +41,7 @@ class Bugreport extends Controller { return Views::view( 'bugreport.create' ); } $result = self::$bugreport->create( App::$activeUser->ID, Input::post( 'url' ), Input::post( 'ourl' ), Input::post( 'repeat' ), Input::post( 'entry' ) ); - if ( true === $result ) { + if ( false != $result ) { Session::flash( 'success', 'Your Bug Report has been received. We may contact you for more information at the email address you provided.' ); Redirect::to( 'home/index' ); } else { diff --git a/app/plugins/bugreport/models/bugreport.php b/app/plugins/bugreport/models/bugreport.php index 5ae20ab..df23b78 100644 --- a/app/plugins/bugreport/models/bugreport.php +++ b/app/plugins/bugreport/models/bugreport.php @@ -13,7 +13,6 @@ namespace TheTempusProject\Models; use TheTempusProject\Bedrock\Functions\Check; -use TheTempusProject\Bedrock\Classes\Config; use TheTempusProject\Canary\Bin\Canary as Debug; use TheTempusProject\Canary\Classes\CustomException; use TheTempusProject\Classes\DatabaseModel; diff --git a/app/plugins/bugreport/plugin.php b/app/plugins/bugreport/plugin.php index 3ac70ea..c509440 100644 --- a/app/plugins/bugreport/plugin.php +++ b/app/plugins/bugreport/plugin.php @@ -49,10 +49,11 @@ class Bugreport extends Plugin { 'default' => false, ], ]; - public $footer_links = [ + public $contact_footer_links = [ [ 'text' => 'Bug Report', 'url' => '{ROOT_URL}bugreport', + 'filter' => 'loggedin', ], ]; public $admin_links = [ diff --git a/app/plugins/bugreport/views/admin/list.html b/app/plugins/bugreport/views/admin/list.html index c165570..87cccb9 100644 --- a/app/plugins/bugreport/views/admin/list.html +++ b/app/plugins/bugreport/views/admin/list.html @@ -1,42 +1,45 @@ -Bug Reports -{PAGINATION} -
    - - - - - - - - - - - - - {LOOP} - - - - - - - - - {/LOOP} - {ALT} - - - - {/ALT} - -
    IDTimeDescription - -
    {ID}{DTC}{time}{/DTC}{description} - -
    - No results to show. -
    - -
    -
    -clear all \ No newline at end of file +
    + Bug Reports +
    + {ADMIN_BREADCRUMBS} +
    + + + + + + + + + + + + + {LOOP} + + + + + + + + + {/LOOP} + {ALT} + + + + {/ALT} + +
    IDTimeDescription + +
    {ID}{DTC}{time}{/DTC}{description} + +
    + No results to show. +
    + +
    +
    + clear all +
    \ No newline at end of file diff --git a/app/plugins/bugreport/views/admin/view.html b/app/plugins/bugreport/views/admin/view.html index 529d647..9054ea7 100644 --- a/app/plugins/bugreport/views/admin/view.html +++ b/app/plugins/bugreport/views/admin/view.html @@ -1,64 +1,69 @@ -
    -
    -
    -
    -
    -

    Bug Report

    -
    -
    -
    -
    - - +
    +
    +
    + {ADMIN_BREADCRUMBS} +
    + +
    +

    Bug Report

    +
    + + +
    +
    + +
    + + + + + - - + + - - + + - - + + - - + + + + + + + + + + - - - - - - - - - - - - - + - -
    ID:{ID}
    ID{ID}Time submitted{DTC}{time}{/DTC}
    Time submitted{DTC}{time}{/DTC}URL:{URL}
    Submitted by{submittedBy}Original URL{OURL}
    IP{ip}Multiple occurrences?{repeatText}
    IP:{ip}
    User:{submittedBy}
    URL:{URL}
    Original URL{OURL}
    Multiple occurrences?{repeatText}
    DescriptionDescription:
    {description}
    -
    -
    -
    - +
    + + + -
    -
    -
    +
    +
    +
    +
    \ No newline at end of file diff --git a/app/plugins/bugreport/views/create.html b/app/plugins/bugreport/views/create.html index e357701..8230842 100644 --- a/app/plugins/bugreport/views/create.html +++ b/app/plugins/bugreport/views/create.html @@ -1,36 +1,52 @@ -Bug Report -

    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.

    -

    We read each and every bug report submitted, and by submitting this form you allow us to send you a follow up email.

    -
    - - -

    - What is the URL of the page you actually received the error on? (The URL is the website address. Example: {ROOT_URL}home) -

    - - -

    - What is the URL of the page you were on before you received the error? (The URL is the website address. Example: {ROOT_URL}home/newhome) -

    - -
    - -
    -
    - -
    -
    - -
    - +
    +

    Bug Report

    +
    +

    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.

    +

    We read each and every bug report submitted, and by submitting this form you allow us to send you a follow-up email.

    + + +
    + + + + What is the URL of the page you actually received the error on? (The URL is the website address. Example: {ROOT_URL}home) +
    -
    - - - \ No newline at end of file + + +
    + + + + What is the URL of the page you were on before you received the error? (The URL is the website address. Example: {ROOT_URL}home/newhome) + +
    + + +
    + +
    + + +
    +
    + + +
    +
    + + +
    + + +
    + + + + + +
    + +
    + +
    \ No newline at end of file diff --git a/app/plugins/comments/controllers/admin/comments.php b/app/plugins/comments/controllers/admin/comments.php index 75bed79..4c9ead6 100644 --- a/app/plugins/comments/controllers/admin/comments.php +++ b/app/plugins/comments/controllers/admin/comments.php @@ -49,9 +49,13 @@ class Comments extends AdminController { $this->index(); } - public function viewComment( $data = null ) { - $commentData = self::$comments->findById( $data ); - if ( $commentData !== false ) { + public function viewComments( $contentID = null ) { + if ( empty( $contentID ) ) { + Issues::add( 'error', 'Content ID not found.' ); + return $this->index(); + } + $contentData = self::$comments->findById( $data ); + if ( empty( $contentID ) ) { return Views::view( 'comments.list', $commentData ); } Issues::add( 'error', 'Comment not found.' ); diff --git a/app/plugins/comments/models/comments.php b/app/plugins/comments/models/comments.php index 8287d10..662fc47 100644 --- a/app/plugins/comments/models/comments.php +++ b/app/plugins/comments/models/comments.php @@ -170,7 +170,7 @@ class Comments extends DatabaseModel { $where = ['contentType', '=', $contentType]; } if ( empty( $limit ) ) { - $commentData = self::$db->getPaginated( $this->tableName, $where, 'created', 'DESC' ); + $commentData = self::$db->get( $this->tableName, $where, 'created', 'DESC' ); } else { $commentData = self::$db->get( $this->tableName, $where, 'created', 'DESC', [0, $limit] ); } diff --git a/app/plugins/comments/plugin.php b/app/plugins/comments/plugin.php index a7cb138..146da01 100644 --- a/app/plugins/comments/plugin.php +++ b/app/plugins/comments/plugin.php @@ -25,9 +25,10 @@ use TheTempusProject\Houdini\Classes\Views; use TheTempusProject\Classes\Forms; use TheTempusProject\Hermes\Functions\Redirect; use TheTempusProject\Bedrock\Functions\Session; -use TheTempusProject\Models\Comments as CommentModel; +use TheTempusProject\Models\Comments as CommentsModel; class Comments extends Plugin { + protected static $comments; public $pluginName = 'TP Comments'; public $pluginAuthor = 'JoeyK'; public $pluginWebsite = 'https://TheTempusProject.com'; @@ -61,7 +62,6 @@ class Comments extends Plugin { ] ], ]; - public $comments; public function __construct( $load = false ) { if ( !empty(App::$activePerms) ) { @@ -75,11 +75,16 @@ class Comments extends Plugin { 'replace' => ( App::$isMod ? '$1' : '' ), 'enabled' => true, ]; - $this->getModel(); + self::$comments = new CommentsModel; parent::__construct( $load ); } public function formPost( $type, $content, $redirect ) { + if ( ! $this->checkEnabled() ) { + Debug::info( 'Comments Plugin is disabled in the control panel.' ); + Issues::add( 'error', 'Comments are disabled.' ); + return false; + } if ( !App::$isLoggedIn ) { Session::flash( 'notice', 'You must be logged in to post comments.' ); return Redirect::to( $redirect . $content->ID ); @@ -88,7 +93,7 @@ class Comments extends Plugin { Session::flash( 'error', [ 'There was a problem with your comment form.' => Check::userErrors() ] ); return Redirect::to( $redirect . $content->ID ); } - if ( !$this->comments->create( $type, $content->ID, Input::post( 'comment' ) ) ) { + if ( !self::$comments->create( $type, $content->ID, Input::post( 'comment' ) ) ) { Session::flash( 'error', [ 'There was a problem posting your comment.' => Check::userErrors() ] ); } else { Session::flash( 'success', 'Comment posted' ); @@ -97,6 +102,11 @@ class Comments extends Plugin { } public function formEdit( $type, $content, $redirect ) { + if ( ! $this->checkEnabled() ) { + Debug::info( 'Comments Plugin is disabled in the control panel.' ); + Issues::add( 'error', 'Comments are disabled.' ); + return false; + } if ( !App::$isLoggedIn ) { Session::flash( 'notice', 'You must be logged in to do that.' ); return Redirect::to( $type ); @@ -112,7 +122,7 @@ class Comments extends Plugin { Issues::add( 'error', [ 'There was a problem editing your comment.' => Check::userErrors() ] ); return Views::view( 'comments.admin.edit', $content ); } - if ( !$this->comments->update( $content->ID, Input::post( 'comment' ) ) ) { + if ( !self::$comments->update( $content->ID, Input::post( 'comment' ) ) ) { Issues::add( 'error', [ 'There was a problem editing your comment.' => Check::userErrors() ] ); return Views::view( 'comments.admin.edit', $content ); } @@ -121,6 +131,11 @@ class Comments extends Plugin { } public function formDelete( $type, $content, $redirect ) { + if ( ! $this->checkEnabled() ) { + Debug::info( 'Comments Plugin is disabled in the control panel.' ); + Issues::add( 'error', 'Comments are disabled.' ); + return false; + } if ( !App::$isLoggedIn ) { Session::flash( 'notice', 'You must be logged in to do that.' ); return Redirect::to( $type ); @@ -129,18 +144,11 @@ class Comments extends Plugin { Session::flash( 'error', 'You do not have permission to edit this comment' ); return Redirect::to( $type ); } - if ( !$this->comments->delete( (array) $content->ID ) ) { + if ( !self::$comments->delete( (array) $content->ID ) ) { Session::flash( 'error', 'There was an error with your request.' ); } else { Session::flash( 'success', 'Comment has been deleted' ); } return Redirect::to( $redirect . $content->contentID ); } - - public function getModel() { - if ( empty($this->comments) ) { - $this->comments = new CommentModel; - } - return $this->comments; - } } diff --git a/app/plugins/comments/views/admin/dashboard.html b/app/plugins/comments/views/admin/dashboard.html index f62a77f..5034c50 100644 --- a/app/plugins/comments/views/admin/dashboard.html +++ b/app/plugins/comments/views/admin/dashboard.html @@ -1,5 +1,5 @@ New Comments - +
    @@ -13,8 +13,8 @@ - - + + {/LOOP} {ALT} diff --git a/app/plugins/comments/views/admin/edit.html b/app/plugins/comments/views/admin/edit.html index e46f4aa..47553e3 100644 --- a/app/plugins/comments/views/admin/edit.html +++ b/app/plugins/comments/views/admin/edit.html @@ -1,9 +1,25 @@ - -
    -
    - -
    +
    +
    + Edit Comment +
    + {ADMIN_BREADCRUMBS} + +
    +
    + +
    + +
    +
    + + + + + +
    + +
    +
    +
    - - - \ No newline at end of file +
    \ No newline at end of file diff --git a/app/plugins/comments/views/admin/list.html b/app/plugins/comments/views/admin/list.html index 57c08e1..33b160e 100644 --- a/app/plugins/comments/views/admin/list.html +++ b/app/plugins/comments/views/admin/list.html @@ -1,43 +1,45 @@ -Recent Comments -{PAGINATION} -
    -
    {authorName} {content}
    - - - - - - - - - - - - - {LOOP} - - - - - - - - - - {/LOOP} - {ALT} - - - - {/ALT} - -
    AuthorSubjectCommentTime - -
    {authorName}{contentTitle}{content}{DTC}{created}{/DTC} - -
    - No results to show. -
    - - -
    \ No newline at end of file +
    + Recent Comments +
    + {ADMIN_BREADCRUMBS} +
    + + + + + + + + + + + + + + {LOOP} + + + + + + + + + + {/LOOP} + {ALT} + + + + {/ALT} + +
    AuthorSubjectCommentTime + +
    {authorName}{contentTitle}{content}{DTC}{created}{/DTC} + +
    + No results to show. +
    + +
    +
    \ No newline at end of file diff --git a/app/plugins/comments/views/control.html b/app/plugins/comments/views/control.html index 31dbbde..26661f4 100644 --- a/app/plugins/comments/views/control.html +++ b/app/plugins/comments/views/control.html @@ -1,8 +1,8 @@ \ No newline at end of file diff --git a/app/plugins/comments/views/create.html b/app/plugins/comments/views/create.html index 098552a..af241f5 100644 --- a/app/plugins/comments/views/create.html +++ b/app/plugins/comments/views/create.html @@ -1,10 +1,14 @@ -
    -
    -
    - -
    + +
    +
    - + - \ No newline at end of file + diff --git a/app/plugins/comments/views/list.html b/app/plugins/comments/views/list.html index 4f7e1bf..1cdf0fc 100644 --- a/app/plugins/comments/views/list.html +++ b/app/plugins/comments/views/list.html @@ -1,22 +1,23 @@ -
    -
    - -

    Comments

    - {count} +
    +
    +

    + Comments +

    + {count}
    -
    -
      +
      +
        {LOOP} -
      • -
        -
        - +
      • +
        +
        + User Avatar
        -
        -
        -
        - By: {authorName} on {DTC date}{created}{/DTC} -
        +
        +
        + + By: {authorName} on {DTC date}{created}{/DTC} +
        {content} @@ -27,13 +28,9 @@
      • {/LOOP} {ALT} -
      • -
        -
        -
        -

        Be the first to comment.

        -
        -
        +
      • +
        +

        Be the first to comment.

      • {/ALT} diff --git a/app/plugins/feedback/controllers/admin/feedback.php b/app/plugins/contact/controllers/admin/contact.php similarity index 63% rename from app/plugins/feedback/controllers/admin/feedback.php rename to app/plugins/contact/controllers/admin/contact.php index 582f537..1a1aa4a 100644 --- a/app/plugins/feedback/controllers/admin/feedback.php +++ b/app/plugins/contact/controllers/admin/contact.php @@ -1,10 +1,10 @@ * @link https://TheTempusProject.com @@ -18,29 +18,29 @@ use TheTempusProject\Houdini\Classes\Views; use TheTempusProject\Houdini\Classes\Navigation; use TheTempusProject\Houdini\Classes\Components; use TheTempusProject\Classes\AdminController; -use TheTempusProject\Models\Feedback as FeedbackModel; +use TheTempusProject\Models\Contact as ContactModel; -class Feedback extends AdminController { - protected static $feedback; +class Contact extends AdminController { + protected static $contact; public function __construct() { parent::__construct(); - self::$title = 'Admin - Feedback'; - self::$feedback = new FeedbackModel; - $view = Navigation::activePageSelect( 'nav.admin', '/admin/feedback' ); + self::$title = 'Admin - Contact'; + self::$contact = new ContactModel; + $view = Navigation::activePageSelect( 'nav.admin', '/admin/contact' ); Components::set( 'ADMINNAV', $view ); } public function view( $id = null ) { - Views::view( 'feedback.admin.view', self::$feedback->findById( $id ) ); + Views::view( 'contact.admin.view', self::$contact->findById( $id ) ); } public function delete( $data = null ) { if ( Input::exists( 'submit' ) ) { $data = Input::post( 'F_' ); } - if ( self::$feedback->delete( (array) $data ) ) { - Issues::add( 'success', 'feedback deleted' ); + if ( self::$contact->delete( (array) $data ) ) { + Issues::add( 'success', 'contact deleted' ); } else { Issues::add( 'error', 'There was an error with your request.' ); } @@ -48,11 +48,11 @@ class Feedback extends AdminController { } public function clear( $data = null ) { - self::$feedback->clear(); + self::$contact->clear(); $this->index(); } public function index( $data = null ) { - Views::view( 'feedback.admin.list', self::$feedback->listPaginated() ); + Views::view( 'contact.admin.list', self::$contact->listPaginated() ); } } diff --git a/app/plugins/feedback/controllers/feedback.php b/app/plugins/contact/controllers/contact.php similarity index 58% rename from app/plugins/feedback/controllers/feedback.php rename to app/plugins/contact/controllers/contact.php index 685a573..a849547 100644 --- a/app/plugins/feedback/controllers/feedback.php +++ b/app/plugins/contact/controllers/contact.php @@ -1,10 +1,10 @@ * @link https://TheTempusProject.com @@ -20,25 +20,25 @@ use TheTempusProject\Bedrock\Functions\Check; use TheTempusProject\Bedrock\Functions\Input; use TheTempusProject\Bedrock\Functions\Session; use TheTempusProject\Hermes\Functions\Redirect; -use TheTempusProject\Models\Feedback as FeedbackModel; +use TheTempusProject\Models\Contact as ContactModel; -class Feedback extends Controller { - protected static $feedback; +class Contact extends Controller { + protected static $contact; public function index() { - self::$feedback = new FeedbackModel; - self::$title = 'Feedback - {SITENAME}'; - self::$pageDescription = 'At {SITENAME}, we value our users\' input. You can provide any feedback or suggestions using this form.'; + self::$contact = new ContactModel; + self::$title = 'Contact - {SITENAME}'; + self::$pageDescription = 'At {SITENAME}, we value our users\' input. You can provide any contact or suggestions using this form.'; if ( !Input::exists() ) { - return Views::view( 'feedback.feedback' ); + return Views::view( 'contact.create' ); } - if ( !Forms::check( 'feedback' ) ) { + if ( !Forms::check( 'contact' ) ) { Issues::add( 'error', [ 'There was an error with your form, please check your submission and try again.' => Check::userErrors() ] ); - return Views::view( 'feedback.feedback' ); + return Views::view( 'contact.create' ); } - $result = self::$feedback->create( Input::post( 'name' ), Input::post( 'feedbackEmail' ), Input::post( 'entry' ) ); + $result = self::$contact->create( Input::post( 'name' ), Input::post( 'contactEmail' ), Input::post( 'entry' ) ); if ( $result ) { - Session::flash( 'success', 'Thank you! Your feedback has been received.' ); + Session::flash( 'success', 'Thank you! Your contact has been received.' ); Redirect::to( 'home/index' ); } else { Issues::add( 'error', [ 'There was an error with your form, please check your submission and try again.' => Check::userErrors() ] ); diff --git a/app/plugins/feedback/forms.php b/app/plugins/contact/forms.php similarity index 69% rename from app/plugins/feedback/forms.php rename to app/plugins/contact/forms.php index 36bd805..f2265cb 100644 --- a/app/plugins/feedback/forms.php +++ b/app/plugins/contact/forms.php @@ -1,35 +1,35 @@ * @link https://TheTempusProject.com * @license https://opensource.org/licenses/MIT [MIT LICENSE] */ -namespace TheTempusProject\Plugins\Feedback; +namespace TheTempusProject\Plugins\Contact; use TheTempusProject\Bedrock\Functions\Input; use TheTempusProject\Bedrock\Functions\Check; use TheTempusProject\Classes\Forms; -class FeedbackForms extends Forms { +class ContactForms extends Forms { /** * Adds these functions to the form list. */ public function __construct() { - self::addHandler( 'feedback', __CLASS__, 'feedback' ); + self::addHandler( 'contact', __CLASS__, 'contact' ); } /** - * Validates the feedback form. + * Validates the contact form. * * @return {bool} */ - public static function feedback() { + public static function contact() { if ( !Input::exists( 'name' ) ) { Check::addUserError( 'You must provide a name.' ); return false; @@ -38,12 +38,12 @@ class FeedbackForms extends Forms { Check::addUserError( 'Invalid name.' ); return false; } - if ( !empty( Input::post( 'feedbackEmail' ) ) && !Check::email( Input::post( 'feedbackEmail' ) ) ) { + if ( !empty( Input::post( 'contactEmail' ) ) && !Check::email( Input::post( 'contactEmail' ) ) ) { Check::addUserError( 'Invalid Email.' ); return false; } if ( Input::post( 'entry' ) == '' ) { - Check::addUserError( 'Feedback cannot be empty.' ); + Check::addUserError( 'Contact cannot be empty.' ); return false; } if ( !Check::token() ) { @@ -53,4 +53,4 @@ class FeedbackForms extends Forms { } } -new FeedbackForms; +new ContactForms; diff --git a/app/plugins/feedback/models/feedback.php b/app/plugins/contact/models/contact.php similarity index 79% rename from app/plugins/feedback/models/feedback.php rename to app/plugins/contact/models/contact.php index 398c36b..8730883 100644 --- a/app/plugins/feedback/models/feedback.php +++ b/app/plugins/contact/models/contact.php @@ -1,12 +1,12 @@ * @link https://TheTempusProject.com @@ -14,13 +14,12 @@ */ namespace TheTempusProject\Models; -use TheTempusProject\Bedrock\Classes\Config; use TheTempusProject\Bedrock\Functions\Check; use TheTempusProject\Canary\Bin\Canary as Debug; use TheTempusProject\Classes\DatabaseModel; -use TheTempusProject\Plugins\Feedback as Plugin; +use TheTempusProject\Plugins\Contact as Plugin; -class Feedback extends DatabaseModel { +class Contact extends DatabaseModel { public $tableName = 'feedback'; public $databaseMatrix = [ [ 'name', 'varchar', '128' ], @@ -40,7 +39,7 @@ class Feedback extends DatabaseModel { } /** - * Saves a feedback form to the db. + * Saves a contact form to the db. * * @param string $name -the name on the form * @param string $email -the email provided @@ -49,7 +48,7 @@ class Feedback extends DatabaseModel { */ public function create( $name, $email, $feedback ) { if ( !$this->plugin->checkEnabled() ) { - Debug::info( 'Feedback is disabled in the config.' ); + Debug::info( 'Contact is disabled in the config.' ); return false; } $fields = [ @@ -60,14 +59,14 @@ class Feedback extends DatabaseModel { 'ip' => $_SERVER['REMOTE_ADDR'], ]; if ( !self::$db->insert( $this->tableName, $fields ) ) { - Debug::info( 'Feedback::create - failed to insert to db' ); + Debug::info( 'Contact::create - failed to insert to db' ); return false; } return self::$db->lastId(); } /** - * Function to clear feedback from the DB. + * Function to clear contact from the DB. * * @todo is there a way i could check for success here I'm pretty sure this is just a bad idea? * @return bool @@ -77,8 +76,8 @@ class Feedback extends DatabaseModel { self::$log = new Log; } self::$db->delete( $this->tableName, ['ID', '>=', '0'] ); - self::$log->admin( 'Cleared Feedback' ); - Debug::info( 'Feedback Cleared' ); + self::$log->admin( 'Contacts Cleared' ); + Debug::info( 'Contacts Cleared' ); return true; } } diff --git a/app/plugins/feedback/plugin.php b/app/plugins/contact/plugin.php similarity index 63% rename from app/plugins/feedback/plugin.php rename to app/plugins/contact/plugin.php index cd4e3c7..98cf81b 100644 --- a/app/plugins/feedback/plugin.php +++ b/app/plugins/contact/plugin.php @@ -1,10 +1,10 @@ * @link https://TheTempusProject.com @@ -14,18 +14,18 @@ namespace TheTempusProject\Plugins; use TheTempusProject\Classes\Plugin; -class Feedback extends Plugin { - public $pluginName = 'TP Feedback'; +class Contact extends Plugin { + public $pluginName = 'TP Contact'; public $pluginAuthor = 'JoeyK'; public $pluginWebsite = 'https://TheTempusProject.com'; public $modelVersion = '1.0'; public $pluginVersion = '3.0'; - public $pluginDescription = 'A simple plugin which adds a form and management for user submitted feedback.'; - public $configName = 'feedback'; + public $pluginDescription = 'A simple plugin which adds a form and management for user submitted contact forms.'; + public $configName = 'contact'; public $configMatrix = [ 'enabled' => [ 'type' => 'radio', - 'pretty' => 'Enable User Feedback.', + 'pretty' => 'Enable User Contact.', 'default' => true, ], 'sendEmail' => [ @@ -36,25 +36,25 @@ class Feedback extends Plugin { 'emailTemplate' => [ 'type' => 'text', 'pretty' => 'Email Template', - 'default' => 'feedbackEmail', + 'default' => 'contactEmail', ], ]; public $permissionMatrix = [ - 'feedback' => [ - 'pretty' => 'Can Submit Feedback', - 'default' => false, + 'contact' => [ + 'pretty' => 'Can Submit Contact', + 'default' => true, ], ]; - public $footer_links = [ + public $contact_footer_links = [ [ - 'text' => 'Feedback', - 'url' => '{ROOT_URL}feedback', + 'text' => 'Contact', + 'url' => '{ROOT_URL}contact', ], ]; public $admin_links = [ [ - 'text' => ' Feedback', - 'url' => '{ROOT_URL}admin/feedback', + 'text' => ' Contact', + 'url' => '{ROOT_URL}admin/contact', ], ]; } diff --git a/app/plugins/contact/views/admin/list.html b/app/plugins/contact/views/admin/list.html new file mode 100644 index 0000000..f155fe5 --- /dev/null +++ b/app/plugins/contact/views/admin/list.html @@ -0,0 +1,45 @@ +
        + Contact Forms +
        + {ADMIN_BREADCRUMBS} +
        + + + + + + + + + + + + + {LOOP} + + + + + + + + + {/LOOP} + {ALT} + + + + {/ALT} + +
        IDTimeFeedback + +
        {ID}{DTC}{time}{/DTC}{feedback} + +
        + No results to show. +
        + +
        +
        + clear all +
        \ No newline at end of file diff --git a/app/plugins/contact/views/admin/view.html b/app/plugins/contact/views/admin/view.html new file mode 100644 index 0000000..8daf820 --- /dev/null +++ b/app/plugins/contact/views/admin/view.html @@ -0,0 +1,61 @@ +
        +
        +
        + {ADMIN_BREADCRUMBS} +
        + +
        +

        Contact Form

        +
        + + +
        +
        + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
        ID:{ID}
        Time submitted{DTC}{time}{/DTC}
        Email:{email}
        Name:{name}
        IP:{ip}
        Feedback:
        {feedback}
        +
        +
        + + + +
        +
        +
        +
        \ No newline at end of file diff --git a/app/plugins/contact/views/create.html b/app/plugins/contact/views/create.html new file mode 100644 index 0000000..946f66a --- /dev/null +++ b/app/plugins/contact/views/create.html @@ -0,0 +1,45 @@ +
        +

        Contact Us

        +
        +

        + Here at {SITENAME}, we highly value your feedback. We constantly strive to provide our users with the highest level of quality in everything we do. +

        +

        + If you would like to provide any suggestions or comments on our service, we ask that you please fill out the quick form below and let us know what's on your mind. +

        +
        +
        + +
        + +
        + +
        +
        + + +
        + +
        + +
        +
        + + +
        + +
        + + Max: 2000 characters +
        +
        + + + + + +
        + +
        +
        +
        diff --git a/app/plugins/feedback/views/admin/list.html b/app/plugins/feedback/views/admin/list.html deleted file mode 100644 index ad93fce..0000000 --- a/app/plugins/feedback/views/admin/list.html +++ /dev/null @@ -1,42 +0,0 @@ -Feedback -{PAGINATION} -
        - - - - - - - - - - - - - {LOOP} - - - - - - - - - {/LOOP} - {ALT} - - - - {/ALT} - -
        IDTimeFeedback - -
        {ID}{DTC}{time}{/DTC}{feedback} - -
        - No results to show. -
        - -
        -
        -clear all \ No newline at end of file diff --git a/app/plugins/feedback/views/admin/view.html b/app/plugins/feedback/views/admin/view.html deleted file mode 100644 index 1bea819..0000000 --- a/app/plugins/feedback/views/admin/view.html +++ /dev/null @@ -1,56 +0,0 @@ -
        -
        -
        -
        -
        -

        Feedback

        -
        -
        -
        -
        - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
        ID:{ID}
        Time submitted:{DTC}{time}{/DTC}
        IP:{ip}
        Email:{email}
        Name{name}
        Feedback
        {feedback}
        -
        -
        -
        - -
        -
        -
        -
        \ No newline at end of file diff --git a/app/plugins/feedback/views/feedback.html b/app/plugins/feedback/views/feedback.html deleted file mode 100644 index cfdab0b..0000000 --- a/app/plugins/feedback/views/feedback.html +++ /dev/null @@ -1,27 +0,0 @@ -
        - Feedback -

        Here at {SITENAME} we highly value your feedback. We constantly strive to provide our users with the highest level of quality in everything we do.

        -

        If you would like to provide any suggestions or comments on our service, we ask that you please fill out the quick form below and let us know what's on your mind.

        -
        -
        - -
        - -
        -
        -
        - -
        - -
        -
        -
        - -
        - -
        -
        -
        - -
        -
        \ No newline at end of file diff --git a/app/plugins/messages/models/message.php b/app/plugins/messages/models/message.php index 501a731..6236fd2 100644 --- a/app/plugins/messages/models/message.php +++ b/app/plugins/messages/models/message.php @@ -61,7 +61,7 @@ class Message extends DatabaseModel { Debug::info( 'Invalid user ID' ); return false; } - $messageData = self::$db->getPaginated( $this->tableName, [ 'ID', '=', $parent ] ); + $messageData = self::$db->get( $this->tableName, [ 'ID', '=', $parent ] ); if ( $messageData->count() == 0 ) { Debug::info( 'Message not found.' ); return false; @@ -71,7 +71,7 @@ class Message extends DatabaseModel { if ( $type !== null ) { $params = array_merge( $params, [ 'AND', $type, '=', $user ] ); } - $messageData = self::$db->getPaginated( $this->tableName, $params, 'ID', 'DESC', [ 0, 1 ] ); + $messageData = self::$db->get( $this->tableName, $params, 'ID', 'DESC', [ 0, 1 ] ); if ( $messageData->count() != 0 ) { if ( $messageData->first()->recieverDeleted == 0 ) { $message = $messageData->first(); @@ -93,7 +93,7 @@ class Message extends DatabaseModel { Debug::info( 'Invalid ID' ); return false; } - $messageData = self::$db->getPaginated( $this->tableName, [ 'ID', '=', $id ] ); + $messageData = self::$db->get( $this->tableName, [ 'ID', '=', $id ] ); if ( $messageData->count() == 0 ) { Debug::info( 'Message not found.' ); return false; @@ -122,7 +122,7 @@ class Message extends DatabaseModel { } else { $find = $message->ID; } - $messageData = self::$db->getPaginated( $this->tableName, [ 'ID', '=', $find, 'OR', 'Parent', '=', $find ], 'ID', 'ASC' )->results(); + $messageData = self::$db->get( $this->tableName, [ 'ID', '=', $find, 'OR', 'Parent', '=', $find ], 'ID', 'ASC' )->results(); Components::set( 'PID', $find ); if ( $markRead == true ) { @@ -138,7 +138,7 @@ class Message extends DatabaseModel { $limit = 10; } $limit = [ 0, $limit ]; - $messageData = self::$db->getPaginated( + $messageData = self::$db->get( $this->tableName, [ 'parent', '=', 0, @@ -154,7 +154,7 @@ class Message extends DatabaseModel { $limit ); if ( $messageData->count() == 0 ) { - Debug::info( 'No messages found' ); + Debug::info( 'getInbox: No messages found' ); return false; } $filters = [ @@ -175,7 +175,7 @@ class Message extends DatabaseModel { $limit = 10; } $limit = [ 0, $limit ]; - $messageData = self::$db->getPaginated( + $messageData = self::$db->get( $this->tableName, [ 'parent', '=', 0, @@ -187,7 +187,7 @@ class Message extends DatabaseModel { $limit ); if ( $messageData->count() == 0 ) { - Debug::info( 'No messages found' ); + Debug::info( 'getOutbox: No messages found' ); return false; } $filters = [ diff --git a/app/plugins/messages/plugin.php b/app/plugins/messages/plugin.php index 743890c..13624ef 100644 --- a/app/plugins/messages/plugin.php +++ b/app/plugins/messages/plugin.php @@ -20,7 +20,6 @@ use TheTempusProject\Houdini\Classes\Views; class Messages extends Plugin { public $pluginName = 'TP Messages'; - public $configName = 'messages'; public $pluginAuthor = 'JoeyK'; public $pluginWebsite = 'https://TheTempusProject.com'; public $modelVersion = '1.0'; @@ -33,25 +32,24 @@ class Messages extends Plugin { ], ]; private static $loaded = false; + public function __construct() { - // This was taken directly from the main app - // load the message template data as part of the template - $messages = new Message; - Components::set( 'MESSAGE_COUNT', $messages->unreadCount() ); - if ( $messages->unreadCount() > 0 ) { - $messageBadge = Views::simpleView( 'messages.badge' ); - } else { - $messageBadge = ''; - } - Components::set( 'MBADGE', $messageBadge ); - if ( App::$isLoggedIn ) { - Components::set( 'RECENT_MESSAGES', Views::simpleView( 'messages.nav.recentMessagesDropdown', $messages->getInbox( 5 ) ) ); - } else { - Components::set( 'RECENT_MESSAGES', '' ); - } if ( ! self::$loaded ) { + $messages = new Message; + Components::set( 'MESSAGE_COUNT', $messages->unreadCount() ); + if ( $messages->unreadCount() > 0 ) { + $messageBadge = Views::simpleView( 'messages.badge' ); + } else { + $messageBadge = ''; + } + Components::set( 'MBADGE', $messageBadge ); + if ( App::$isLoggedIn ) { + Components::set( 'RECENT_MESSAGES', Views::simpleView( 'messages.nav.recentMessagesDropdown', $messages->getInbox( 5 ) ) ); + } else { + Components::set( 'RECENT_MESSAGES', '' ); + } App::$topNavRight .= '{RECENT_MESSAGES}'; - App::$topNavRightDropdown .= '
      • Inbox {MBADGE}
      • '; + App::$topNavRightDropdown .= '
      • Inbox {MBADGE}
      • '; self::$loaded = true; } parent::__construct(); diff --git a/app/plugins/messages/views/create.html b/app/plugins/messages/views/create.html index 6958c8b..d1cd308 100644 --- a/app/plugins/messages/views/create.html +++ b/app/plugins/messages/views/create.html @@ -1,25 +1,57 @@ -
        - New Message -
        -
        - -
        - +
        +
        +
        + + New Message +
        + +
        + +
        + +
        +
        + + +
        + +
        + +
        +
        + + +
        + + +
        +
        + + +
        -
        - -
        - -
        -
        -
        - -
        - -
        -
        -
        - -
        - \ No newline at end of file +
      diff --git a/app/plugins/messages/views/mesage.html b/app/plugins/messages/views/mesage.html index 305d84b..e076f5b 100644 --- a/app/plugins/messages/views/mesage.html +++ b/app/plugins/messages/views/mesage.html @@ -1,14 +1,14 @@
      -
      -
      +
      +
      {LOOP} {SINGLE} -
      -

      {subject}

      +
      +

      {subject}

      {/SINGLE} -
      +
      {userFrom}
      @@ -23,10 +23,10 @@
      -