feat(demo): add story 1 — Sorano: Rock and Time
This commit is contained in:
@@ -0,0 +1,7 @@
|
||||
<?php
|
||||
|
||||
// This file is for older Grav versions (needed during plugin update).
|
||||
|
||||
if (!class_exists(\Grav\Plugin\Shortcodes\Shortcode::class, false)) {
|
||||
require_once __DIR__ . '/shortcodes/Shortcode.php';
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
<?php
|
||||
|
||||
// This file is for older Grav versions (needed during plugin update).
|
||||
|
||||
@trigger_error(
|
||||
'\\Grav\\Plugin\\ShortcodeManager class is deprecated, use \\Grav\\Plugin\\ShortcodeCore\\ShortcodeManager instead',
|
||||
E_USER_DEPRECATED
|
||||
);
|
||||
|
||||
if (!class_exists(\Grav\Plugin\ShortcodeCore\ShortcodeManager::class, false)) {
|
||||
require_once __DIR__ . '/plugin/ShortcodeManager.php';
|
||||
}
|
||||
|
||||
// Create alias for the deprecated class.
|
||||
class_alias(\Grav\Plugin\ShortcodeCore\ShortcodeManager::class, \Grav\Plugin\ShortcodeManager::class);
|
||||
@@ -0,0 +1,7 @@
|
||||
<?php
|
||||
|
||||
// This file is for older Grav versions (needed during plugin update).
|
||||
|
||||
if (!class_exists(\Grav\Plugin\ShortcodeCore\ShortcodeObject::class, false)) {
|
||||
require_once __DIR__ . '/shortcodes/ShortcodeObject.php';
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
<?php
|
||||
|
||||
namespace Grav\Plugin\ShortcodeCore;
|
||||
|
||||
// Check if the new class has been autoloaded. If not, trigger deprecation error.
|
||||
if (!class_exists(\Grav\Plugin\Shortcodes\Shortcode::class, false)) {
|
||||
@trigger_error(
|
||||
Shortcode::class . ' class is deprecated, use \\Grav\\Plugin\\Shortcodes\\Shortcode instead',
|
||||
E_USER_DEPRECATED
|
||||
);
|
||||
}
|
||||
|
||||
// Create alias for the deprecated class.
|
||||
class_alias(\Grav\Plugin\Shortcodes\Shortcode::class, Shortcode::class);
|
||||
|
||||
// Make sure that both IDE and composer knows about the deprecated class.
|
||||
if (false) {
|
||||
/**
|
||||
* @deprecated 4.2.1 This was a bad idea, reverting back to the old class.
|
||||
*/
|
||||
abstract class Shortcode extends \Grav\Plugin\Shortcodes\Shortcode
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,403 @@
|
||||
<?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 = [];
|
||||
}
|
||||
|
||||
/**
|
||||
* reset the states
|
||||
*/
|
||||
public function resetStates()
|
||||
{
|
||||
$this->states = [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset all accumulated state (objects, assets, states).
|
||||
* Useful for batch processing scenarios like search indexing
|
||||
* where memory needs to be freed between pages.
|
||||
*/
|
||||
public function resetAll()
|
||||
{
|
||||
$this->objects = [];
|
||||
$this->assets = [];
|
||||
$this->states = [];
|
||||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
<?php
|
||||
|
||||
namespace Grav\Plugin\ShortcodeCore;
|
||||
|
||||
class ShortcodeObject
|
||||
{
|
||||
protected $obj_name;
|
||||
protected $obj_object;
|
||||
|
||||
public function __construct($name, $object)
|
||||
{
|
||||
$this->obj_name = $name;
|
||||
$this->obj_object = $object;
|
||||
}
|
||||
|
||||
public function __toString()
|
||||
{
|
||||
return $this->obj_object;
|
||||
}
|
||||
|
||||
public function name()
|
||||
{
|
||||
return $this->obj_name;
|
||||
}
|
||||
|
||||
public function object()
|
||||
{
|
||||
return $this->obj_object;
|
||||
}
|
||||
}
|
||||
|
||||
// Make sure we also autoload the deprecated class.
|
||||
class_exists(\Grav\Plugin\Shortcodes\ShortcodeObject::class);
|
||||
@@ -0,0 +1,27 @@
|
||||
<?php
|
||||
|
||||
namespace Grav\Plugin\ShortcodeCore;
|
||||
|
||||
use Grav\Common\Grav;
|
||||
|
||||
class ShortcodeTwigVar
|
||||
{
|
||||
public function __call($name, $args)
|
||||
{
|
||||
/** @var ShortcodeManager $shortcode */
|
||||
$shortcode = Grav::instance()['shortcode'];
|
||||
$objects = $shortcode->getObjects();
|
||||
|
||||
if ($objects) {
|
||||
return $objects[$name] ?? [];
|
||||
}
|
||||
|
||||
$page_meta = Grav::instance()['page']->getContentMeta('shortcodeMeta');
|
||||
if (isset($page_meta['shortcode'])) {
|
||||
$objects = (array) $page_meta['shortcode'];
|
||||
return $objects[$name] ?? [];
|
||||
}
|
||||
|
||||
return [];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
<?php
|
||||
namespace Grav\Plugin\Shortcodes;
|
||||
|
||||
use Thunder\Shortcode\Shortcode\ShortcodeInterface;
|
||||
|
||||
class AlignShortcode extends Shortcode
|
||||
{
|
||||
public function init()
|
||||
{
|
||||
$this->shortcode->getHandlers()->add('center', static function(ShortcodeInterface $sc) {
|
||||
return '<div style="text-align: center;">' . $sc->getContent() . '</div>';
|
||||
});
|
||||
|
||||
$this->shortcode->getHandlers()->add('left', static function(ShortcodeInterface $sc) {
|
||||
return '<div style="text-align: left;">' . $sc->getContent() . '</div>';
|
||||
});
|
||||
|
||||
$this->shortcode->getHandlers()->add('right', static function(ShortcodeInterface $sc) {
|
||||
return '<div style="text-align: right;">' . $sc->getContent() . '</div>';
|
||||
});
|
||||
|
||||
$this->shortcode->getHandlers()->add('justify', static function(ShortcodeInterface $sc) {
|
||||
return '<div style="text-align: justify;">' . $sc->getContent() . '</div>';
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
<?php
|
||||
namespace Grav\Plugin\Shortcodes;
|
||||
|
||||
use Thunder\Shortcode\Shortcode\ShortcodeInterface;
|
||||
|
||||
class ColorShortcode extends Shortcode
|
||||
{
|
||||
public function init()
|
||||
{
|
||||
$this->shortcode->getHandlers()->add('color', function(ShortcodeInterface $sc) {
|
||||
$color = $sc->getParameter('color', $this->getBbCode($sc));
|
||||
|
||||
return '<span style="color: ' . $color . ';">' . $sc->getContent() . '</span>';
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
<?php
|
||||
namespace Grav\Plugin\Shortcodes;
|
||||
|
||||
use Thunder\Shortcode\Shortcode\ShortcodeInterface;
|
||||
|
||||
class ColumnsShortcode extends Shortcode
|
||||
{
|
||||
public function init()
|
||||
{
|
||||
$this->shortcode->getHandlers()->add('columns', static function(ShortcodeInterface $sc) {
|
||||
$column_count = (int)$sc->getParameter('count', 2);
|
||||
$column_width = $sc->getParameter('width', 'auto');
|
||||
$column_gap = $sc->getParameter('gap', 'normal');
|
||||
$column_rule = $sc->getParameter('rule', false);
|
||||
|
||||
$css_style = 'columns:' . $column_count . ' ' . $column_width . ';-moz-columns:' . $column_count . ' ' . $column_width . ';';
|
||||
$css_style .= 'column-gap:' . $column_gap . ';-moz-column-gap:' . $column_gap . ';';
|
||||
|
||||
if ($column_rule) {
|
||||
$css_style .= 'column-rule:' . $column_rule . ';-moz-column-rule:' . $column_rule . ';';
|
||||
}
|
||||
|
||||
return '<div class="sc-columns" style="' . $css_style . '">' . $sc->getContent() . '</div>';
|
||||
});
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
<?php
|
||||
namespace Grav\Plugin\Shortcodes;
|
||||
|
||||
use Thunder\Shortcode\Shortcode\ShortcodeInterface;
|
||||
|
||||
class DetailsShortcode extends Shortcode
|
||||
{
|
||||
public function init()
|
||||
{
|
||||
$this->shortcode->getHandlers()->add('details', function(ShortcodeInterface $sc) {
|
||||
// Get summary/title
|
||||
$summary = $sc->getParameter('summary', $this->getBbCode($sc));
|
||||
$summaryHTML = $summary ? '<summary>' . $summary . '</summary>' : '';
|
||||
|
||||
// Get classes for details
|
||||
$class = $sc->getParameter('class', $this->getBbCode($sc));
|
||||
$classHTML = (isset($class) and $class !== $summary) ? 'class="' . $class . '"' : '';
|
||||
|
||||
// Get content
|
||||
$content = $sc->getContent();
|
||||
|
||||
// Return the details/summary block
|
||||
return '<details ' . $classHTML . '>' . $summaryHTML . $content . '</details>';
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
<?php
|
||||
namespace Grav\Plugin\Shortcodes;
|
||||
|
||||
use Thunder\Shortcode\Shortcode\ShortcodeInterface;
|
||||
|
||||
class DivShortcode extends Shortcode
|
||||
{
|
||||
public function init()
|
||||
{
|
||||
$this->shortcode->getHandlers()->add('div', static function(ShortcodeInterface $sc) {
|
||||
$id = $sc->getParameter('id');
|
||||
$class = $sc->getParameter('class');
|
||||
$style = $sc->getParameter('style');
|
||||
|
||||
$id_output = $id ? ' id="' . $id . '" ': '';
|
||||
$class_output = $class ? ' class="' . $class . '"' : '';
|
||||
$style_output = $style ? ' style="' . $style . '"' : '';
|
||||
|
||||
return '<div ' . $id_output . $class_output . $style_output . '>' . $sc->getContent() . '</div>';
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
<?php
|
||||
namespace Grav\Plugin\Shortcodes;
|
||||
|
||||
use Grav\Common\Utils;
|
||||
use Thunder\Shortcode\Shortcode\ShortcodeInterface;
|
||||
|
||||
class FigureShortcode extends Shortcode
|
||||
{
|
||||
public function init()
|
||||
{
|
||||
$this->shortcode->getHandlers()->add('figure', function(ShortcodeInterface $sc) {
|
||||
$id = $sc->getParameter('id');
|
||||
$class = $sc->getParameter('class');
|
||||
$caption = $sc->getParameter('caption');
|
||||
$page = $this->grav['page'];
|
||||
|
||||
// Process any markdown on caption
|
||||
$caption = Utils::processMarkdown($caption, false, $page);
|
||||
|
||||
$id_output = $id ? 'id="' . $id . '" ': '';
|
||||
$class_output = $class ? 'class="' . $class . '"' : '';
|
||||
$caption_output = $caption ? '<figcaption>' . $caption . '</figcaption>' : '';
|
||||
|
||||
return '<figure ' . $id_output . ' ' . $class_output . '>'.$sc->getContent(). $caption_output . '</figure>';
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
<?php
|
||||
namespace Grav\Plugin\Shortcodes;
|
||||
|
||||
use Grav\Common\Utils;
|
||||
use Thunder\Shortcode\Shortcode\ShortcodeInterface;
|
||||
|
||||
class FontAwesomeShortcode extends Shortcode
|
||||
{
|
||||
public function init()
|
||||
{
|
||||
$this->shortcode->getHandlers()->add('fa', function(ShortcodeInterface $sc) {
|
||||
// Load assets if required
|
||||
if ($this->config->get('plugins.shortcode-core.fontawesome.load', false)) {
|
||||
$this->shortcode->addAssets('css', $this->config->get('plugins.shortcode-core.fontawesome.url'));
|
||||
}
|
||||
if ($this->config->get('plugins.shortcode-core.fontawesome.v5', false)) {
|
||||
$v5classes = ['fab', 'fal', 'fas', 'far', 'fad'];
|
||||
} else {
|
||||
$v5classes = [];
|
||||
}
|
||||
|
||||
// Get shortcode content and parameters
|
||||
$str = $sc->getContent();
|
||||
$icon = $sc->getParameter('icon', $sc->getParameter('fa', $this->getBbCode($sc)));
|
||||
|
||||
if (!Utils::startsWith($icon, 'fa-')) {
|
||||
$icon = 'fa-'.$icon;
|
||||
}
|
||||
|
||||
if ($icon) {
|
||||
$fa_class = 'fa';
|
||||
$extras = explode(',', $sc->getParameter('extras', ''));
|
||||
|
||||
foreach($extras as $extra) {
|
||||
if(!empty($extra)) {
|
||||
if(in_array($extra, $v5classes, true)) {
|
||||
$fa_class = $extra;
|
||||
continue;
|
||||
}
|
||||
if(!Utils::startsWith($extra, 'fa-')) {
|
||||
$extra = 'fa-' . $extra;
|
||||
}
|
||||
$icon .= ' ' . $extra;
|
||||
}
|
||||
}
|
||||
|
||||
return '<i class="' . $fa_class . ' ' . $icon . '">' . $str . '</i>';
|
||||
}
|
||||
|
||||
return '';
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
<?php
|
||||
namespace Grav\Plugin\Shortcodes;
|
||||
|
||||
use Thunder\Shortcode\Shortcode\ShortcodeInterface;
|
||||
|
||||
class HShortcode extends Shortcode
|
||||
{
|
||||
public function init()
|
||||
{
|
||||
$this->shortcode->getHandlers()->add('h1', function(ShortcodeInterface $sc) {
|
||||
return $this->header(1, $sc);
|
||||
});
|
||||
|
||||
$this->shortcode->getHandlers()->add('h2', function(ShortcodeInterface $sc) {
|
||||
return $this->header(2, $sc);
|
||||
});
|
||||
|
||||
$this->shortcode->getHandlers()->add('h3', function(ShortcodeInterface $sc) {
|
||||
return $this->header(3, $sc);
|
||||
});
|
||||
|
||||
$this->shortcode->getHandlers()->add('h4', function(ShortcodeInterface $sc) {
|
||||
return $this->header(4, $sc);
|
||||
});
|
||||
|
||||
$this->shortcode->getHandlers()->add('h5', function(ShortcodeInterface $sc) {
|
||||
return $this->header(5, $sc);
|
||||
});
|
||||
|
||||
$this->shortcode->getHandlers()->add('h6', function(ShortcodeInterface $sc) {
|
||||
return $this->header(6, $sc);
|
||||
});
|
||||
|
||||
|
||||
}
|
||||
|
||||
protected function header($level, ShortcodeInterface $sc)
|
||||
{
|
||||
$id = $sc->getParameter('id');
|
||||
$class = $sc->getParameter('class');
|
||||
$tag = 'h' . $level;
|
||||
|
||||
$id_output = $id ? ' id="' . $id . '" ': '';
|
||||
$class_output = $class ? ' class="' . $class . '"' : '';
|
||||
|
||||
return "<{$tag}{$id_output}{$class_output}>{$sc->getContent()}</{$tag}>";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
<?php
|
||||
namespace Grav\Plugin\Shortcodes;
|
||||
|
||||
use Grav\Common\Language\Language;
|
||||
use Thunder\Shortcode\Shortcode\ShortcodeInterface;
|
||||
|
||||
class LanguageShortcode extends Shortcode
|
||||
{
|
||||
|
||||
public function init()
|
||||
{
|
||||
$this->shortcode->getHandlers()->add('lang', function(ShortcodeInterface $sc) {
|
||||
$lang = $this->getBbCode($sc);
|
||||
|
||||
if ($lang) {
|
||||
$list = explode(',', $lang);
|
||||
array_walk($list, 'trim');
|
||||
|
||||
/** @var Language $language */
|
||||
$language = $this->grav['language'];
|
||||
$current = $language->getLanguage();
|
||||
|
||||
if (in_array($current, $list)) {
|
||||
return $sc->getContent();
|
||||
}
|
||||
}
|
||||
|
||||
return '';
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,360 @@
|
||||
<?php
|
||||
/**
|
||||
* Based on Lorem Ipsum Generator by Josh Sherman
|
||||
*
|
||||
* Licensed under The MIT License.
|
||||
* Redistribution of these files must retain the above copyright notice.
|
||||
*
|
||||
* @author Josh Sherman <hello@joshtronic.com>
|
||||
* @copyright Copyright 2014, 2015, 2016, 2017, 2018, 2019 Josh Sherman
|
||||
* @license http://www.opensource.org/licenses/mit-license.html
|
||||
* @link https://github.com/joshtronic/php-loremipsum
|
||||
*/
|
||||
|
||||
namespace Grav\Plugin\Shortcodes;
|
||||
|
||||
use Thunder\Shortcode\Shortcode\ShortcodeInterface;
|
||||
|
||||
class LoremShortcode extends Shortcode
|
||||
{
|
||||
/**
|
||||
* First
|
||||
*
|
||||
* Whether or not we should be starting the string with "Lorem ipsum..."
|
||||
*
|
||||
* @access private
|
||||
* @var boolean
|
||||
*/
|
||||
private $first = true;
|
||||
/**
|
||||
* Words
|
||||
*
|
||||
* A lorem ipsum vocabulary of sorts. Not a complete list as I'm unsure if
|
||||
* a complete list exists and if so, where to get it.
|
||||
*
|
||||
* @access private
|
||||
* @var array
|
||||
*/
|
||||
public $words = [
|
||||
// Lorem ipsum...
|
||||
'lorem', 'ipsum', 'dolor', 'sit', 'amet', 'consectetur', 'adipiscing', 'elit',
|
||||
// and the rest of the vocabulary
|
||||
'a', 'ac', 'accumsan', 'ad', 'aenean', 'aliquam', 'aliquet', 'ante',
|
||||
'aptent', 'arcu', 'at', 'auctor', 'augue', 'bibendum', 'blandit',
|
||||
'class', 'commodo', 'condimentum', 'congue', 'consequat', 'conubia',
|
||||
'convallis', 'cras', 'cubilia', 'curabitur', 'curae', 'cursus',
|
||||
'dapibus', 'diam', 'dictum', 'dictumst', 'dignissim', 'dis', 'donec',
|
||||
'dui', 'duis', 'efficitur', 'egestas', 'eget', 'eleifend', 'elementum',
|
||||
'enim', 'erat', 'eros', 'est', 'et', 'etiam', 'eu', 'euismod', 'ex',
|
||||
'facilisi', 'facilisis', 'fames', 'faucibus', 'felis', 'fermentum',
|
||||
'feugiat', 'finibus', 'fringilla', 'fusce', 'gravida', 'habitant',
|
||||
'habitasse', 'hac', 'hendrerit', 'himenaeos', 'iaculis', 'id',
|
||||
'imperdiet', 'in', 'inceptos', 'integer', 'interdum', 'justo',
|
||||
'lacinia', 'lacus', 'laoreet', 'lectus', 'leo', 'libero', 'ligula',
|
||||
'litora', 'lobortis', 'luctus', 'maecenas', 'magna', 'magnis',
|
||||
'malesuada', 'massa', 'mattis', 'mauris', 'maximus', 'metus', 'mi',
|
||||
'molestie', 'mollis', 'montes', 'morbi', 'mus', 'nam', 'nascetur',
|
||||
'natoque', 'nec', 'neque', 'netus', 'nibh', 'nisi', 'nisl', 'non',
|
||||
'nostra', 'nulla', 'nullam', 'nunc', 'odio', 'orci', 'ornare',
|
||||
'parturient', 'pellentesque', 'penatibus', 'per', 'pharetra',
|
||||
'phasellus', 'placerat', 'platea', 'porta', 'porttitor', 'posuere',
|
||||
'potenti', 'praesent', 'pretium', 'primis', 'proin', 'pulvinar',
|
||||
'purus', 'quam', 'quis', 'quisque', 'rhoncus', 'ridiculus', 'risus',
|
||||
'rutrum', 'sagittis', 'sapien', 'scelerisque', 'sed', 'sem', 'semper',
|
||||
'senectus', 'sociosqu', 'sodales', 'sollicitudin', 'suscipit',
|
||||
'suspendisse', 'taciti', 'tellus', 'tempor', 'tempus', 'tincidunt',
|
||||
'torquent', 'tortor', 'tristique', 'turpis', 'ullamcorper', 'ultrices',
|
||||
'ultricies', 'urna', 'ut', 'varius', 'vehicula', 'vel', 'velit',
|
||||
'venenatis', 'vestibulum', 'vitae', 'vivamus', 'viverra', 'volutpat',
|
||||
'vulputate',
|
||||
];
|
||||
|
||||
public function init()
|
||||
{
|
||||
$this->shortcode->getHandlers()->add('lorem', function(ShortcodeInterface $sc) {
|
||||
$paragraphs = $sc->getParameter('p', $this->getBbCode($sc));
|
||||
$paragraph_tag = $sc->getParameter('tag', 'p');
|
||||
$sentences = $sc->getParameter('s');
|
||||
$words = $sc->getParameter('w');
|
||||
|
||||
if ($words) {
|
||||
return $this->words($words);
|
||||
}
|
||||
if ($sentences) {
|
||||
return $this->sentences($sentences);
|
||||
}
|
||||
|
||||
return $this->paragraphs($paragraphs ?? 1, $paragraph_tag);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Word
|
||||
*
|
||||
* Generates a single word of lorem ipsum.
|
||||
*
|
||||
* @access public
|
||||
* @param mixed $tags string or array of HTML tags to wrap output with
|
||||
* @return string generated lorem ipsum word
|
||||
*/
|
||||
public function word($tags = false)
|
||||
{
|
||||
return $this->words(1, $tags);
|
||||
}
|
||||
/**
|
||||
* Words Array
|
||||
*
|
||||
* Generates an array of lorem ipsum words.
|
||||
*
|
||||
* @access public
|
||||
* @param integer $count how many words to generate
|
||||
* @param mixed $tags string or array of HTML tags to wrap output with
|
||||
* @return array generated lorem ipsum words
|
||||
*/
|
||||
public function wordsArray($count = 1, $tags = false)
|
||||
{
|
||||
return $this->words($count, $tags, true);
|
||||
}
|
||||
/**
|
||||
* Words
|
||||
*
|
||||
* Generates words of lorem ipsum.
|
||||
*
|
||||
* @access public
|
||||
* @param integer $count how many words to generate
|
||||
* @param mixed $tags string or array of HTML tags to wrap output with
|
||||
* @param boolean $array whether an array or a string should be returned
|
||||
* @return mixed string or array of generated lorem ipsum words
|
||||
*/
|
||||
public function words($count = 1, $tags = false, $array = false)
|
||||
{
|
||||
$words = [];
|
||||
$word_count = 0;
|
||||
// Shuffles and appends the word list to compensate for count
|
||||
// arguments that exceed the size of our vocabulary list
|
||||
while ($word_count < $count) {
|
||||
$shuffle = true;
|
||||
while ($shuffle) {
|
||||
$this->shuffle();
|
||||
// Checks that the last word of the list and the first word of
|
||||
// the list that's about to be appended are not the same
|
||||
if (!$word_count || $words[$word_count - 1] != $this->words[0]) {
|
||||
$words = array_merge($words, $this->words);
|
||||
$word_count = count($words);
|
||||
$shuffle = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
$words = array_slice($words, 0, (int) $count);
|
||||
|
||||
return $this->output($words, $tags, $array);
|
||||
}
|
||||
/**
|
||||
* Sentence
|
||||
*
|
||||
* Generates a full sentence of lorem ipsum.
|
||||
*
|
||||
* @access public
|
||||
* @param mixed $tags string or array of HTML tags to wrap output with
|
||||
* @return string generated lorem ipsum sentence
|
||||
*/
|
||||
public function sentence($tags = false)
|
||||
{
|
||||
return $this->sentences(1, $tags);
|
||||
}
|
||||
/**
|
||||
* Sentences Array
|
||||
*
|
||||
* Generates an array of lorem ipsum sentences.
|
||||
*
|
||||
* @access public
|
||||
* @param integer $count how many sentences to generate
|
||||
* @param mixed $tags string or array of HTML tags to wrap output with
|
||||
* @return array generated lorem ipsum sentences
|
||||
*/
|
||||
public function sentencesArray($count = 1, $tags = false)
|
||||
{
|
||||
return $this->sentences($count, $tags, true);
|
||||
}
|
||||
/**
|
||||
* Sentences
|
||||
*
|
||||
* Generates sentences of lorem ipsum.
|
||||
*
|
||||
* @access public
|
||||
* @param integer $count how many sentences to generate
|
||||
* @param mixed $tags string or array of HTML tags to wrap output with
|
||||
* @param boolean $array whether an array or a string should be returned
|
||||
* @return mixed string or array of generated lorem ipsum sentences
|
||||
*/
|
||||
public function sentences($count = 1, $tags = false, $array = false)
|
||||
{
|
||||
$sentences = [];
|
||||
for ($i = 0; $i < $count; $i++) {
|
||||
$sentences[] = $this->wordsArray($this->gauss(24.46, 5.08));
|
||||
}
|
||||
$this->punctuate($sentences);
|
||||
|
||||
return $this->output($sentences, $tags, $array);
|
||||
}
|
||||
/**
|
||||
* Paragraph
|
||||
*
|
||||
* Generates a full paragraph of lorem ipsum.
|
||||
*
|
||||
* @access public
|
||||
* @param mixed $tags string or array of HTML tags to wrap output with
|
||||
* @return string generated lorem ipsum paragraph
|
||||
*/
|
||||
public function paragraph($tags = false)
|
||||
{
|
||||
return $this->paragraphs(1, $tags);
|
||||
}
|
||||
/**
|
||||
* Paragraph Array
|
||||
*
|
||||
* Generates an array of lorem ipsum paragraphs.
|
||||
*
|
||||
* @access public
|
||||
* @param integer $count how many paragraphs to generate
|
||||
* @param mixed $tags string or array of HTML tags to wrap output with
|
||||
* @return array generated lorem ipsum paragraphs
|
||||
*/
|
||||
public function paragraphsArray($count = 1, $tags = false)
|
||||
{
|
||||
return $this->paragraphs($count, $tags, true);
|
||||
}
|
||||
/**
|
||||
* Paragraphss
|
||||
*
|
||||
* Generates paragraphs of lorem ipsum.
|
||||
*
|
||||
* @access public
|
||||
* @param integer $count how many paragraphs to generate
|
||||
* @param mixed $tags string or array of HTML tags to wrap output with
|
||||
* @param boolean $array whether an array or a string should be returned
|
||||
* @return mixed string or array of generated lorem ipsum paragraphs
|
||||
*/
|
||||
public function paragraphs($count = 1, $tags = false, $array = false)
|
||||
{
|
||||
$paragraphs = [];
|
||||
for ($i = 0; $i < $count; $i++) {
|
||||
$paragraphs[] = $this->sentences($this->gauss(5.8, 1.93));
|
||||
}
|
||||
|
||||
return $this->output($paragraphs, $tags, $array, "\n\n");
|
||||
}
|
||||
/**
|
||||
* Gaussian Distribution
|
||||
*
|
||||
* This is some smart kid stuff. I went ahead and combined the N(0,1) logic
|
||||
* with the N(m,s) logic into this single function. Used to calculate the
|
||||
* number of words in a sentence, the number of sentences in a paragraph
|
||||
* and the distribution of commas in a sentence.
|
||||
*
|
||||
* @access private
|
||||
* @param double $mean average value
|
||||
* @param double $std_dev stadnard deviation
|
||||
* @return double calculated distribution
|
||||
*/
|
||||
private function gauss($mean, $std_dev)
|
||||
{
|
||||
$x = mt_rand() / mt_getrandmax();
|
||||
$y = mt_rand() / mt_getrandmax();
|
||||
$z = sqrt(-2 * log($x)) * cos(2 * pi() * $y);
|
||||
|
||||
return $z * $std_dev + $mean;
|
||||
}
|
||||
/**
|
||||
* Shuffle
|
||||
*
|
||||
* Shuffles the words, forcing "Lorem ipsum..." at the beginning if it is
|
||||
* the first time we are generating the text.
|
||||
*
|
||||
* @access private
|
||||
*/
|
||||
private function shuffle()
|
||||
{
|
||||
if ($this->first) {
|
||||
$this->first = array_slice($this->words, 0, 8);
|
||||
$this->words = array_slice($this->words, 8);
|
||||
shuffle($this->words);
|
||||
$this->words = $this->first + $this->words;
|
||||
$this->first = false;
|
||||
} else {
|
||||
shuffle($this->words);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Punctuate
|
||||
*
|
||||
* Applies punctuation to a sentence. This includes a period at the end,
|
||||
* the injection of commas as well as capitalizing the first letter of the
|
||||
* first word of the sentence.
|
||||
*
|
||||
* @access private
|
||||
* @param array $sentences the sentences we would like to punctuate
|
||||
*/
|
||||
private function punctuate(&$sentences)
|
||||
{
|
||||
foreach ($sentences as $key => $sentence) {
|
||||
$words = count($sentence);
|
||||
// Only worry about commas on sentences longer than 4 words
|
||||
if ($words > 4) {
|
||||
$mean = log($words, 6);
|
||||
$std_dev = $mean / 6;
|
||||
$commas = round($this->gauss($mean, $std_dev));
|
||||
for ($i = 1; $i <= $commas; $i++) {
|
||||
$word = round($i * $words / ($commas + 1));
|
||||
if ($word < ($words - 1) && $word > 0) {
|
||||
$sentence[$word] .= ',';
|
||||
}
|
||||
}
|
||||
}
|
||||
$sentences[$key] = ucfirst(implode(' ', $sentence) . '.');
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Output
|
||||
*
|
||||
* Does the rest of the processing of the strings. This includes wrapping
|
||||
* the strings in HTML tags, handling transformations with the ability of
|
||||
* back referencing and determining if the passed array should be converted
|
||||
* into a string or not.
|
||||
*
|
||||
* @access private
|
||||
* @param string|string[] $strings an array of generated strings
|
||||
* @param mixed $tags string or array of HTML tags to wrap output with
|
||||
* @param boolean $array whether an array or a string should be returned
|
||||
* @param string $delimiter the string to use when calling implode()
|
||||
* @return string|string[] string or array of generated lorem ipsum text
|
||||
*/
|
||||
private function output($strings, $tags, $array, $delimiter = ' ')
|
||||
{
|
||||
if ($tags) {
|
||||
if (!is_array($tags)) {
|
||||
$tags = [$tags];
|
||||
} else {
|
||||
// Flips the array so we can work from the inside out
|
||||
$tags = array_reverse($tags);
|
||||
}
|
||||
foreach ($strings as $key => $string) {
|
||||
foreach ($tags as $tag) {
|
||||
// Detects / applies back reference
|
||||
if ($tag[0] === '<') {
|
||||
$string = str_replace('$1', $string, $tag);
|
||||
} else {
|
||||
$string = sprintf('<%1$s>%2$s</%1$s>', $tag, $string);
|
||||
}
|
||||
$strings[$key] = $string;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!$array) {
|
||||
$strings = implode($delimiter, $strings);
|
||||
}
|
||||
|
||||
return $strings;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
<?php
|
||||
namespace Grav\Plugin\Shortcodes;
|
||||
|
||||
use Thunder\Shortcode\Shortcode\ShortcodeInterface;
|
||||
|
||||
class MarkShortcode extends Shortcode
|
||||
{
|
||||
public function init()
|
||||
{
|
||||
$this->shortcode->getHandlers()->add('mark', function(ShortcodeInterface $sc) {
|
||||
$style = $sc->getParameter('style', $this->getBbCode($sc));
|
||||
$class = $sc->getParameter('class', 'default');
|
||||
|
||||
$css_class = 'class="mark-class-' . $class . '"';
|
||||
|
||||
if ($style === 'block') {
|
||||
$css_style = 'style="display:block;"';
|
||||
$content = trim($sc->getContent(), "\n");
|
||||
} else {
|
||||
$css_style = '';
|
||||
$content = $sc->getContent();
|
||||
}
|
||||
|
||||
return "<mark {$css_class} {$css_style}>{$content}</mark>";
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
<?php
|
||||
namespace Grav\Plugin\Shortcodes;
|
||||
|
||||
use Thunder\Shortcode\Shortcode\ShortcodeInterface;
|
||||
|
||||
class NoticeShortcode extends Shortcode
|
||||
{
|
||||
public function init()
|
||||
{
|
||||
$this->shortcode->getHandlers()->add('notice', function(ShortcodeInterface $sc) {
|
||||
$css_enabled = $this->grav['config']->get('plugins.shortcode-core.css.notice_enabled', true);
|
||||
if ($css_enabled) {
|
||||
$this->shortcode->addAssets('css', 'plugin://shortcode-core/css/shortcode-notice.css');
|
||||
}
|
||||
|
||||
$output = $this->twig->processTemplate('shortcodes/notice.html.twig', [
|
||||
'type' => $sc->getParameter('notice', $this->getBbCode($sc)) ?: 'info',
|
||||
'content' => $sc->getContent(),
|
||||
]);
|
||||
|
||||
return $output;
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
<?php
|
||||
namespace Grav\Plugin\Shortcodes;
|
||||
|
||||
use Thunder\Shortcode\EventHandler\FilterRawEventHandler;
|
||||
use Thunder\Shortcode\Events;
|
||||
use Thunder\Shortcode\Shortcode\ShortcodeInterface;
|
||||
|
||||
class RawShortcode extends Shortcode
|
||||
{
|
||||
public function init()
|
||||
{
|
||||
$this->shortcode->getHandlers()->add('raw', static function(ShortcodeInterface $sc) {
|
||||
return trim($sc->getContent());
|
||||
});
|
||||
|
||||
$this->shortcode->getEvents()->addListener(Events::FILTER_SHORTCODES, new FilterRawEventHandler(['raw']));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
<?php
|
||||
namespace Grav\Plugin\Shortcodes;
|
||||
|
||||
use Thunder\Shortcode\Shortcode\ShortcodeInterface;
|
||||
|
||||
/**
|
||||
* `[read-file file="theme://foo.md" /]` — include the contents of a file
|
||||
* located by a Grav stream URI into the page.
|
||||
*
|
||||
* Delegates to `\Grav\Common\Helpers\FileReader::read()` so it inherits the
|
||||
* same hardening as the Twig `read_file()` function: stream-only paths,
|
||||
* `security.read_file.allowed_streams` allow-list, extension allow-list,
|
||||
* canonical realpath containment, max size cap. Anything outside those
|
||||
* constraints (raw filesystem paths, traversal, encoded `..`, disallowed
|
||||
* extensions, oversize files, missing files) returns the empty string and
|
||||
* the shortcode renders as nothing — just like the Twig function returns
|
||||
* `false`, which Twig prints as empty.
|
||||
*
|
||||
* Registered as a *raw* handler, so it runs before Markdown — included
|
||||
* Markdown files become part of the page's Markdown source and get rendered
|
||||
* normally. HTML / SVG / JSON files come through verbatim.
|
||||
*
|
||||
* Grav 1.7 fallback: `FileReader` was introduced in Grav 2.0.0-rc.2. On Grav
|
||||
* 1.7 the helper isn't available, so the shortcode emits an HTML comment
|
||||
* pointing at the version requirement. The comment isn't visible to readers
|
||||
* but is easy for an author or theme dev to find when viewing source — much
|
||||
* better than the alternative (no handler registered, so `[read-file ...]`
|
||||
* leaks as literal text into the rendered page). On 1.7 sites that need
|
||||
* file inclusion from page content, use the legacy Twig `read_file()`
|
||||
* function instead.
|
||||
*/
|
||||
class ReadFileShortcode extends Shortcode
|
||||
{
|
||||
public function init()
|
||||
{
|
||||
// Always register the handler — even on Grav 1.7 — so a stray
|
||||
// `[read-file ...]` in content doesn't leak as literal text.
|
||||
// `FileReader` ships with Grav 2.0.0-rc.2+; older Gravs get a
|
||||
// diagnostic HTML comment.
|
||||
$hasFileReader = class_exists(\Grav\Common\Helpers\FileReader::class);
|
||||
|
||||
// Closure intentionally non-static — `$this->getBbCode($sc)` provides
|
||||
// the `[read-file=theme://foo.md /]` BBCode-style shorthand alongside
|
||||
// the `[read-file file="theme://foo.md" /]` named-attribute form.
|
||||
$handler = function (ShortcodeInterface $sc) use ($hasFileReader) {
|
||||
if (!$hasFileReader) {
|
||||
return '<!-- [read-file] requires Grav >= 2.0.0-rc.2 -->';
|
||||
}
|
||||
|
||||
$file = $sc->getParameter('file', $this->getBbCode($sc));
|
||||
if (!is_string($file) || $file === '') {
|
||||
return '';
|
||||
}
|
||||
|
||||
$contents = \Grav\Common\Helpers\FileReader::read($file);
|
||||
|
||||
return $contents === false ? '' : $contents;
|
||||
};
|
||||
|
||||
// Register on the raw-handler container so the shortcode runs in the
|
||||
// pre-Markdown pass. Included `.md` files flow into the page's
|
||||
// Markdown source and render normally; HTML / SVG / JSON pass
|
||||
// through to the post-Markdown HTML untouched.
|
||||
$this->shortcode->getRawHandlers()->add('read-file', $handler);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
<?php
|
||||
namespace Grav\Plugin\Shortcodes;
|
||||
|
||||
use Thunder\Shortcode\Shortcode\ShortcodeInterface;
|
||||
|
||||
class SafeEmailShortcode extends Shortcode
|
||||
{
|
||||
public function init()
|
||||
{
|
||||
$this->shortcode->getHandlers()->add('safe-email', function(ShortcodeInterface $sc) {
|
||||
// Load assets if required
|
||||
if ($this->config->get('plugins.shortcode-core.fontawesome.load', false)) {
|
||||
$this->shortcode->addAssets('css', $this->config->get('plugins.shortcode-core.fontawesome.url'));
|
||||
}
|
||||
|
||||
// Get shortcode content and parameters
|
||||
$addr_str = $sc->getContent();
|
||||
$icon = $sc->getParameter('icon', false);
|
||||
$icon_base = "fa fa-";
|
||||
$autolink = $sc->getParameter('autolink', false);
|
||||
$subject = $sc->getParameter('subject', false);
|
||||
|
||||
// Add subject, if any, to the link target.
|
||||
$link_str = $addr_str;
|
||||
if ($subject) {
|
||||
$subject = html_entity_decode($subject);
|
||||
$link_str .= '?subject=' . rawurlencode($subject);
|
||||
}
|
||||
|
||||
// Encode display text and link target
|
||||
$email_disp = static::encodeText($addr_str);
|
||||
$email_link = static::encodeText($link_str);
|
||||
|
||||
// Handle autolinking
|
||||
if ($autolink) {
|
||||
$output = '<a href="mailto:' . $email_link . '">' . $email_disp . '</a>';
|
||||
} else {
|
||||
$output = $email_disp;
|
||||
}
|
||||
|
||||
// Handle icon option
|
||||
if ($icon) {
|
||||
if ($this->config->get('plugins.shortcode-core.fontawesome.v5', false)) {
|
||||
if (preg_match("/^(?P<weight>fa[srlbd]) fa-(?<icon>.+)/", $icon, $icon_parts)) {
|
||||
$icon_base = $icon_parts["weight"] . " fa-";
|
||||
$icon = $icon_parts["icon"];
|
||||
}
|
||||
}
|
||||
|
||||
$output = '<i class="'. $icon_base . $icon . '"></i> ' . $output;
|
||||
}
|
||||
|
||||
return $output;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* encodes text as numeric HTML entities
|
||||
* @param string $text the text to encode
|
||||
* @return string the encoded text
|
||||
*/
|
||||
private static function encodeText($text)
|
||||
{
|
||||
$encoded = '';
|
||||
$str_len = strlen($text);
|
||||
|
||||
for ($i = 0; $i < $str_len; $i++) {
|
||||
$encoded .= '&#' . ord($text[$i]). ';';
|
||||
}
|
||||
|
||||
return $encoded;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
<?php
|
||||
namespace Grav\Plugin\Shortcodes;
|
||||
|
||||
use Thunder\Shortcode\Shortcode\ShortcodeInterface;
|
||||
|
||||
class SectionShortcode extends Shortcode
|
||||
{
|
||||
public function init()
|
||||
{
|
||||
$this->shortcode->getHandlers()->add('section', function(ShortcodeInterface $sc) {
|
||||
$name = $sc->getParameter('name');
|
||||
$page = $sc->getParameter('page');
|
||||
$content = $sc->getContent();
|
||||
|
||||
if (empty($content) && isset($page)) {
|
||||
if ($target = $this->grav['pages']->find($page)) {
|
||||
if ($shortcodeObject = $target->contentMeta()['shortcodeMeta']['shortcode'][$sc->getName()][$name] ?? false) {
|
||||
return (string) $shortcodeObject;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$object = new \Grav\Plugin\ShortcodeCore\ShortcodeObject($name, $sc->getContent());
|
||||
$this->shortcode->addObject($sc->getName(), $object);
|
||||
});
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
<?php
|
||||
|
||||
namespace Grav\Plugin\Shortcodes;
|
||||
|
||||
use Grav\Common\Config\Config;
|
||||
use Grav\Common\Grav;
|
||||
use Grav\Common\Twig\Twig;
|
||||
use Grav\Plugin\ShortcodeCore\ShortcodeManager;
|
||||
use Thunder\Shortcode\Shortcode\ShortcodeInterface;
|
||||
|
||||
abstract class Shortcode
|
||||
{
|
||||
/** @var ShortcodeManager */
|
||||
protected $shortcode;
|
||||
|
||||
/** @var Grav */
|
||||
protected $grav;
|
||||
|
||||
/** @var Config */
|
||||
protected $config;
|
||||
|
||||
/** @var Twig */
|
||||
protected $twig;
|
||||
|
||||
/**
|
||||
* Shortcode constructor.
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
$this->grav = Grav::instance();
|
||||
$this->shortcode = $this->grav['shortcode'];
|
||||
$this->config = $this->grav['config'];
|
||||
$this->twig = $this->grav['twig'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize shortcode handler
|
||||
*/
|
||||
public function init()
|
||||
{
|
||||
user_error(__METHOD__ . '() method will be abstract in the future, please override it!', E_USER_DEPRECATED);
|
||||
|
||||
// FIXME: This code had to be put back because of some plugins do not properly initialize themselves.
|
||||
$this->shortcode->getHandlers()->add('u', static function(ShortcodeInterface $shortcode) {
|
||||
return $shortcode->getContent();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the name of the class if required
|
||||
*
|
||||
* @return string the name of the class
|
||||
*/
|
||||
public function getName()
|
||||
{
|
||||
return get_class($this);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getParser()
|
||||
{
|
||||
return $this->config->get('plugins.shortcode-core.parser');
|
||||
}
|
||||
|
||||
/**
|
||||
* @param ShortcodeInterface $sc
|
||||
* @param string|null $default
|
||||
* @return string|null
|
||||
*/
|
||||
public function getBbCode(ShortcodeInterface $sc, $default = null)
|
||||
{
|
||||
$code = $default;
|
||||
|
||||
if ($this->getParser() === 'wordpress') {
|
||||
$params = $sc->getParameters();
|
||||
if (is_array($params)) {
|
||||
$keys = array_keys($params);
|
||||
$code = trim(array_shift($keys), '=');
|
||||
}
|
||||
} else {
|
||||
$code = $sc->getBbCode();
|
||||
}
|
||||
|
||||
return $code;
|
||||
}
|
||||
}
|
||||
|
||||
// Make sure we also autoload the deprecated class.
|
||||
class_exists(\Grav\Plugin\ShortcodeCore\Shortcode::class);
|
||||
@@ -0,0 +1,24 @@
|
||||
<?php
|
||||
|
||||
namespace Grav\Plugin\Shortcodes;
|
||||
|
||||
// Check if the new class has been autoloaded. If not, trigger deprecation error.
|
||||
if (!class_exists(\Grav\Plugin\ShortcodeCore\ShortcodeObject::class, false)) {
|
||||
@trigger_error(
|
||||
ShortcodeObject::class . ' class is deprecated, use \\Grav\\Plugin\\ShortcodeCore\\ShortcodeObject instead',
|
||||
E_USER_DEPRECATED
|
||||
);
|
||||
}
|
||||
|
||||
// Create alias for the deprecated class.
|
||||
class_alias(\Grav\Plugin\ShortcodeCore\ShortcodeObject::class, ShortcodeObject::class);
|
||||
|
||||
// Make sure that both IDE and composer knows about the deprecated class.
|
||||
if (false) {
|
||||
/**
|
||||
* @deprecated 4.2.0 Use \Grav\Plugin\ShortcodeCore\ShortcodeObject instead
|
||||
*/
|
||||
class ShortcodeObject extends \Grav\Plugin\ShortcodeCore\ShortcodeObject
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
<?php
|
||||
namespace Grav\Plugin\Shortcodes;
|
||||
|
||||
use Thunder\Shortcode\Shortcode\ShortcodeInterface;
|
||||
|
||||
class SizeShortcode extends Shortcode
|
||||
{
|
||||
public function init()
|
||||
{
|
||||
$this->shortcode->getHandlers()->add('size', function(ShortcodeInterface $sc) {
|
||||
$size = $sc->getParameter('size', $this->getBbCode($sc));
|
||||
if (is_numeric($size)) {
|
||||
$size .= 'px';
|
||||
}
|
||||
|
||||
return '<span style="font-size: ' . $size . ';">' . $sc->getContent() . '</span>';
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
<?php
|
||||
namespace Grav\Plugin\Shortcodes;
|
||||
|
||||
use Thunder\Shortcode\Shortcode\ShortcodeInterface;
|
||||
|
||||
class SpanShortcode extends Shortcode
|
||||
{
|
||||
public function init()
|
||||
{
|
||||
$this->shortcode->getHandlers()->add('span', static function(ShortcodeInterface $sc) {
|
||||
$id = $sc->getParameter('id');
|
||||
$class = $sc->getParameter('class');
|
||||
$style = $sc->getParameter('style');
|
||||
|
||||
$id_output = $id ? 'id="' . $id . '" ': '';
|
||||
$class_output = $class ? 'class="' . $class . '"' : '';
|
||||
$style_output = $style ? 'style="' . $style . '"' : '';
|
||||
|
||||
return '<span ' . $id_output . ' ' . $class_output . ' ' . $style_output . '>' . $sc->getContent() . '</span>';
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
<?php
|
||||
namespace Grav\Plugin\Shortcodes;
|
||||
|
||||
use Grav\Common\Language\Language;
|
||||
use Thunder\Shortcode\Shortcode\ShortcodeInterface;
|
||||
|
||||
/**
|
||||
* Translate a language string by key, the safe in-content replacement for
|
||||
* `{{ 'SOME.KEY'|t }}`. The key is author-supplied; the value comes from the
|
||||
* site's language files, which are trusted (authored by developers and
|
||||
* translators), so the result is returned as-is — translations may legitimately
|
||||
* contain HTML. Untrusted, user-controlled values belong in [uri] instead,
|
||||
* which escapes its output.
|
||||
*
|
||||
* Usage:
|
||||
* [translate]PLUGIN_ERROR.ERROR_MESSAGE[/translate]
|
||||
* [translate=PLUGIN_ERROR.ERROR_MESSAGE /]
|
||||
* [translate key="MY.GREETING" Andy /] (extra params become substitutions)
|
||||
*/
|
||||
class TranslateShortcode extends Shortcode
|
||||
{
|
||||
public function init()
|
||||
{
|
||||
$this->shortcode->getHandlers()->add('translate', function (ShortcodeInterface $sc) {
|
||||
// Key from [translate=KEY], the `key` param, or the wrapped content.
|
||||
$key = $sc->getParameter('key', $this->getBbCode($sc));
|
||||
if ($key === null || $key === '') {
|
||||
$key = $sc->getContent();
|
||||
}
|
||||
$key = trim((string) $key);
|
||||
|
||||
if ($key === '') {
|
||||
return '';
|
||||
}
|
||||
|
||||
// Any remaining bare parameters are passed through as ordered
|
||||
// substitution arguments (sprintf-style placeholders in the string).
|
||||
$args = [];
|
||||
foreach ($sc->getParameters() as $name => $value) {
|
||||
if ($name === 'key' || $name === $key) {
|
||||
continue;
|
||||
}
|
||||
$args[] = $value === null ? $name : $value;
|
||||
}
|
||||
|
||||
/** @var Language $language */
|
||||
$language = $this->grav['language'];
|
||||
|
||||
return $language->translate(array_merge([$key], $args));
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
<?php
|
||||
namespace Grav\Plugin\Shortcodes;
|
||||
|
||||
use Thunder\Shortcode\Shortcode\ShortcodeInterface;
|
||||
|
||||
class UnderlineShortcode extends Shortcode
|
||||
{
|
||||
public function init()
|
||||
{
|
||||
$this->shortcode->getHandlers()->add('u', static function(ShortcodeInterface $sc) {
|
||||
return '<span style="text-decoration: underline;">' . $sc->getContent() . '</span>';
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
<?php
|
||||
namespace Grav\Plugin\Shortcodes;
|
||||
|
||||
use Grav\Common\Uri;
|
||||
use Thunder\Shortcode\Shortcode\ShortcodeInterface;
|
||||
|
||||
/**
|
||||
* Print a value from the current URL, the safe in-content replacement for
|
||||
* `{{ uri.param('foo') }}` and friends. The value is user-controlled (it comes
|
||||
* straight from the request), so it is ALWAYS HTML-escaped on output — that
|
||||
* filtering is the whole point of using the shortcode instead of raw Twig in
|
||||
* content. There is deliberately no raw/unescaped opt-out.
|
||||
*
|
||||
* Usage:
|
||||
* [uri param="user" /] {{ uri.param('user') }}
|
||||
* [uri query="q" /] {{ uri.query('q') }}
|
||||
* [uri param="ref" default="home" /]
|
||||
*/
|
||||
class UriShortcode extends Shortcode
|
||||
{
|
||||
/** Sources we expose, mapped to the Uri accessor that reads them. */
|
||||
private const SOURCES = ['param', 'query'];
|
||||
|
||||
public function init()
|
||||
{
|
||||
$this->shortcode->getHandlers()->add('uri', function (ShortcodeInterface $sc) {
|
||||
/** @var Uri $uri */
|
||||
$uri = $this->grav['uri'];
|
||||
|
||||
$value = null;
|
||||
foreach (self::SOURCES as $source) {
|
||||
$name = $sc->getParameter($source);
|
||||
if ($name !== null && $name !== '') {
|
||||
// Uri::param() / Uri::query() — read the named value.
|
||||
$value = $uri->{$source}((string) $name);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ($value === null || $value === false || $value === '') {
|
||||
$value = $sc->getParameter('default', '');
|
||||
}
|
||||
|
||||
// User-controlled input: escape unconditionally.
|
||||
return htmlspecialchars((string) $value, ENT_QUOTES, 'UTF-8');
|
||||
});
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user