init branch for Bludit v4
This commit is contained in:
parent
8c4a6d8f9e
commit
226750af09
29 changed files with 2027 additions and 635 deletions
|
@ -6,7 +6,7 @@ AddDefaultCharset UTF-8
|
|||
RewriteEngine on
|
||||
|
||||
# Base directory
|
||||
#RewriteBase /
|
||||
RewriteBase /
|
||||
|
||||
# Deny direct access to the next directories
|
||||
RewriteRule ^bl-content/(databases|workspaces|pages|tmp)/.*$ - [R=404,L]
|
||||
|
|
|
@ -19,8 +19,8 @@ checkRole(array('admin', 'editor', 'author'));
|
|||
// ============================================================================
|
||||
|
||||
if ($_SERVER['REQUEST_METHOD'] == 'POST') {
|
||||
createPage($_POST);
|
||||
Redirect::page('content');
|
||||
$pageKey = createPage($_POST);
|
||||
Redirect::page('edit-content/'.$pageKey);
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
a {
|
||||
color: #0078D4;
|
||||
color: #1A66A6;
|
||||
}
|
||||
|
||||
a:hover {
|
||||
color: #003f6f;
|
||||
color: #1A66A6;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
|
@ -12,7 +12,7 @@ a:hover {
|
|||
}
|
||||
|
||||
.text-primary {
|
||||
color: #0078D4!important;
|
||||
color: #06A8C5!important;
|
||||
}
|
||||
|
||||
.text-danger {
|
||||
|
@ -28,22 +28,61 @@ a.text-danger:hover {
|
|||
border-radius: 2px;
|
||||
}
|
||||
|
||||
.btn-sm {
|
||||
padding: .25rem .75rem;
|
||||
}
|
||||
|
||||
.btn-save {
|
||||
color: #5b8e09;
|
||||
}
|
||||
|
||||
.btn-save:hover {
|
||||
color: #466d07;
|
||||
}
|
||||
|
||||
.btn-cancel:hover {
|
||||
color: #000;
|
||||
}
|
||||
|
||||
.btn-primary {
|
||||
background-color: #0078D4;
|
||||
border-color: #0078D4;
|
||||
background-color: #0378D3;
|
||||
border-color: #0378D3;
|
||||
}
|
||||
|
||||
.btn-primary:hover {
|
||||
background-color: #4585CF;
|
||||
border-color: #4a90e2;
|
||||
background-color: #0270c4;
|
||||
border-color: #0270c4;
|
||||
}
|
||||
|
||||
.btn-light.focus, .btn-light:focus {
|
||||
box-shadow: none;
|
||||
.btn-primary-disabled {
|
||||
background-color: #71b6ff !important;
|
||||
border-color: #71b6ff !important;
|
||||
}
|
||||
|
||||
.btn.focus, .btn:focus {
|
||||
box-shadow: none;
|
||||
.btn-secondary {
|
||||
background-color: #6c757d;
|
||||
border-color: #6c757d;
|
||||
}
|
||||
|
||||
.btn-secondary:hover {
|
||||
background-color: #5a6268;
|
||||
border-color: #5a6268;
|
||||
}
|
||||
|
||||
.btn-light {
|
||||
color: #212529;
|
||||
background-color: #f3f3f3;
|
||||
border-color: #ced4d9;
|
||||
}
|
||||
|
||||
.btn-light.focus,
|
||||
.btn-light:focus {
|
||||
box-shadow: none !important;
|
||||
}
|
||||
|
||||
.btn.focus,
|
||||
.btn:focus {
|
||||
box-shadow: none !important;
|
||||
}
|
||||
|
||||
/* Form */
|
||||
|
@ -59,4 +98,14 @@ a.text-danger:hover {
|
|||
.table thead th {
|
||||
font-size: 0.8em;
|
||||
text-transform: uppercase!important;
|
||||
}
|
||||
|
||||
/* Right sidebar Options */
|
||||
#sidebarOptions .card {
|
||||
background: none;
|
||||
border: none;
|
||||
}
|
||||
|
||||
#sidebarOptions a {
|
||||
color: #212529;
|
||||
}
|
|
@ -1,19 +1,13 @@
|
|||
|
||||
html {
|
||||
height: 100%;
|
||||
font-size: 0.9rem;
|
||||
background: #fcfcfc;
|
||||
background: #EFEFEF;
|
||||
}
|
||||
|
||||
body {
|
||||
background: #fcfcfc;
|
||||
background: #EFEFEF;
|
||||
color: #1b1b1b;
|
||||
}
|
||||
|
||||
/* Prevent events in iframes */
|
||||
/* iframe{
|
||||
pointer-events: none;
|
||||
} */
|
||||
|
||||
/*
|
||||
ICONS
|
||||
*/
|
||||
|
@ -25,7 +19,6 @@ body {
|
|||
/*
|
||||
SIDEBAR
|
||||
*/
|
||||
|
||||
div.sidebar .nav-item a {
|
||||
padding-left:0;
|
||||
padding-right:0;
|
||||
|
@ -61,9 +54,9 @@ div.sidebar .nav-item h4 {
|
|||
BOOTSTRAP Hacks
|
||||
*/
|
||||
|
||||
@media (min-width: 1200px) {
|
||||
@media (min-width: 1300px) {
|
||||
.container {
|
||||
max-width: 1250px;
|
||||
max-width: 1350px;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -78,28 +71,6 @@ div.sidebar .nav-item h4 {
|
|||
}
|
||||
|
||||
|
||||
|
||||
|
||||
.btn-light {
|
||||
color: #212529;
|
||||
background-color: #f3f3f3;
|
||||
border-color: #ced4d9;
|
||||
}
|
||||
|
||||
.btn-form {
|
||||
background-color: #F3F3F3;
|
||||
border-color: #DDD;
|
||||
color: #000;
|
||||
}
|
||||
|
||||
.btn-form:hover {
|
||||
background-color: rgb(228, 228, 228);
|
||||
border-color: #DDD;
|
||||
color: #000;
|
||||
}
|
||||
|
||||
|
||||
|
||||
code {
|
||||
padding: 3px 5px 2px;
|
||||
margin: 0 1px;
|
||||
|
@ -112,28 +83,6 @@ code {
|
|||
cursor: pointer;
|
||||
}
|
||||
|
||||
.modal-body {
|
||||
padding: 2rem;
|
||||
}
|
||||
|
||||
.modal-footer {
|
||||
background-color: rgb(247, 247, 247);
|
||||
}
|
||||
|
||||
.modal-dialog .btn-link {
|
||||
color: #000;
|
||||
}
|
||||
|
||||
/*
|
||||
LOGIN
|
||||
*/
|
||||
|
||||
body.login {
|
||||
background: rgb(255,255,255);
|
||||
background: linear-gradient(0deg, rgba(255,255,255,1) 0%, rgba(250,250,250,1) 53%);
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
DASHBOARD
|
||||
|
@ -301,10 +250,10 @@ td.child {
|
|||
max-width: 100%;
|
||||
right: 0;
|
||||
}
|
||||
#jseditorToolbarRight button {
|
||||
#editorToolbarRight button {
|
||||
font-size: 0px !important;
|
||||
}
|
||||
#jseditorToolbarRight button span {
|
||||
#editorToolbarRight button span {
|
||||
font-size: 16px !important;
|
||||
}
|
||||
.contentTools .btn {
|
||||
|
@ -359,6 +308,16 @@ img.profilePicture {
|
|||
cursor: pointer;
|
||||
}
|
||||
|
||||
.switch-icon-publish {
|
||||
.switch-icon-published {
|
||||
color: #1cb11c;
|
||||
}
|
||||
|
||||
.switch-icon-draft {
|
||||
color: #b11c1c;
|
||||
}
|
||||
|
||||
.switch-icon-unlisted,
|
||||
.switch-icon-static,
|
||||
.switch-icon-sticky {
|
||||
color: #1c81b1;
|
||||
}
|
|
@ -25,10 +25,10 @@
|
|||
<?php $L->p('Content') ?></a>
|
||||
</li>
|
||||
<?php if (!checkRole(array('admin'),false)): ?>
|
||||
<li class="nav-item">
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="<?php echo HTML_PATH_ADMIN_ROOT.'edit-user/'.$login->username() ?>">
|
||||
<?php $L->p('Profile') ?></a>
|
||||
</li>
|
||||
<?php $L->p('Profile') ?></a>
|
||||
</li>
|
||||
<?php endif; ?>
|
||||
<?php if (checkRole(array('admin'),false)): ?>
|
||||
<li class="nav-item">
|
||||
|
@ -57,15 +57,15 @@
|
|||
</li>
|
||||
<?php endif; ?>
|
||||
<?php if (checkRole(array('admin'),false)): ?>
|
||||
<?php
|
||||
if (!empty($plugins['adminSidebar'])) {
|
||||
foreach ($plugins['adminSidebar'] as $pluginSidebar) {
|
||||
echo '<li class="nav-item">';
|
||||
echo $pluginSidebar->adminSidebar();
|
||||
echo '</li>';
|
||||
<?php
|
||||
if (!empty($plugins['adminSidebar'])) {
|
||||
foreach ($plugins['adminSidebar'] as $pluginSidebar) {
|
||||
echo '<li class="nav-item">';
|
||||
echo $pluginSidebar->adminSidebar();
|
||||
echo '</li>';
|
||||
}
|
||||
}
|
||||
}
|
||||
?>
|
||||
?>
|
||||
<?php endif; ?>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="<?php echo HTML_PATH_ADMIN_ROOT.'logout' ?>">
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<html class="h-100">
|
||||
<hea>
|
||||
<title><?php echo $layout['title'] ?></title>
|
||||
<meta charset="<?php echo CHARSET ?>">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
|
||||
|
@ -21,7 +21,8 @@
|
|||
echo Theme::css(array(
|
||||
'jquery.datetimepicker.min.css',
|
||||
'select2.min.css',
|
||||
'select2-bootstrap4.min.css'
|
||||
'select2-bootstrap4.min.css',
|
||||
'token-autocomplete.css'
|
||||
), DOMAIN_CORE_CSS);
|
||||
?>
|
||||
|
||||
|
@ -33,7 +34,9 @@
|
|||
echo Theme::js(array(
|
||||
'jquery.datetimepicker.full.min.js',
|
||||
'select2.full.min.js',
|
||||
'functions.js'
|
||||
'token-autocomplete.js',
|
||||
'functions.js',
|
||||
'api.js'
|
||||
), DOMAIN_CORE_JS, null);
|
||||
?>
|
||||
|
||||
|
|
|
@ -209,7 +209,7 @@ EOF;
|
|||
|
||||
$html = '<div class="form-group m-0">';
|
||||
if (!empty($args['label'])) {
|
||||
$html .= '<label class="mt-4 mb-2 pb-2 border-bottom text-uppercase w-100" for="'.$id.'">'.$args['label'].'</label>';
|
||||
$html .= '<h6 class="mt-4 mb-2 pb-2 text-uppercase">'.$args['label'].'</h6>';
|
||||
}
|
||||
|
||||
$html .= '<textarea class="'.$class.'" id="'.$id.'" name="'.$args['name'].'" rows="'.$args['rows'].'" placeholder="'.$args['placeholder'].'">'.$args['value'].'</textarea>';
|
||||
|
@ -361,7 +361,7 @@ EOF;
|
|||
$html = '<div class="form-group m-0">';
|
||||
|
||||
if (!empty($args['label'])) {
|
||||
$html .= '<label class="mt-4 mb-2 pb-2 border-bottom text-uppercase w-100" for="'.$id.'">'.$args['label'].'</label>';
|
||||
$html .= '<h6 class="mt-4 mb-2 pb-2 text-uppercase">'.$args['label'].'</h6>';
|
||||
}
|
||||
|
||||
$html .= '<select id="'.$id.'" name="'.$args['name'].'" class="'.$class.'">';
|
||||
|
@ -382,7 +382,7 @@ EOF;
|
|||
|
||||
public static function formInputHidden($args)
|
||||
{
|
||||
return '<input type="hidden" id="js'.$args['name'].'" name="'.$args['name'].'" value="'.$args['value'].'">';
|
||||
return '<input type="hidden" id="'.$args['name'].'" name="'.$args['name'].'" value="'.$args['value'].'">';
|
||||
}
|
||||
|
||||
public static function alert($args)
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<html class="h-100">
|
||||
<head>
|
||||
<title>Bludit</title>
|
||||
<meta charset="<?php echo CHARSET ?>">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
|
||||
<meta name="robots" content="noindex,nofollow">
|
||||
<meta name="robots" content="noindex, nofollow">
|
||||
|
||||
<!-- Favicon -->
|
||||
<link rel="shortcut icon" type="image/x-icon" href="<?php echo HTML_PATH_CORE_IMG.'favicon.png?version='.BLUDIT_VERSION ?>">
|
||||
|
@ -27,7 +27,7 @@
|
|||
<!-- Plugins -->
|
||||
<?php Theme::plugins('loginHead') ?>
|
||||
</head>
|
||||
<body class="login">
|
||||
<body class="h-100">
|
||||
|
||||
<!-- Plugins -->
|
||||
<?php Theme::plugins('loginBodyBegin') ?>
|
||||
|
@ -35,9 +35,9 @@
|
|||
<!-- Alert -->
|
||||
<?php include('html/alert.php'); ?>
|
||||
|
||||
<div class="container">
|
||||
<div class="row justify-content-md-center pt-5">
|
||||
<div class="col-md-4 pt-5">
|
||||
<div class="container h-100">
|
||||
<div class="row h-100 justify-content-center align-items-center">
|
||||
<div class="col-8 col-md-6 col-lg-4">
|
||||
<?php
|
||||
if (Sanitize::pathFile(PATH_ADMIN_VIEWS, $layout['view'].'.php')) {
|
||||
include(PATH_ADMIN_VIEWS.$layout['view'].'.php');
|
||||
|
@ -51,4 +51,4 @@
|
|||
<?php Theme::plugins('loginBodyEnd') ?>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
</html>
|
|
@ -1 +1 @@
|
|||
Subproject commit 09138280134ec8014982f260c1ebb6a5b0091fdc
|
||||
Subproject commit c97e1681dda9576128298d299e42973646df2475
|
|
@ -47,8 +47,8 @@ echo Bootstrap::formOpen(array(
|
|||
?>
|
||||
|
||||
<!-- TOOLBAR -->
|
||||
<div id="jseditorToolbar" class="mb-1">
|
||||
<div id="jseditorToolbarRight" class="btn-group btn-group-sm float-right" role="group" aria-label="Toolbar right">
|
||||
<div id="editorToolbar" class="mb-1">
|
||||
<div id="editorToolbarRight" class="btn-group btn-group-sm float-right" role="group" aria-label="Toolbar right">
|
||||
<button type="button" class="btn btn-light" id="jsmediaManagerOpenModal" data-toggle="modal" data-target="#jsmediaManagerModal"><span class="fa fa-image"></span> <?php $L->p('Images') ?></button>
|
||||
<button type="button" class="btn btn-light" id="jsoptionsSidebar" style="z-index:30"><span class="fa fa-cog"></span> <?php $L->p('Options') ?></button>
|
||||
</div>
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
<?php defined('BLUDIT') or die('Bludit CMS.');
|
||||
|
||||
echo '<h1 class="text-center mb-5 mt-5 font-weight-normal" style="color: #555;">BLUDIT</h1>';
|
||||
echo '<h1 class="text-center font-weight-normal mb-5" style="color: #555;">'.$site->title().'</h1>';
|
||||
|
||||
echo Bootstrap::formOpen(array());
|
||||
|
||||
|
@ -11,7 +11,7 @@ echo Bootstrap::formOpen(array());
|
|||
|
||||
echo '
|
||||
<div class="form-group">
|
||||
<input type="text" value="'.(isset($_POST['username'])?htmlspecialchars($_POST['username']):'').'" class="form-control form-control-lg" id="jsusername" name="username" placeholder="'.$L->g('Username').'" autofocus>
|
||||
<input type="text" value="'.(isset($_POST['username'])?Sanitize::html($_POST['username']):'').'" class="form-control form-control-lg" id="jsusername" name="username" placeholder="'.$L->g('Username').'" autofocus>
|
||||
</div>
|
||||
';
|
||||
|
||||
|
@ -34,4 +34,6 @@ echo Bootstrap::formOpen(array());
|
|||
|
||||
echo '</form>';
|
||||
|
||||
echo '<p class="mt-5 text-right">Powered by <a href="https://www.bludit.com">Bludit</a></p>'
|
||||
|
||||
?>
|
||||
|
|
|
@ -1,454 +1,121 @@
|
|||
<?php defined('BLUDIT') or die('Bludit CMS.'); ?>
|
||||
|
||||
<?php
|
||||
|
||||
// Start form
|
||||
echo Bootstrap::formOpen(array(
|
||||
'id'=>'jsform',
|
||||
'class'=>'d-flex flex-column h-100'
|
||||
));
|
||||
|
||||
// Token CSRF
|
||||
echo Bootstrap::formInputHidden(array(
|
||||
'name'=>'tokenCSRF',
|
||||
'value'=>$security->getTokenCSRF()
|
||||
));
|
||||
|
||||
// UUID
|
||||
// The UUID is generated in the controller
|
||||
echo Bootstrap::formInputHidden(array(
|
||||
'name'=>'uuid',
|
||||
'value'=>$uuid
|
||||
));
|
||||
|
||||
// Type = published, draft, sticky, static
|
||||
echo Bootstrap::formInputHidden(array(
|
||||
'name'=>'type',
|
||||
'value'=>'published'
|
||||
));
|
||||
|
||||
// Cover image
|
||||
echo Bootstrap::formInputHidden(array(
|
||||
'name'=>'coverImage',
|
||||
'value'=>''
|
||||
));
|
||||
|
||||
// Content
|
||||
echo Bootstrap::formInputHidden(array(
|
||||
'name'=>'content',
|
||||
'value'=>''
|
||||
));
|
||||
?>
|
||||
|
||||
<!-- TOOLBAR -->
|
||||
<div id="jseditorToolbar" class="mb-1">
|
||||
<div id="jseditorToolbarRight" class="btn-group btn-group-sm float-right" role="group" aria-label="Toolbar right">
|
||||
<button type="button" class="btn btn-light" id="jsmediaManagerOpenModal" data-toggle="modal" data-target="#jsmediaManagerModal"><span class="fa fa-image"></span> <?php $L->p('Images') ?></button>
|
||||
<button type="button" class="btn btn-light" id="jsoptionsSidebar" style="z-index:30"><span class="fa fa-cog"></span> <?php $L->p('Options') ?></button>
|
||||
</div>
|
||||
|
||||
<div id="jseditorToolbarLeft">
|
||||
<button id="jsbuttonSave" type="button" class="btn btn-sm btn-primary" ><?php $L->p('Save') ?></button>
|
||||
<button id="jsbuttonPreview" type="button" class="btn btn-sm btn-secondary"><?php $L->p('Preview') ?></button>
|
||||
<span id="jsbuttonSwitch" data-switch="publish" class="ml-2 text-secondary switch-button"><i class="fa fa-square switch-icon-publish"></i> <?php $L->p('Publish') ?></span>
|
||||
</div>
|
||||
</div>
|
||||
<script>
|
||||
$(document).ready(function() {
|
||||
$("#jsoptionsSidebar").on("click", function() {
|
||||
$("#jseditorSidebar").toggle();
|
||||
$("#jsshadow").toggle();
|
||||
// ----------------------------------------------------------------------------
|
||||
// Variables for the view
|
||||
// ----------------------------------------------------------------------------
|
||||
var _pageKey = null; // The page key is generated the first time the user click on the button "Save"
|
||||
var _uuid = '<?php echo $uuid ?>'; // The UUID is generated at the begining if the user uploaded files to the page
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Functions for the view
|
||||
// ----------------------------------------------------------------------------
|
||||
// Default function for the editor area (textarea)
|
||||
// This helps if the user doesn't activate any plugin as editor
|
||||
if (typeof editorGetContent != 'function') {
|
||||
window.editorGetContent = function(){
|
||||
return $('#editor').val();
|
||||
};
|
||||
}
|
||||
if (typeof editorInsertMedia != 'function') {
|
||||
window.editorInsertMedia = function(filename){
|
||||
$('#editor').val($('#editor').val()+'<img src="'+filename+'" alt="">');
|
||||
};
|
||||
}
|
||||
|
||||
// Creates or save the page
|
||||
// This function set the global variable "_pageKey"
|
||||
function save(args) {
|
||||
args['uuid'] = _uuid;
|
||||
// If the "page key" doesn't exists means the page not was created
|
||||
// Create the page to generate a "page key"
|
||||
if (_pageKey == null) {
|
||||
logs('Creating page');
|
||||
api.createPage(args).then(function(key) {
|
||||
logs('Page created. Key: '+key);
|
||||
// Set the global variable with the page key
|
||||
_pageKey = key;
|
||||
// Disable the button save and change text
|
||||
//$("#btnSave").attr("disabled", true).html("Saved");
|
||||
});
|
||||
|
||||
$("#jsshadow").on("click", function() {
|
||||
$("#jseditorSidebar").toggle();
|
||||
$("#jsshadow").toggle();
|
||||
} else {
|
||||
logs('Saving page');
|
||||
args['pageKey'] = _pageKey;
|
||||
api.savePage(args).then(function(key) {
|
||||
logs('Page saved. Old key: '+_pageKey+' / New key: '+key);
|
||||
// Set the global variable with the page key
|
||||
// The page key can change after save the page so you need to set again the variable
|
||||
_pageKey = key;
|
||||
// Disable the button save and change text
|
||||
//$("#btnSave").attr("disabled", true).html("Saved");
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
||||
<!-- SIDEBAR OPTIONS -->
|
||||
<div id="jseditorSidebar">
|
||||
<nav>
|
||||
<div class="nav nav-tabs" id="nav-tab" role="tablist">
|
||||
<a class="nav-link active show" id="nav-general-tab" data-toggle="tab" href="#nav-general" role="tab" aria-controls="general"><?php $L->p('General') ?></a>
|
||||
<a class="nav-link" id="nav-advanced-tab" data-toggle="tab" href="#nav-advanced" role="tab" aria-controls="advanced"><?php $L->p('Advanced') ?></a>
|
||||
<?php if (!empty($site->customFields())): ?>
|
||||
<a class="nav-link" id="nav-custom-tab" data-toggle="tab" href="#nav-custom" role="tab" aria-controls="custom"><?php $L->p('Custom') ?></a>
|
||||
<?php endif ?>
|
||||
<a class="nav-link" id="nav-seo-tab" data-toggle="tab" href="#nav-seo" role="tab" aria-controls="seo"><?php $L->p('SEO') ?></a>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<div class="tab-content pr-3 pl-3 pb-3">
|
||||
<div id="nav-general" class="tab-pane fade show active" role="tabpanel" aria-labelledby="general-tab">
|
||||
<?php
|
||||
// Category
|
||||
echo Bootstrap::formSelectBlock(array(
|
||||
'name'=>'category',
|
||||
'label'=>$L->g('Category'),
|
||||
'selected'=>'',
|
||||
'class'=>'',
|
||||
'emptyOption'=>'- '.$L->g('Uncategorized').' -',
|
||||
'options'=>$categories->getKeyNameArray()
|
||||
));
|
||||
|
||||
// Description
|
||||
echo Bootstrap::formTextareaBlock(array(
|
||||
'name'=>'description',
|
||||
'label'=>$L->g('Description'),
|
||||
'selected'=>'',
|
||||
'class'=>'',
|
||||
'value'=>'',
|
||||
'rows'=>5,
|
||||
'placeholder'=>$L->get('this-field-can-help-describe-the-content')
|
||||
));
|
||||
?>
|
||||
|
||||
<!-- Cover Image -->
|
||||
<label class="mt-4 mb-2 pb-2 border-bottom text-uppercase w-100"><?php $L->p('Cover Image') ?></label>
|
||||
<div>
|
||||
<img id="jscoverImagePreview" class="mx-auto d-block w-100" alt="Cover image preview" src="<?php echo HTML_PATH_CORE_IMG ?>default.svg" />
|
||||
</div>
|
||||
<div class="mt-2 text-center">
|
||||
<button type="button" id="jsbuttonSelectCoverImage" class="btn btn-primary btn-sm"><?php echo $L->g('Select cover image') ?></button>
|
||||
<button type="button" id="jsbuttonRemoveCoverImage" class="btn btn-secondary btn-sm"><?php echo $L->g('Remove cover image') ?></button>
|
||||
</div>
|
||||
<script>
|
||||
$(document).ready(function() {
|
||||
$("#jscoverImagePreview").on("click", function() {
|
||||
openMediaManager();
|
||||
});
|
||||
|
||||
$("#jsbuttonSelectCoverImage").on("click", function() {
|
||||
openMediaManager();
|
||||
});
|
||||
|
||||
$("#jsbuttonRemoveCoverImage").on("click", function() {
|
||||
$("#jscoverImage").val('');
|
||||
$("#jscoverImagePreview").attr('src', HTML_PATH_CORE_IMG+'default.svg');
|
||||
});
|
||||
});
|
||||
</script>
|
||||
</div>
|
||||
<div id="nav-advanced" class="tab-pane fade" role="tabpanel" aria-labelledby="advanced-tab">
|
||||
<?php
|
||||
// Date
|
||||
echo Bootstrap::formInputTextBlock(array(
|
||||
'name'=>'date',
|
||||
'label'=>$L->g('Date'),
|
||||
'placeholder'=>'',
|
||||
'value'=>Date::current(DB_DATE_FORMAT),
|
||||
'tip'=>$L->g('date-format-format')
|
||||
));
|
||||
|
||||
// Type
|
||||
echo Bootstrap::formSelectBlock(array(
|
||||
'name'=>'typeSelector',
|
||||
'label'=>$L->g('Type'),
|
||||
'selected'=>'',
|
||||
'options'=>array(
|
||||
'published'=>'- '.$L->g('Default').' -',
|
||||
'sticky'=>$L->g('Sticky'),
|
||||
'static'=>$L->g('Static')
|
||||
),
|
||||
'tip'=>''
|
||||
));
|
||||
|
||||
// Position
|
||||
echo Bootstrap::formInputTextBlock(array(
|
||||
'name'=>'position',
|
||||
'label'=>$L->g('Position'),
|
||||
'tip'=>$L->g('Field used when ordering content by position'),
|
||||
'value'=>$pages->nextPositionNumber()
|
||||
));
|
||||
|
||||
// Tags
|
||||
echo Bootstrap::formInputTextBlock(array(
|
||||
'name'=>'tags',
|
||||
'label'=>$L->g('Tags'),
|
||||
'placeholder'=>'',
|
||||
'tip'=>$L->g('Write the tags separated by comma')
|
||||
));
|
||||
|
||||
// Parent
|
||||
echo Bootstrap::formSelectBlock(array(
|
||||
'name'=>'parent',
|
||||
'label'=>$L->g('Parent'),
|
||||
'options'=>array(),
|
||||
'selected'=>false,
|
||||
'class'=>'',
|
||||
'tip'=>$L->g('Start typing a page title to see a list of suggestions.'),
|
||||
));
|
||||
|
||||
?>
|
||||
|
||||
<script>
|
||||
$(document).ready(function() {
|
||||
var parent = $("#jsparent").select2({
|
||||
placeholder: "",
|
||||
allowClear: true,
|
||||
theme: "bootstrap4",
|
||||
minimumInputLength: 2,
|
||||
ajax: {
|
||||
url: HTML_PATH_ADMIN_ROOT+"ajax/get-published",
|
||||
data: function (params) {
|
||||
var query = {
|
||||
checkIsParent: true,
|
||||
query: params.term
|
||||
}
|
||||
return query;
|
||||
},
|
||||
processResults: function (data) {
|
||||
return data;
|
||||
}
|
||||
},
|
||||
escapeMarkup: function(markup) {
|
||||
return markup;
|
||||
},
|
||||
templateResult: function(data) {
|
||||
var html = data.text;
|
||||
if (data.type=="static") {
|
||||
html += '<span class="badge badge-pill badge-light">'+data.type+'</span>';
|
||||
}
|
||||
return html;
|
||||
}
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
||||
<?php
|
||||
// Template
|
||||
echo Bootstrap::formInputTextBlock(array(
|
||||
'name'=>'template',
|
||||
'label'=>$L->g('Template'),
|
||||
'placeholder'=>'',
|
||||
'value'=>'',
|
||||
'tip'=>$L->g('Write a template name to filter the page in the theme and change the style of the page.')
|
||||
));
|
||||
|
||||
echo Bootstrap::formInputTextBlock(array(
|
||||
'name'=>'externalCoverImage',
|
||||
'label'=>$L->g('External cover image'),
|
||||
'placeholder'=>"https://",
|
||||
'value'=>'',
|
||||
'tip'=>$L->g('Set a cover image from external URL, such as a CDN or some server dedicated for images.')
|
||||
));
|
||||
|
||||
// Username
|
||||
echo Bootstrap::formInputTextBlock(array(
|
||||
'name'=>'',
|
||||
'label'=>$L->g('Author'),
|
||||
'placeholder'=>'',
|
||||
'value'=>$login->username(),
|
||||
'tip'=>'',
|
||||
'disabled'=>true
|
||||
));
|
||||
?>
|
||||
|
||||
<script>
|
||||
$(document).ready(function() {
|
||||
|
||||
// Changes in External cover image input
|
||||
$("#jsexternalCoverImage").change(function() {
|
||||
$("#jscoverImage").val( $(this).val() );
|
||||
});
|
||||
|
||||
// Generate slug when the user type the title
|
||||
$("#jstitle").keyup(function() {
|
||||
var text = $(this).val();
|
||||
var parent = $("#jsparent").val();
|
||||
var currentKey = "";
|
||||
var ajax = new bluditAjax();
|
||||
var callBack = $("#jsslug");
|
||||
ajax.generateSlug(text, parent, currentKey, callBack);
|
||||
});
|
||||
|
||||
// Datepicker
|
||||
$("#jsdate").datetimepicker({format:DB_DATE_FORMAT});
|
||||
|
||||
|
||||
});
|
||||
</script>
|
||||
</div>
|
||||
<?php if (!empty($site->customFields())): ?>
|
||||
<div id="nav-custom" class="tab-pane fade" role="tabpanel" aria-labelledby="custom-tab">
|
||||
<?php
|
||||
$customFields = $site->customFields();
|
||||
foreach ($customFields as $field=>$options) {
|
||||
if ( !isset($options['position']) ) {
|
||||
if ($options['type']=="string") {
|
||||
echo Bootstrap::formInputTextBlock(array(
|
||||
'name'=>'custom['.$field.']',
|
||||
'label'=>(isset($options['label'])?$options['label']:''),
|
||||
'value'=>(isset($options['default'])?$options['default']:''),
|
||||
'tip'=>(isset($options['tip'])?$options['tip']:''),
|
||||
'placeholder'=>(isset($options['placeholder'])?$options['placeholder']:'')
|
||||
));
|
||||
} elseif ($options['type']=="bool") {
|
||||
echo Bootstrap::formCheckbox(array(
|
||||
'name'=>'custom['.$field.']',
|
||||
'label'=>(isset($options['label'])?$options['label']:''),
|
||||
'placeholder'=>(isset($options['placeholder'])?$options['placeholder']:''),
|
||||
'checked'=>(isset($options['checked'])?true:false),
|
||||
'labelForCheckbox'=>(isset($options['tip'])?$options['tip']:'')
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
?>
|
||||
</div>
|
||||
<?php endif ?>
|
||||
<div id="nav-seo" class="tab-pane fade" role="tabpanel" aria-labelledby="seo-tab">
|
||||
<?php
|
||||
// Friendly URL
|
||||
echo Bootstrap::formInputTextBlock(array(
|
||||
'name'=>'slug',
|
||||
'tip'=>$L->g('URL associated with the content'),
|
||||
'label'=>$L->g('Friendly URL'),
|
||||
'placeholder'=>$L->g('Leave empty for autocomplete by Bludit.')
|
||||
));
|
||||
|
||||
// Robots
|
||||
echo Bootstrap::formCheckbox(array(
|
||||
'name'=>'noindex',
|
||||
'label'=>'Robots',
|
||||
'labelForCheckbox'=>$L->g('apply-code-noindex-code-to-this-page'),
|
||||
'placeholder'=>'',
|
||||
'checked'=>false,
|
||||
'tip'=>$L->g('This tells search engines not to show this page in their search results.')
|
||||
));
|
||||
|
||||
// Robots
|
||||
echo Bootstrap::formCheckbox(array(
|
||||
'name'=>'nofollow',
|
||||
'label'=>'',
|
||||
'labelForCheckbox'=>$L->g('apply-code-nofollow-code-to-this-page'),
|
||||
'placeholder'=>'',
|
||||
'checked'=>false,
|
||||
'tip'=>$L->g('This tells search engines not to follow links on this page.')
|
||||
));
|
||||
|
||||
// Robots
|
||||
echo Bootstrap::formCheckbox(array(
|
||||
'name'=>'noarchive',
|
||||
'label'=>'',
|
||||
'labelForCheckbox'=>$L->g('apply-code-noarchive-code-to-this-page'),
|
||||
'placeholder'=>'',
|
||||
'checked'=>false,
|
||||
'tip'=>$L->g('This tells search engines not to save a cached copy of this page.')
|
||||
));
|
||||
?>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Custom fields: TOP -->
|
||||
<?php
|
||||
$customFields = $site->customFields();
|
||||
foreach ($customFields as $field=>$options) {
|
||||
if ( isset($options['position']) && ($options['position']=='top') ) {
|
||||
if ($options['type']=="string") {
|
||||
echo Bootstrap::formInputTextBlock(array(
|
||||
'name'=>'custom['.$field.']',
|
||||
'label'=>(isset($options['label'])?$options['label']:''),
|
||||
'value'=>(isset($options['default'])?$options['default']:''),
|
||||
'tip'=>(isset($options['tip'])?$options['tip']:''),
|
||||
'placeholder'=>(isset($options['placeholder'])?$options['placeholder']:''),
|
||||
'class'=>'mb-2',
|
||||
'labelClass'=>'mb-2 pb-2 border-bottom text-uppercase w-100'
|
||||
|
||||
));
|
||||
} elseif ($options['type']=="bool") {
|
||||
echo Bootstrap::formCheckbox(array(
|
||||
'name'=>'custom['.$field.']',
|
||||
'label'=>(isset($options['label'])?$options['label']:''),
|
||||
'placeholder'=>(isset($options['placeholder'])?$options['placeholder']:''),
|
||||
'checked'=>(isset($options['checked'])?true:false),
|
||||
'labelForCheckbox'=>(isset($options['tip'])?$options['tip']:''),
|
||||
'class'=>'mb-2',
|
||||
'labelClass'=>'mb-2 pb-2 border-bottom text-uppercase w-100'
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
?>
|
||||
|
||||
// Close all modals
|
||||
$('.modal').modal('hide');
|
||||
return true;
|
||||
}
|
||||
|
||||
<!-- Title -->
|
||||
<div id="jseditorTitle" class="form-group mb-1">
|
||||
<input id="jstitle" name="title" type="text" class="form-control form-control-lg rounded-0" value="" placeholder="<?php $L->p('Enter title') ?>">
|
||||
</div>
|
||||
// Open the modal and store the current value
|
||||
// The current value is store to recover it if the user click in the button "Cancel"
|
||||
function openModal(fieldName) {
|
||||
var value = $('#'+fieldName).val();
|
||||
localStorage.setItem(fieldName, value);
|
||||
$('#modal-'+fieldName).modal('show');
|
||||
}
|
||||
|
||||
<!-- Editor -->
|
||||
<textarea id="jseditor" class="editable h-100 mb-1"></textarea>
|
||||
// Close the modal when the user click in the button "Cancel"
|
||||
// The function also recover the old value
|
||||
function closeModal(fieldName) {
|
||||
var value = localStorage.getItem(fieldName);
|
||||
$('#'+fieldName).val(value);
|
||||
$('#modal-'+fieldName).modal('hide');
|
||||
}
|
||||
|
||||
<!-- Custom fields: BOTTOM -->
|
||||
<?php
|
||||
$customFields = $site->customFields();
|
||||
foreach ($customFields as $field=>$options) {
|
||||
if ( isset($options['position']) && ($options['position']=='bottom') ) {
|
||||
if ($options['type']=="string") {
|
||||
echo Bootstrap::formInputTextBlock(array(
|
||||
'name'=>'custom['.$field.']',
|
||||
'label'=>(isset($options['label'])?$options['label']:''),
|
||||
'value'=>(isset($options['default'])?$options['default']:''),
|
||||
'tip'=>(isset($options['tip'])?$options['tip']:''),
|
||||
'placeholder'=>(isset($options['placeholder'])?$options['placeholder']:''),
|
||||
'class'=>'mt-2',
|
||||
'labelClass'=>'mb-2 pb-2 border-bottom text-uppercase w-100'
|
||||
// This function is to catch all key press
|
||||
// Provides Shortcuts
|
||||
// The editor plugin need to call this function for the event "keydown"
|
||||
function keypress(event) {
|
||||
console.log(event);
|
||||
|
||||
));
|
||||
} elseif ($options['type']=="bool") {
|
||||
echo Bootstrap::formCheckbox(array(
|
||||
'name'=>'custom['.$field.']',
|
||||
'label'=>(isset($options['label'])?$options['label']:''),
|
||||
'placeholder'=>(isset($options['placeholder'])?$options['placeholder']:''),
|
||||
'checked'=>(isset($options['checked'])?true:false),
|
||||
'labelForCheckbox'=>(isset($options['tip'])?$options['tip']:''),
|
||||
'class'=>'mt-2',
|
||||
'labelClass'=>'mb-2 pb-2 border-bottom text-uppercase w-100'
|
||||
));
|
||||
}
|
||||
// Shortcuts
|
||||
// ------------------------------------------------------------------------
|
||||
// Ctrl+S or Command+S
|
||||
if ((event.ctrlKey || event.metaKey) && event.which == 83) {
|
||||
var args = {
|
||||
title: $('#title').val(),
|
||||
content: editorGetContent()
|
||||
}
|
||||
save(args);
|
||||
$('#btnSave').addClass('btn-primary-disabled').html('<?php $L->p('Saved') ?>');
|
||||
event.preventDefault();
|
||||
return false;
|
||||
}
|
||||
?>
|
||||
|
||||
</form>
|
||||
$('#btnSave').removeClass('btn-primary-disabled').html('<?php $L->p('Save') ?>');
|
||||
}
|
||||
|
||||
<!-- Modal for Media Manager -->
|
||||
<?php include(PATH_ADMIN_THEMES.'booty/html/media.php'); ?>
|
||||
|
||||
<script>
|
||||
// ----------------------------------------------------------------------------
|
||||
// Events for the view
|
||||
// ----------------------------------------------------------------------------
|
||||
$(document).ready(function() {
|
||||
|
||||
// Define function if they doesn't exist
|
||||
// This helps if the user doesn't activate any plugin as editor
|
||||
if (typeof editorGetContent != "function") {
|
||||
window.editorGetContent = function(){
|
||||
return $("#jseditor").val();
|
||||
};
|
||||
}
|
||||
if (typeof editorInsertMedia != "function") {
|
||||
window.editorInsertMedia = function(filename){
|
||||
$("#jseditor").val($('#jseditor').val()+'<img src="'+filename+'" alt="">');
|
||||
};
|
||||
}
|
||||
|
||||
// Button switch
|
||||
$("#jsbuttonSwitch").on("click", function() {
|
||||
if ($(this).data("switch")=="publish") {
|
||||
$(this).html('<i class="fa fa-square switch-icon-draft"></i> <?php $L->p('Draft') ?>');
|
||||
$(this).data("switch", "draft");
|
||||
} else {
|
||||
$(this).html('<i class="fa fa-square switch-icon-publish"></i> <?php $L->p('Publish') ?>');
|
||||
$(this).data("switch", "publish");
|
||||
}
|
||||
// Main interface events
|
||||
// ------------------------------------------------------------------------
|
||||
$(this).keydown(function(event){
|
||||
keypress(event);
|
||||
});
|
||||
|
||||
// Button preview
|
||||
$("#jsbuttonPreview").on("click", function() {
|
||||
var uuid = $("#jsuuid").val();
|
||||
$('#btnSave').on('click', function() {
|
||||
var args = {
|
||||
title: $('#title').val(),
|
||||
content: editorGetContent()
|
||||
}
|
||||
save(args);
|
||||
$(this).addClass('btn-primary-disabled').html('<?php $L->p('Saved') ?>');
|
||||
});
|
||||
|
||||
$("#btnPreview").on("click", function() {
|
||||
var title = $("#jstitle").val();
|
||||
var content = editorGetContent();
|
||||
bluditAjax.saveAsDraft(uuid, title, content).then(function(data) {
|
||||
|
@ -457,23 +124,431 @@ $(document).ready(function() {
|
|||
});
|
||||
});
|
||||
|
||||
// Button Save
|
||||
$("#jsbuttonSave").on("click", function() {
|
||||
// If the switch is setted to "published", get the value from the selector
|
||||
if ($("#jsbuttonSwitch").data("switch")=="publish") {
|
||||
var value = $("#jstypeSelector option:selected").val();
|
||||
$("#jstype").val(value);
|
||||
} else {
|
||||
$("#jstype").val("draft");
|
||||
}
|
||||
|
||||
// Get the content
|
||||
$("#jscontent").val( editorGetContent() );
|
||||
|
||||
// Submit the form
|
||||
$("#jsform").submit();
|
||||
$('#btnCurrenStatus').on('click', function() {
|
||||
openModal('status');
|
||||
});
|
||||
|
||||
$('#category').on("change", function() {
|
||||
$('#btnSave').html('<?php $L->p('Save') ?>');
|
||||
});
|
||||
|
||||
// Modal description events
|
||||
// ------------------------------------------------------------------------
|
||||
$('#btnSaveDescription').on('click', function() {
|
||||
var args = {
|
||||
description: $('#description').val()
|
||||
};
|
||||
save(args);
|
||||
});
|
||||
|
||||
$('#btnCancelDescription').on('click', function() {
|
||||
closeModal('description');
|
||||
});
|
||||
|
||||
// Modal date events
|
||||
// ------------------------------------------------------------------------
|
||||
$('#btnSaveDate').on('click', function() {
|
||||
var args = {
|
||||
date: $('#date').val()
|
||||
};
|
||||
save(args);
|
||||
});
|
||||
|
||||
$('#btnCancelDate').on('click', function() {
|
||||
closeModal('date');
|
||||
});
|
||||
|
||||
// Modal friendly-url events
|
||||
// ------------------------------------------------------------------------
|
||||
$('#btnSaveFriendlyURL').on('click', function() {
|
||||
var args = {
|
||||
slug: $('#friendlyURL').val()
|
||||
};
|
||||
save(args);
|
||||
});
|
||||
|
||||
$('#btnCancelFriendlyURL').on('click', function() {
|
||||
closeModal('FriendlyURL');
|
||||
});
|
||||
|
||||
$('#btnGenURLFromTitle').on('click', function() {
|
||||
var args = {
|
||||
text: $('#title').val(),
|
||||
parentKey: $('#parent').val(),
|
||||
pageKey: _pageKey
|
||||
}
|
||||
api.friendlyURL(args).then(function(slug) {
|
||||
$('#friendlyURL').val(slug);
|
||||
});
|
||||
});
|
||||
|
||||
// Modal status events
|
||||
// ------------------------------------------------------------------------
|
||||
$('#btnSaveStatus').on('click', function() {
|
||||
var args = {
|
||||
type: $('input[name="status"]:checked').val()
|
||||
};
|
||||
save(args);
|
||||
|
||||
if (args['type']=='draft') {
|
||||
$('#btnCurrenStatus').html('<i class="fa fa-square switch-icon-draft"></i> <?php $L->p('Draft') ?>');
|
||||
} else if (args['type']=='published') {
|
||||
$('#btnCurrenStatus').html('<i class="fa fa-square switch-icon-published"></i> <?php $L->p('Published') ?>');
|
||||
} else if (args['type']=='unlisted') {
|
||||
$('#btnCurrenStatus').html('<i class="fa fa-square switch-icon-unlisted"></i> <?php $L->p('Unlisted') ?>');
|
||||
} else if (args['type']=='sticky') {
|
||||
$('#btnCurrenStatus').html('<i class="fa fa-square switch-icon-sticky"></i> <?php $L->p('Sticky') ?>');
|
||||
} else if (args['type']=='static') {
|
||||
$('#btnCurrenStatus').html('<i class="fa fa-square switch-icon-static"></i> <?php $L->p('Static') ?>');
|
||||
}
|
||||
});
|
||||
|
||||
$('#btnCancelStatus').on('click', function() {
|
||||
closeModal('status');
|
||||
});
|
||||
|
||||
// Modal parent events
|
||||
// ------------------------------------------------------------------------
|
||||
$('#btnSaveParent').on('click', function() {
|
||||
var args = {
|
||||
parent: $('#parent').val()
|
||||
};
|
||||
save(args);
|
||||
});
|
||||
|
||||
$('#btnCancelParent').on('click', function() {
|
||||
closeModal('parent');
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Initlization for the view
|
||||
// ----------------------------------------------------------------------------
|
||||
$(document).ready(function() {
|
||||
// nothing here yet
|
||||
// how do you hang your toilet paper ? over or under ?
|
||||
});
|
||||
</script>
|
||||
|
||||
<div class="container-fluid h-100">
|
||||
<div class="row h-100">
|
||||
<div class="col-sm-9 h-100">
|
||||
|
||||
<!-- Toolbar > Save, Preview, Status and Options -->
|
||||
<div id="editorToolbar" class="mb-2">
|
||||
<div id="editorToolbarRight" class="btn-group btn-group-sm float-right" role="group" aria-label="Toolbar right">
|
||||
<div class="dropdown">
|
||||
<button type="button" class="btn switch-button btn-sm dropdown-toggle" type="button" id="dropdownMenuOptions" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false"><span class="fa fa-cog"></span> <?php $L->p('Options') ?></button>
|
||||
<div class="dropdown-menu" aria-labelledby="dropdownMenuOptions">
|
||||
<a onclick="openModal('description')" class="dropdown-item" href="#"><i class="fa fa-comment"></i> Description</a>
|
||||
<a onclick="openModal('date')" class="dropdown-item" href="#"><i class="fa fa-calendar"></i> Publish date</a>
|
||||
<a onclick="openModal('friendlyURL')" class="dropdown-item" href="#"><i class="fa fa-link"></i> Change URL</a>
|
||||
<a onclick="openModal('status')" class="dropdown-item" href="#"><i class="fa fa-eye"></i> Status</a>
|
||||
<a onclick="openModal('seo')" class="dropdown-item" href="#"><i class="fa fa-compass"></i> SEO features</a>
|
||||
<a onclick="openModal('parent')" class="dropdown-item" href="#"><i class="fa fa-sitemap"></i> Parent page</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="editorToolbarLeft">
|
||||
<button id="btnSave" type="button" class="btn btn-sm btn-primary" ><?php $L->p('Save') ?></button>
|
||||
<button id="btnPreview" type="button" class="btn btn-sm btn-secondary"><?php $L->p('Preview') ?></button>
|
||||
<span id="btnCurrenStatus" class="ml-2 switch-button"><i class="fa fa-square switch-icon-draft"></i> <?php $L->p('Draft') ?></span>
|
||||
</div>
|
||||
</div>
|
||||
<!-- End Toolbar > Save, Preview, Status and Options -->
|
||||
|
||||
<!-- Modal Description -->
|
||||
<div class="modal" id="modal-description" tabindex="-1" aria-labelledby="modal-description" aria-hidden="true">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<div class="modal-body">
|
||||
<div class="form-group m-0">
|
||||
<label for="parent" class="font-weight-bold">Page description</label>
|
||||
<textarea id="description" name="description" class="form-control" rows="3"></textarea>
|
||||
<small class="form-text text-muted"><?php echo $L->get('this-field-can-help-describe-the-content') ?></small>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer modal-footer pl-2 pr-2 pt-1 pb-1">
|
||||
<button id="btnCancelDescription" type="button" class="btn btn-cancel font-weight-bold mr-auto"><i class="fa fa-times"></i> Cancel</button>
|
||||
<button id="btnSaveDescription" type="button" class="btn btn-save font-weight-bold"><i class="fa fa-check"></i> Save</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- End Modal Description -->
|
||||
|
||||
<!-- Modal Date -->
|
||||
<div class="modal" id="modal-date" aria-labelledby="modal-date" tabindex="-1" aria-hidden="true">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<div class="modal-body">
|
||||
<div class="form-group m-0">
|
||||
<label for="date" class="font-weight-bold">Publish date</label>
|
||||
<input id="date" name="date" type="text" class="form-control" value="<?php echo Date::current(DB_DATE_FORMAT) ?>">
|
||||
<small class="form-text text-muted"><?php echo $L->g('date-format-format') ?></small>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer modal-footer pl-2 pr-2 pt-1 pb-1">
|
||||
<button id="btnCancelDate" type="button" class="btn btn-cancel font-weight-bold mr-auto"><i class="fa fa-times"></i> Cancel</button>
|
||||
<button id="btnSaveDate" type="button" class="btn btn-save font-weight-bold"><i class="fa fa-check"></i> Save</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<script>
|
||||
$(document).ready(function() {
|
||||
$("#date").datetimepicker({format:DB_DATE_FORMAT});
|
||||
});
|
||||
</script>
|
||||
<!-- End Modal Date -->
|
||||
|
||||
<!-- Modal friendly URL -->
|
||||
<div class="modal" id="modal-friendlyURL" tabindex="-1" aria-labelledby="modal-friendlyURL" aria-hidden="true">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<div class="modal-body">
|
||||
<div class="form-group m-0">
|
||||
<div class="d-flex mb-2">
|
||||
<label for="friendlyURL" class="p-0 m-0 mr-auto font-weight-bold">Page URL</label>
|
||||
<button id="btnGenURLFromTitle" type="button" class="btn p-0 m-0"><i class="fa fa-magic"></i> Generate from page title</button>
|
||||
</div>
|
||||
<input id="friendlyURL" name="friendlyURL" type="text" class="form-control" value="">
|
||||
<small class="form-text text-muted">https://www.varlogdiego.com/my-page-about-k8s</small>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer modal-footer pl-2 pr-2 pt-1 pb-1">
|
||||
<button id="btnCancelfriendlyURL" type="button" class="btn btn-cancel font-weight-bold mr-auto"><i class="fa fa-times"></i> Cancel</button>
|
||||
<button id="btnSavefriendlyURL" type="button" class="btn btn-save font-weight-bold"><i class="fa fa-check"></i> Save</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- End Modal friendly URL -->
|
||||
|
||||
<!-- Modal Parent -->
|
||||
<div class="modal" id="modal-parent" aria-labelledby="modal-parent" tabindex="-1" aria-hidden="true">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<div class="modal-body">
|
||||
<div class="form-group m-0">
|
||||
<label for="parent" class="font-weight-bold">Parent page</label>
|
||||
<select id="parent" name="parent" class="custom-select"></select>
|
||||
<small class="form-text text-muted"><?php echo $L->g('Start typing a page title to see a list of suggestions.') ?></small>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer modal-footer pl-2 pr-2 pt-1 pb-1">
|
||||
<button id="btnCancelParent" type="button" class="btn btn-cancel font-weight-bold mr-auto"><i class="fa fa-times"></i> Cancel</button>
|
||||
<button id="btnSaveParent" type="button" class="btn btn-save font-weight-bold"><i class="fa fa-check"></i> Save</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<script>
|
||||
$(document).ready(function() {
|
||||
var parent = $("#parent").select2({
|
||||
placeholder: "",
|
||||
allowClear: true,
|
||||
theme: "bootstrap4",
|
||||
minimumInputLength: 2,
|
||||
ajax: {
|
||||
url: HTML_PATH_ADMIN_ROOT+"ajax/get-published",
|
||||
data: function (params) {
|
||||
var query = {
|
||||
checkIsParent: true,
|
||||
query: params.term
|
||||
}
|
||||
return query;
|
||||
},
|
||||
processResults: function (data) {
|
||||
return data;
|
||||
}
|
||||
},
|
||||
escapeMarkup: function(markup) {
|
||||
return markup;
|
||||
},
|
||||
templateResult: function(data) {
|
||||
var html = data.text;
|
||||
if (data.type=="static") {
|
||||
html += '<span class="badge badge-pill badge-light">'+data.type+'</span>';
|
||||
}
|
||||
return html;
|
||||
}
|
||||
});
|
||||
});
|
||||
</script>
|
||||
<!-- End Modal Parent -->
|
||||
|
||||
<!-- Modal Status -->
|
||||
<div class="modal" id="modal-status" tabindex="-1" aria-labelledby="modal-status" aria-hidden="true">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<div class="modal-body">
|
||||
<div class="form-group m-0">
|
||||
<label class="font-weight-bold">Page status</label>
|
||||
</div>
|
||||
<div class="form-check mb-2">
|
||||
<input id="statusDraft" name="status" class="form-check-input" type="radio" value="draft" checked>
|
||||
<label class="form-check-label" for="statusDraft">Draft</label>
|
||||
<small class="form-text text-muted">Page as draft, is not visible for visitors.</small>
|
||||
</div>
|
||||
<div class="form-check mb-2">
|
||||
<input id="statusPublish" name="status" class="form-check-input" type="radio" value="published">
|
||||
<label class="form-check-label" for="statusPublish">Publish</label>
|
||||
<small class="form-text text-muted">Publish the page, everyone can see it.</small>
|
||||
</div>
|
||||
<hr>
|
||||
<div class="form-check mb-2">
|
||||
<input id="statusSticky" name="status" class="form-check-input" type="radio" value="sticky">
|
||||
<label class="form-check-label" for="statusSticky">Publish as sticky</label>
|
||||
<small class="form-text text-muted">The page can be seen by everyone in the top of the main page.</small>
|
||||
</div>
|
||||
<div class="form-check mb-2">
|
||||
<input id="statusStatic" name="status" class="form-check-input" type="radio" value="static">
|
||||
<label class="form-check-label" for="statusStatic">Publish as static</label>
|
||||
<small class="form-text text-muted">The page can be seen by everyone as static page.</small>
|
||||
</div>
|
||||
<div class="form-check mb-2">
|
||||
<input id="statusUnlisted" name="status" class="form-check-input" type="radio" value="unlisted">
|
||||
<label class="form-check-label" for="statusUnlisted">Publish as unlisted</label>
|
||||
<small class="form-text text-muted">The page can be seen and shared by anyone with the link.</small>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer modal-footer pl-2 pr-2 pt-1 pb-1">
|
||||
<button id="btnCancelStatus" type="button" class="btn btn-cancel font-weight-bold mr-auto" data-dismiss="modal"><i class="fa fa-times"></i> Cancel</button>
|
||||
<button id="btnSaveStatus" type="button" class="btn btn-save font-weight-bold"><i class="fa fa-check"></i> Save</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- End Modal Status -->
|
||||
|
||||
<!-- Modal SEO -->
|
||||
<div class="modal" id="modal-seo" tabindex="-1" aria-labelledby="modal-seo" aria-hidden="true">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<div class="modal-body">
|
||||
<div class="form-group m-0">
|
||||
<label class="font-weight-bold">SEO features</label>
|
||||
</div>
|
||||
<div class="form-check mb-2">
|
||||
<input id="noindex" name="noindex" class="form-check-input" type="checkbox" value="noindex">
|
||||
<label class="form-check-label" for="noindex"><?php echo $L->g('apply-code-noindex-code-to-this-page') ?></label>
|
||||
<small class="form-text text-muted"><?php echo $L->g('This tells search engines not to show this page in their search results.') ?></small>
|
||||
</div>
|
||||
<div class="form-check mb-2">
|
||||
<input id="nofollow" name="nofollow" class="form-check-input" type="checkbox" value="nofollow">
|
||||
<label class="form-check-label" for="nofollow"><?php echo $L->g('apply-code-nofollow-code-to-this-page') ?></label>
|
||||
<small class="form-text text-muted"><?php echo $L->g('This tells search engines not to follow links on this page.') ?></small>
|
||||
</div>
|
||||
<div class="form-check mb-2">
|
||||
<input id="noarchive" name="noarchive" class="form-check-input" type="checkbox" value="noarchive">
|
||||
<label class="form-check-label" for="noarchive"><?php echo $L->g('apply-code-noarchive-code-to-this-page') ?></label>
|
||||
<small class="form-text text-muted"><?php echo $L->g('This tells search engines not to save a cached copy of this page.') ?></small>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer modal-footer pl-2 pr-2 pt-1 pb-1">
|
||||
<button type="button" class="btn btn-cancel font-weight-bold mr-auto" data-dismiss="modal"><i class="fa fa-times"></i> Cancel</button>
|
||||
<button type="button" class="btn btn-save font-weight-bold"><i class="fa fa-check"></i> Save</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- End Modal SEO -->
|
||||
|
||||
<form class="d-flex flex-column h-100" id="jsform" method="post" action="" autocomplete="off">
|
||||
<!-- Title -->
|
||||
<div id="jseditorTitle" class="form-group mb-2">
|
||||
<input id="title" name="title" type="text" class="form-control form-control-lg rounded-0" value="" placeholder="<?php $L->p('Enter title') ?>">
|
||||
</div>
|
||||
<!-- End Title -->
|
||||
|
||||
<!-- Editor -->
|
||||
<textarea id="jseditor" class="editable h-100 mb-2"></textarea>
|
||||
<!-- End Editor -->
|
||||
</form>
|
||||
|
||||
</div> <!-- End <div class="col-sm-9 h-100"> -->
|
||||
|
||||
<div class="col-sm-3 h-100">
|
||||
|
||||
<!-- Cover Image -->
|
||||
<h6 class="mt-1 mb-2 pb-2 text-uppercase"><?php $L->p('Cover Image') ?></h6>
|
||||
<div>
|
||||
<img id="jscoverImagePreview" class="mx-auto d-block w-100" alt="Cover image preview" src="<?php echo HTML_PATH_CORE_IMG ?>default.svg" />
|
||||
</div>
|
||||
<!-- End Cover Image -->
|
||||
|
||||
<!-- Images -->
|
||||
<h6 class="mt-4 mb-2 pb-2 text-uppercase"><?php $L->p('Images') ?></h6>
|
||||
|
||||
<div class="media text-muted pt-3">
|
||||
<svg class="align-self-center mr-3 rounded" width="32" height="32" xmlns="http://www.w3.org/2000/svg" preserveAspectRatio="xMidYMid slice" focusable="false" role="img" aria-label="Placeholder: 32x32"><title>Placeholder</title><rect width="100%" height="100%" fill="#007bff"></rect><text x="50%" y="50%" fill="#007bff" dy=".3em">32x32</text></svg>
|
||||
<div class="media-body">
|
||||
<div class="mt-0">
|
||||
photo1.jpg
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="media text-muted pt-3">
|
||||
<svg class="align-self-center mr-3 rounded" width="32" height="32" xmlns="http://www.w3.org/2000/svg" preserveAspectRatio="xMidYMid slice" focusable="false" role="img" aria-label="Placeholder: 32x32"><title>Placeholder</title><rect width="100%" height="100%" fill="#007bff"></rect><text x="50%" y="50%" fill="#007bff" dy=".3em">32x32</text></svg>
|
||||
<div class="media-body">
|
||||
<div class="mt-0">
|
||||
photo2.jpg
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<small class="d-block text-right mt-3">
|
||||
<a href="#">All images</a>
|
||||
</small>
|
||||
<!-- End Images -->
|
||||
|
||||
<!-- Category -->
|
||||
<div class="form-group m-0">
|
||||
<h6 class="mt-4 mb-2 pb-2 text-uppercase">Category</h6>
|
||||
<select id="category" name="category" class="custom-select">
|
||||
<option value="">- Uncategorized -</option>
|
||||
<?php foreach ($categories->db as $key=>$fields): ?>
|
||||
<option value="<?php echo $key ?>"><?php echo $fields['name']?></option>
|
||||
<?php endforeach; ?>
|
||||
</select>
|
||||
</div>
|
||||
<!-- End Category -->
|
||||
|
||||
<!-- Tags -->
|
||||
<h6 class="mt-4 mb-2 pb-2 text-uppercase">Tags</h6>
|
||||
<div id="tags"></div>
|
||||
<script>
|
||||
$(document).ready(function() {
|
||||
let tokenAutocomplete = new TokenAutocomplete({
|
||||
name: 'tags',
|
||||
selector: '#tags',
|
||||
noMatchesText: 'No matching results...',
|
||||
minCharactersForSuggestion: 2,
|
||||
initialSuggestions: [
|
||||
<?php
|
||||
foreach ($tags->db as $key=>$fields) {
|
||||
echo '{value: "'.$key.'", text: "'.$fields['name'].'"},';
|
||||
}
|
||||
?>
|
||||
]
|
||||
});
|
||||
tokenAutocomplete.debug(true);
|
||||
});
|
||||
</script>
|
||||
<!-- End Tags -->
|
||||
|
||||
</div> <!-- End <div class="col-sm-3 h-100"> -->
|
||||
</div> <!-- End <div class="row h-100"> -->
|
||||
</div> <!-- End <div class="container-fluid h-100"> -->
|
||||
|
||||
<script>
|
||||
$(document).ready(function() {
|
||||
|
||||
|
||||
// Autosave
|
||||
var currentContent = editorGetContent();
|
||||
setInterval(function() {
|
||||
|
|
644
bl-kernel/admin/views/new-content_collapse.php
Normal file
644
bl-kernel/admin/views/new-content_collapse.php
Normal file
|
@ -0,0 +1,644 @@
|
|||
<?php defined('BLUDIT') or die('Bludit CMS.'); ?>
|
||||
|
||||
<div class="container-fluid h-100">
|
||||
<div class="row h-100">
|
||||
<div class="col-sm-9 h-100">
|
||||
|
||||
<?php
|
||||
|
||||
// Start form
|
||||
echo Bootstrap::formOpen(array(
|
||||
'id'=>'jsform',
|
||||
'class'=>'d-flex flex-column h-100'
|
||||
));
|
||||
|
||||
// Token CSRF
|
||||
echo Bootstrap::formInputHidden(array(
|
||||
'name'=>'tokenCSRF',
|
||||
'value'=>$security->getTokenCSRF()
|
||||
));
|
||||
|
||||
// UUID
|
||||
// The UUID is generated in the controller
|
||||
echo Bootstrap::formInputHidden(array(
|
||||
'name'=>'uuid',
|
||||
'value'=>$uuid
|
||||
));
|
||||
|
||||
// Type = published, draft, sticky, static
|
||||
echo Bootstrap::formInputHidden(array(
|
||||
'name'=>'type',
|
||||
'value'=>'published'
|
||||
));
|
||||
|
||||
// Cover image
|
||||
echo Bootstrap::formInputHidden(array(
|
||||
'name'=>'coverImage',
|
||||
'value'=>''
|
||||
));
|
||||
|
||||
// Content
|
||||
echo Bootstrap::formInputHidden(array(
|
||||
'name'=>'content',
|
||||
'value'=>''
|
||||
));
|
||||
?>
|
||||
|
||||
<!-- TOOLBAR -->
|
||||
<div id="editorToolbar" class="mb-1">
|
||||
<div id="editorToolbarRight" class="btn-group btn-group-sm float-right" role="group" aria-label="Toolbar right">
|
||||
<button type="button" class="btn btn-light" id="jsmediaManagerOpenModal" data-toggle="modal" data-target="#jsmediaManagerModal"><span class="fa fa-image"></span> <?php $L->p('Images') ?></button>
|
||||
<button type="button" class="btn btn-light" id="jsoptionsSidebar" style="z-index:30"><span class="fa fa-cog"></span> <?php $L->p('Options') ?></button>
|
||||
</div>
|
||||
|
||||
<div id="jseditorToolbarLeft">
|
||||
<button id="jsbuttonSave" type="button" class="btn btn-sm btn-primary" ><?php $L->p('Save') ?></button>
|
||||
<button id="jsbuttonPreview" type="button" class="btn btn-sm btn-secondary"><?php $L->p('Preview') ?></button>
|
||||
<span id="jsbuttonSwitch" data-switch="publish" class="ml-2 text-secondary switch-button"><i class="fa fa-square switch-icon-publish"></i> <?php $L->p('Publish') ?></span>
|
||||
</div>
|
||||
</div>
|
||||
<script>
|
||||
$(document).ready(function() {
|
||||
$("#jsoptionsSidebar").on("click", function() {
|
||||
$("#jseditorSidebar").toggle();
|
||||
$("#jsshadow").toggle();
|
||||
});
|
||||
|
||||
$("#jsshadow").on("click", function() {
|
||||
$("#jseditorSidebar").toggle();
|
||||
$("#jsshadow").toggle();
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
||||
<!-- SIDEBAR OPTIONS -->
|
||||
<div id="jseditorSidebar">
|
||||
<nav>
|
||||
<div class="nav nav-tabs" id="nav-tab" role="tablist">
|
||||
<a class="nav-link active show" id="nav-general-tab" data-toggle="tab" href="#nav-general" role="tab" aria-controls="general"><?php $L->p('General') ?></a>
|
||||
<a class="nav-link" id="nav-advanced-tab" data-toggle="tab" href="#nav-advanced" role="tab" aria-controls="advanced"><?php $L->p('Advanced') ?></a>
|
||||
<?php if (!empty($site->customFields())): ?>
|
||||
<a class="nav-link" id="nav-custom-tab" data-toggle="tab" href="#nav-custom" role="tab" aria-controls="custom"><?php $L->p('Custom') ?></a>
|
||||
<?php endif ?>
|
||||
<a class="nav-link" id="nav-seo-tab" data-toggle="tab" href="#nav-seo" role="tab" aria-controls="seo"><?php $L->p('SEO') ?></a>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<div class="tab-content pr-3 pl-3 pb-3">
|
||||
<div id="nav-general" class="tab-pane fade show active" role="tabpanel" aria-labelledby="general-tab">
|
||||
<?php
|
||||
// Category
|
||||
echo Bootstrap::formSelectBlock(array(
|
||||
'name'=>'category',
|
||||
'label'=>$L->g('Category'),
|
||||
'selected'=>'',
|
||||
'class'=>'',
|
||||
'emptyOption'=>'- '.$L->g('Uncategorized').' -',
|
||||
'options'=>$categories->getKeyNameArray()
|
||||
));
|
||||
|
||||
// Description
|
||||
echo Bootstrap::formTextareaBlock(array(
|
||||
'name'=>'description',
|
||||
'label'=>$L->g('Description'),
|
||||
'selected'=>'',
|
||||
'class'=>'',
|
||||
'value'=>'',
|
||||
'rows'=>5,
|
||||
'placeholder'=>$L->get('this-field-can-help-describe-the-content')
|
||||
));
|
||||
?>
|
||||
|
||||
<!-- Cover Image -->
|
||||
<label class="mt-4 mb-2 pb-2 border-bottom text-uppercase w-100"><?php $L->p('Cover Image') ?></label>
|
||||
<div>
|
||||
<img id="jscoverImagePreview" class="mx-auto d-block w-100" alt="Cover image preview" src="<?php echo HTML_PATH_CORE_IMG ?>default.svg" />
|
||||
</div>
|
||||
<div class="mt-2 text-center">
|
||||
<button type="button" id="jsbuttonSelectCoverImage" class="btn btn-primary btn-sm"><?php echo $L->g('Select cover image') ?></button>
|
||||
<button type="button" id="jsbuttonRemoveCoverImage" class="btn btn-secondary btn-sm"><?php echo $L->g('Remove cover image') ?></button>
|
||||
</div>
|
||||
<script>
|
||||
$(document).ready(function() {
|
||||
$("#jscoverImagePreview").on("click", function() {
|
||||
openMediaManager();
|
||||
});
|
||||
|
||||
$("#jsbuttonSelectCoverImage").on("click", function() {
|
||||
openMediaManager();
|
||||
});
|
||||
|
||||
$("#jsbuttonRemoveCoverImage").on("click", function() {
|
||||
$("#jscoverImage").val('');
|
||||
$("#jscoverImagePreview").attr('src', HTML_PATH_CORE_IMG+'default.svg');
|
||||
});
|
||||
});
|
||||
</script>
|
||||
</div>
|
||||
<div id="nav-advanced" class="tab-pane fade" role="tabpanel" aria-labelledby="advanced-tab">
|
||||
<?php
|
||||
// Date
|
||||
echo Bootstrap::formInputTextBlock(array(
|
||||
'name'=>'date',
|
||||
'label'=>$L->g('Date'),
|
||||
'placeholder'=>'',
|
||||
'value'=>Date::current(DB_DATE_FORMAT),
|
||||
'tip'=>$L->g('date-format-format')
|
||||
));
|
||||
|
||||
// Type
|
||||
echo Bootstrap::formSelectBlock(array(
|
||||
'name'=>'typeSelector',
|
||||
'label'=>$L->g('Type'),
|
||||
'selected'=>'',
|
||||
'options'=>array(
|
||||
'published'=>'- '.$L->g('Default').' -',
|
||||
'sticky'=>$L->g('Sticky'),
|
||||
'static'=>$L->g('Static')
|
||||
),
|
||||
'tip'=>''
|
||||
));
|
||||
|
||||
// Position
|
||||
echo Bootstrap::formInputTextBlock(array(
|
||||
'name'=>'position',
|
||||
'label'=>$L->g('Position'),
|
||||
'tip'=>$L->g('Field used when ordering content by position'),
|
||||
'value'=>$pages->nextPositionNumber()
|
||||
));
|
||||
|
||||
// Tags
|
||||
echo Bootstrap::formInputTextBlock(array(
|
||||
'name'=>'tags',
|
||||
'label'=>$L->g('Tags'),
|
||||
'placeholder'=>'',
|
||||
'tip'=>$L->g('Write the tags separated by comma')
|
||||
));
|
||||
|
||||
// Parent
|
||||
echo Bootstrap::formSelectBlock(array(
|
||||
'name'=>'parent',
|
||||
'label'=>$L->g('Parent'),
|
||||
'options'=>array(),
|
||||
'selected'=>false,
|
||||
'class'=>'',
|
||||
'tip'=>$L->g('Start typing a page title to see a list of suggestions.'),
|
||||
));
|
||||
|
||||
?>
|
||||
|
||||
<script>
|
||||
$(document).ready(function() {
|
||||
var parent = $("#jsparent").select2({
|
||||
placeholder: "",
|
||||
allowClear: true,
|
||||
theme: "bootstrap4",
|
||||
minimumInputLength: 2,
|
||||
ajax: {
|
||||
url: HTML_PATH_ADMIN_ROOT+"ajax/get-published",
|
||||
data: function (params) {
|
||||
var query = {
|
||||
checkIsParent: true,
|
||||
query: params.term
|
||||
}
|
||||
return query;
|
||||
},
|
||||
processResults: function (data) {
|
||||
return data;
|
||||
}
|
||||
},
|
||||
escapeMarkup: function(markup) {
|
||||
return markup;
|
||||
},
|
||||
templateResult: function(data) {
|
||||
var html = data.text;
|
||||
if (data.type=="static") {
|
||||
html += '<span class="badge badge-pill badge-light">'+data.type+'</span>';
|
||||
}
|
||||
return html;
|
||||
}
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
||||
<?php
|
||||
// Template
|
||||
echo Bootstrap::formInputTextBlock(array(
|
||||
'name'=>'template',
|
||||
'label'=>$L->g('Template'),
|
||||
'placeholder'=>'',
|
||||
'value'=>'',
|
||||
'tip'=>$L->g('Write a template name to filter the page in the theme and change the style of the page.')
|
||||
));
|
||||
|
||||
echo Bootstrap::formInputTextBlock(array(
|
||||
'name'=>'externalCoverImage',
|
||||
'label'=>$L->g('External cover image'),
|
||||
'placeholder'=>"https://",
|
||||
'value'=>'',
|
||||
'tip'=>$L->g('Set a cover image from external URL, such as a CDN or some server dedicated for images.')
|
||||
));
|
||||
|
||||
// Username
|
||||
echo Bootstrap::formInputTextBlock(array(
|
||||
'name'=>'',
|
||||
'label'=>$L->g('Author'),
|
||||
'placeholder'=>'',
|
||||
'value'=>$login->username(),
|
||||
'tip'=>'',
|
||||
'disabled'=>true
|
||||
));
|
||||
?>
|
||||
|
||||
<script>
|
||||
$(document).ready(function() {
|
||||
|
||||
// Changes in External cover image input
|
||||
$("#jsexternalCoverImage").change(function() {
|
||||
$("#jscoverImage").val( $(this).val() );
|
||||
});
|
||||
|
||||
// Generate slug when the user type the title
|
||||
$("#jstitle").keyup(function() {
|
||||
var text = $(this).val();
|
||||
var parent = $("#jsparent").val();
|
||||
var currentKey = "";
|
||||
var ajax = new bluditAjax();
|
||||
var callBack = $("#jsslug");
|
||||
ajax.generateSlug(text, parent, currentKey, callBack);
|
||||
});
|
||||
|
||||
// Datepicker
|
||||
$("#jsdate").datetimepicker({format:DB_DATE_FORMAT});
|
||||
|
||||
|
||||
});
|
||||
</script>
|
||||
</div>
|
||||
<?php if (!empty($site->customFields())): ?>
|
||||
<div id="nav-custom" class="tab-pane fade" role="tabpanel" aria-labelledby="custom-tab">
|
||||
<?php
|
||||
$customFields = $site->customFields();
|
||||
foreach ($customFields as $field=>$options) {
|
||||
if ( !isset($options['position']) ) {
|
||||
if ($options['type']=="string") {
|
||||
echo Bootstrap::formInputTextBlock(array(
|
||||
'name'=>'custom['.$field.']',
|
||||
'label'=>(isset($options['label'])?$options['label']:''),
|
||||
'value'=>(isset($options['default'])?$options['default']:''),
|
||||
'tip'=>(isset($options['tip'])?$options['tip']:''),
|
||||
'placeholder'=>(isset($options['placeholder'])?$options['placeholder']:'')
|
||||
));
|
||||
} elseif ($options['type']=="bool") {
|
||||
echo Bootstrap::formCheckbox(array(
|
||||
'name'=>'custom['.$field.']',
|
||||
'label'=>(isset($options['label'])?$options['label']:''),
|
||||
'placeholder'=>(isset($options['placeholder'])?$options['placeholder']:''),
|
||||
'checked'=>(isset($options['checked'])?true:false),
|
||||
'labelForCheckbox'=>(isset($options['tip'])?$options['tip']:'')
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
?>
|
||||
</div>
|
||||
<?php endif ?>
|
||||
<div id="nav-seo" class="tab-pane fade" role="tabpanel" aria-labelledby="seo-tab">
|
||||
<?php
|
||||
// Friendly URL
|
||||
echo Bootstrap::formInputTextBlock(array(
|
||||
'name'=>'slug',
|
||||
'tip'=>$L->g('URL associated with the content'),
|
||||
'label'=>$L->g('Friendly URL'),
|
||||
'placeholder'=>$L->g('Leave empty for autocomplete by Bludit.')
|
||||
));
|
||||
|
||||
// Robots
|
||||
echo Bootstrap::formCheckbox(array(
|
||||
'name'=>'noindex',
|
||||
'label'=>'Robots',
|
||||
'labelForCheckbox'=>$L->g('apply-code-noindex-code-to-this-page'),
|
||||
'placeholder'=>'',
|
||||
'checked'=>false,
|
||||
'tip'=>$L->g('This tells search engines not to show this page in their search results.')
|
||||
));
|
||||
|
||||
// Robots
|
||||
echo Bootstrap::formCheckbox(array(
|
||||
'name'=>'nofollow',
|
||||
'label'=>'',
|
||||
'labelForCheckbox'=>$L->g('apply-code-nofollow-code-to-this-page'),
|
||||
'placeholder'=>'',
|
||||
'checked'=>false,
|
||||
'tip'=>$L->g('This tells search engines not to follow links on this page.')
|
||||
));
|
||||
|
||||
// Robots
|
||||
echo Bootstrap::formCheckbox(array(
|
||||
'name'=>'noarchive',
|
||||
'label'=>'',
|
||||
'labelForCheckbox'=>$L->g('apply-code-noarchive-code-to-this-page'),
|
||||
'placeholder'=>'',
|
||||
'checked'=>false,
|
||||
'tip'=>$L->g('This tells search engines not to save a cached copy of this page.')
|
||||
));
|
||||
?>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Custom fields: TOP -->
|
||||
<?php
|
||||
$customFields = $site->customFields();
|
||||
foreach ($customFields as $field=>$options) {
|
||||
if ( isset($options['position']) && ($options['position']=='top') ) {
|
||||
if ($options['type']=="string") {
|
||||
echo Bootstrap::formInputTextBlock(array(
|
||||
'name'=>'custom['.$field.']',
|
||||
'label'=>(isset($options['label'])?$options['label']:''),
|
||||
'value'=>(isset($options['default'])?$options['default']:''),
|
||||
'tip'=>(isset($options['tip'])?$options['tip']:''),
|
||||
'placeholder'=>(isset($options['placeholder'])?$options['placeholder']:''),
|
||||
'class'=>'mb-2',
|
||||
'labelClass'=>'mb-2 pb-2 border-bottom text-uppercase w-100'
|
||||
|
||||
));
|
||||
} elseif ($options['type']=="bool") {
|
||||
echo Bootstrap::formCheckbox(array(
|
||||
'name'=>'custom['.$field.']',
|
||||
'label'=>(isset($options['label'])?$options['label']:''),
|
||||
'placeholder'=>(isset($options['placeholder'])?$options['placeholder']:''),
|
||||
'checked'=>(isset($options['checked'])?true:false),
|
||||
'labelForCheckbox'=>(isset($options['tip'])?$options['tip']:''),
|
||||
'class'=>'mb-2',
|
||||
'labelClass'=>'mb-2 pb-2 border-bottom text-uppercase w-100'
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
?>
|
||||
|
||||
|
||||
<!-- Title -->
|
||||
<div id="jseditorTitle" class="form-group mb-1">
|
||||
<input id="jstitle" name="title" type="text" class="form-control form-control-lg rounded-0" value="" placeholder="<?php $L->p('Enter title') ?>">
|
||||
</div>
|
||||
|
||||
<!-- Editor -->
|
||||
<textarea id="jseditor" class="editable h-100 mb-1"></textarea>
|
||||
|
||||
<!-- Custom fields: BOTTOM -->
|
||||
<?php
|
||||
$customFields = $site->customFields();
|
||||
foreach ($customFields as $field=>$options) {
|
||||
if ( isset($options['position']) && ($options['position']=='bottom') ) {
|
||||
if ($options['type']=="string") {
|
||||
echo Bootstrap::formInputTextBlock(array(
|
||||
'name'=>'custom['.$field.']',
|
||||
'label'=>(isset($options['label'])?$options['label']:''),
|
||||
'value'=>(isset($options['default'])?$options['default']:''),
|
||||
'tip'=>(isset($options['tip'])?$options['tip']:''),
|
||||
'placeholder'=>(isset($options['placeholder'])?$options['placeholder']:''),
|
||||
'class'=>'mt-2',
|
||||
'labelClass'=>'mb-2 pb-2 border-bottom text-uppercase w-100'
|
||||
|
||||
));
|
||||
} elseif ($options['type']=="bool") {
|
||||
echo Bootstrap::formCheckbox(array(
|
||||
'name'=>'custom['.$field.']',
|
||||
'label'=>(isset($options['label'])?$options['label']:''),
|
||||
'placeholder'=>(isset($options['placeholder'])?$options['placeholder']:''),
|
||||
'checked'=>(isset($options['checked'])?true:false),
|
||||
'labelForCheckbox'=>(isset($options['tip'])?$options['tip']:''),
|
||||
'class'=>'mt-2',
|
||||
'labelClass'=>'mb-2 pb-2 border-bottom text-uppercase w-100'
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
?>
|
||||
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<div class="col-sm-3 h-100 p-0">
|
||||
|
||||
<!-- Options -->
|
||||
<div class="accordion" id="sidebarOptions">
|
||||
|
||||
<!-- Options > General -->
|
||||
<div class="general card">
|
||||
<div class="card-header m-0 p-2" id="headingOne">
|
||||
<a href="#optionsGeneral" class="w-100 text-left text-uppercase font-weight-bold" data-toggle="collapse" data-target="#optionsGeneral" aria-expanded="true" aria-controls="optionsGeneral">
|
||||
General <span class="float-right fa fa-angle-down"></span>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div id="optionsGeneral" class="collapse" aria-labelledby="headingOne" data-parent="#sidebarOptions">
|
||||
<div class="card-body m-0 p-0">
|
||||
|
||||
<!-- Options > General > Cover Image -->
|
||||
<label class="mt-4 mb-2 pb-2 text-uppercase w-100 font-weight-bold"><?php $L->p('Cover Image') ?></label>
|
||||
<div>
|
||||
<img id="jscoverImagePreview" class="mx-auto d-block w-100" alt="Cover image preview" src="<?php echo HTML_PATH_CORE_IMG ?>default.svg" />
|
||||
</div>
|
||||
<script>
|
||||
$(document).ready(function() {
|
||||
$("#jscoverImagePreview").on("click", function() {
|
||||
openMediaManager();
|
||||
});
|
||||
|
||||
$("#jsbuttonSelectCoverImage").on("click", function() {
|
||||
openMediaManager();
|
||||
});
|
||||
|
||||
$("#jsbuttonRemoveCoverImage").on("click", function() {
|
||||
$("#jscoverImage").val('');
|
||||
$("#jscoverImagePreview").attr('src', HTML_PATH_CORE_IMG+'default.svg');
|
||||
});
|
||||
});
|
||||
</script>
|
||||
<!-- End Options > General > Cover Image -->
|
||||
|
||||
<!-- Options > General > Category and Description -->
|
||||
<?php
|
||||
// Category
|
||||
echo Bootstrap::formSelectBlock(array(
|
||||
'name'=>'category',
|
||||
'label'=>$L->g('Category'),
|
||||
'selected'=>'',
|
||||
'class'=>'',
|
||||
'emptyOption'=>'- '.$L->g('Uncategorized').' -',
|
||||
'options'=>$categories->getKeyNameArray()
|
||||
));
|
||||
|
||||
// Description
|
||||
echo Bootstrap::formTextareaBlock(array(
|
||||
'name'=>'description',
|
||||
'label'=>$L->g('Description'),
|
||||
'selected'=>'',
|
||||
'class'=>'',
|
||||
'value'=>'',
|
||||
'rows'=>5,
|
||||
'placeholder'=>$L->get('this-field-can-help-describe-the-content')
|
||||
));
|
||||
?>
|
||||
<!-- End Options > General > Category and Description -->
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- End Options > General -->
|
||||
|
||||
<!-- Options > Advanced -->
|
||||
<div class="advanced card">
|
||||
<div class="card-header m-0 p-2" id="headingOne">
|
||||
<a href="#optionsAdvanced" class="w-100 text-left text-uppercase font-weight-bold" data-toggle="collapse" data-target="#optionsAdvanced" aria-expanded="true" aria-controls="optionsAdvanced">
|
||||
Advanced <span class="float-right fa fa-angle-down"></span>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div id="optionsAdvanced" class="collapse" aria-labelledby="headingOne" data-parent="#sidebarOptions">
|
||||
<div class="card-body m-0 p-0">
|
||||
|
||||
<!-- Options > General > Cover Image -->
|
||||
<label class="mt-4 mb-2 pb-2 border-bottom text-uppercase w-100"><?php $L->p('Cover Image') ?></label>
|
||||
<div>
|
||||
<img id="jscoverImagePreview" class="mx-auto d-block w-100" alt="Cover image preview" src="<?php echo HTML_PATH_CORE_IMG ?>default.svg" />
|
||||
</div>
|
||||
<script>
|
||||
$(document).ready(function() {
|
||||
$("#jscoverImagePreview").on("click", function() {
|
||||
openMediaManager();
|
||||
});
|
||||
|
||||
$("#jsbuttonSelectCoverImage").on("click", function() {
|
||||
openMediaManager();
|
||||
});
|
||||
|
||||
$("#jsbuttonRemoveCoverImage").on("click", function() {
|
||||
$("#jscoverImage").val('');
|
||||
$("#jscoverImagePreview").attr('src', HTML_PATH_CORE_IMG+'default.svg');
|
||||
});
|
||||
});
|
||||
</script>
|
||||
<!-- End Options > General > Cover Image -->
|
||||
|
||||
<!-- Options > General > Category and Description -->
|
||||
<?php
|
||||
// Category
|
||||
echo Bootstrap::formSelectBlock(array(
|
||||
'name'=>'category',
|
||||
'label'=>$L->g('Category'),
|
||||
'selected'=>'',
|
||||
'class'=>'',
|
||||
'emptyOption'=>'- '.$L->g('Uncategorized').' -',
|
||||
'options'=>$categories->getKeyNameArray()
|
||||
));
|
||||
|
||||
// Description
|
||||
echo Bootstrap::formTextareaBlock(array(
|
||||
'name'=>'description',
|
||||
'label'=>$L->g('Description'),
|
||||
'selected'=>'',
|
||||
'class'=>'',
|
||||
'value'=>'',
|
||||
'rows'=>5,
|
||||
'placeholder'=>$L->get('this-field-can-help-describe-the-content')
|
||||
));
|
||||
?>
|
||||
<!-- End Options > General > Category and Description -->
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- End Options > General -->
|
||||
|
||||
</div>
|
||||
<!-- End Options -->
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Modal for Media Manager -->
|
||||
<?php include(PATH_ADMIN_THEMES.'booty/html/media.php'); ?>
|
||||
|
||||
<script>
|
||||
$(document).ready(function() {
|
||||
|
||||
// Define function if they doesn't exist
|
||||
// This helps if the user doesn't activate any plugin as editor
|
||||
if (typeof editorGetContent != "function") {
|
||||
window.editorGetContent = function(){
|
||||
return $("#jseditor").val();
|
||||
};
|
||||
}
|
||||
if (typeof editorInsertMedia != "function") {
|
||||
window.editorInsertMedia = function(filename){
|
||||
$("#jseditor").val($('#jseditor').val()+'<img src="'+filename+'" alt="">');
|
||||
};
|
||||
}
|
||||
|
||||
// Button switch
|
||||
$("#jsbuttonSwitch").on("click", function() {
|
||||
if ($(this).data("switch")=="publish") {
|
||||
$(this).html('<i class="fa fa-square switch-icon-draft"></i> <?php $L->p('Draft') ?>');
|
||||
$(this).data("switch", "draft");
|
||||
} else {
|
||||
$(this).html('<i class="fa fa-square switch-icon-publish"></i> <?php $L->p('Publish') ?>');
|
||||
$(this).data("switch", "publish");
|
||||
}
|
||||
});
|
||||
|
||||
// Button preview
|
||||
$("#jsbuttonPreview").on("click", function() {
|
||||
var uuid = $("#jsuuid").val();
|
||||
var title = $("#jstitle").val();
|
||||
var content = editorGetContent();
|
||||
bluditAjax.saveAsDraft(uuid, title, content).then(function(data) {
|
||||
var preview = window.open("<?php echo DOMAIN_PAGES.'autosave-'.$uuid.'?preview='.md5('autosave-'.$uuid) ?>", "bludit-preview");
|
||||
preview.focus();
|
||||
});
|
||||
});
|
||||
|
||||
// Button Save
|
||||
$("#jsbuttonSave").on("click", function() {
|
||||
// If the switch is setted to "published", get the value from the selector
|
||||
if ($("#jsbuttonSwitch").data("switch")=="publish") {
|
||||
var value = $("#jstypeSelector option:selected").val();
|
||||
$("#jstype").val(value);
|
||||
} else {
|
||||
$("#jstype").val("draft");
|
||||
}
|
||||
|
||||
// Get the content
|
||||
$("#jscontent").val( editorGetContent() );
|
||||
|
||||
// Submit the form
|
||||
$("#jsform").submit();
|
||||
});
|
||||
|
||||
// Autosave
|
||||
var currentContent = editorGetContent();
|
||||
setInterval(function() {
|
||||
var uuid = $("#jsuuid").val();
|
||||
var title = $("#jstitle").val() + "[<?php $L->p('Autosave') ?>]";
|
||||
var content = editorGetContent();
|
||||
// Autosave when content has at least 100 characters
|
||||
if (content.length<100) {
|
||||
return false;
|
||||
}
|
||||
// Autosave only when the user change the content
|
||||
if (currentContent!=content) {
|
||||
currentContent = content;
|
||||
bluditAjax.saveAsDraft(uuid, title, content).then(function(data) {
|
||||
if (data.status==0) {
|
||||
showAlert("<?php $L->p('Autosave') ?>");
|
||||
}
|
||||
});
|
||||
}
|
||||
},1000*60*AUTOSAVE_INTERVAL);
|
||||
|
||||
});
|
||||
</script>
|
|
@ -2,12 +2,14 @@
|
|||
header('Content-Type: application/json');
|
||||
|
||||
/*
|
||||
| Returns a list of pages and the title contains the query string
|
||||
| The returned list have published, sticky and statics pages
|
||||
| Returns a list of pages that the title contains the query string.
|
||||
| The returned list have published, sticky and statics pages.
|
||||
| It's possible to filter the pages are parents by the flag "checkIsParent".
|
||||
|
|
||||
| @_POST['query'] string The string to search in the title of the pages
|
||||
| @_POST['query'] string The string to search in the title of the pages.
|
||||
| @_POST['checkIsParent'] boolean TRUE returns only parent pages, FALSE returns all pages.
|
||||
|
|
||||
| @return array
|
||||
| @return json Ex. {"results":[{"disabled":false,"id":"follow-bludit","text":"Follow Bludit","type":"published"}]}
|
||||
*/
|
||||
|
||||
// $_GET
|
||||
|
|
|
@ -1,14 +1,20 @@
|
|||
<?php defined('BLUDIT') or die('Bludit CMS.');
|
||||
|
||||
// Start the session
|
||||
// If the session is not possible to start the admin area is not available
|
||||
// If the session is not started the admin area is not available
|
||||
Session::start();
|
||||
if (Session::started()===false) {
|
||||
exit('Bludit CMS. Session initialization failed.');
|
||||
}
|
||||
|
||||
// The login object contains the authentication system and/or the current user logged
|
||||
$login = new Login();
|
||||
|
||||
// Initialize plugins
|
||||
include(PATH_RULES.'60.plugins.php');
|
||||
|
||||
// Parameters for the controller and view
|
||||
// For example "title" keeps the HTML tag <title>
|
||||
$layout = array(
|
||||
'controller'=>null,
|
||||
'view'=>null,
|
||||
|
@ -19,14 +25,14 @@ $layout = array(
|
|||
'title'=>'Bludit'
|
||||
);
|
||||
|
||||
// Get the Controller
|
||||
// Get from the URL the controller and view
|
||||
$explodeSlug = $url->explodeSlug();
|
||||
$layout['controller'] = $layout['view'] = $layout['slug'] = empty($explodeSlug[0])?'dashboard':$explodeSlug[0];
|
||||
unset($explodeSlug[0]);
|
||||
|
||||
// Get the Plugins
|
||||
include(PATH_RULES.'60.plugins.php');
|
||||
// Check if the user want to access to an admin controller or view from a plugin
|
||||
// Check if the user want to get access to an admin controller or view from a plugin
|
||||
// To get access to a plugin controller or view the URL should be: http://localhost/admin/plugin/<PLUGIN NAME>
|
||||
// $explodeSlug = [0=>'<PLUGIN NAME>']
|
||||
if ($layout['controller'] === 'plugin' && !empty($explodeSlug)) {
|
||||
// Lowercase plugins class name to search by case-insensitive
|
||||
$pluginsLowerCases = array_change_key_case($pluginsInstalled);
|
||||
|
@ -53,52 +59,50 @@ if ($layout['slug']==='ajax') {
|
|||
header('HTTP/1.1 401 User not logged.');
|
||||
exit(0);
|
||||
}
|
||||
// --- ADMIN AREA ---
|
||||
else
|
||||
{
|
||||
// Boot rules
|
||||
include(PATH_RULES.'69.pages.php');
|
||||
include(PATH_RULES.'99.header.php');
|
||||
include(PATH_RULES.'99.paginator.php');
|
||||
include(PATH_RULES.'99.themes.php');
|
||||
include(PATH_RULES.'99.security.php');
|
||||
|
||||
// Page not found.
|
||||
// User not logged.
|
||||
// Slug is login.
|
||||
if ($url->notFound() || !$login->isLogged() || ($url->slug()==='login') ) {
|
||||
$layout['controller'] = 'login';
|
||||
$layout['view'] = 'login';
|
||||
$layout['template'] = 'login.php';
|
||||
// Boot rules
|
||||
include(PATH_RULES.'69.pages.php');
|
||||
include(PATH_RULES.'99.header.php');
|
||||
include(PATH_RULES.'99.paginator.php');
|
||||
include(PATH_RULES.'99.themes.php');
|
||||
include(PATH_RULES.'99.security.php');
|
||||
|
||||
// Generate the tokenCSRF for the user not logged, when the user log-in the token will be change.
|
||||
$security->generateTokenCSRF();
|
||||
}
|
||||
// Define layout login-form for:
|
||||
// - User not logged
|
||||
// - Page not found
|
||||
// - Slug is login. http://localhost/admin/login
|
||||
if ($url->notFound() || !$login->isLogged() || ($url->slug()==='login') ) {
|
||||
$layout['controller'] = 'login';
|
||||
$layout['view'] = 'login';
|
||||
$layout['template'] = 'login.php';
|
||||
|
||||
// Define variables
|
||||
$ADMIN_CONTROLLER = $layout['controller'];
|
||||
$ADMIN_VIEW = $layout['view'];
|
||||
|
||||
// Load plugins before the admin area will be load.
|
||||
Theme::plugins('beforeAdminLoad');
|
||||
|
||||
// Load init.php if the theme has one.
|
||||
if (Sanitize::pathFile(PATH_ADMIN_THEMES, $site->adminTheme().DS.'init.php')) {
|
||||
include(PATH_ADMIN_THEMES.$site->adminTheme().DS.'init.php');
|
||||
}
|
||||
|
||||
// Load controller.
|
||||
if (Sanitize::pathFile(PATH_ADMIN_CONTROLLERS, $layout['controller'].'.php')) {
|
||||
include(PATH_ADMIN_CONTROLLERS.$layout['controller'].'.php');
|
||||
} elseif ($layout['plugin'] && method_exists($layout['plugin'], 'adminController')) {
|
||||
$layout['plugin']->adminController();
|
||||
}
|
||||
|
||||
// Load view and theme.
|
||||
if (Sanitize::pathFile(PATH_ADMIN_THEMES, $site->adminTheme().DS.$layout['template'])) {
|
||||
include(PATH_ADMIN_THEMES.$site->adminTheme().DS.$layout['template']);
|
||||
}
|
||||
|
||||
// Load plugins after the admin area is loaded.
|
||||
Theme::plugins('afterAdminLoad');
|
||||
// Generate the tokenCSRF for the user not logged, when the user log-in the token will change
|
||||
$security->generateTokenCSRF();
|
||||
}
|
||||
|
||||
// Define global variables
|
||||
$ADMIN_CONTROLLER = $layout['controller'];
|
||||
$ADMIN_VIEW = $layout['view'];
|
||||
|
||||
// Execute plugins before load the admin area
|
||||
execPluginsByHook('beforeAdminLoad');
|
||||
|
||||
// Load init.php if the theme has one
|
||||
if (Sanitize::pathFile(PATH_ADMIN_THEMES, $site->adminTheme().DS.'init.php')) {
|
||||
include(PATH_ADMIN_THEMES.$site->adminTheme().DS.'init.php');
|
||||
}
|
||||
|
||||
// Load controller
|
||||
if (Sanitize::pathFile(PATH_ADMIN_CONTROLLERS, $layout['controller'].'.php')) {
|
||||
include(PATH_ADMIN_CONTROLLERS.$layout['controller'].'.php');
|
||||
} elseif ($layout['plugin'] && method_exists($layout['plugin'], 'adminController')) {
|
||||
$layout['plugin']->adminController();
|
||||
}
|
||||
|
||||
// Load view and theme
|
||||
if (Sanitize::pathFile(PATH_ADMIN_THEMES, $site->adminTheme().DS.$layout['template'])) {
|
||||
include(PATH_ADMIN_THEMES.$site->adminTheme().DS.$layout['template']);
|
||||
}
|
||||
|
||||
// Execute plugins after the admin area is loaded
|
||||
execPluginsByHook('afterAdminLoad');
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<?php defined('BLUDIT') or die('Bludit CMS.');
|
||||
|
||||
// ============================================================================
|
||||
// Variables
|
||||
// Global Variables
|
||||
// ============================================================================
|
||||
|
||||
$plugins = array(
|
||||
|
@ -37,12 +37,12 @@ $plugins = array(
|
|||
'loginBodyBegin'=>array(),
|
||||
'loginBodyEnd'=>array(),
|
||||
|
||||
'all'=>array()
|
||||
'all'=>array() // $plugins['all'] keep installed and not installed plugins
|
||||
);
|
||||
|
||||
$pluginsEvents = $plugins;
|
||||
unset($pluginsEvents['all']);
|
||||
|
||||
// This array has only the installed plugins
|
||||
// The array key is the "plugin class name" and the value is the object
|
||||
// pluginsInstalled[pluginClass] = $Plugin
|
||||
$pluginsInstalled = array();
|
||||
|
||||
// ============================================================================
|
||||
|
@ -52,19 +52,20 @@ $pluginsInstalled = array();
|
|||
function buildPlugins()
|
||||
{
|
||||
global $plugins;
|
||||
global $pluginsEvents;
|
||||
global $pluginsInstalled;
|
||||
global $L;
|
||||
global $site;
|
||||
|
||||
// This array is only to get the hooks names
|
||||
$pluginsHooks = $plugins;
|
||||
unset($pluginsHooks['all']); // remove "all" because is not a valid hook
|
||||
|
||||
// Get declared clasess BEFORE load plugins clasess
|
||||
$currentDeclaredClasess = get_declared_classes();
|
||||
|
||||
// List plugins directories
|
||||
// Load plugins clasess
|
||||
$list = Filesystem::listDirectories(PATH_PLUGINS);
|
||||
// Load each plugin clasess
|
||||
foreach ($list as $pluginPath) {
|
||||
// Check if the directory has the plugin.php
|
||||
if (file_exists($pluginPath.DS.'plugin.php')) {
|
||||
include_once($pluginPath.DS.'plugin.php');
|
||||
}
|
||||
|
@ -89,8 +90,7 @@ function buildPlugins()
|
|||
$Plugin->setMetadata('name',$database['plugin-data']['name']);
|
||||
$Plugin->setMetadata('description',$database['plugin-data']['description']);
|
||||
|
||||
// Remove name and description from the language file loaded and add new words if there are
|
||||
// This function overwrite the key=>value
|
||||
// Remove name and description from the language and includes new words to the global language dictionary
|
||||
unset($database['plugin-data']);
|
||||
if (!empty($database)) {
|
||||
$L->add($database);
|
||||
|
@ -99,22 +99,24 @@ function buildPlugins()
|
|||
// $plugins['all'] Array with all plugins, installed and not installed
|
||||
$plugins['all'][$pluginClass] = $Plugin;
|
||||
|
||||
// If the plugin is installed insert on the hooks
|
||||
if ($Plugin->installed()) {
|
||||
// Include custom hooks
|
||||
// Include the plugin installed in the global array
|
||||
$pluginsInstalled[$pluginClass] = $Plugin;
|
||||
|
||||
// Define new hooks from custom hooks
|
||||
if (!empty($Plugin->customHooks)) {
|
||||
foreach ($Plugin->customHooks as $customHook) {
|
||||
if (!isset($plugins[$customHook])) {
|
||||
$plugins[$customHook] = array();
|
||||
$pluginsEvents[$customHook] = array();
|
||||
foreach ($Plugin->customHooks as $hook) {
|
||||
if (!isset($plugins[$hook])) {
|
||||
$plugins[$hook] = array();
|
||||
$pluginsHooks[$hook] = array();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$pluginsInstalled[$pluginClass] = $Plugin;
|
||||
foreach ($pluginsEvents as $event=>$value) {
|
||||
if (method_exists($Plugin, $event)) {
|
||||
array_push($plugins[$event], $Plugin);
|
||||
// Insert the plugin into the hooks
|
||||
foreach ($pluginsHooks as $hook=>$value) {
|
||||
if (method_exists($Plugin, $hook)) {
|
||||
array_push($plugins[$hook], $Plugin);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
79
bl-kernel/css/token-autocomplete.css
Normal file
79
bl-kernel/css/token-autocomplete.css
Normal file
|
@ -0,0 +1,79 @@
|
|||
.token-autocomplete-container {
|
||||
display: block;
|
||||
flex-wrap: wrap;
|
||||
border: 1px solid #E6E6E6;
|
||||
background-color: #FFFFFF;
|
||||
}
|
||||
|
||||
.token-autocomplete-container, .token-autocomplete-container * {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.token-autocomplete-container .token-autocomplete-input {
|
||||
display: block;
|
||||
line-height: 32px;
|
||||
margin: 4px 2px;
|
||||
padding: 0px 8px;
|
||||
}
|
||||
|
||||
.token-autocomplete-container .token-autocomplete-input:empty::before {
|
||||
content: attr(data-placeholder);
|
||||
color: rgb(0,0,0,0.6);
|
||||
}
|
||||
|
||||
.token-autocomplete-container .token-autocomplete-token {
|
||||
width: 100%;
|
||||
display: block;
|
||||
padding: 2px 8px;
|
||||
background: #ccc;
|
||||
}
|
||||
|
||||
.token-autocomplete-container .token-autocomplete-token:hover {
|
||||
background-color: #ddd;
|
||||
}
|
||||
|
||||
.token-autocomplete-container .token-autocomplete-token .token-autocomplete-token-delete {
|
||||
cursor: pointer;
|
||||
font-size: 24px;
|
||||
line-height: 16px;
|
||||
margin-left: 4px;
|
||||
pointer-events: auto;
|
||||
border-radius: 50%;
|
||||
height: 24px;
|
||||
width: 24px;
|
||||
display: inline-block;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.token-autocomplete-container .token-autocomplete-token .token-autocomplete-token-delete:hover {
|
||||
background-color: #e55858;
|
||||
}
|
||||
|
||||
.token-autocomplete-container .token-autocomplete-suggestions {
|
||||
display: none;
|
||||
width: 100%;
|
||||
list-style-type: none;
|
||||
padding: 0px;
|
||||
margin: 0px;
|
||||
}
|
||||
|
||||
.token-autocomplete-container .token-autocomplete-suggestions li {
|
||||
width: 100%;
|
||||
padding: 8px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.token-autocomplete-container .token-autocomplete-suggestions li.token-autocomplete-suggestion-active {
|
||||
color: #747474;
|
||||
background-color: #fdfdfd;
|
||||
}
|
||||
|
||||
.token-autocomplete-container .token-autocomplete-suggestions li.token-autocomplete-suggestion-highlighted {
|
||||
background-color: #95caec;
|
||||
}
|
||||
|
||||
.token-autocomplete-container .token-autocomplete-suggestions li .token-autocomplete-suggestion-description {
|
||||
display:block;
|
||||
font-size: 0.7em;
|
||||
color: #808080;
|
||||
}
|
|
@ -194,9 +194,10 @@ function getPlugin($pluginClassName) {
|
|||
return false;
|
||||
}
|
||||
|
||||
// Returns TRUE if the plugin is activaed / installed, FALSE otherwise
|
||||
// Check if the plugin is activated / installed
|
||||
// Returns TRUE if the plugin is activated / installed, FALSE otherwise
|
||||
function pluginActivated($pluginClassName) {
|
||||
global $plugins;
|
||||
global $plugins;
|
||||
|
||||
if (isset($plugins['all'][$pluginClassName])) {
|
||||
return $plugins['all'][$pluginClassName]->installed();
|
||||
|
@ -204,6 +205,8 @@ function pluginActivated($pluginClassName) {
|
|||
return false;
|
||||
}
|
||||
|
||||
// Activate / install the plugin
|
||||
// Returns TRUE if the plugin is successfully activated, FALSE otherwise
|
||||
function activatePlugin($pluginClassName) {
|
||||
global $plugins;
|
||||
global $syslog;
|
||||
|
@ -227,6 +230,8 @@ function activatePlugin($pluginClassName) {
|
|||
return false;
|
||||
}
|
||||
|
||||
// Deactivate / uninstall the plugin
|
||||
// Returns TRUE if the plugin is successfully deactivated, FALSE otherwise
|
||||
function deactivatePlugin($pluginClassName) {
|
||||
global $plugins;
|
||||
global $syslog;
|
||||
|
@ -291,6 +296,14 @@ function changePluginsPosition($pluginClassList) {
|
|||
return true;
|
||||
}
|
||||
|
||||
// Execute the plugins by hook
|
||||
function execPluginsByHook($hook, $args = array()) {
|
||||
global $plugins;
|
||||
foreach ($plugins[$hook] as $plugin) {
|
||||
echo call_user_func_array(array($plugin, $hook), $args);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
Create a new page
|
||||
|
||||
|
|
|
@ -27,7 +27,7 @@ class Filesystem {
|
|||
// $chunk = amount of chunks, FALSE if you don't want to chunk
|
||||
public static function listFiles($path, $regex='*', $extension='*', $sortByDate=false, $chunk=false)
|
||||
{
|
||||
error_log($path.$regex.'.'.$extension);
|
||||
Log::set('list files = '.$path.$regex.'.'.$extension, LOG_TYPE_INFO);
|
||||
$files = glob($path.$regex.'.'.$extension);
|
||||
|
||||
if (empty($files)) {
|
||||
|
|
|
@ -1 +1,8 @@
|
|||
<svg width="429" height="238" viewBox="0 0 429 238" xmlns="http://www.w3.org/2000/svg"><title>missing-image-4x3</title><g fill-rule="nonzero" fill="none"><path d="M0 0h429v238H0z" fill="#F0F1F2"/><path d="M160.875 79v77.7h107.25V79h-107.25zm101.888 4.9v39.8l-28.85-26.1-28.744 26.8-14.693-13.7-24.239 22.7V83.9h96.525zm-96.525 56.5l24.345-22.7 36.358 33.9h-60.704v-11.2zm68.21 11.3l-25.525-23.8 24.99-23.3 28.85 26.1v21h-28.314z" fill="#DCDDDF"/></g></svg>
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<svg width="429" height="238" viewBox="0 0 429 238" xmlns="http://www.w3.org/2000/svg">
|
||||
<title>missing-image-4x3</title>
|
||||
<g fill-rule="nonzero" fill="none">
|
||||
<rect x="0.083" y="-0.184" width="428.965" height="237.991" style="fill: rgb(164, 164, 164);"/>
|
||||
<path d="M160.875 79v77.7h107.25V79h-107.25zm101.888 4.9v39.8l-28.85-26.1-28.744 26.8-14.693-13.7-24.239 22.7V83.9h96.525zm-96.525 56.5l24.345-22.7 36.358 33.9h-60.704v-11.2zm68.21 11.3l-25.525-23.8 24.99-23.3 28.85 26.1v21h-28.314z" fill="#DCDDDF"/>
|
||||
</g>
|
||||
</svg>
|
Before Width: | Height: | Size: 456 B After Width: | Height: | Size: 568 B |
89
bl-kernel/js/api.js
Normal file
89
bl-kernel/js/api.js
Normal file
|
@ -0,0 +1,89 @@
|
|||
class API {
|
||||
|
||||
constructor(apiURL, apiToken, apiAuth) {
|
||||
this.apiURL = "http://localhost:9000/api/";
|
||||
this.body = {
|
||||
token: '45643a4071fad6a12261bb0763550feb',
|
||||
authentication: '18a8410f0043d004c2e87f404170e112'
|
||||
}
|
||||
}
|
||||
|
||||
async createPage(args) {
|
||||
var url = this.apiURL+"pages";
|
||||
var body = Object.assign({}, this.body, args);
|
||||
try {
|
||||
var response = await fetch(url, {
|
||||
credentials: "same-origin",
|
||||
method: "POST",
|
||||
body: JSON.stringify(body),
|
||||
headers: new Headers({
|
||||
"Content-Type": "application/json"
|
||||
})
|
||||
});
|
||||
var json = await response.json();
|
||||
return json.data.key;
|
||||
}
|
||||
catch (err) {
|
||||
console.log(err);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
Save page fields
|
||||
|
||||
$args['pageKey'] string Page key for the page to edit
|
||||
$args array Arguments can be any of the fields from a page
|
||||
|
||||
returns string New page key
|
||||
*/
|
||||
async savePage(args) {
|
||||
var url = this.apiURL+"pages/"+args['pageKey'];
|
||||
var body = Object.assign({}, this.body, args);
|
||||
try {
|
||||
var response = await fetch(url, {
|
||||
credentials: "same-origin",
|
||||
method: "PUT",
|
||||
body: JSON.stringify(body),
|
||||
headers: new Headers({
|
||||
"Content-Type": "application/json"
|
||||
})
|
||||
});
|
||||
var json = await response.json();
|
||||
return json.data.key;
|
||||
}
|
||||
catch (err) {
|
||||
console.log(err);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
Generates unique slug text for the a page
|
||||
|
||||
$args['pageKey'] string Page key for the page to generate the slug url
|
||||
$args['text'] string Text that you want to generate the slug url
|
||||
$args['parentKey'] string Parent page key if the page has one, if not empty string
|
||||
|
||||
returns string Slug text
|
||||
*/
|
||||
async friendlyURL(args) {
|
||||
var url = this.apiURL+"helper/friendly-url/";
|
||||
var parameters = "?token="+this.body.token+"&authentication="+this.body.authentication;
|
||||
parameters = parameters+"&pageKey="+args['pageKey'];
|
||||
parameters = parameters+"&text="+args['text'];
|
||||
parameters = parameters+"&parentKey="+args['parentKey'];
|
||||
try {
|
||||
const response = await fetch(url+parameters, {
|
||||
method: "GET"
|
||||
});
|
||||
var json = await response.json();
|
||||
return json.data;
|
||||
}
|
||||
catch (err) {
|
||||
console.log(err);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -1,5 +1,39 @@
|
|||
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 {
|
||||
|
|
|
@ -34,3 +34,9 @@ function getCookie(name) {
|
|||
function deleteCookie(name) {
|
||||
document.cookie = name+'=; Max-Age=-999;';
|
||||
}
|
||||
|
||||
function logs(message) {
|
||||
if (DEBUG_MODE) {
|
||||
console.log(message);
|
||||
}
|
||||
}
|
372
bl-kernel/js/token-autocomplete.js
Normal file
372
bl-kernel/js/token-autocomplete.js
Normal file
|
@ -0,0 +1,372 @@
|
|||
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;
|
||||
}());
|
||||
//# sourceMappingURL=token-autocomplete.js.map
|
|
@ -19,5 +19,7 @@ echo 'var AUTOSAVE_INTERVAL = "'.AUTOSAVE_INTERVAL.'";'.PHP_EOL;
|
|||
echo 'var PAGE_BREAK = "'.PAGE_BREAK.'";'.PHP_EOL;
|
||||
echo 'var tokenCSRF = "'.$security->getTokenCSRF().'";'.PHP_EOL;
|
||||
echo 'var UPLOAD_MAX_FILESIZE = '.Text::toBytes( ini_get('upload_max_filesize') ).';'.PHP_EOL;
|
||||
echo 'var DEBUG_MODE = '.(DEBUG_MODE?'true':'false').';'.PHP_EOL;
|
||||
echo 'var api = new API("", "", "", tokenCSRF);'.PHP_EOL;
|
||||
|
||||
?>
|
||||
?>
|
||||
|
|
|
@ -143,8 +143,8 @@ class pluginAPI extends Plugin {
|
|||
}
|
||||
// (PUT) /api/pages/<key>
|
||||
elseif ( ($method==='PUT') && ($parameters[0]==='pages') && !empty($parameters[1]) && $writePermissions ) {
|
||||
$pageKey = $parameters[1];
|
||||
$data = $this->editPage($pageKey, $inputs);
|
||||
$inputs['key'] = $parameters[1];
|
||||
$data = $this->editPage($inputs);
|
||||
}
|
||||
// (DELETE) /api/pages/<key>
|
||||
elseif ( ($method==='DELETE') && ($parameters[0]==='pages') && !empty($parameters[1]) && $writePermissions ) {
|
||||
|
@ -204,6 +204,13 @@ class pluginAPI extends Plugin {
|
|||
$pageKey = $parameters[1];
|
||||
$data = $this->uploadFile($pageKey);
|
||||
}
|
||||
// (GET) /api/helper/<helper-name>
|
||||
elseif ( ($method==='GET') && ($parameters[0]==='helper') && !empty($parameters[1]) ) {
|
||||
$helperName = $parameters[1];
|
||||
if ($helperName=='friendly-url') {
|
||||
$data = $this->getFriendlyURL($inputs);
|
||||
}
|
||||
}
|
||||
else {
|
||||
$this->response(401, 'Unauthorized', array('message'=>'Access denied or invalid endpoint.'));
|
||||
}
|
||||
|
@ -429,14 +436,13 @@ class pluginAPI extends Plugin {
|
|||
);
|
||||
}
|
||||
|
||||
private function editPage($key, $args)
|
||||
private function editPage($args)
|
||||
{
|
||||
// Unsanitize content because all values are sanitized
|
||||
if (isset($args['content'])) {
|
||||
$args['content'] = Sanitize::htmlDecode($args['content']);
|
||||
}
|
||||
|
||||
$args['key'] = $key;
|
||||
$newKey = editPage($args);
|
||||
|
||||
if ($newKey===false) {
|
||||
|
@ -761,4 +767,25 @@ class pluginAPI extends Plugin {
|
|||
'message'=>'Error moving the file to the final path.'
|
||||
);
|
||||
}
|
||||
|
||||
/*
|
||||
Generates unique slug text for the a page
|
||||
|
||||
$args['text'] string
|
||||
$args['parentKey'] string
|
||||
$args['pageKey'] string
|
||||
|
||||
returns['data'] string
|
||||
*/
|
||||
private function getFriendlyURL($args)
|
||||
{
|
||||
global $pages;
|
||||
$slug = $pages->generateKey($args['text'], $args['parentKey'], true, $args['pageKey']);
|
||||
|
||||
return array(
|
||||
'status'=>'0',
|
||||
'message'=>'Friendly URL generated.',
|
||||
'data'=>$slug
|
||||
);
|
||||
}
|
||||
}
|
|
@ -91,6 +91,18 @@ return <<<EOF
|
|||
return easymde.value();
|
||||
}
|
||||
|
||||
// Insert HTML content at the cursor position
|
||||
// Function required for Bludit
|
||||
function editorInsertContent(html, type='') {
|
||||
var text = easymde.value();
|
||||
if (type == 'image') {
|
||||
easymde.value(text + "" + "\\n");
|
||||
} else {
|
||||
easymde.value(html + "\\n");
|
||||
}
|
||||
easymde.codemirror.refresh();
|
||||
}
|
||||
|
||||
easymde = new EasyMDE({
|
||||
element: document.getElementById("jseditor"),
|
||||
status: false,
|
||||
|
|
|
@ -92,6 +92,12 @@ $html = <<<EOF
|
|||
return tinymce.get('jseditor').getContent();
|
||||
}
|
||||
|
||||
// Insert HTML content at the cursor position
|
||||
// Function required for Bludit
|
||||
function editorInsertContent(html, type='') {
|
||||
tinymce.activeEditor.insertContent(html);
|
||||
}
|
||||
|
||||
tinymce.init({
|
||||
selector: "#jseditor",
|
||||
auto_focus: "jseditor",
|
||||
|
@ -115,7 +121,12 @@ $html = <<<EOF
|
|||
toolbar1: "$toolbar1",
|
||||
toolbar2: "$toolbar2",
|
||||
language: "$lang",
|
||||
content_css: "$content_css"
|
||||
content_css: "$content_css",
|
||||
init_instance_callback: function(editor) {
|
||||
editor.on("keydown", function(event) {
|
||||
keypress(event);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
</script>
|
||||
|
|
BIN
bl-themes/tagg.zip
Normal file
BIN
bl-themes/tagg.zip
Normal file
Binary file not shown.
Loading…
Add table
Reference in a new issue