275 lines
8.1 KiB
PHP
275 lines
8.1 KiB
PHP
<?php
|
|
// SPDX-License-Identifier: EUPL-1.2
|
|
// Authors: see README.md
|
|
|
|
use PicoContentEditor\Status;
|
|
use PicoContentEditor\Auth;
|
|
use PicoContentEditor\Edits;
|
|
use PicoContentEditor\Uploads;
|
|
use PicoContentEditor\EditorsHandlers\ContentTools;
|
|
use SeaCMS\Api\ApiAware;
|
|
|
|
/**
|
|
* A content editor plugin for Pico 3, using ContentTools.
|
|
*
|
|
* Supports PicoUsers plugin for authentification
|
|
* {@link https://github.com/nliautaud/pico-users}
|
|
*
|
|
* @author Nicolas Liautaud
|
|
* @license http://opensource.org/licenses/MIT The MIT License
|
|
* @link https://github.com/nliautaud/pico-content-editor
|
|
* @link http://picocms.org
|
|
*/
|
|
class PicoContentEditor extends AbstractPicoPlugin implements ApiAware
|
|
{
|
|
/**
|
|
* Pico API version.
|
|
* @var int
|
|
*/
|
|
const API_VERSION = 4;
|
|
|
|
/**
|
|
* EditorHandler (ContentToolsHandler, QuillHandler...)
|
|
* @var ContentTools
|
|
*/
|
|
private $editor;
|
|
/**
|
|
* Files edits manager.
|
|
* @var Edits
|
|
*/
|
|
private $edits;
|
|
/**
|
|
* Uploads manager.
|
|
* @var Uploads
|
|
*/
|
|
private $uploads;
|
|
|
|
/**
|
|
* return api routes
|
|
* @return array
|
|
*/
|
|
public function registerApiRoutes():array
|
|
{
|
|
return [
|
|
];
|
|
}
|
|
|
|
/**
|
|
* Init plugin, process page edits and file uploads.
|
|
*
|
|
* The end-editable mark @see{Edits::ENDMARK} need to be
|
|
* striped away because it's somewhat breaking the page rendering,
|
|
* and thus @see{Edits::saveRegions()} has to be done here
|
|
* in addition to @see{self::onPageRendered()}.
|
|
*
|
|
* Triggered after Pico has read the contents of the file to serve
|
|
*
|
|
* @see Pico::getRawContent()
|
|
* @param string &$rawContent raw file contents
|
|
* @return void
|
|
*/
|
|
public function onContentLoaded(&$rawContent)
|
|
{
|
|
if ($this->getPluginSetting('debug')) {
|
|
ini_set('display_startup_errors', 1);
|
|
ini_set('display_errors', 1);
|
|
error_reporting(-1);
|
|
}
|
|
|
|
Auth::init($this);
|
|
$this->editor = new ContentTools();
|
|
$this->uploads = new Uploads($this);
|
|
$this->edits = new Edits();
|
|
|
|
$this->processPageEdits($rawContent);
|
|
|
|
// remove the end-editable mark
|
|
$rawContent = preg_replace('`'.Edits::ENDMARK.'`', '', $rawContent);
|
|
}
|
|
|
|
/**
|
|
* Triggered when Pico registers the twig template engine
|
|
*
|
|
* @see Pico::getTwig()
|
|
*
|
|
* @param Twig_Environment &$twig Twig instance
|
|
*/
|
|
public function onTwigRegistered(Twig_Environment &$twig)
|
|
{
|
|
$twigLoader = $twig->getLoader();
|
|
$templateDir = $this->getPluginsDir().'PicoContentEditor/templates';
|
|
if (is_dir($templateDir)){
|
|
$twigLoader->addPath($templateDir, 'PicoContentEditor');
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Register `{{ content_editor }}` who outputs the editor scripts
|
|
* and `{{ content_editor_meta }}` who outputs the metadata editor.
|
|
*
|
|
* Triggered before Pico renders the page
|
|
*
|
|
* @see DummyPlugin::onPageRendered()
|
|
* @param string &$templateName file name of the template
|
|
* @param array &$twigVariables template variables
|
|
* @return void
|
|
*/
|
|
public function onPageRendering(&$templateName, array &$twigVariables)
|
|
{
|
|
if (!$this->getPluginSetting('show', true) || !Auth::can(Auth::EDIT)) {
|
|
return;
|
|
}
|
|
|
|
// $twigVariables['canEdit'] = true;
|
|
$twigVariables['canEdit'] = false;
|
|
$twigVariables['dataLang'] = $this->editor::assets($this, "{$this->getPluginsDir()}PicoContentEditor");
|
|
}
|
|
/**
|
|
* If the call is a save query, save the edited regions and output the JSON response.
|
|
*
|
|
* Triggered after Pico has rendered the page
|
|
*
|
|
* @param string &$output contents which will be sent to the user
|
|
* @return void
|
|
*/
|
|
public function onPageRendered(&$output)
|
|
{
|
|
$this->processOverallEdits($output);
|
|
|
|
if (!$this->edits->beenReceived() && !$this->uploads->beenReceived()) {
|
|
return;
|
|
}
|
|
|
|
// output response
|
|
$response = new stdClass();
|
|
$response->status = Status::getStatus();
|
|
$response->edited = $this->edits->output();
|
|
$response->file = $this->uploads->output();
|
|
$response->debug = $this->getPluginSetting('debug', false);
|
|
$output = json_encode($response);
|
|
}
|
|
/**
|
|
* Save page metadata and edited regions if authorized.
|
|
*
|
|
* @param string $rawContent raw file contents
|
|
* @return void
|
|
*/
|
|
public function processPageEdits($rawContent)
|
|
{
|
|
if (!$this->edits->beenReceived()) {
|
|
return;
|
|
}
|
|
if (Auth::can(Auth::SAVE)) {
|
|
$this->edits->saveMeta(
|
|
$rawContent,
|
|
$this->getRawMeta(),
|
|
$this->getRequestFile()
|
|
);
|
|
$saved = $this->edits->saveRegions($rawContent, $this, $this->editor);
|
|
if ($saved) {
|
|
Status::add(true, 'The page have been saved');
|
|
}
|
|
} else {
|
|
Status::add(false, 'You don\'t have the rights to save content');
|
|
}
|
|
}
|
|
/**
|
|
* Save edited regions from overall output if authorized, including themes files.
|
|
*
|
|
* @param string $output contents which will be sent to the user
|
|
* @return void
|
|
*/
|
|
public function processOverallEdits($output)
|
|
{
|
|
if (!$this->edits->beenReceived()) {
|
|
return;
|
|
}
|
|
if (Auth::can(Auth::SAVE)) {
|
|
$saved = $this->edits->saveRegions($output, $this, $this->editor);
|
|
if ($saved) {
|
|
Status::add(true, 'The theme files have been saved');
|
|
}
|
|
}
|
|
}
|
|
/**
|
|
* Return the current page raw metadata.
|
|
*
|
|
* @return string
|
|
*/
|
|
private function getRawMeta()
|
|
{
|
|
$pattern = "/^(\/(\*)|---)[[:blank:]]*(?:\r)?\n"
|
|
. "(?:(.*?)(?:\r)?\n)?(?(2)\*\/|---)[[:blank:]]*(?:(?:\r)?\n|$)/s";
|
|
if (preg_match($pattern, $this->getRawContent(), $rawMetaMatches)
|
|
&& isset($rawMetaMatches[3])) {
|
|
return $rawMetaMatches[3];
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Return a plugin setting, either on the page metadata or on the pico config file.
|
|
*
|
|
* @param string $name name of a setting
|
|
* @param mixed $default optional default value to return when the setting doesn't exist
|
|
* @return mixed return the setting value from the page metadata, or from the config file,
|
|
* or the given default value, or NULL
|
|
*/
|
|
public function getPluginSetting($name, $default = null, $caseSensitive = false)
|
|
{
|
|
if ($name === null) {
|
|
return null;
|
|
}
|
|
if (!$caseSensitive) {
|
|
$name = strtolower($name);
|
|
}
|
|
|
|
static $c;
|
|
static $clow;
|
|
if (!$c) {
|
|
$c = get_called_class();
|
|
if (!$caseSensitive) {
|
|
$clow = strtolower($c);
|
|
}
|
|
}
|
|
|
|
// from page metadata
|
|
static $pageMeta;
|
|
if (!$pageMeta) {
|
|
$pageMeta = $this->getFileMeta();
|
|
if ($pageMeta && !$caseSensitive) {
|
|
$pageMeta = self::deepArrayKeyCase($pageMeta, CASE_LOWER);
|
|
}
|
|
}
|
|
if ($pageMeta && isset($pageMeta[$clow]) && isset($pageMeta[$clow][$name])) {
|
|
return $pageMeta[$clow][$name];
|
|
}
|
|
|
|
// from config file
|
|
static $pluginConfig;
|
|
if (!$pluginConfig) {
|
|
$pluginConfig = $this->getConfig($c, array());
|
|
if (!$caseSensitive) {
|
|
$pluginConfig = self::deepArrayKeyCase($pluginConfig, CASE_LOWER);
|
|
}
|
|
}
|
|
return isset($pluginConfig[$name]) ? $pluginConfig[$name] : $default;
|
|
}
|
|
|
|
/**
|
|
* Change the case of every array keys, recursively.
|
|
*
|
|
* @param array $arr
|
|
* @param CASE_UPPER|CASE_LOWER $case
|
|
* @return array
|
|
*/
|
|
private static function deepArrayKeyCase($arr, $case)
|
|
{
|
|
return array_map(function ($item) use ($case) {
|
|
if (is_array($item)) {
|
|
$item = self::deepArrayKeyCase($item, $case);
|
|
}
|
|
return $item;
|
|
}, array_change_key_case($arr, $case));
|
|
}
|
|
}
|