223 lines
7.6 KiB
PHP
223 lines
7.6 KiB
PHP
<?php
|
|
namespace Grav\Plugin;
|
|
|
|
use Composer\Autoload\ClassLoader;
|
|
use Grav\Common\Data;
|
|
use Grav\Common\Grav;
|
|
use Grav\Common\Page\Interfaces\PageInterface;
|
|
use Grav\Common\Plugin;
|
|
use Grav\Common\Utils;
|
|
use Grav\Plugin\PageToc\MarkupFixer;
|
|
use Grav\Plugin\PageToc\TocGenerator;
|
|
use RocketTheme\Toolbox\Event\Event;
|
|
use Twig\TwigFunction;
|
|
|
|
|
|
/**
|
|
* Class PageTOCPlugin
|
|
* @package Grav\Plugin
|
|
*/
|
|
class PageTOCPlugin extends Plugin
|
|
{
|
|
protected $start;
|
|
protected $end;
|
|
protected $toc_regex = '#\[TOC\s*\/?\]#i';
|
|
|
|
protected $fixer;
|
|
protected $generator;
|
|
|
|
/**
|
|
* @return array
|
|
*
|
|
* The getSubscribedEvents() gives the core a list of events
|
|
* that the plugin wants to listen to. The key of each
|
|
* array section is the event that the plugin listens to
|
|
* and the value (in the form of an array) contains the
|
|
* callable (or function) as well as the priority. The
|
|
* higher the number the higher the priority.
|
|
*/
|
|
public static function getSubscribedEvents()
|
|
{
|
|
return [
|
|
'onPluginsInitialized' => ['onPluginsInitialized', 0]
|
|
];
|
|
}
|
|
|
|
/**
|
|
* Composer autoload
|
|
*
|
|
* @return ClassLoader
|
|
*/
|
|
public function autoload(): ClassLoader
|
|
{
|
|
return require __DIR__ . '/vendor/autoload.php';
|
|
}
|
|
|
|
/**
|
|
* Initialize the plugin
|
|
*/
|
|
public function onPluginsInitialized()
|
|
{
|
|
// Don't proceed if we are in the admin plugin
|
|
if ($this->isAdmin()) {
|
|
$this->enable([
|
|
'onBlueprintCreated' => ['onBlueprintCreated', 0],
|
|
]);
|
|
return;
|
|
}
|
|
|
|
// Enable the main event we are interested in
|
|
$this->enable([
|
|
'onShortcodeHandlers' => ['onShortcodeHandlers', 0],
|
|
'onTwigInitialized' => ['onTwigInitialized', 0],
|
|
'onTwigTemplatePaths' => ['onTwigTemplatePaths', 0],
|
|
'onTwigSiteVariables' => ['onTwigSiteVariables', 0],
|
|
'onPageContentProcessed' => ['onPageContentProcessed', -20],
|
|
]);
|
|
}
|
|
|
|
public function onShortcodeHandlers()
|
|
{
|
|
$this->grav['shortcode']->registerAllShortcodes(__DIR__ . '/classes/shortcodes');
|
|
}
|
|
|
|
public function onPageContentProcessed(Event $event)
|
|
{
|
|
/** @var PageInterface $page */
|
|
$page = $event['page'];
|
|
|
|
$content = $page->content();
|
|
$shortcode_exists = preg_match($this->toc_regex, $content);
|
|
$active = $this->configVar('active', $page, false);
|
|
|
|
// Set ID anchors if needed
|
|
if ($active || $shortcode_exists) {
|
|
$this->registerTwigFunctions();
|
|
$markup_fixer = new MarkupFixer();
|
|
$content = $markup_fixer->fix($content, $this->getAnchorOptions($page));
|
|
$page->setRawContent($content);
|
|
}
|
|
|
|
// Replace shortcode if found
|
|
if ($shortcode_exists) {
|
|
$toc = $this->grav['twig']->processTemplate('components/page-toc.html.twig', ['page' => $page, 'active' => true]);
|
|
$content = preg_replace($this->toc_regex, $toc, $content);
|
|
$page->setRawContent($content);
|
|
}
|
|
}
|
|
|
|
public function onTwigInitialized()
|
|
{
|
|
$this->registerTwigFunctions();
|
|
}
|
|
|
|
public function onTwigSiteVariables()
|
|
{
|
|
if ($this->grav['config']->get('plugins.page-toc.include_css')) {
|
|
$this->grav['assets']->addCss('plugin://page-toc/assets/page-toc-anchors.css');
|
|
}
|
|
if ($this->grav['config']->get('plugins.page-toc.anchors.copy_to_clipboard')) {
|
|
$this->grav['assets']->addJs('plugin://page-toc/assets/page-toc-anchors.js', ['group' => 'bottom', 'defer' => 'defer']);
|
|
}
|
|
}
|
|
|
|
public function registerTwigFunctions()
|
|
{
|
|
static $functions_registered;
|
|
|
|
if ($functions_registered) {
|
|
return;
|
|
}
|
|
|
|
$this->generator = new TocGenerator();
|
|
$this->fixer = new MarkupFixer();
|
|
$twig = $this->grav['twig']->twig();
|
|
|
|
$twig->addFunction(new TwigFunction('toc', function ($markup, $start = null, $depth = null) {
|
|
$options = $this->getTocOptions(null, $start, $depth);
|
|
return $this->generator->getHtmlMenu($markup, $options['start'], $options['depth']);
|
|
}, ['is_safe' => ['html']]));
|
|
|
|
$twig->addFunction(new TwigFunction('toc_ordered', function ($markup, $start = null, $depth = null) {
|
|
$options = $this->getTocOptions(null, $start, $depth);
|
|
return $this->generator->getHtmlMenu($markup, $options['start'], $options['depth'], null, true);
|
|
}, ['is_safe' => ['html']]));
|
|
|
|
$twig->addFunction(new TwigFunction('toc_items', function ($markup, $start = null, $depth = null) {
|
|
$options = $this->getTocOptions(null, $start, $depth);
|
|
return $this->generator->getMenu($markup, $options['start'], $options['depth']);
|
|
}));
|
|
|
|
$twig->addFunction(new TwigFunction('add_anchors', function ($markup, $start = null, $depth = null) {
|
|
$options = $this->getAnchorOptions(null, $start, $depth);
|
|
return $this->fixer->fix($markup, $options);
|
|
}, ['is_safe' => ['html']]));
|
|
|
|
$twig->addFunction(new TwigFunction('toc_config_var', function ($var) {
|
|
return static::configVar($var);
|
|
}));
|
|
|
|
$functions_registered = true;
|
|
}
|
|
|
|
public function onTwigTemplatePaths()
|
|
{
|
|
$this->grav['twig']->twig_paths[] = __DIR__ . '/templates';
|
|
}
|
|
|
|
/**
|
|
* Extend page blueprints with TOC options.
|
|
*
|
|
* @param Event $event
|
|
*/
|
|
public function onBlueprintCreated(Event $event)
|
|
{
|
|
static $inEvent = false;
|
|
|
|
/** @var Data\Blueprint $blueprint */
|
|
$blueprint = $event['blueprint'];
|
|
$form = $blueprint->form();
|
|
|
|
$advanced_tab_exists = isset($form['fields']['tabs']['fields']['advanced']);
|
|
|
|
if (!$inEvent && $advanced_tab_exists) {
|
|
$inEvent = true;
|
|
$blueprints = new Data\Blueprints(__DIR__ . '/blueprints/');
|
|
$extends = $blueprints->get('page-toc');
|
|
$blueprint->extend($extends, true);
|
|
$inEvent = false;
|
|
}
|
|
}
|
|
|
|
protected function getTocOptions(PageInterface $page = null, $start = null, $depth = null): array
|
|
{
|
|
$page = $page ?? $this->grav['page'];
|
|
return [
|
|
'start' => $start ?? $this->configVar('start', $page,1),
|
|
'depth' => $depth ?? $this->configVar('depth', $page,6),
|
|
];
|
|
}
|
|
|
|
protected function getAnchorOptions(PageInterface $page = null, $start = null, $depth = null): array
|
|
{
|
|
$page = $page ?? $this->grav['page'];
|
|
return [
|
|
'start' => (int) ($start ?? $this->configVar('anchors.start', $page,1)),
|
|
'depth' => (int) ($depth ?? $this->configVar('anchors.depth', $page,6)),
|
|
'hclass' => $this->configVar('hclass', $page,null),
|
|
'link' => $this->configVar('anchors.link', $page,true),
|
|
'position' => $this->configVar('anchors.position', $page,'before'),
|
|
'aria' => $this->configVar('anchors.aria', $page,'Anchor'),
|
|
'icon' => $this->configVar('anchors.icon', $page,'#'),
|
|
'class' => $this->configVar('anchors.class', $page,null),
|
|
'maxlen' => (int) ($this->configVar('anchors.slug_maxlen', $page,null)),
|
|
'prefix' => $this->configVar('anchors.slug_prefix', $page,null),
|
|
];
|
|
}
|
|
|
|
public static function configVar($var, $page = null, $default = null)
|
|
{
|
|
return Plugin::inheritedConfigOption('page-toc', $var, $page, $default);
|
|
}
|
|
}
|