From 1fb4d2eb57e75f332078930dc52ac8e8137c0df6 Mon Sep 17 00:00:00 2001 From: Joey Kimsey Date: Mon, 3 Feb 2025 10:19:40 -0500 Subject: [PATCH] wip --- app/classes/config.php | 2 +- app/classes/database_model.php | 1 - app/classes/forms.php | 26 ++ app/classes/installer.php | 8 +- app/classes/permissions.php | 2 +- app/classes/plugin.php | 4 +- app/classes/preferences.php | 2 +- app/config/constants.php | 2 +- app/controllers/admin/images.php | 288 ++++++++++++++++++++-- app/models/group.php | 8 + app/models/user.php | 6 - app/plugins/blog/templates/blog.tpl | 7 +- app/plugins/bugreport/plugin.php | 2 +- app/plugins/contact/plugin.php | 2 +- app/views/admin/images/list/combined.html | 30 +++ app/views/admin/images/rename.html | 35 +++ app/views/admin/images/upload.html | 29 +++ app/views/admin/images/view.html | 66 +++++ app/views/auth/register.html | 9 - app/views/faq.html | 112 ++++----- app/views/forms/folderSelect.html | 17 ++ app/views/forms/folderSelectChild.html | 6 + app/views/forms/folderSelectParent.html | 14 ++ app/views/install/routing.html | 2 +- app/views/install/terms.html | 2 +- app/views/install/verify.html | 2 +- app/views/install/welcome.html | 6 +- app/views/start.html | 17 +- bin/tempus_project.php | 30 ++- install.php | 13 +- 30 files changed, 619 insertions(+), 131 deletions(-) create mode 100644 app/views/admin/images/list/combined.html create mode 100644 app/views/admin/images/rename.html create mode 100644 app/views/admin/images/upload.html create mode 100644 app/views/admin/images/view.html create mode 100644 app/views/forms/folderSelect.html create mode 100644 app/views/forms/folderSelectChild.html create mode 100644 app/views/forms/folderSelectParent.html diff --git a/app/classes/config.php b/app/classes/config.php index 6f3609c..0a28176 100644 --- a/app/classes/config.php +++ b/app/classes/config.php @@ -73,7 +73,7 @@ class Config extends BedrockConfig { $html .= '
'; $html .= '

Current Image

'; $html .= '
'; - $html .= 'User Avatar'; + $html .= 'configured image'; $html .= '
'; } $html .= '
'; diff --git a/app/classes/database_model.php b/app/classes/database_model.php index 5b873b6..16f66c7 100644 --- a/app/classes/database_model.php +++ b/app/classes/database_model.php @@ -121,7 +121,6 @@ class DatabaseModel extends BedrockDatabaseModel { $errors = []; foreach ( self::$installFlags as $flag_name ) { if ( empty( $options[$flag_name] ) ) { - $module_data[ $flag_name ] = INSTALL_STATUS_SKIPPED; continue; } diff --git a/app/classes/forms.php b/app/classes/forms.php index 1d6c4cb..afea762 100644 --- a/app/classes/forms.php +++ b/app/classes/forms.php @@ -115,6 +115,8 @@ class Forms extends Check { self::addHandler( 'adminCreateToken', __CLASS__, 'adminCreateToken' ); self::addHandler( 'apiLogin', __CLASS__, 'apiLogin' ); self::addHandler( 'updatePreference', __CLASS__, 'updatePreference' ); + self::addHandler( 'renameIImage', __CLASS__, 'renameIImage' ); + self::addHandler( 'addImage', __CLASS__, 'addImage' ); self::addHandler( 'installStart', __CLASS__, 'install', [ 'start' ] ); self::addHandler( 'installAgreement', __CLASS__, 'install', [ 'agreement' ] ); self::addHandler( 'installCheck', __CLASS__, 'install', [ 'check' ] ); @@ -663,4 +665,28 @@ class Forms extends Check { } return true; } + + public static function renameIImage() { + if ( !Input::exists( 'filelocation' ) ) { + self::addUserError( 'You must specify a location' ); + return false; + } + if ( !Input::exists( 'newname' ) ) { + self::addUserError( 'You must specify a new name' ); + return false; + } + return true; + } + + public static function addImage() { + if ( !Input::exists( 'folderSelect' ) ) { + self::addUserError( 'You must specify a location' ); + return false; + } + if ( !Input::exists( 'uploadImage' ) ) { + self::addUserError( 'You must include a file.' ); + return false; + } + return true; + } } diff --git a/app/classes/installer.php b/app/classes/installer.php index 02ea1bf..dcd289f 100644 --- a/app/classes/installer.php +++ b/app/classes/installer.php @@ -158,6 +158,10 @@ class Installer { } else { self::$installJson['modules'][$name]['enabled_txt'] = 'No'; } + // in this case only, we save an array to remove the objects later, so an array stored is a success. + if ( ! empty( self::$installJson['modules'][$name]['resources_installed'] ) && is_array( self::$installJson['modules'][$name]['resources_installed'] ) ) { + self::$installJson['modules'][$name]['resources_installed'] = INSTALL_STATUS_SUCCESS; + } } return self::$installJson['modules'][$name]; } @@ -422,7 +426,7 @@ class Installer { } // exclude any flags that have already been successfully installed - if ( !empty( $module_data->$flag_type ) && $module_data->$flag_type == INSTALL_STATUS_SUCCESS ) { + if ( ! empty( $module_data->$flag_type ) && $module_data->$flag_type == INSTALL_STATUS_SUCCESS ) { Debug::warn( "$flag_type has already been successfully installed" ); $flags[ $flag_type ] = false; } @@ -530,7 +534,7 @@ class Installer { } foreach ( $flags as $flag_type ) { - if ( ! in_array( $modelInfo[ $flag_type ], [ INSTALL_STATUS_SUCCESS, INSTALL_STATUS_NOT_REQUIRED ] ) ) { + if ( empty( $modelInfo[ $flag_type ] ) || ! in_array( $modelInfo[ $flag_type ], [ INSTALL_STATUS_SUCCESS, INSTALL_STATUS_NOT_REQUIRED ] ) ) { $modelInfo['installStatus'] = INSTALL_STATUS_PARTIALLY_INSTALLED; break; } diff --git a/app/classes/permissions.php b/app/classes/permissions.php index bef0368..5842418 100644 --- a/app/classes/permissions.php +++ b/app/classes/permissions.php @@ -245,7 +245,7 @@ class Permissions { public static function getFieldEditHtml( $name, $default, $pretty ) { $fieldname = str_ireplace( '/', '-', $name ); - $fieldHtml = Forms::getSwitchHtml( $fieldname, [ 'true', 'false' ], $default ); + $fieldHtml = Forms::getSwitchHtml( $fieldname, $default ); $html = ''; $html .= '
'; $html .= ''; diff --git a/app/classes/plugin.php b/app/classes/plugin.php index 76b1c67..7364455 100644 --- a/app/classes/plugin.php +++ b/app/classes/plugin.php @@ -89,7 +89,7 @@ class Plugin { foreach ( self::PLUGIN_FLAG_MAP as $flag_name => $function_name ) { if ( empty( $options[$flag_name] ) ) { - $module_data[ $flag_name ] = INSTALL_STATUS_SKIPPED; + // $module_data[ $flag_name ] = INSTALL_STATUS_SKIPPED; continue; } @@ -280,7 +280,7 @@ class Plugin { public function installResources( $options = '' ) { if ( empty( $this->resourceMatrix ) ) { Debug::log( 'resourceMatrix is empty' ); - return true; + return INSTALL_STATUS_NOT_REQUIRED; } $ids = []; foreach( $this->resourceMatrix as $tableName => $entries ) { diff --git a/app/classes/preferences.php b/app/classes/preferences.php index c32d08c..c582d1b 100644 --- a/app/classes/preferences.php +++ b/app/classes/preferences.php @@ -276,7 +276,7 @@ class Preferences { $html .= '
'; $html .= '

Current Image

'; $html .= '
'; - $html .= 'User Avatar'; + $html .= 'preferred image'; $html .= '
'; } $html .= '
'; diff --git a/app/config/constants.php b/app/config/constants.php index 2f1863c..ab609ba 100644 --- a/app/config/constants.php +++ b/app/config/constants.php @@ -39,7 +39,7 @@ if ( ! defined( 'CONFIG_DIRECTORY' ) ) { define( 'CANARY_SECURE_HASH', 'd73ed7591a30f0ca7d686a0e780f0d05' ); # Tempus Project Core define( 'APP_NAME', 'The Tempus Project'); -define( 'TP_DEFAULT_LOGO', 'images/logo.png'); +define( 'TP_DEFAULT_LOGO', 'images/logoWhite.png'); // Check define( 'MINIMUM_PHP_VERSION', 8.1); // Cookies diff --git a/app/controllers/admin/images.php b/app/controllers/admin/images.php index 0499c41..e2abda8 100644 --- a/app/controllers/admin/images.php +++ b/app/controllers/admin/images.php @@ -23,8 +23,154 @@ use TheTempusProject\Bedrock\Functions\Input; use TheTempusProject\Bedrock\Functions\Check; use TheTempusProject\Hermes\Functions\Redirect; use TheTempusProject\Bedrock\Functions\Session; +use TheTempusProject\Hermes\Functions\Route as Routes; +use TheTempusProject\Bedrock\Functions\Upload; +use RecursiveIteratorIterator; +use RecursiveDirectoryIterator; +use FilesystemIterator; class Images extends AdminController { + private $directories = [ + APP_ROOT_DIRECTORY . 'images', + APP_ROOT_DIRECTORY . 'app/images', + APP_ROOT_DIRECTORY . 'app/plugins' + ]; + + private $excludedDirectories = [ + '.', + '..', + 'vendor', + 'docker', + 'logs', + 'gitlab', + 'uploads', + 'config', + ]; + + public function upload() { + if ( Input::exists( 'submit' ) ) { + $route = ''; + $destination = ''; + if ( !TTPForms::check( 'addImage' ) ) { + Issues::add( 'error', [ 'There was an error with your image upload.' => Check::userErrors() ] ); + } else { + $folder = Input::post( 'folderSelect' ) . DIRECTORY_SEPARATOR; + // dv( $folder ); + $upload = Upload::image( 'uploadImage', $folder ); + if ( $upload ) { + $route = str_replace( APP_ROOT_DIRECTORY, '', $folder ); + $destination = $route . Upload::last(); + Issues::add( 'success', 'Image uploaded.' ); + } else { + Issues::add( 'error', [ 'There was an error with your image upload.' => Check::userErrors() ] ); + } + } + } + + $folders = $this->getDirectoriesRecursive( APP_ROOT_DIRECTORY ); + $folderHtml = $this->generateFolderHtml( $folders ); + Components::set( 'FOLDER_SELECT_ROOT', APP_ROOT_DIRECTORY ); + Components::set( 'FOLDER_SELECT', Views::simpleView( 'forms.folderSelect', $folderHtml ) ); + Views::view( 'admin.images.upload' ); + } + + + + + + + + + + + + + + + + + + + + + private function getFolderObject( $folder, $subdirs = '' ) { + $names = explode( DIRECTORY_SEPARATOR, $folder ); + $folderName = array_pop( $names ); + $out = [ + 'folderName' => $folderName, + 'location' => $folder, + 'subdirs' => $subdirs, + ]; + if ( ! empty( $subdirs ) ) { + $out['folderexpand'] = ''; + } else { + $out['folderexpand'] = ''; + } + return (object) $out; + } + + private function generateFolderHtml( $folders ) { + $rows = []; + foreach ( $folders as $top => $sub ) { + $object = $this->getFolderObject( $top ); + if ( $top == $sub ) { + $html = ''; + } else { + $children = $this->generateFolderHtml( $sub ); + Components::set( 'parentfolderName', $object->folderName ); + $html = Views::simpleView( 'forms.folderSelectParent', $children ); + Components::set( 'parentfolderName', '' ); + } + $rows[] = $this->getFolderObject( $top, $html ); + } + return $rows; + } + + private function getDirectoriesRecursive( $directory ) { + $dirs = []; + + $directory = rtrim( $directory, DIRECTORY_SEPARATOR ); + $directory = $directory. DIRECTORY_SEPARATOR; + + $files = scandir( $directory ); + $filteredFiles = array_values( array_diff( $files, $this->excludedDirectories ) ); + + foreach ( $filteredFiles as $key => $filename ) { + $long_name = $directory . $filename; + $is_dir = ( ( strpos( $filename, '.' ) === false ) && ( is_dir( $long_name ) === true ) ); + if ( $is_dir ) { + $recursive_dirs = $this->getDirectoriesRecursive( $long_name ); + if ( empty( $recursive_dirs ) ) { + $recursive_dirs = $long_name; + } + $dirs[$long_name] = $recursive_dirs; + } + } + + return $dirs; + } + + + + + + + + + + + + + + + + + + + + + + public function __construct() { parent::__construct(); @@ -76,39 +222,147 @@ class Images extends AdminController { Views::view( 'admin.images.create' ); } - public function delete( $id = null ) { + public function delete() { if ( self::$token->delete( [ $id ] ) ) { Session::flash( 'success', 'Token deleted.' ); } Redirect::to( 'admin/images' ); } - public function edit( $id = null ) { - $token = self::$token->findById( $id ); + public function rename() { + + if ( ! Input::exists( 'fileLocation' ) ) { + Session::flash( 'warning', 'Unknown image.' ); + Redirect::to( 'admin/images' ); + } + + Components::set( 'filelocation', Input::get( 'fileLocation' ) ); + if ( Input::exists( 'submit' ) ) { - if ( !TTPForms::check( 'adminEditToken' ) ) { - Issues::add( 'error', [ 'There was an error with your token.' => Check::userErrors() ] ); + if ( !TTPForms::check( 'renameIImage' ) ) { + Issues::add( 'error', [ 'There was an error renaming the image.' => Check::userErrors() ] ); } else { - if ( self::$token->update( - $id, - Input::post( 'name' ), - Input::post( 'notes' ), - Input::post( 'token_type' ) - ) ) { - Session::flash( 'success', 'Token Updated' ); + $result = $this->renameFile( Input::post( 'filelocation' ), Input::post( 'newname' ) ); + + if ( ! empty( $result ) ) { + Session::flash( 'success', 'Image has been renamed.' ); Redirect::to( 'admin/images' ); + } else { + Issues::add( 'error', [ 'There was an error with the install.' => $this->installer->getErrors() ] ); } } } - Forms::selectOption( $token->token_type ); - return Views::view( 'admin.images.edit', $token ); + + return Views::view( 'admin.images.rename' ); } public function index() { - return Views::view( 'admin.images.list', self::$token->listPaginated() ); + return Views::view( 'admin.images.list.combined', $this->getAllImageDetails() ); } - public function view( $id = null ) { - return Views::view( 'admin.images.view', self::$token->findById( $id ) ); + public function view() { + if ( Input::exists( 'fileLocation' ) ) { + return Views::view( 'admin.images.view', $this->getImageByLocation( Input::get( 'fileLocation' ) ) ); + } + return $this->index(); + } + + private function getAllImages() { + $files = []; + foreach ($this->directories as $dir) { + if ($dir === 'app/plugins') { + $pluginDirs = glob($dir . '/*', GLOB_ONLYDIR); + foreach ($pluginDirs as $pluginDir) { + $imageDir = $pluginDir . '/images'; + if (is_dir($imageDir)) { + $files = array_merge($files, $this->scanDirectoryRecursively($imageDir)); + } + } + } else { + $files = array_merge($files, $this->scanDirectory($dir)); + } + } + return $files; + } + + private function scanDirectory($path) { + return glob($path . '/*.{jpg,jpeg,png,gif,webp}', GLOB_BRACE) ?: []; + } + + private function scanDirectoryRecursively($path) { + $files = []; + $iterator = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($path, FilesystemIterator::SKIP_DOTS)); + + foreach ($iterator as $file) { + if (preg_match('/\.(jpg|jpeg|png|gif|webp)$/i', $file->getFilename())) { + $files[] = $file->getPathname(); + } + } + + return $files; + } + + private function getAllImageDetails() { + $images = []; + $files = $this->getAllImages(); + foreach ( $files as $file ) { + $images[] = $this->getImageByLocation( $file ); + } + return $images; + } + + private function getImageByLocation( $location ) { + $realPath = realpath( $location ); + + return (object) [ + 'filename' => basename( $location ), + 'extension' => pathinfo( $location , PATHINFO_EXTENSION), + 'fileSize' => $this->formatFileSize(filesize( $location )), + 'location' => $realPath, + 'locationSafe' => urlencode( $realPath ), + 'url' => Routes::getAddress() . str_replace( APP_ROOT_DIRECTORY, '', $realPath ), + 'folder' => dirname( $location ) + ]; + } + + private function formatFileSize($size) { + $units = ['B', 'KB', 'MB', 'GB', 'TB']; + $i = 0; + while ($size >= 1024 && $i < count($units) - 1) { + $size /= 1024; + $i++; + } + return round($size, 2) . ' ' . $units[$i]; + } + + private function renameFile( $currentLocation, $newFilename ) { + // Ensure the file exists + if (!file_exists($currentLocation)) { + throw new \Exception("File does not exist: $currentLocation"); + } + + // Extract directory and current extension + $directory = dirname($currentLocation); + $currentExtension = pathinfo($currentLocation, PATHINFO_EXTENSION); + $newExtension = pathinfo($newFilename, PATHINFO_EXTENSION); + + // Ensure the file extension has not changed + if (strcasecmp($currentExtension, $newExtension) !== 0) { + throw new \Exception("File extension cannot be changed."); + } + + // Construct the new file path + $newLocation = $directory . DIRECTORY_SEPARATOR . $newFilename; + + // Ensure the new file name does not already exist + if (file_exists($newLocation)) { + throw new \Exception("A file with the new name already exists: $newFilename"); + } + + // Attempt to rename the file + if (!rename($currentLocation, $newLocation)) { + throw new \Exception("Failed to rename file."); + } + return true; } } diff --git a/app/models/group.php b/app/models/group.php index f5331a3..d5a85f1 100644 --- a/app/models/group.php +++ b/app/models/group.php @@ -46,6 +46,14 @@ class Group extends DatabaseModel { 'pretty' => 'Access Administrator Areas', 'default' => false, ], + 'uploadImages' => [ + 'pretty' => 'Upload images (such as avatars)', + 'default' => false, + ], + 'maintenanceAccess' => [ + 'pretty' => 'Upload images (such as avatars)', + 'default' => false, + ], ]; public $resourceMatrix = [ [ diff --git a/app/models/user.php b/app/models/user.php index 286663d..90c52de 100644 --- a/app/models/user.php +++ b/app/models/user.php @@ -47,12 +47,6 @@ class User extends DatabaseModel { public $searchFields = [ 'username', ]; - public $permissionMatrix = [ - 'uploadImages' => [ - 'pretty' => 'Upload images (such as avatars)', - 'default' => false, - ], - ]; public $preferenceMatrix = [ 'gender' => [ 'pretty' => 'Gender', diff --git a/app/plugins/blog/templates/blog.tpl b/app/plugins/blog/templates/blog.tpl index eb9530f..cb23a4e 100644 --- a/app/plugins/blog/templates/blog.tpl +++ b/app/plugins/blog/templates/blog.tpl @@ -45,19 +45,19 @@ - + {SITENAME} Logo - + {SITENAME} Logo