wiki-grav/plugins/shortcode-core/classes/plugin/ShortcodeManager.php
2023-06-02 08:07:58 +02:00

384 lines
10 KiB
PHP

<?php
namespace Grav\Plugin\ShortcodeCore;
use Grav\Common\Config\Config;
use Grav\Common\Data\Data;
use Grav\Common\Grav;
use Grav\Common\Page\Interfaces\PageInterface;
use Thunder\Shortcode\EventContainer\EventContainer;
use Thunder\Shortcode\HandlerContainer\HandlerContainer;
use Thunder\Shortcode\Parser\RegexParser;
use Thunder\Shortcode\Parser\RegularParser;
use Thunder\Shortcode\Parser\WordpressParser;
use Thunder\Shortcode\Processor\Processor;
use Thunder\Shortcode\Shortcode\ShortcodeInterface;
use Thunder\Shortcode\Syntax\CommonSyntax;
class ShortcodeManager
{
/** @var Grav $grav */
protected $grav;
/** @var Config */
protected $config;
/** @var PageInterface $page */
protected $page;
/** @var HandlerContainer $handlers */
protected $handlers;
/** @var HandlerContainer $raw_handlers */
protected $raw_handlers;
/** @var EventContainer $events */
protected $events;
/** @var array */
protected $assets;
/** @var array */
protected $states;
/** @var array */
protected $objects;
/**
* initialize some internal instance variables
*/
public function __construct()
{
$this->grav = Grav::instance();
$this->config = $this->grav['config'];
$this->handlers = new HandlerContainer();
$this->raw_handlers = new HandlerContainer();
$this->events = new EventContainer();
$this->states = [];
$this->assets = [];
$this->objects = [];
}
/**
* add CSS and JS assets to the Manager so that they can be saved to cache
* for subsequent cached pages
*
* @param mixed $actionOrAsset the type of asset (JS or CSS) or, if the second parameter is omitted,
* a collection or an array of asset.
* @param string $asset the asset path in question
*/
public function addAssets($actionOrAsset, $asset = null)
{
if ($asset == null) {
if (is_array($actionOrAsset)) {
$this->assets[''] = array_merge($this->assets[''] ?? array(), $actionOrAsset);
} else {
$this->assets[''] [] = $actionOrAsset;
}
} else {
if (isset($this->assets[$actionOrAsset]) && in_array($asset, $this->assets[$actionOrAsset], true)) {
return;
}
$this->assets[$actionOrAsset] [] = $asset;
}
}
/**
* return a multi-dimensional array of all the assets
*
* @return array the assets array
*/
public function getAssets()
{
return $this->assets;
}
/**
* reset the assets
*/
public function resetAssets()
{
$this->assets = [];
}
/**
* adds ad object
* @param string $key The key to look up the object
* @param object $object The object to store
*/
public function addObject($key, $object)
{
$new = [$object->name() => $object];
if (array_key_exists($key, $this->objects)) {
$current = (array)$this->objects[$key];
$this->objects[$key] = $current + $new;
} else {
$this->objects[$key] = $new;
}
}
/**
* sets all the objects
* @param array $objects The objects array
*/
public function setObjects($objects)
{
$this->objects = $objects;
}
/**
* return all the objects
* @return array The objects array
*/
public function getObjects()
{
return $this->objects;
}
/**
* reset the objects
*/
public function resetObjects()
{
$this->objects = [];
}
/**
* returns the current handler container object
*
* @return HandlerContainer
*/
public function getHandlers()
{
return $this->handlers;
}
/**
* returns the current raw handler container object
*
* @return HandlerContainer
*/
public function getRawHandlers()
{
return $this->raw_handlers;
}
/**
* returns the current event container object
*
* @return EventContainer
*/
public function getEvents()
{
return $this->events;
}
/**
* Register an individual shortcode with the manager so it can be operated on by the Shortcode library
*
* @param string $name the name of the shortcode (should match the classname)
* @param string|null $directory directory where the shortcode is located
*/
public function registerShortcode($name, $directory = null)
{
$className = 'Grav\\Plugin\\Shortcodes\\' . basename($name, '.php');
if (!class_exists($className) && $directory) {
$path = rtrim($directory, '/').'/'.$name;
require_once $path;
}
// Make sure the class exists, extends Shortcode and is not abstract.
if (class_exists($className) && is_subclass_of($className, \Grav\Plugin\Shortcodes\Shortcode::class)) {
$reflection = new \ReflectionClass($className);
if (!$reflection->isAbstract()) {
/** @var \Grav\Plugin\Shortcodes\Shortcode $shortcode */
$shortcode = new $className();
$shortcode->init();
}
}
}
/**
* register all files as shortcodes in a particular directory
* @param string $directory directory where the shortcodes are located
* @param array $options Extra options
*/
public function registerAllShortcodes($directory, array $options = [])
{
try {
$ignore = $options['ignore'] ?? [];
foreach (new \DirectoryIterator($directory) as $file) {
if ($file->isDot() || \in_array($file->getBasename('.php'), $ignore, true)) {
continue;
}
$this->registerShortcode($file->getFilename(), $directory);
}
} catch (\UnexpectedValueException $e) {
Grav::instance()['log']->error('ShortcodeCore Plugin: Directory not found => ' . $directory);
}
}
/**
* setup the markdown parser to handle shortcodes properly
*
* @param object $markdown the markdown parser object
*/
public function setupMarkdown($markdown)
{
$markdown->addBlockType('[', 'ShortCodes', true, false);
$markdown->blockShortCodes = function($Line) {
$valid_shortcodes = implode('|', $this->handlers->getNames());
$regex = '/^\[\/?(?:' . $valid_shortcodes . ')[^\]]*\]$/';
if (preg_match($regex, trim($Line['body']), $matches)) {
return [
'markup' => $Line['body'],
];
}
return null;
};
}
/**
* process the content by running over all the known shortcodes with the
* chosen parser
*
* @param PageInterface $page the page to work on
* @param Data $config configuration merged with the page config
* @param null $handlers
* @return string
*/
public function processContent(PageInterface $page, Data $config, $handlers = null)
{
$parser = $this->getParser($config->get('parser'));
if (!$handlers) {
$handlers = $this->handlers;
}
if ($page && $config->get('enabled')) {
$this->page = $page;
$content = $page->getRawContent();
$processor = new Processor(new $parser(new CommonSyntax()), $handlers);
$processor = $processor->withEventContainer($this->events);
return $processor->process($content);
}
return null;
}
public function processRawContent(PageInterface $page, Data $config)
{
return $this->processContent($page, $config, $this->raw_handlers);
}
/**
* Allow the processing of shortcodes directly on a string
* For example when used by Twig directly
*
* @param $str
* @param null $handlers
* @return string
*/
public function processShortcodes($str, $handlers = null)
{
$parser = $this->getParser($this->config->get('parser'));
if (!$handlers) {
$handlers = $this->handlers;
}
$processor = new Processor(new $parser(new CommonSyntax()), $handlers);
return $processor->process($str);
}
public function processShortcodesRaw($str)
{
return $this->processShortcodes($str, $this->raw_handlers);
}
/**
* set a state of a particular item with a hash for retrieval later
*
* @param string $hash a unique hash code
* @param object $item some item to store
*/
public function setStates($hash, $item)
{
$this->states[$hash][] = $item;
}
/**
* returns the shortcode of a specific hash
*
* @param string $hash unique id of state
* @return ShortcodeInterface shortcode stored for this hash
*/
public function getStates($hash)
{
if (array_key_exists($hash, $this->states)) {
return $this->states[$hash];
}
return null;
}
/**
* helper method to create a unique shortcode based on the content
*
* @param ShortcodeInterface $shortcode
* @return string
*/
public function getId(ShortcodeInterface $shortcode)
{
return substr(md5($shortcode->getShortcodeText()), -10);
}
/**
* Sets the current page context
*
* @param PageInterface $page
*/
public function setPage(PageInterface $page)
{
$this->page = $page;
}
/** gets the current page context if set */
public function getPage()
{
return $this->page;
}
/**
* Get the appropriate parser object
*
* @param $parser
* @return string
*/
protected function getParser($parser)
{
switch($parser)
{
case 'regular':
$parser = RegularParser::class;
break;
case 'wordpress':
$parser = WordpressParser::class;
break;
default:
$parser = RegexParser::class;
break;
}
return $parser;
}
}