From af0b8e9fb7b31d4470d16b287a4a48837ecac66a Mon Sep 17 00:00:00 2001 From: Kazhnuz Date: Tue, 15 Jul 2025 12:40:38 +0200 Subject: [PATCH] =?UTF-8?q?=E2=9C=A8=20Add=20a=20global=20image=20gallery?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixes #41 --- bl-kernel/admin/controllers/edit-media.php | 49 ++++++ bl-kernel/admin/controllers/medias.php | 30 ++++ bl-kernel/admin/controllers/new-media.php | 49 ++++++ bl-kernel/admin/themes/koblog/html/navbar.php | 2 + .../admin/themes/koblog/html/sidebar.php | 8 + bl-kernel/admin/views/edit-media.php | 61 +++++++ bl-kernel/admin/views/medias.php | 41 +++++ bl-kernel/admin/views/new-media.php | 49 ++++++ bl-kernel/boot/init.php | 6 + bl-kernel/functions.php | 151 ++++++++++++++++++ bl-kernel/media.class.php | 44 +++++ bl-kernel/medias.class.php | 80 ++++++++++ install.php | 6 + 13 files changed, 576 insertions(+) create mode 100644 bl-kernel/admin/controllers/edit-media.php create mode 100644 bl-kernel/admin/controllers/medias.php create mode 100644 bl-kernel/admin/controllers/new-media.php create mode 100644 bl-kernel/admin/views/edit-media.php create mode 100644 bl-kernel/admin/views/medias.php create mode 100644 bl-kernel/admin/views/new-media.php create mode 100644 bl-kernel/media.class.php create mode 100644 bl-kernel/medias.class.php diff --git a/bl-kernel/admin/controllers/edit-media.php b/bl-kernel/admin/controllers/edit-media.php new file mode 100644 index 00000000..642d5576 --- /dev/null +++ b/bl-kernel/admin/controllers/edit-media.php @@ -0,0 +1,49 @@ +g('The changes have been saved') ); + } + } else { + $key = editMedia($_POST, $_FILES); + if ($key!==false) { + Alert::set( $L->g('The changes have been saved') ); + Redirect::page('edit-media/'.$key); + } + } + + Redirect::page('medias'); +} + +// ============================================================================ +// Main after POST +// ============================================================================ +try { + $mediaKey = $layout['parameters']; + $media = new Media($mediaKey); +} catch (Exception $e) { + Log::set(__METHOD__.LOG_SEP.'Error occurred when trying to get the page: '.$mediaKey, LOG_TYPE_ERROR); + Redirect::page('medias'); +} + +// Title of the page +$layout['title'] .= ' - '.$L->g('Edit media').' - '.$media->name(); \ No newline at end of file diff --git a/bl-kernel/admin/controllers/medias.php b/bl-kernel/admin/controllers/medias.php new file mode 100644 index 00000000..c17b75f4 --- /dev/null +++ b/bl-kernel/admin/controllers/medias.php @@ -0,0 +1,30 @@ +g('Medias'); \ No newline at end of file diff --git a/bl-kernel/admin/controllers/new-media.php b/bl-kernel/admin/controllers/new-media.php new file mode 100644 index 00000000..6bc8ef7b --- /dev/null +++ b/bl-kernel/admin/controllers/new-media.php @@ -0,0 +1,49 @@ +g('New media').' - '.$layout['title']; \ No newline at end of file diff --git a/bl-kernel/admin/themes/koblog/html/navbar.php b/bl-kernel/admin/themes/koblog/html/navbar.php index e3be3f30..c78b738e 100644 --- a/bl-kernel/admin/themes/koblog/html/navbar.php +++ b/bl-kernel/admin/themes/koblog/html/navbar.php @@ -17,6 +17,8 @@ + + @@ -26,6 +30,10 @@ p('Categories') ?> + + diff --git a/bl-kernel/admin/views/edit-media.php b/bl-kernel/admin/views/edit-media.php new file mode 100644 index 00000000..c2e86d38 --- /dev/null +++ b/bl-kernel/admin/views/edit-media.php @@ -0,0 +1,61 @@ + + +'jsform', 'class'=>'tab-content','enctype'=>'multipart/form-data')); ?> + +
+$L->g(string: 'Edit media'), 'icon'=>'image')); ?> +
+ + p('Cancel') ?> +
+
+ +
+ <?php echo $media->alt() ?> +
+ +
+
+ 'key', + 'value' => $media->key() + )); + + echo Bootstrap::formInputHidden(array( + 'name'=>'tokenCSRF', + 'value'=>$security->getTokenCSRF() + )); + + echo Bootstrap::formInputText(array( + 'name'=>'name', + 'label'=>$L->g('Name'), + 'value'=>$media->name(), + 'class'=>'', + 'placeholder'=>'', + 'tip'=>'' + )); + + echo Bootstrap::formTextarea(array( + 'name'=>'alt', + 'label'=>$L->g('Alt Text'), + 'value'=>$media->alt(), + 'class'=>'', + 'placeholder'=>'', + 'tip'=>'Add a description of the text', + 'rows'=>3 + )); + ?> +
+ +
+ + Prout +
+
+
+
+ + \ No newline at end of file diff --git a/bl-kernel/admin/views/medias.php b/bl-kernel/admin/views/medias.php new file mode 100644 index 00000000..c07b14d0 --- /dev/null +++ b/bl-kernel/admin/views/medias.php @@ -0,0 +1,41 @@ +"; + +echo Bootstrap::pageTitle(array('title'=>$L->g('Medias'), 'icon'=>'images')); + +echo "
"; +echo Bootstrap::link(array( + 'title'=>$L->g('New media'), + 'href'=>HTML_PATH_ADMIN_ROOT.'new-media', + 'icon'=>'plus', + 'class'=>'btn btn-outline-success' +)); +echo "
"; + +echo ' +
+ + + + + + + + +'; + +foreach ($medias->content() as $key=>$media) { + echo ''; + echo ''; + echo ''; + echo ''; +} + +echo ' + +
'.$L->g('Name').''.$L->g('URL').'
'.''.$media->alt().''.$media->name().''.$media->permalink().'
+
+'; + +?> \ No newline at end of file diff --git a/bl-kernel/admin/views/new-media.php b/bl-kernel/admin/views/new-media.php new file mode 100644 index 00000000..59210043 --- /dev/null +++ b/bl-kernel/admin/views/new-media.php @@ -0,0 +1,49 @@ + + +'jsform', 'class'=>'tab-content','enctype'=>'multipart/form-data')); ?> + +
+$L->g(string: 'New media'), 'icon'=>'image')); ?> +
+ + p('Cancel') ?> +
+
+ +
+'tokenCSRF', + 'value'=>$security->getTokenCSRF() + )); + + echo Bootstrap::formInputText(array( + 'name'=>'name', + 'label'=>$L->g('Name'), + 'value'=>isset($_POST['name'])?$_POST['name']:'', + 'class'=>'', + 'placeholder'=>'', + 'tip'=>'' + )); + + echo Bootstrap::formTextarea(array( + 'name'=>'alt', + 'label'=>$L->g('Alt Text'), + 'value'=>isset($_POST['alt'])?$_POST['alt']:'', + 'class'=>'', + 'placeholder'=>'', + 'tip'=>'Add a description of the text', + 'rows'=>3 + )); +?> +
+ +
+ + Prout +
+
+ +
+ + \ No newline at end of file diff --git a/bl-kernel/boot/init.php b/bl-kernel/boot/init.php index f1876a20..cd890db1 100644 --- a/bl-kernel/boot/init.php +++ b/bl-kernel/boot/init.php @@ -54,6 +54,7 @@ define('PATH_WORKSPACES', PATH_CONTENT . 'workspaces' . DS); define('PATH_UPLOADS_PAGES', PATH_UPLOADS . 'pages' . DS); define('PATH_UPLOADS_PROFILES', PATH_UPLOADS . 'profiles' . DS); define('PATH_UPLOADS_THUMBNAILS', PATH_UPLOADS . 'thumbnails' . DS); +define('PATH_UPLOADS_MEDIAS', PATH_UPLOADS . 'medias' . DS); define('PATH_ADMIN', PATH_KERNEL . 'admin' . DS); define('PATH_ADMIN_THEMES', PATH_ADMIN . 'themes' . DS); @@ -64,6 +65,7 @@ define('DEBUG_FILE', PATH_CONTENT . 'debug.txt'); // PAGES DATABASE define('DB_PAGES', PATH_DATABASES . 'pages.php'); +define('DB_MEDIAS', PATH_DATABASES . 'medias.php'); define('DB_SITE', PATH_DATABASES . 'site.php'); define('DB_CATEGORIES', PATH_DATABASES . 'categories.php'); define('DB_TAGS', PATH_DATABASES . 'tags.php'); @@ -90,6 +92,7 @@ include(PATH_ABSTRACT . 'plugin.class.php'); // Inclde Classes include(PATH_KERNEL . 'pages.class.php'); +include(PATH_KERNEL . 'medias.class.php'); include(PATH_KERNEL . 'users.class.php'); include(PATH_KERNEL . 'tags.class.php'); include(PATH_KERNEL . 'authors.class.php'); @@ -100,6 +103,7 @@ include(PATH_KERNEL . 'site.class.php'); include(PATH_KERNEL . 'categories.class.php'); include(PATH_KERNEL . 'syslog.class.php'); include(PATH_KERNEL . 'pagex.class.php'); +include(PATH_KERNEL . 'media.class.php'); include(PATH_KERNEL . 'category.class.php'); include(PATH_KERNEL . 'tag.class.php'); include(PATH_KERNEL . 'user.class.php'); @@ -133,6 +137,7 @@ include(PATH_HELPERS . 'pluginsettings.class.php'); // Objects $pages = new Pages(); +$medias = new Medias(); $users = new Users(); $tags = new Tags(); $authors = new Authors(); @@ -189,6 +194,7 @@ define('HTML_PATH_UPLOADS', HTML_PATH_ROOT . 'bl-content/uploads/'); define('HTML_PATH_UPLOADS_PAGES', HTML_PATH_UPLOADS . 'pages/'); define('HTML_PATH_UPLOADS_PROFILES', HTML_PATH_UPLOADS . 'profiles/'); define('HTML_PATH_UPLOADS_THUMBNAILS', HTML_PATH_UPLOADS . 'thumbnails/'); +define('HTML_PATH_UPLOADS_MEDIAS', HTML_PATH_UPLOADS . 'medias/'); define('HTML_PATH_PLUGINS', HTML_PATH_ROOT . 'bl-plugins/'); define('HTML_PATH_MODULES', HTML_PATH_ROOT . 'bl-modules/'); diff --git a/bl-kernel/functions.php b/bl-kernel/functions.php index 681242a9..a073a56e 100644 --- a/bl-kernel/functions.php +++ b/bl-kernel/functions.php @@ -370,6 +370,157 @@ function changePluginsPosition($pluginClassList) return true; } +/* + Create a new media + + The array $args support all the keys from variable $dbFields of the class medias.class.php + If you don't pass all the keys, the default values are used, the default values are from $dbFields in the class medias.class.php +*/ +function createMedia($args, $files) +{ + global $medias; + global $syslog; + global $L; + global $site; + + if (!$args['name'] || $args['name'] == "") { + return false; + } + + $key = Text::cleanUrl($args['name']); + + // Check path traversal on $filename + if (Text::stringContains($files['inputFile']['name'], DS, false)) { + $message = 'Path traversal detected.'; + Log::set($message, LOG_TYPE_ERROR); + return false; + } + + // File extension + $fileExtension = Filesystem::extension($files['inputFile']['name']); + $fileExtension = Text::lowercase($fileExtension); + if (!in_array($fileExtension, $GLOBALS['ALLOWED_IMG_EXTENSION'])) { + $message = $L->g('File type is not supported. Allowed types:').' '.implode(', ',$GLOBALS['ALLOWED_IMG_EXTENSION']); + Log::set($message, LOG_TYPE_ERROR); + return false; + } + + // File MIME Type + $fileMimeType = Filesystem::mimeType($files['inputFile']['tmp_name']); + if ($fileMimeType!==false) { + if (!in_array($fileMimeType, $GLOBALS['ALLOWED_IMG_MIMETYPES'])) { + $message = $L->g('File mime type is not supported. Allowed types:').' '.implode(', ',$GLOBALS['ALLOWED_IMG_MIMETYPES']); + Log::set($message, LOG_TYPE_ERROR); + return false; + } + } + + // Final filename + $filename = $key.'.'.$fileExtension; + + // Delete old image + // $oldFilename = $site->logo(false); + // if ($oldFilename) { + // Filesystem::rmfile(PATH_UPLOADS.$oldFilename); + // } + + if (!Filesystem::directoryExists(PATH_UPLOADS_MEDIAS)) { + Filesystem::mkdir(PATH_UPLOADS_MEDIAS, true); + } + + // Move from temporary directory to uploads + Filesystem::mv($_FILES['inputFile']['tmp_name'], PATH_UPLOADS_MEDIAS.$filename); + + // Permissions + chmod(PATH_UPLOADS_MEDIAS.$filename, 0644); + + $medias->add($args, $filename); + + // Add to syslog + $syslog->add(array( + 'dictionaryKey' => 'new-media-uploaded', + 'notes' => (empty($args['name']) ? $key : $args['name']) + )); + + return $key; +} + +/* + Edit media +*/ +function editMedia($args, $files) +{ + global $medias; + global $syslog; + global $L; + global $site; + + $key = $args['key']; + $filename = ""; + + $media = new Media($key); + + if ($files['inputFile']['name']) { + // Check path traversal on $filename + if (Text::stringContains($files['inputFile']['name'], DS, false)) { + $message = 'Path traversal detected.'; + Log::set($message, LOG_TYPE_ERROR); + die($message); + return; + } + + // File extension + $fileExtension = Filesystem::extension($files['inputFile']['name']); + $fileExtension = Text::lowercase($fileExtension); + if (!in_array($fileExtension, $GLOBALS['ALLOWED_IMG_EXTENSION'])) { + $message = $L->g('File type is not supported. Allowed types:').' '.implode(', ',$GLOBALS['ALLOWED_IMG_EXTENSION']); + Log::set($message, LOG_TYPE_ERROR); + die($message); + return; + } + + // File MIME Type + $fileMimeType = Filesystem::mimeType($files['inputFile']['tmp_name']); + if ($fileMimeType!==false) { + if (!in_array($fileMimeType, $GLOBALS['ALLOWED_IMG_MIMETYPES'])) { + $message = $L->g('File mime type is not supported. Allowed types:').' '.implode(', ',$GLOBALS['ALLOWED_IMG_MIMETYPES']); + Log::set($message, LOG_TYPE_ERROR); + die($message); + return; + } + } + + // Final filename + $filename = $key.'.'.$fileExtension; + + // Delete old image + $oldFilename = $media->filename(); + if ($oldFilename) { + Filesystem::rmfile(PATH_UPLOADS_MEDIAS.$oldFilename); + } + + if (!Filesystem::directoryExists(PATH_UPLOADS_MEDIAS)) { + Filesystem::mkdir(PATH_UPLOADS_MEDIAS, true); + } + + // Move from temporary directory to uploads + Filesystem::mv($_FILES['inputFile']['tmp_name'], PATH_UPLOADS_MEDIAS.$filename); + + // Permissions + chmod(PATH_UPLOADS_MEDIAS.$filename, 0644); + } + + $medias->edit($key, $args, $filename != "" ? $filename : $media->filename()); + + // Add to syslog + $syslog->add(array( + 'dictionaryKey' => 'media-edited', + 'notes' => (empty($args['name']) ? $key : $args['name']) + )); + + return $key; +} + /* Create a new page diff --git a/bl-kernel/media.class.php b/bl-kernel/media.class.php new file mode 100644 index 00000000..40ef61c0 --- /dev/null +++ b/bl-kernel/media.class.php @@ -0,0 +1,44 @@ +db[$key])) { + $this->vars['name'] = $medias->db[$key]['name']; + $this->vars['alt'] = $medias->db[$key]['alt']; + $this->vars['filename'] = $medias->db[$key]['filename']; + $this->vars['key'] = $key; + + //$this->vars['permalink'] = DOMAIN_TAGS . $key; + //$this->vars['list'] = $medias->db[$key]['list']; + } else { + $errorMessage = 'Media not found in database by key ['.$key.']'; + Log::set(__METHOD__.LOG_SEP.$errorMessage); + throw new Exception($errorMessage); + } + } + + function key() { + return $this->vars['key']; + } + + function name() { + return $this->vars['name']; + } + + function alt() { + return $this->vars['alt']; + } + + function filename() { + return $this->vars['filename']; + } + + function permalink() { + return HTML_PATH_UPLOADS_MEDIAS.$this->filename(); + } +} \ No newline at end of file diff --git a/bl-kernel/medias.class.php b/bl-kernel/medias.class.php new file mode 100644 index 00000000..8293c9b6 --- /dev/null +++ b/bl-kernel/medias.class.php @@ -0,0 +1,80 @@ + '', + 'alt' => '' + ); + + function __construct() + { + parent::__construct(DB_MEDIAS); + } + + public function getDefaultFields() + { + return $this->dbFields; + } + + // Return an array with the database for a page, FALSE otherwise + public function getMediaDB($key) + { + if ($this->exists($key)) { + return $this->db[$key]; + } + + return false; + } + + // Return TRUE if the page exists, FALSE otherwise + public function exists($key) + { + return isset($this->db[$key]); + } + + // Get all the medias + public function content() + { + $content = array(); + foreach ($this->db as $field => $value) { + $content[$field] = new Media($field); + } + return $content; + } + + public function add($args, $filename) + { + $row = array(); + foreach ($this->dbFields as $field => $value) { + $row[$field] = $args[$field]; + } + $row['filename'] = $filename; + $key = Text::cleanUrl($row['name']); + + // Insert in database + $this->db[$key] = $row; + + // Save database + $this->save(); + } + + public function edit($key, $args, $filename) { + $row = array(); + foreach ($this->dbFields as $field => $value) { + $row[$field] = $args[$field]; + } + if ($filename && $filename != "") { + $row['filename'] = $filename; + } + + // Insert in database + $this->db[$key] = $row; + + // Save database + $this->save(); + } + +} \ No newline at end of file diff --git a/install.php b/install.php index 0d999daf..75a07eaa 100644 --- a/install.php +++ b/install.php @@ -54,6 +54,7 @@ define('PATH_PLUGINS_DATABASES', PATH_CONTENT . 'databases' . DS . 'plugins' . D define('PATH_UPLOADS_PROFILES', PATH_UPLOADS . 'profiles' . DS); define('PATH_UPLOADS_THUMBNAILS', PATH_UPLOADS . 'thumbnails' . DS); define('PATH_UPLOADS_PAGES', PATH_UPLOADS . 'pages' . DS); +define('PATH_UPLOADS_MEDIAS', PATH_UPLOADS . 'medias' . DS); define('PATH_HELPERS', PATH_KERNEL . 'helpers' . DS); define('PATH_ABSTRACT', PATH_KERNEL . 'abstract' . DS); @@ -313,6 +314,11 @@ function install($adminPassword, $timezone) error_log('[ERROR] ' . $errorText, 0); } + if (!mkdir(PATH_UPLOADS_MEDIAS, DIR_PERMISSIONS, true)) { + $errorText = 'Error when trying to created the directory=>' . PATH_UPLOADS_PAGES; + error_log('[ERROR] ' . $errorText, 0); + } + // ============================================================================ // Create files // ============================================================================