Add a global image gallery

Fixes #41
This commit is contained in:
Kazhnuz 2025-07-15 12:40:38 +02:00
parent cc8db166b5
commit af0b8e9fb7
13 changed files with 576 additions and 0 deletions

View file

@ -0,0 +1,49 @@
<?php defined('KOBLOG') or die('Koblog CMS.');
// ============================================================================
// Check role
// ============================================================================
checkRole(array('admin'));
// ============================================================================
// Functions
// ============================================================================
// ============================================================================
// Main before POST
// ============================================================================
// ============================================================================
// POST Method
// ============================================================================
if ($_SERVER['REQUEST_METHOD'] == 'POST') {
if ($_POST['type']==='delete') {
if (deleteMedia($_POST['key'])) {
Alert::set( $L->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();

View file

@ -0,0 +1,30 @@
<?php defined('KOBLOG') or die('Koblog CMS.');
// ============================================================================
// Check role
// ============================================================================
checkRole(array('admin', 'editor', 'author'));
// ============================================================================
// Functions
// ============================================================================
// ============================================================================
// Main before POST
// ============================================================================
// ============================================================================
// POST Method
// ============================================================================
// ============================================================================
// Main after POST
// ============================================================================
// ============================================================================
// Main after POST
// ============================================================================
// Title of the page
$layout['title'] .= ' - '.$L->g('Medias');

View file

@ -0,0 +1,49 @@
<?php defined('KOBLOG') or die('Koblog CMS.');
// ============================================================================
// Check role
// ============================================================================
checkRole(array('admin'));
// ============================================================================
// Functions
// ============================================================================
// ============================================================================
// Main before POST
// ============================================================================
// ============================================================================
// POST Method
// ============================================================================
if ($_SERVER['REQUEST_METHOD'] == 'POST') {
createMedia($_POST, $_FILES);
Redirect::page('medias');
}
// ============================================================================
// Main after POST
// ============================================================================
// Images prefix directory
define('PAGE_IMAGES_KEY', $uuid);
// Images and thubmnails directories
if (IMAGE_RESTRICT) {
define('PAGE_IMAGES_DIRECTORY', (IMAGE_RELATIVE_TO_ABSOLUTE? '' : HTML_PATH_UPLOADS_PAGES.PAGE_IMAGES_KEY.'/'));
define('PAGE_IMAGES_URL', (IMAGE_RELATIVE_TO_ABSOLUTE? '' : DOMAIN_UPLOADS_PAGES.PAGE_IMAGES_KEY.'/'));
define('PAGE_THUMBNAILS_DIRECTORY', PATH_UPLOADS_PAGES.PAGE_IMAGES_KEY.DS.'thumbnails'.DS);
define('PAGE_THUMBNAILS_HTML', HTML_PATH_UPLOADS_PAGES.PAGE_IMAGES_KEY.'/thumbnails/');
define('PAGE_THUMBNAILS_URL', DOMAIN_UPLOADS_PAGES.PAGE_IMAGES_KEY.'/thumbnails/');
} else {
define('PAGE_IMAGES_DIRECTORY', (IMAGE_RELATIVE_TO_ABSOLUTE? '' : HTML_PATH_UPLOADS));
define('PAGE_IMAGES_URL', (IMAGE_RELATIVE_TO_ABSOLUTE? '' : DOMAIN_UPLOADS));
define('PAGE_THUMBNAILS_DIRECTORY', PATH_UPLOADS_THUMBNAILS);
define('PAGE_THUMBNAILS_HTML', HTML_PATH_UPLOADS_THUMBNAILS);
define('PAGE_THUMBNAILS_URL', DOMAIN_UPLOADS_THUMBNAILS);
}
// Title of the page
$layout['title'] = $L->g('New media').' - '.$layout['title'];

View file

@ -17,6 +17,8 @@
<li class="nav-item d-lg-none">
<a class="nav-link" href="<?php echo HTML_PATH_ADMIN_ROOT.'content' ?>">
<?php $L->p('Content') ?></a>
<a class="nav-link" href="<?php echo HTML_PATH_ADMIN_ROOT.'medias' ?>">
<?php $L->p('Medias') ?></a>
</li>
<?php if (!checkRole(array('admin'),false)): ?>
<li class="nav-item d-lg-none">

View file

@ -11,6 +11,10 @@
<li class="nav-item">
<a class="nav-link" href="<?php echo HTML_PATH_ADMIN_ROOT.'content' ?>"><span class="fa fa-archive fa-fw"></span><?php $L->p('Articles and pages') ?></a>
</li>
<li class="nav-item">
<a class="nav-link" href="<?php echo HTML_PATH_ADMIN_ROOT.'medias' ?>"><span class="fa fa-images fa-fw"></span><?php $L->p('Medias') ?></a>
</li>
<?php endif; ?>
<?php if (checkRole(array('admin'),false)): ?>
@ -26,6 +30,10 @@
<a class="nav-link" href="<?php echo HTML_PATH_ADMIN_ROOT.'categories' ?>"><span class="fa fa-tags fa-fw"></span><?php $L->p('Categories') ?></a>
</li>
<li class="nav-item">
<a class="nav-link" href="<?php echo HTML_PATH_ADMIN_ROOT.'medias' ?>"><span class="fa fa-images fa-fw"></span><?php $L->p('Medias') ?></a>
</li>
<li class="nav-item mt-3">
<h4><?php $L->p('Customization') ?></h4>
</li>

View file

@ -0,0 +1,61 @@
<?php defined('KOBLOG') or die('Koblog CMS.'); ?>
<?php echo Bootstrap::formOpen(array('id'=>'jsform', 'class'=>'tab-content','enctype'=>'multipart/form-data')); ?>
<div class="d-flex justify-content-between mb-2">
<?php echo Bootstrap::pageTitle(array('title'=>$L->g(string: 'Edit media'), 'icon'=>'image')); ?>
<div>
<button type="submit" class="btn btn-primary" name="save"><?php $L->p('Save') ?></button>
<a class="btn btn-secondary" href="<?php echo HTML_PATH_ADMIN_ROOT.'medias' ?>" role="button"><?php $L->p('Cancel') ?></a>
</div>
</div>
<div class="preview text-center mb-4">
<img class="img-thumbnail" alt="<?php echo $media->alt() ?>" src="<?php echo $media->permalink() ?>" style="max-height:200px;width:auto;" />
</div>
<div class="card">
<div class="card-body">
<?php
// Current page key
echo Bootstrap::formInputHidden(array(
'name' => '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
));
?>
<div class="form-group row mb-2">
<label class="col-sm-2 col-form-label" for="jssiteLogoInputFile"><?php $L->p('Upload image'); ?></label>
<div class="col-sm-10">
<input id="jssiteLogoInputFile" class="form-control custom-file-input" type="file" name="inputFile">
<small class="form-text text-muted">Prout</small>
</div>
</div>
</div>
</div>
<?php echo Bootstrap::formClose(); ?>

View file

@ -0,0 +1,41 @@
<?php defined('KOBLOG') or die('Koblog CMS.');
echo "<div class='d-flex justify-content-between align-content-center'>";
echo Bootstrap::pageTitle(array('title'=>$L->g('Medias'), 'icon'=>'images'));
echo "<div>";
echo Bootstrap::link(array(
'title'=>$L->g('New media'),
'href'=>HTML_PATH_ADMIN_ROOT.'new-media',
'icon'=>'plus',
'class'=>'btn btn-outline-success'
));
echo "</div></div>";
echo '
<div class="card mt-3">
<table class="table m-0">
<thead>
<tr class="card-header">
<th class="border-bottom-0" scope="col">'.$L->g('Name').'</th>
<th class="border-bottom-0" scope="col">'.$L->g('URL').'</th>
</tr>
</thead>
<tbody>
';
foreach ($medias->content() as $key=>$media) {
echo '<tr class="card-tablebody">';
echo '<td><a href="'.HTML_PATH_ADMIN_ROOT.'edit-media/'.$key.'">'.'<img class="me-2" src="'.$media->permalink().'" alt="'.$media->alt().'" width="32" height="32" />'.$media->name().'</a></td>';
echo '<td><a href="'.$media->permalink().'">'.$media->permalink().'</a></td>';
echo '</tr>';
}
echo '
</tbody>
</table>
</div>
';
?>

View file

@ -0,0 +1,49 @@
<?php defined('KOBLOG') or die('Koblog CMS.'); ?>
<?php echo Bootstrap::formOpen(array('id'=>'jsform', 'class'=>'tab-content','enctype'=>'multipart/form-data')); ?>
<div class="d-flex justify-content-between mb-2">
<?php echo Bootstrap::pageTitle(array('title'=>$L->g(string: 'New media'), 'icon'=>'image')); ?>
<div>
<button type="submit" class="btn btn-primary" name="save"><?php $L->p('Save') ?></button>
<a class="btn btn-secondary" href="<?php echo HTML_PATH_ADMIN_ROOT.'medias' ?>" role="button"><?php $L->p('Cancel') ?></a>
</div>
</div>
<div class="card"><div class="card-body">
<?php
echo Bootstrap::formInputHidden(array(
'name'=>'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
));
?>
<div class="form-group row mb-2">
<label class="col-sm-2 col-form-label" for="jssiteLogoInputFile"><?php $L->p('Upload image'); ?></label>
<div class="col-sm-10">
<input id="jssiteLogoInputFile" class="form-control custom-file-input" type="file" name="inputFile">
<small class="form-text text-muted">Prout</small>
</div>
</div>
</div></div>
<?php echo Bootstrap::formClose(); ?>

View file

@ -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/');

View file

@ -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

44
bl-kernel/media.class.php Normal file
View file

@ -0,0 +1,44 @@
<?php defined('KOBLOG') or die('Koblog CMS.');
class Media {
protected $vars;
function __construct($key)
{
global $medias;
if (isset($medias->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();
}
}

View file

@ -0,0 +1,80 @@
<?php defined('KOBLOG') or die('Koblog CMS.');
class Medias extends dbJSON
{
protected $parentKeyList = array();
protected $dbFields = array(
'name' => '',
'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();
}
}

View file

@ -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
// ============================================================================