refactor and delete old files
This commit is contained in:
parent
b455bbb055
commit
ae1c99c813
18 changed files with 16 additions and 1239 deletions
|
@ -1,66 +1,16 @@
|
||||||
<?php defined('BLUDIT') or die('Bludit CMS.');
|
<?php defined('BLUDIT') or die('Bludit CMS.');
|
||||||
|
|
||||||
|
// ============================================================================
|
||||||
|
// Authorization
|
||||||
|
// ============================================================================
|
||||||
|
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
// Functions
|
// Functions
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
function updateBludit() {
|
|
||||||
global $site;
|
|
||||||
global $syslog;
|
|
||||||
|
|
||||||
// New installation
|
|
||||||
if ($site->currentBuild()==0) {
|
|
||||||
$site->set(array('currentBuild'=>BLUDIT_BUILD));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if Bludit need to be update
|
|
||||||
if ( ($site->currentBuild() < BLUDIT_BUILD) || isset($_GET['update']) ) {
|
|
||||||
Log::set('UPDATE SYSTEM - Starting.');
|
|
||||||
|
|
||||||
// Updates only for version less than Bludit v3.0 rc-3
|
|
||||||
if ($site->currentBuild()<='20180910') {
|
|
||||||
@mkdir(PATH_WORKSPACES, DIR_PERMISSIONS, true);
|
|
||||||
$plugins = array('simple-stats', 'pluginRSS', 'pluginSitemap', 'pluginTimeMachineX', 'pluginBackup');
|
|
||||||
foreach ($plugins as $plugin) {
|
|
||||||
if (pluginActivated($plugin)) {
|
|
||||||
Log::set('UPDATE SYSTEM - Re-enable plugin: '.$plugin);
|
|
||||||
deactivatePlugin($plugin);
|
|
||||||
activatePlugin($plugin);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Updates only for version less than Bludit v3.1
|
|
||||||
if ($site->currentBuild()<='20180921') {
|
|
||||||
@mkdir(PATH_UPLOADS_PAGES, DIR_PERMISSIONS, true);
|
|
||||||
$site->set(array('imageRelativeToAbsolute'=>true, 'imageRestrict'=>false));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set the current build number
|
|
||||||
$site->set(array('currentBuild'=>BLUDIT_BUILD));
|
|
||||||
Log::set('UPDATE SYSTEM - Finished.');
|
|
||||||
|
|
||||||
// Add to syslog
|
|
||||||
$syslog->add(array(
|
|
||||||
'dictionaryKey'=>'system-updated',
|
|
||||||
'notes'=>'Bludit v'.BLUDIT_VERSION
|
|
||||||
));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
// Main before POST
|
// Main
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
|
|
||||||
// ============================================================================
|
// HTML <title>
|
||||||
// POST Method
|
$layout['title'] = $L->g('Dashboard') . ' - ' . $layout['title'];
|
||||||
// ============================================================================
|
|
||||||
|
|
||||||
// ============================================================================
|
|
||||||
// Main after POST
|
|
||||||
// ============================================================================
|
|
||||||
|
|
||||||
// Try update Bludit
|
|
||||||
updateBludit();
|
|
||||||
|
|
||||||
// Title of the page
|
|
||||||
$layout['title'] .= ' - '.$L->g('Dashboard');
|
|
|
@ -29,14 +29,5 @@ if (!method_exists($plugin, 'form')) {
|
||||||
Redirect::page('plugins');
|
Redirect::page('plugins');
|
||||||
}
|
}
|
||||||
|
|
||||||
// Save the settings
|
|
||||||
if ($_SERVER['REQUEST_METHOD'] == 'POST') {
|
|
||||||
$plugin->post();
|
|
||||||
$syslog->add(array(
|
|
||||||
'dictionaryKey'=>'plugin-configured',
|
|
||||||
'notes'=>$plugin->name()
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
// HTML <title>
|
// HTML <title>
|
||||||
$layout['title'] = $L->g('Plugin'). ' [ ' .$plugin->name(). ' ] ' . ' - ' . $layout['title'];
|
$layout['title'] = $L->g('Plugin'). ' [ ' .$plugin->name(). ' ] ' . ' - ' . $layout['title'];
|
|
@ -14,5 +14,5 @@ checkRole(array('admin'));
|
||||||
// Main
|
// Main
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
|
|
||||||
// View HTML <title>
|
// HTML <title>
|
||||||
$layout['title'] = $L->g('Settings') . ' - ' . $layout['title'];
|
$layout['title'] = $L->g('Settings') . ' - ' . $layout['title'];
|
|
@ -33,13 +33,13 @@
|
||||||
echo HTML::jquery();
|
echo HTML::jquery();
|
||||||
echo HTML::jsBootstrap();
|
echo HTML::jsBootstrap();
|
||||||
echo HTML::jsSortable();
|
echo HTML::jsSortable();
|
||||||
|
echo HTML::bootbox();
|
||||||
echo HTML::js(array(
|
echo HTML::js(array(
|
||||||
'jquery.datetimepicker.full.min.js',
|
'jquery.datetimepicker.full.min.js',
|
||||||
'jquery-ui.min.js',
|
'jquery-ui.min.js',
|
||||||
'select2.full.min.js',
|
'select2.full.min.js',
|
||||||
'tagsinput-revisited.min.js',
|
'tagsinput-revisited.min.js',
|
||||||
'functions.js',
|
'functions.js',
|
||||||
'bootbox.all.min.js',
|
|
||||||
'api.js'
|
'api.js'
|
||||||
), DOMAIN_CORE_JS);
|
), DOMAIN_CORE_JS);
|
||||||
?>
|
?>
|
||||||
|
|
|
@ -1 +0,0 @@
|
||||||
Subproject commit c97e1681dda9576128298d299e42973646df2475
|
|
|
@ -1,43 +0,0 @@
|
||||||
<?php defined('BLUDIT') or die('Bludit CMS.');
|
|
||||||
header('Content-Type: application/json');
|
|
||||||
|
|
||||||
/*
|
|
||||||
| Delete an image from a particular page
|
|
||||||
|
|
|
||||||
| @_POST['filename'] string Name of the file to delete
|
|
||||||
| @_POST['uuid'] string Page UUID
|
|
||||||
|
|
|
||||||
| @return array
|
|
||||||
*/
|
|
||||||
|
|
||||||
// $_POST
|
|
||||||
// ----------------------------------------------------------------------------
|
|
||||||
$filename = isset($_POST['filename']) ? $_POST['filename'] : false;
|
|
||||||
$uuid = empty($_POST['uuid']) ? false : $_POST['uuid'];
|
|
||||||
// ----------------------------------------------------------------------------
|
|
||||||
|
|
||||||
if ($filename===false) {
|
|
||||||
ajaxResponse(1, 'The filename is empty.');
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($uuid && IMAGE_RESTRICT) {
|
|
||||||
$imagePath = PATH_UPLOADS_PAGES.$uuid.DS;
|
|
||||||
$thumbnailPath = PATH_UPLOADS_PAGES.$uuid.DS.'thumbnails'.DS;
|
|
||||||
} else {
|
|
||||||
$imagePath = PATH_UPLOADS;
|
|
||||||
$thumbnailPath = PATH_UPLOADS_THUMBNAILS;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Delete image
|
|
||||||
if (Sanitize::pathFile($imagePath.$filename)) {
|
|
||||||
Filesystem::rmfile($imagePath.$filename);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Delete thumbnail
|
|
||||||
if (Sanitize::pathFile($thumbnailPath.$filename)) {
|
|
||||||
Filesystem::rmfile($thumbnailPath.$filename);
|
|
||||||
}
|
|
||||||
|
|
||||||
ajaxResponse(0, 'Image deleted.');
|
|
||||||
|
|
||||||
?>
|
|
|
@ -1,62 +0,0 @@
|
||||||
<?php defined('BLUDIT') or die('Bludit CMS.');
|
|
||||||
header('Content-Type: application/json');
|
|
||||||
|
|
||||||
/*
|
|
||||||
| Returns a list of images from a particular page
|
|
||||||
|
|
|
||||||
| @_POST['pageNumber'] int Page number for the paginator
|
|
||||||
| @_POST['path'] string Pre-defined name for the directory to read, its pre-defined to avoid security issues
|
|
||||||
| @_POST['uuid'] string Page UUID
|
|
||||||
|
|
|
||||||
| @return array
|
|
||||||
*/
|
|
||||||
|
|
||||||
// $_POST
|
|
||||||
// ----------------------------------------------------------------------------
|
|
||||||
// $_POST['pageNumber'] > 0
|
|
||||||
$pageNumber = empty($_POST['pageNumber']) ? 1 : (int)$_POST['pageNumber'];
|
|
||||||
$pageNumber = $pageNumber - 1;
|
|
||||||
|
|
||||||
$path = empty($_POST['path']) ? false : $_POST['path'];
|
|
||||||
$uuid = empty($_POST['uuid']) ? false : $_POST['uuid'];
|
|
||||||
// ----------------------------------------------------------------------------
|
|
||||||
|
|
||||||
// Set the path to get the file list
|
|
||||||
if ($path=='thumbnails') {
|
|
||||||
if ($uuid && IMAGE_RESTRICT) {
|
|
||||||
$path = PATH_UPLOADS_PAGES.$uuid.DS.'thumbnails'.DS;
|
|
||||||
} else {
|
|
||||||
$path = PATH_UPLOADS_THUMBNAILS;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
ajaxResponse(1, 'Invalid path.');
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get all files from the directory $path, also split the array by numberOfItems
|
|
||||||
// The function listFiles split in chunks
|
|
||||||
$listOfFilesByPage = Filesystem::listFiles($path, '*', '*', MEDIA_MANAGER_SORT_BY_DATE, MEDIA_MANAGER_NUMBER_OF_FILES);
|
|
||||||
|
|
||||||
// Check if the page number exists in the chunks
|
|
||||||
if (isset($listOfFilesByPage[$pageNumber])) {
|
|
||||||
|
|
||||||
// Get only the filename from the chunk
|
|
||||||
$files = array();
|
|
||||||
foreach ($listOfFilesByPage[$pageNumber] as $file) {
|
|
||||||
$filename = basename($file);
|
|
||||||
array_push($files, $filename);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Returns the number of chunks for the paginator
|
|
||||||
// Returns the files inside the chunk
|
|
||||||
ajaxResponse(0, 'List of files and number of chunks.', array(
|
|
||||||
'numberOfPages'=>count($listOfFilesByPage),
|
|
||||||
'files'=>$files
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
ajaxResponse(0, 'List of files and number of chunks.', array(
|
|
||||||
'numberOfPages'=>0,
|
|
||||||
'files'=>array()
|
|
||||||
));
|
|
||||||
|
|
||||||
?>
|
|
|
@ -1,22 +0,0 @@
|
||||||
<?php defined('BLUDIT') or die('Bludit CMS.');
|
|
||||||
header('Content-Type: application/json');
|
|
||||||
|
|
||||||
/*
|
|
||||||
| Delete the site logo
|
|
||||||
| This script delete the file and set and empty string in the database
|
|
||||||
|
|
|
||||||
| @return array
|
|
||||||
*/
|
|
||||||
|
|
||||||
// Delete the file
|
|
||||||
$logoFilename = $site->logo(false);
|
|
||||||
if ($logoFilename) {
|
|
||||||
Filesystem::rmfile(PATH_UPLOADS.$logoFilename);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Remove the logo from the database
|
|
||||||
$site->set(array('logo'=>''));
|
|
||||||
|
|
||||||
ajaxResponse(0, 'Logo removed.');
|
|
||||||
|
|
||||||
?>
|
|
|
@ -1,70 +0,0 @@
|
||||||
<?php defined('BLUDIT') or die('Bludit CMS.');
|
|
||||||
header('Content-Type: application/json');
|
|
||||||
|
|
||||||
/*
|
|
||||||
| Upload site logo
|
|
||||||
| The final filename is the site's name and the extension is the same as the file uploaded
|
|
||||||
|
|
|
||||||
| @_FILES['inputFile'] multipart/form-data File from form
|
|
||||||
|
|
|
||||||
| @return array
|
|
||||||
*/
|
|
||||||
|
|
||||||
if (!isset($_FILES['inputFile'])) {
|
|
||||||
ajaxResponse(1, 'Error trying to upload the site logo.');
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check path traversal on $filename
|
|
||||||
if (Text::stringContains($_FILES['inputFile']['name'], DS, false)) {
|
|
||||||
$message = 'Path traversal detected.';
|
|
||||||
Log::set($message, LOG_TYPE_ERROR);
|
|
||||||
ajaxResponse(1, $message);
|
|
||||||
}
|
|
||||||
|
|
||||||
// File extension
|
|
||||||
$fileExtension = Filesystem::extension($_FILES['inputFile']['name']);
|
|
||||||
$fileExtension = Text::lowercase($fileExtension);
|
|
||||||
if (!in_array($fileExtension, $GLOBALS['ALLOWED_IMG_EXTENSIONS'])) {
|
|
||||||
$message = $L->g('File type is not supported. Allowed types:').' '.implode(', ',$GLOBALS['ALLOWED_IMG_EXTENSIONS']);
|
|
||||||
Log::set($message, LOG_TYPE_ERROR);
|
|
||||||
ajaxResponse(1, $message);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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);
|
|
||||||
ajaxResponse(1, $message);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Final filename
|
|
||||||
$filename = 'logo.'.$fileExtension;
|
|
||||||
if (Text::isNotEmpty( $site->title() )) {
|
|
||||||
$filename = $site->title().'.'.$fileExtension;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Delete old image
|
|
||||||
$oldFilename = $site->logo(false);
|
|
||||||
if ($oldFilename) {
|
|
||||||
Filesystem::rmfile(PATH_UPLOADS.$oldFilename);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Move from temporary directory to uploads
|
|
||||||
Filesystem::mv($_FILES['inputFile']['tmp_name'], PATH_UPLOADS.$filename);
|
|
||||||
|
|
||||||
// Permissions
|
|
||||||
chmod(PATH_UPLOADS.$filename, 0644);
|
|
||||||
|
|
||||||
// Store the filename in the database
|
|
||||||
$site->set(array('logo'=>$filename));
|
|
||||||
|
|
||||||
ajaxResponse(0, 'Image uploaded.', array(
|
|
||||||
'filename'=>$filename,
|
|
||||||
'absoluteURL'=>DOMAIN_UPLOADS.$filename,
|
|
||||||
'absolutePath'=>PATH_UPLOADS.$filename
|
|
||||||
));
|
|
||||||
|
|
||||||
?>
|
|
|
@ -1,74 +0,0 @@
|
||||||
<?php defined('BLUDIT') or die('Bludit CMS.');
|
|
||||||
header('Content-Type: application/json');
|
|
||||||
|
|
||||||
// $_POST
|
|
||||||
// ----------------------------------------------------------------------------
|
|
||||||
// (string) $_POST['username']
|
|
||||||
$username = empty($_POST['username']) ? false : $_POST['username'];
|
|
||||||
// ----------------------------------------------------------------------------
|
|
||||||
|
|
||||||
if ($username===false) {
|
|
||||||
ajaxResponse(1, 'Error in username.');
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( ($login->role()!='admin') && ($login->username()!=$username) ) {
|
|
||||||
ajaxResponse(1, 'Error in username.');
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!isset($_FILES['profilePictureInputFile'])) {
|
|
||||||
ajaxResponse(1, 'Error trying to upload the profile picture.');
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check path traversal
|
|
||||||
if (Text::stringContains($username, DS, false)) {
|
|
||||||
$message = 'Path traversal detected.';
|
|
||||||
Log::set($message, LOG_TYPE_ERROR);
|
|
||||||
ajaxResponse(1, $message);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check file extension
|
|
||||||
$fileExtension = Filesystem::extension($_FILES['profilePictureInputFile']['name']);
|
|
||||||
$fileExtension = Text::lowercase($fileExtension);
|
|
||||||
if (!in_array($fileExtension, $GLOBALS['ALLOWED_IMG_EXTENSIONS']) ) {
|
|
||||||
$message = $L->g('File type is not supported. Allowed types:').' '.implode(', ',$GLOBALS['ALLOWED_IMG_EXTENSIONS']);
|
|
||||||
Log::set($message, LOG_TYPE_ERROR);
|
|
||||||
ajaxResponse(1, $message);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check file MIME Type
|
|
||||||
$fileMimeType = Filesystem::mimeType($_FILES['profilePictureInputFile']['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);
|
|
||||||
ajaxResponse(1, $message);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Tmp filename
|
|
||||||
$tmpFilename = $username.'.'.$fileExtension;
|
|
||||||
|
|
||||||
// Final filename
|
|
||||||
$filename = $username.'.png';
|
|
||||||
|
|
||||||
// Move from temporary directory to uploads folder
|
|
||||||
rename($_FILES['profilePictureInputFile']['tmp_name'], PATH_TMP.$tmpFilename);
|
|
||||||
|
|
||||||
// Resize and convert to png
|
|
||||||
$image = new Image();
|
|
||||||
$image->setImage(PATH_TMP.$tmpFilename, PROFILE_IMG_WIDTH, PROFILE_IMG_HEIGHT, 'crop');
|
|
||||||
$image->saveImage(PATH_UPLOADS_PROFILES.$filename, PROFILE_IMG_QUALITY, false, true);
|
|
||||||
|
|
||||||
// Delete temporary file
|
|
||||||
Filesystem::rmfile(PATH_TMP.$tmpFilename);
|
|
||||||
|
|
||||||
// Permissions
|
|
||||||
chmod(PATH_UPLOADS_PROFILES.$filename, 0644);
|
|
||||||
|
|
||||||
ajaxResponse(0, 'Image uploaded.', array(
|
|
||||||
'filename'=>$filename,
|
|
||||||
'absoluteURL'=>DOMAIN_UPLOADS_PROFILES.$filename,
|
|
||||||
'absolutePath'=>PATH_UPLOADS_PROFILES.$filename
|
|
||||||
));
|
|
||||||
|
|
||||||
?>
|
|
|
@ -1,97 +0,0 @@
|
||||||
<?php defined('BLUDIT') or die('Bludit CMS.');
|
|
||||||
header('Content-Type: application/json');
|
|
||||||
|
|
||||||
/*
|
|
||||||
| Upload an image to a particular page
|
|
||||||
|
|
|
||||||
| @_POST['uuid'] string Page uuid
|
|
||||||
|
|
|
||||||
| @return array
|
|
||||||
*/
|
|
||||||
|
|
||||||
// $_POST
|
|
||||||
// ----------------------------------------------------------------------------
|
|
||||||
$uuid = empty($_POST['uuid']) ? false : $_POST['uuid'];
|
|
||||||
// ----------------------------------------------------------------------------
|
|
||||||
|
|
||||||
// Check path traversal on $uuid
|
|
||||||
if ($uuid) {
|
|
||||||
if (Text::stringContains($uuid, DS, false)) {
|
|
||||||
$message = 'Path traversal detected.';
|
|
||||||
Log::set($message, LOG_TYPE_ERROR);
|
|
||||||
ajaxResponse(1, $message);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set upload directory
|
|
||||||
if ($uuid && IMAGE_RESTRICT) {
|
|
||||||
$imageDirectory = PATH_UPLOADS_PAGES.$uuid.DS;
|
|
||||||
$thumbnailDirectory = $imageDirectory.'thumbnails'.DS;
|
|
||||||
if (!Filesystem::directoryExists($thumbnailDirectory)) {
|
|
||||||
Filesystem::mkdir($thumbnailDirectory, true);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
$imageDirectory = PATH_UPLOADS;
|
|
||||||
$thumbnailDirectory = PATH_UPLOADS_THUMBNAILS;
|
|
||||||
}
|
|
||||||
|
|
||||||
$images = array();
|
|
||||||
foreach ($_FILES['images']['name'] as $uuid=>$filename) {
|
|
||||||
// Check for errors
|
|
||||||
if ($_FILES['images']['error'][$uuid] != 0) {
|
|
||||||
$message = $L->g('Maximum load file size allowed:').' '.ini_get('upload_max_filesize');
|
|
||||||
Log::set($message, LOG_TYPE_ERROR);
|
|
||||||
ajaxResponse(1, $message);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Convert URL characters such as spaces or quotes to characters
|
|
||||||
$filename = urldecode($filename);
|
|
||||||
|
|
||||||
// Check path traversal on $filename
|
|
||||||
if (Text::stringContains($filename, DS, false)) {
|
|
||||||
$message = 'Path traversal detected.';
|
|
||||||
Log::set($message, LOG_TYPE_ERROR);
|
|
||||||
ajaxResponse(1, $message);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check file extension
|
|
||||||
$fileExtension = Filesystem::extension($filename);
|
|
||||||
$fileExtension = Text::lowercase($fileExtension);
|
|
||||||
if (!in_array($fileExtension, $GLOBALS['ALLOWED_IMG_EXTENSIONS']) ) {
|
|
||||||
$message = $L->g('File type is not supported. Allowed types:').' '.implode(', ',$GLOBALS['ALLOWED_IMG_EXTENSIONS']);
|
|
||||||
Log::set($message, LOG_TYPE_ERROR);
|
|
||||||
ajaxResponse(1, $message);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check file MIME Type
|
|
||||||
$fileMimeType = Filesystem::mimeType($_FILES['images']['tmp_name'][$uuid]);
|
|
||||||
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);
|
|
||||||
ajaxResponse(1, $message);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Move from PHP tmp file to Bludit tmp directory
|
|
||||||
Filesystem::mv($_FILES['images']['tmp_name'][$uuid], PATH_TMP.$filename);
|
|
||||||
|
|
||||||
// Transform the image and generate the thumbnail
|
|
||||||
$image = transformImage(PATH_TMP.$filename, $imageDirectory, $thumbnailDirectory);
|
|
||||||
|
|
||||||
if ($image) {
|
|
||||||
chmod($image, 0644);
|
|
||||||
$filename = Filesystem::filename($image);
|
|
||||||
array_push($images, $filename);
|
|
||||||
} else {
|
|
||||||
$message = 'Error after transformImage() function.';
|
|
||||||
Log::set($message, LOG_TYPE_ERROR);
|
|
||||||
ajaxResponse(1, $message);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ajaxResponse(0, 'Images uploaded.', array(
|
|
||||||
'images'=>$images
|
|
||||||
));
|
|
||||||
|
|
||||||
?>
|
|
|
@ -83,7 +83,7 @@ include(PATH_KERNEL.'language.class.php');
|
||||||
include(PATH_KERNEL.'site.class.php');
|
include(PATH_KERNEL.'site.class.php');
|
||||||
include(PATH_KERNEL.'categories.class.php');
|
include(PATH_KERNEL.'categories.class.php');
|
||||||
include(PATH_KERNEL.'syslog.class.php');
|
include(PATH_KERNEL.'syslog.class.php');
|
||||||
include(PATH_KERNEL.'pagex.class.php');
|
include(PATH_KERNEL.'page.class.php');
|
||||||
include(PATH_KERNEL.'category.class.php');
|
include(PATH_KERNEL.'category.class.php');
|
||||||
include(PATH_KERNEL.'tag.class.php');
|
include(PATH_KERNEL.'tag.class.php');
|
||||||
include(PATH_KERNEL.'user.class.php');
|
include(PATH_KERNEL.'user.class.php');
|
||||||
|
@ -99,7 +99,6 @@ include(PATH_KERNEL.'functions.php');
|
||||||
include(PATH_HELPERS.'text.class.php');
|
include(PATH_HELPERS.'text.class.php');
|
||||||
include(PATH_HELPERS.'log.class.php');
|
include(PATH_HELPERS.'log.class.php');
|
||||||
include(PATH_HELPERS.'date.class.php');
|
include(PATH_HELPERS.'date.class.php');
|
||||||
include(PATH_HELPERS.'theme.class.php');
|
|
||||||
include(PATH_HELPERS.'session.class.php');
|
include(PATH_HELPERS.'session.class.php');
|
||||||
include(PATH_HELPERS.'redirect.class.php');
|
include(PATH_HELPERS.'redirect.class.php');
|
||||||
include(PATH_HELPERS.'sanitize.class.php');
|
include(PATH_HELPERS.'sanitize.class.php');
|
||||||
|
@ -113,7 +112,6 @@ include(PATH_HELPERS.'tcp.class.php');
|
||||||
include(PATH_HELPERS.'dom.class.php');
|
include(PATH_HELPERS.'dom.class.php');
|
||||||
include(PATH_HELPERS.'cookie.class.php');
|
include(PATH_HELPERS.'cookie.class.php');
|
||||||
include(PATH_HELPERS.'bootstrap.class.php');
|
include(PATH_HELPERS.'bootstrap.class.php');
|
||||||
|
|
||||||
include(PATH_HELPERS.'html.class.php');
|
include(PATH_HELPERS.'html.class.php');
|
||||||
|
|
||||||
if (file_exists(PATH_KERNEL.'bludit.pro.php')) {
|
if (file_exists(PATH_KERNEL.'bludit.pro.php')) {
|
||||||
|
|
|
@ -52,6 +52,12 @@ class HTML {
|
||||||
return '<script src="'.DOMAIN_CORE_VENDORS.'jquery/jquery.min.js?version='.BLUDIT_VERSION.'"></script>'.PHP_EOL;
|
return '<script src="'.DOMAIN_CORE_VENDORS.'jquery/jquery.min.js?version='.BLUDIT_VERSION.'"></script>'.PHP_EOL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static function bootbox($attributes='')
|
||||||
|
{
|
||||||
|
// https://bootbox.com/
|
||||||
|
return '<script '.$attributes.' src="'.DOMAIN_CORE_VENDORS.'bootbox/bootbox.all.min.js?version='.BLUDIT_VERSION.'"></script>'.PHP_EOL;
|
||||||
|
}
|
||||||
|
|
||||||
public static function jsBootstrap($attributes='')
|
public static function jsBootstrap($attributes='')
|
||||||
{
|
{
|
||||||
// https://getbootstrap.com/
|
// https://getbootstrap.com/
|
||||||
|
|
|
@ -1,280 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
// DEPRECATED CLASS
|
|
||||||
// WILL BE REMOVED IN BLUDIT V4
|
|
||||||
class Theme {
|
|
||||||
|
|
||||||
public static function socialNetworks()
|
|
||||||
{
|
|
||||||
global $site;
|
|
||||||
$socialNetworks = array(
|
|
||||||
'github'=>'Github',
|
|
||||||
'gitlab'=>'GitLab',
|
|
||||||
'twitter'=>'Twitter',
|
|
||||||
'facebook'=>'Facebook',
|
|
||||||
'instagram'=>'Instagram',
|
|
||||||
'codepen'=>'Codepen',
|
|
||||||
'linkedin'=>'Linkedin',
|
|
||||||
'xing'=>'Xing',
|
|
||||||
'mastodon'=>'Mastodon',
|
|
||||||
'vk'=>'VK'
|
|
||||||
);
|
|
||||||
|
|
||||||
foreach ($socialNetworks as $key=>$label) {
|
|
||||||
if (!$site->{$key}()) {
|
|
||||||
unset($socialNetworks[$key]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return $socialNetworks;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static function title()
|
|
||||||
{
|
|
||||||
global $site;
|
|
||||||
return $site->title();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static function description()
|
|
||||||
{
|
|
||||||
global $site;
|
|
||||||
return $site->description();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static function slogan()
|
|
||||||
{
|
|
||||||
global $site;
|
|
||||||
return $site->slogan();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static function footer()
|
|
||||||
{
|
|
||||||
global $site;
|
|
||||||
return $site->footer();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static function lang()
|
|
||||||
{
|
|
||||||
global $language;
|
|
||||||
return $language->currentLanguageShortVersion();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static function rssUrl()
|
|
||||||
{
|
|
||||||
if (pluginActivated('pluginRSS')) {
|
|
||||||
return DOMAIN_BASE.'rss.xml';
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static function sitemapUrl()
|
|
||||||
{
|
|
||||||
if (pluginActivated('pluginSitemap')) {
|
|
||||||
return DOMAIN_BASE.'sitemap.xml';
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Returns the absolute URL of the site
|
|
||||||
// Ex. https://example.com the method returns https://example.com/
|
|
||||||
// Ex. https://example.com/bludit/ the method returns https://example.com/bludit/
|
|
||||||
public static function siteUrl()
|
|
||||||
{
|
|
||||||
return DOMAIN_BASE;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Returns the absolute URL of admin panel
|
|
||||||
// Ex. https://example.com/admin/ the method returns https://example.com/admin/
|
|
||||||
// Ex. https://example.com/bludit/admin/ the method returns https://example.com/bludit/admin/
|
|
||||||
public static function adminUrl()
|
|
||||||
{
|
|
||||||
return DOMAIN_ADMIN;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static function metaTags($tag)
|
|
||||||
{
|
|
||||||
if ($tag=='title') {
|
|
||||||
return self::metaTagTitle();
|
|
||||||
} elseif ($tag=='description') {
|
|
||||||
return self::metaTagDescription();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static function metaTagTitle()
|
|
||||||
{
|
|
||||||
global $url;
|
|
||||||
global $site;
|
|
||||||
global $tags;
|
|
||||||
global $categories;
|
|
||||||
global $WHERE_AM_I;
|
|
||||||
global $page;
|
|
||||||
|
|
||||||
if ($WHERE_AM_I=='page') {
|
|
||||||
$format = $site->titleFormatPages();
|
|
||||||
$format = Text::replace('{{page-title}}', $page->title(), $format);
|
|
||||||
$format = Text::replace('{{page-description}}', $page->description(), $format);
|
|
||||||
} elseif ($WHERE_AM_I=='tag') {
|
|
||||||
try {
|
|
||||||
$tagKey = $url->slug();
|
|
||||||
$tag = new Tag($tagKey);
|
|
||||||
$format = $site->titleFormatTag();
|
|
||||||
$format = Text::replace('{{tag-name}}', $tag->name(), $format);
|
|
||||||
} catch (Exception $e) {
|
|
||||||
// Tag doesn't exist
|
|
||||||
}
|
|
||||||
|
|
||||||
} elseif ($WHERE_AM_I=='category') {
|
|
||||||
try {
|
|
||||||
$categoryKey = $url->slug();
|
|
||||||
$category = new Category($categoryKey);
|
|
||||||
$format = $site->titleFormatCategory();
|
|
||||||
$format = Text::replace('{{category-name}}', $category->name(), $format);
|
|
||||||
} catch (Exception $e) {
|
|
||||||
// Category doesn't exist
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
$format = $site->titleFormatHomepage();
|
|
||||||
}
|
|
||||||
|
|
||||||
$format = Text::replace('{{site-title}}', $site->title(), $format);
|
|
||||||
$format = Text::replace('{{site-slogan}}', $site->slogan(), $format);
|
|
||||||
$format = Text::replace('{{site-description}}', $site->description(), $format);
|
|
||||||
|
|
||||||
return '<title>'.$format.'</title>'.PHP_EOL;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static function metaTagDescription()
|
|
||||||
{
|
|
||||||
global $site;
|
|
||||||
global $WHERE_AM_I;
|
|
||||||
global $page;
|
|
||||||
global $url;
|
|
||||||
|
|
||||||
$description = $site->description();
|
|
||||||
|
|
||||||
if ($WHERE_AM_I=='page') {
|
|
||||||
$description = $page->description();
|
|
||||||
} elseif ($WHERE_AM_I=='category') {
|
|
||||||
try {
|
|
||||||
$categoryKey = $url->slug();
|
|
||||||
$category = new Category($categoryKey);
|
|
||||||
$description = $category->description();
|
|
||||||
} catch (Exception $e) {
|
|
||||||
// description from the site
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return '<meta name="description" content="'.$description.'">'.PHP_EOL;
|
|
||||||
}
|
|
||||||
|
|
||||||
// DEPRECATED v3.0.0
|
|
||||||
// Return the metatag <title> with a predefine structure
|
|
||||||
public static function headTitle()
|
|
||||||
{
|
|
||||||
return self::metaTagTitle();
|
|
||||||
}
|
|
||||||
|
|
||||||
// DEPRECATED v3.0.0
|
|
||||||
// Return the metatag <decription> with a predefine structure
|
|
||||||
public static function headDescription()
|
|
||||||
{
|
|
||||||
return self::metaTagDescription();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static function charset($charset)
|
|
||||||
{
|
|
||||||
return '<meta charset="'.$charset.'">'.PHP_EOL;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static function viewport($content)
|
|
||||||
{
|
|
||||||
return '<meta name="viewport" content="'.$content.'">'.PHP_EOL;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static function src($file, $base=DOMAIN_THEME)
|
|
||||||
{
|
|
||||||
return $base.$file;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static function css($files, $base=DOMAIN_THEME)
|
|
||||||
{
|
|
||||||
if( !is_array($files) ) {
|
|
||||||
$files = array($files);
|
|
||||||
}
|
|
||||||
|
|
||||||
$links = '';
|
|
||||||
foreach($files as $file) {
|
|
||||||
$links .= '<link rel="stylesheet" type="text/css" href="'.$base.$file.'?version='.BLUDIT_VERSION.'">'.PHP_EOL;
|
|
||||||
}
|
|
||||||
|
|
||||||
return $links;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static function javascript($files, $base=DOMAIN_THEME, $attributes='')
|
|
||||||
{
|
|
||||||
if( !is_array($files) ) {
|
|
||||||
$files = array($files);
|
|
||||||
}
|
|
||||||
|
|
||||||
$scripts = '';
|
|
||||||
foreach($files as $file) {
|
|
||||||
$scripts .= '<script '.$attributes.' src="'.$base.$file.'?version='.BLUDIT_VERSION.'"></script>'.PHP_EOL;
|
|
||||||
}
|
|
||||||
|
|
||||||
return $scripts;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static function js($files, $base=DOMAIN_THEME, $attributes='')
|
|
||||||
{
|
|
||||||
return self::javascript($files, $base, $attributes);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static function plugins($type, $args = array())
|
|
||||||
{
|
|
||||||
global $plugins;
|
|
||||||
foreach ($plugins[$type] as $plugin) {
|
|
||||||
echo call_user_func_array(array($plugin, $type), $args);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static function favicon($file='favicon.png', $typeIcon='image/png')
|
|
||||||
{
|
|
||||||
return '<link rel="icon" href="'.DOMAIN_THEME.$file.'" type="'.$typeIcon.'">'.PHP_EOL;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static function keywords($keywords)
|
|
||||||
{
|
|
||||||
if (is_array($keywords)) {
|
|
||||||
$keywords = implode(',', $keywords);
|
|
||||||
}
|
|
||||||
return '<meta name="keywords" content="'.$keywords.'">'.PHP_EOL;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static function jquery()
|
|
||||||
{
|
|
||||||
return '<script src="'.DOMAIN_CORE_JS.'jquery.min.js?version='.BLUDIT_VERSION.'"></script>'.PHP_EOL;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static function jsBootstrap($attributes='')
|
|
||||||
{
|
|
||||||
return '<script '.$attributes.' src="'.DOMAIN_CORE_JS.'bootstrap.bundle.min.js?version='.BLUDIT_VERSION.'"></script>'.PHP_EOL;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static function cssBootstrap()
|
|
||||||
{
|
|
||||||
return '<link rel="stylesheet" type="text/css" href="'.DOMAIN_CORE_CSS.'bootstrap.min.css?version='.BLUDIT_VERSION.'">'.PHP_EOL;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static function cssBootstrapIcons()
|
|
||||||
{
|
|
||||||
return '<link rel="stylesheet" type="text/css" href="'.DOMAIN_CORE_CSS.'/bootstrap-icons/bootstrap-icons.css?version='.BLUDIT_VERSION.'">'.PHP_EOL;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static function jsSortable($attributes='')
|
|
||||||
{
|
|
||||||
// https://github.com/psfpro/bootstrap-html5sortable
|
|
||||||
return '<script '.$attributes.' src="'.DOMAIN_CORE_JS.'jquery.sortable.min.js?version='.BLUDIT_VERSION.'"></script>'.PHP_EOL;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
?>
|
|
|
@ -1,148 +0,0 @@
|
||||||
/*
|
|
||||||
DEPRECATED CLASS
|
|
||||||
WILL BE REMOVED IN BLUDIT V4
|
|
||||||
*/
|
|
||||||
|
|
||||||
class bluditAjax {
|
|
||||||
|
|
||||||
constructor(apiURL, apiToken, apiAuth, tokenCSRF) {
|
|
||||||
this.apiURL = "http://localhost:9000/api/";
|
|
||||||
this.apiToken = '45643a4071fad6a12261bb0763550feb';
|
|
||||||
this.apiAuth = '18a8410f0043d004c2e87f404170e112';
|
|
||||||
this.tokenCSRF = tokenCSRF;
|
|
||||||
}
|
|
||||||
|
|
||||||
static async savePage(uuid, title, content) {
|
|
||||||
let url = this.apiURL+"pages";
|
|
||||||
try {
|
|
||||||
const response = await fetch(url, {
|
|
||||||
credentials: "same-origin",
|
|
||||||
method: "POST",
|
|
||||||
body: JSON.stringify({
|
|
||||||
tokenCSRF: this.tokenCSRF,
|
|
||||||
token: this.apiToken,
|
|
||||||
authentication: this.apiAuth,
|
|
||||||
uuid: uuid,
|
|
||||||
title: title,
|
|
||||||
content: content
|
|
||||||
}),
|
|
||||||
headers: new Headers({
|
|
||||||
"Content-Type": "application/json"
|
|
||||||
}),
|
|
||||||
});
|
|
||||||
const json = await response.json();
|
|
||||||
return json.data.key;
|
|
||||||
}
|
|
||||||
catch (err) {
|
|
||||||
console.log(err);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static async saveAsDraft(uuid, title, content) {
|
|
||||||
let url = HTML_PATH_ADMIN_ROOT+"ajax/save-as-draft"
|
|
||||||
try {
|
|
||||||
const response = await fetch(url, {
|
|
||||||
credentials: 'same-origin',
|
|
||||||
method: "POST",
|
|
||||||
headers: new Headers({
|
|
||||||
'Content-Type': 'application/x-www-form-urlencoded;charset=UTF-8'
|
|
||||||
}),
|
|
||||||
body: new URLSearchParams({
|
|
||||||
'tokenCSRF': tokenCSRF,
|
|
||||||
'uuid': "autosave-" + uuid,
|
|
||||||
'title': title,
|
|
||||||
'content': content,
|
|
||||||
'type': 'autosave'
|
|
||||||
}),
|
|
||||||
});
|
|
||||||
const json = await response.json();
|
|
||||||
return json;
|
|
||||||
}
|
|
||||||
catch (err) {
|
|
||||||
console.log(err);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static async removeLogo() {
|
|
||||||
let url = HTML_PATH_ADMIN_ROOT+"ajax/logo-remove"
|
|
||||||
try {
|
|
||||||
const response = await fetch(url, {
|
|
||||||
credentials: 'same-origin',
|
|
||||||
method: "POST",
|
|
||||||
headers: new Headers({
|
|
||||||
'Content-Type': 'application/x-www-form-urlencoded;charset=UTF-8'
|
|
||||||
}),
|
|
||||||
body: new URLSearchParams({
|
|
||||||
'tokenCSRF': tokenCSRF
|
|
||||||
}),
|
|
||||||
});
|
|
||||||
const json = await response.json();
|
|
||||||
return json;
|
|
||||||
}
|
|
||||||
catch (err) {
|
|
||||||
console.log(err);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Alert the user when the user is not logged
|
|
||||||
userLogged(callBack) {
|
|
||||||
var ajaxRequest;
|
|
||||||
if (ajaxRequest) {
|
|
||||||
ajaxRequest.abort();
|
|
||||||
}
|
|
||||||
|
|
||||||
console.log("[INFO] [BLUDIT AJAX] [userLogged()] Checking if the user is logged.");
|
|
||||||
|
|
||||||
ajaxRequest = $.ajax({
|
|
||||||
type: "GET",
|
|
||||||
url: HTML_PATH_ADMIN_ROOT+"ajax/user-logged"
|
|
||||||
});
|
|
||||||
|
|
||||||
ajaxRequest.done(function (response, textStatus, jqXHR) {
|
|
||||||
console.log("[INFO] [BLUDIT AJAX] [userLogged()] The user is logged.");
|
|
||||||
});
|
|
||||||
|
|
||||||
ajaxRequest.fail(function (jqXHR, textStatus, errorThrown) {
|
|
||||||
// The fail is produced by admin.php when the user is not logged the ajax request is not possible and returns 401
|
|
||||||
console.log("[INFO] [BLUDIT AJAX] [userLogged()] The user is NOT logged.");
|
|
||||||
if (jqXHR.status==401) {
|
|
||||||
callBack("You are not logged in anymore, so Bludit can't save your settings and content.");
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
generateSlug(text, parentKey, currentKey, callBack) {
|
|
||||||
var ajaxRequest;
|
|
||||||
if (ajaxRequest) {
|
|
||||||
ajaxRequest.abort();
|
|
||||||
}
|
|
||||||
|
|
||||||
ajaxRequest = $.ajax({
|
|
||||||
type: "POST",
|
|
||||||
data: {
|
|
||||||
tokenCSRF: tokenCSRF,
|
|
||||||
text: text,
|
|
||||||
parentKey: parentKey,
|
|
||||||
currentKey: currentKey
|
|
||||||
},
|
|
||||||
url: HTML_PATH_ADMIN_ROOT+"ajax/generate-slug"
|
|
||||||
});
|
|
||||||
|
|
||||||
ajaxRequest.done(function (response, textStatus, jqXHR) {
|
|
||||||
console.log("Bludit AJAX: generateSlug(): done handler");
|
|
||||||
callBack.val(response["slug"]);
|
|
||||||
});
|
|
||||||
|
|
||||||
ajaxRequest.fail(function (jqXHR, textStatus, errorThrown) {
|
|
||||||
console.log("Bludit AJAX: generateSlug(): fail handler");
|
|
||||||
});
|
|
||||||
|
|
||||||
ajaxRequest.always(function () {
|
|
||||||
console.log("Bludit AJAX: generateSlug(): always handler");
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,371 +0,0 @@
|
||||||
var __assign = (this && this.__assign) || function () {
|
|
||||||
__assign = Object.assign || function(t) {
|
|
||||||
for (var s, i = 1, n = arguments.length; i < n; i++) {
|
|
||||||
s = arguments[i];
|
|
||||||
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
|
|
||||||
t[p] = s[p];
|
|
||||||
}
|
|
||||||
return t;
|
|
||||||
};
|
|
||||||
return __assign.apply(this, arguments);
|
|
||||||
};
|
|
||||||
var TokenAutocomplete = /** @class */ (function () {
|
|
||||||
function TokenAutocomplete(options) {
|
|
||||||
this.KEY_BACKSPACE = 8;
|
|
||||||
this.KEY_ENTER = 13;
|
|
||||||
this.KEY_UP = 38;
|
|
||||||
this.KEY_DOWN = 40;
|
|
||||||
this.defaults = {
|
|
||||||
name: '',
|
|
||||||
selector: '',
|
|
||||||
noMatchesText: null,
|
|
||||||
initialTokens: null,
|
|
||||||
initialSuggestions: null,
|
|
||||||
suggestionsUri: '',
|
|
||||||
suggestionRenderer: TokenAutocomplete.Autocomplete.defaultRenderer,
|
|
||||||
minCharactersForSuggestion: 1
|
|
||||||
};
|
|
||||||
this.options = __assign(__assign({}, this.defaults), options);
|
|
||||||
var passedContainer = document.querySelector(this.options.selector);
|
|
||||||
if (!passedContainer) {
|
|
||||||
throw new Error('passed selector does not point to a DOM element.');
|
|
||||||
}
|
|
||||||
this.container = passedContainer;
|
|
||||||
this.container.classList.add('token-autocomplete-container');
|
|
||||||
if (!Array.isArray(this.options.initialTokens) && !Array.isArray(this.options.initialSuggestions)) {
|
|
||||||
this.parseTokensAndSuggestions();
|
|
||||||
}
|
|
||||||
this.hiddenSelect = document.createElement('select');
|
|
||||||
this.hiddenSelect.id = this.container.id + '-select';
|
|
||||||
this.hiddenSelect.name = this.options.name;
|
|
||||||
this.hiddenSelect.setAttribute('multiple', 'true');
|
|
||||||
this.hiddenSelect.style.display = 'none';
|
|
||||||
this.textInput = document.createElement('span');
|
|
||||||
this.textInput.id = this.container.id + '-input';
|
|
||||||
this.textInput.classList.add('token-autocomplete-input');
|
|
||||||
this.textInput.setAttribute('data-placeholder', 'enter some text');
|
|
||||||
this.textInput.contentEditable = 'true';
|
|
||||||
this.container.appendChild(this.textInput);
|
|
||||||
this.container.appendChild(this.hiddenSelect);
|
|
||||||
this.select = new TokenAutocomplete.MultiSelect(this);
|
|
||||||
this.autocomplete = new TokenAutocomplete.Autocomplete(this);
|
|
||||||
this.debug(false);
|
|
||||||
var me = this;
|
|
||||||
if (Array.isArray(this.options.initialTokens)) {
|
|
||||||
this.options.initialTokens.forEach(function (token) {
|
|
||||||
if (typeof token === 'object') {
|
|
||||||
me.select.addToken(token.value, token.text);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
this.textInput.addEventListener('keydown', function (event) {
|
|
||||||
if (event.which == me.KEY_ENTER || event.keyCode == me.KEY_ENTER) {
|
|
||||||
event.preventDefault();
|
|
||||||
var highlightedSuggestion = me.autocomplete.suggestions.querySelector('.token-autocomplete-suggestion-highlighted');
|
|
||||||
if (highlightedSuggestion !== null) {
|
|
||||||
if (highlightedSuggestion.classList.contains('token-autocomplete-suggestion-active')) {
|
|
||||||
me.select.removeTokenWithText(highlightedSuggestion.textContent);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
me.select.addToken(highlightedSuggestion.getAttribute('data-value'), highlightedSuggestion.textContent);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
me.select.addToken(me.textInput.textContent, me.textInput.textContent);
|
|
||||||
}
|
|
||||||
me.clearCurrentInput();
|
|
||||||
}
|
|
||||||
else if (me.textInput.textContent === '' && (event.which == me.KEY_BACKSPACE || event.keyCode == me.KEY_BACKSPACE)) {
|
|
||||||
event.preventDefault();
|
|
||||||
me.select.removeLastToken();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
this.textInput.addEventListener('keyup', function (event) {
|
|
||||||
var _a, _b;
|
|
||||||
if ((event.which == me.KEY_UP || event.keyCode == me.KEY_UP) && me.autocomplete.suggestions.childNodes.length > 0) {
|
|
||||||
var highlightedSuggestion = me.autocomplete.suggestions.querySelector('.token-autocomplete-suggestion-highlighted');
|
|
||||||
var aboveSuggestion = (_a = highlightedSuggestion) === null || _a === void 0 ? void 0 : _a.previousSibling;
|
|
||||||
if (aboveSuggestion != null) {
|
|
||||||
me.autocomplete.highlightSuggestion(aboveSuggestion);
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if ((event.which == me.KEY_DOWN || event.keyCode == me.KEY_DOWN) && me.autocomplete.suggestions.childNodes.length > 0) {
|
|
||||||
var highlightedSuggestion = me.autocomplete.suggestions.querySelector('.token-autocomplete-suggestion-highlighted');
|
|
||||||
var belowSuggestion = (_b = highlightedSuggestion) === null || _b === void 0 ? void 0 : _b.nextSibling;
|
|
||||||
if (belowSuggestion != null) {
|
|
||||||
me.autocomplete.highlightSuggestion(belowSuggestion);
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
me.autocomplete.hideSuggestions();
|
|
||||||
me.autocomplete.clearSuggestions();
|
|
||||||
var value = me.textInput.textContent || '';
|
|
||||||
if (value.length >= me.options.minCharactersForSuggestion) {
|
|
||||||
if (Array.isArray(me.options.initialSuggestions)) {
|
|
||||||
me.options.initialSuggestions.forEach(function (suggestion) {
|
|
||||||
if (typeof suggestion !== 'object') {
|
|
||||||
// the suggestion is of wrong type and therefore ignored
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (value.localeCompare(suggestion.text.slice(0, value.length), undefined, { sensitivity: 'base' }) === 0) {
|
|
||||||
// The suggestion starts with the query text the user entered and will be displayed
|
|
||||||
me.autocomplete.addSuggestion(suggestion);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
if (me.autocomplete.suggestions.childNodes.length > 0) {
|
|
||||||
me.autocomplete.highlightSuggestionAtPosition(0);
|
|
||||||
}
|
|
||||||
else if (me.options.noMatchesText) {
|
|
||||||
me.autocomplete.addSuggestion({ value: '_no_match_', text: me.options.noMatchesText, description: null });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (me.options.suggestionsUri.length > 0) {
|
|
||||||
me.autocomplete.requestSuggestions(value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
this.container.tokenAutocomplete = this;
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* Searches the element given as a container for option elements and creates active tokens (when the option is marked selected)
|
|
||||||
* and suggestions (all options found) from these. During this all found options are removed from the DOM.
|
|
||||||
*/
|
|
||||||
TokenAutocomplete.prototype.parseTokensAndSuggestions = function () {
|
|
||||||
var initialTokens = [];
|
|
||||||
var initialSuggestions = [];
|
|
||||||
var options = this.container.querySelectorAll('option');
|
|
||||||
var me = this;
|
|
||||||
options.forEach(function (option) {
|
|
||||||
if (option.text != null) {
|
|
||||||
if (option.hasAttribute('selected')) {
|
|
||||||
initialTokens.push({ value: option.value, text: option.text });
|
|
||||||
}
|
|
||||||
initialSuggestions.push({ value: option.value, text: option.text, description: null });
|
|
||||||
}
|
|
||||||
me.container.removeChild(option);
|
|
||||||
});
|
|
||||||
if (initialTokens.length > 0) {
|
|
||||||
this.options.initialTokens = initialTokens;
|
|
||||||
}
|
|
||||||
if (initialSuggestions.length > 0) {
|
|
||||||
this.options.initialSuggestions = initialSuggestions;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
/**
|
|
||||||
* Clears the currently present tokens and creates new ones from the given input value.
|
|
||||||
*
|
|
||||||
* @param {(Array\|string)} value - either the name of a single token or a list of tokens to create
|
|
||||||
*/
|
|
||||||
TokenAutocomplete.prototype.val = function (value) {
|
|
||||||
this.select.clear();
|
|
||||||
if (Array.isArray(value)) {
|
|
||||||
var me_1 = this;
|
|
||||||
value.forEach(function (token) {
|
|
||||||
if (typeof token === 'object') {
|
|
||||||
me_1.select.addToken(token.value, token.text);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
this.select.addToken(value.value, value.text);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
TokenAutocomplete.prototype.clearCurrentInput = function () {
|
|
||||||
this.textInput.textContent = '';
|
|
||||||
};
|
|
||||||
TokenAutocomplete.prototype.debug = function (state) {
|
|
||||||
if (state) {
|
|
||||||
this.log = console.log.bind(window.console);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
this.log = function () { };
|
|
||||||
}
|
|
||||||
};
|
|
||||||
var _a;
|
|
||||||
TokenAutocomplete.MultiSelect = /** @class */ (function () {
|
|
||||||
function class_1(parent) {
|
|
||||||
this.parent = parent;
|
|
||||||
this.container = parent.container;
|
|
||||||
this.options = parent.options;
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* Adds a token with the specified name to the list of currently prensent tokens displayed to the user and the hidden select.
|
|
||||||
*
|
|
||||||
* @param {string} tokenText - the name of the token to create
|
|
||||||
*/
|
|
||||||
class_1.prototype.addToken = function (tokenValue, tokenText) {
|
|
||||||
if (tokenValue === null || tokenText === null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
var option = document.createElement('option');
|
|
||||||
option.text = tokenText;
|
|
||||||
option.value = tokenValue;
|
|
||||||
option.setAttribute('selected', 'true');
|
|
||||||
option.setAttribute('data-text', tokenText);
|
|
||||||
option.setAttribute('data-value', tokenValue);
|
|
||||||
this.parent.hiddenSelect.add(option);
|
|
||||||
var token = document.createElement('span');
|
|
||||||
token.classList.add('token-autocomplete-token');
|
|
||||||
token.setAttribute('data-text', tokenText);
|
|
||||||
option.setAttribute('data-value', tokenValue);
|
|
||||||
token.textContent = tokenText;
|
|
||||||
var deleteToken = document.createElement('span');
|
|
||||||
deleteToken.classList.add('token-autocomplete-token-delete');
|
|
||||||
deleteToken.textContent = '\u00D7';
|
|
||||||
token.appendChild(deleteToken);
|
|
||||||
var me = this;
|
|
||||||
deleteToken.addEventListener('click', function (event) {
|
|
||||||
me.removeToken(token);
|
|
||||||
});
|
|
||||||
this.container.insertBefore(token, this.parent.textInput.nextSibling);
|
|
||||||
this.parent.log('added token', token);
|
|
||||||
};
|
|
||||||
/**
|
|
||||||
* Completely clears the currently present tokens from the field.
|
|
||||||
*/
|
|
||||||
class_1.prototype.clear = function () {
|
|
||||||
var tokens = this.container.querySelectorAll('.token-autocomplete-token');
|
|
||||||
var me = this;
|
|
||||||
tokens.forEach(function (token) { me.removeToken(token); });
|
|
||||||
};
|
|
||||||
/**
|
|
||||||
* Removes the last token in the list of currently present token. This is the last added token next to the input field.
|
|
||||||
*/
|
|
||||||
class_1.prototype.removeLastToken = function () {
|
|
||||||
var tokens = this.container.querySelectorAll('.token-autocomplete-token');
|
|
||||||
var token = tokens[tokens.length - 1];
|
|
||||||
this.removeToken(token);
|
|
||||||
};
|
|
||||||
/**
|
|
||||||
* Removes the specified token from the list of currently present tokens.
|
|
||||||
*
|
|
||||||
* @param {Element} token - the token to remove
|
|
||||||
*/
|
|
||||||
class_1.prototype.removeToken = function (token) {
|
|
||||||
var _a, _b;
|
|
||||||
this.container.removeChild(token);
|
|
||||||
var tokenText = token.getAttribute('data-text');
|
|
||||||
var hiddenOption = this.parent.hiddenSelect.querySelector('option[data-text="' + tokenText + '"]');
|
|
||||||
(_b = (_a = hiddenOption) === null || _a === void 0 ? void 0 : _a.parentElement) === null || _b === void 0 ? void 0 : _b.removeChild(hiddenOption);
|
|
||||||
this.parent.log('removed token', token.textContent);
|
|
||||||
};
|
|
||||||
class_1.prototype.removeTokenWithText = function (tokenText) {
|
|
||||||
if (tokenText === null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
var token = this.container.querySelector('.token-autocomplete-token[data-text="' + tokenText + '"]');
|
|
||||||
if (token !== null) {
|
|
||||||
this.removeToken(token);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
return class_1;
|
|
||||||
}());
|
|
||||||
TokenAutocomplete.Autocomplete = (_a = /** @class */ (function () {
|
|
||||||
function class_2(parent) {
|
|
||||||
this.parent = parent;
|
|
||||||
this.container = parent.container;
|
|
||||||
this.options = parent.options;
|
|
||||||
this.renderer = parent.options.suggestionRenderer;
|
|
||||||
this.suggestions = document.createElement('ul');
|
|
||||||
this.suggestions.id = this.container.id + '-suggestions';
|
|
||||||
this.suggestions.classList.add('token-autocomplete-suggestions');
|
|
||||||
this.container.appendChild(this.suggestions);
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* Hides the suggestions dropdown from the user.
|
|
||||||
*/
|
|
||||||
class_2.prototype.hideSuggestions = function () {
|
|
||||||
this.suggestions.style.display = '';
|
|
||||||
};
|
|
||||||
/**
|
|
||||||
* Shows the suggestions dropdown to the user.
|
|
||||||
*/
|
|
||||||
class_2.prototype.showSuggestions = function () {
|
|
||||||
this.suggestions.style.display = 'block';
|
|
||||||
};
|
|
||||||
class_2.prototype.highlightSuggestionAtPosition = function (index) {
|
|
||||||
var suggestions = this.suggestions.querySelectorAll('li');
|
|
||||||
suggestions.forEach(function (suggestion) {
|
|
||||||
suggestion.classList.remove('token-autocomplete-suggestion-highlighted');
|
|
||||||
});
|
|
||||||
suggestions[index].classList.add('token-autocomplete-suggestion-highlighted');
|
|
||||||
};
|
|
||||||
class_2.prototype.highlightSuggestion = function (suggestion) {
|
|
||||||
this.suggestions.querySelectorAll('li').forEach(function (suggestion) {
|
|
||||||
suggestion.classList.remove('token-autocomplete-suggestion-highlighted');
|
|
||||||
});
|
|
||||||
suggestion.classList.add('token-autocomplete-suggestion-highlighted');
|
|
||||||
};
|
|
||||||
/**
|
|
||||||
* Removes all previous suggestions from the dropdown.
|
|
||||||
*/
|
|
||||||
class_2.prototype.clearSuggestions = function () {
|
|
||||||
this.suggestions.innerHTML = '';
|
|
||||||
};
|
|
||||||
/**
|
|
||||||
* Loads suggestions matching the given query from the rest service behind the URI given as an option while initializing the field.
|
|
||||||
*
|
|
||||||
* @param query the query to search suggestions for
|
|
||||||
*/
|
|
||||||
class_2.prototype.requestSuggestions = function (query) {
|
|
||||||
var me = this;
|
|
||||||
var request = new XMLHttpRequest();
|
|
||||||
request.onload = function () {
|
|
||||||
if (Array.isArray(request.response)) {
|
|
||||||
request.response.forEach(function (suggestion) {
|
|
||||||
me.addSuggestion(suggestion);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
request.open('GET', me.options.suggestionsUri + '?query=' + query, true);
|
|
||||||
request.responseType = 'json';
|
|
||||||
request.setRequestHeader('Content-type', 'application/json');
|
|
||||||
request.send();
|
|
||||||
};
|
|
||||||
/**
|
|
||||||
* Adds a suggestion with the given text matching the users input to the dropdown.
|
|
||||||
*
|
|
||||||
* @param {string} suggestionText - the text that should be displayed for the added suggestion
|
|
||||||
*/
|
|
||||||
class_2.prototype.addSuggestion = function (suggestion) {
|
|
||||||
var element = this.renderer(suggestion);
|
|
||||||
element.setAttribute('data-value', suggestion.value);
|
|
||||||
var me = this;
|
|
||||||
element.addEventListener('click', function (_event) {
|
|
||||||
if (suggestion.text == me.options.noMatchesText) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if (element.classList.contains('token-autocomplete-suggestion-active')) {
|
|
||||||
me.parent.select.removeTokenWithText(suggestion.text);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
me.parent.select.addToken(suggestion.value, suggestion.text);
|
|
||||||
}
|
|
||||||
me.clearSuggestions();
|
|
||||||
me.hideSuggestions();
|
|
||||||
me.parent.clearCurrentInput();
|
|
||||||
});
|
|
||||||
if (this.container.querySelector('.token-autocomplete-token[data-text="' + suggestion.text + '"]') !== null) {
|
|
||||||
element.classList.add('token-autocomplete-suggestion-active');
|
|
||||||
}
|
|
||||||
this.suggestions.appendChild(element);
|
|
||||||
this.showSuggestions();
|
|
||||||
me.parent.log('added suggestion', suggestion);
|
|
||||||
};
|
|
||||||
return class_2;
|
|
||||||
}()),
|
|
||||||
_a.defaultRenderer = function (suggestion) {
|
|
||||||
var option = document.createElement('li');
|
|
||||||
option.textContent = suggestion.text;
|
|
||||||
if (suggestion.description) {
|
|
||||||
var description = document.createElement('small');
|
|
||||||
description.textContent = suggestion.description;
|
|
||||||
description.classList.add('token-autocomplete-suggestion-description');
|
|
||||||
option.appendChild(description);
|
|
||||||
}
|
|
||||||
return option;
|
|
||||||
},
|
|
||||||
_a);
|
|
||||||
return TokenAutocomplete;
|
|
||||||
}());
|
|
Loading…
Add table
Reference in a new issue