API ShortcodeParser getter and extension points

This commit is contained in:
Jonathon Menz 2016-11-07 12:45:29 -08:00
parent 683bde6d82
commit ffd9938652

View File

@ -21,6 +21,13 @@ class ShortcodeParser extends Object {
// --------------------------------------------------------------------------------------------------------------
/**
* Registered shortcodes. Items follow this structure:
* [shortcode_name] => Array(
* [0] => class_containing_handler
* [1] => name_of_shortcode_handler_method
* )
*/
protected $shortcodes = array();
// --------------------------------------------------------------------------------------------------------------
@ -96,6 +103,15 @@ class ShortcodeParser extends Object {
if($this->registered($shortcode)) unset($this->shortcodes[$shortcode]);
}
/**
* Get an array containing information about registered shortcodes
*
* @return array
*/
public function getRegisteredShortcodes() {
return $this->shortcodes;
}
/**
* Remove all registered shortcodes.
*/
@ -540,79 +556,90 @@ class ShortcodeParser extends Object {
* @return string
*/
public function parse($content) {
$this->extend('onBeforeParse', $content);
$continue = true;
// If no shortcodes defined, don't try and parse any
if(!$this->shortcodes) return $content;
if(!$this->shortcodes) $continue = false;
// If no content, don't try and parse it
if (!trim($content)) return $content;
else if (!trim($content)) $continue = false;
// If no shortcode tag, don't try and parse it
if (strpos($content, '[') === false) return $content;
else if (strpos($content, '[') === false) $continue = false;
// First we operate in text mode, replacing any shortcodes with marker elements so that later we can
// use a proper DOM
list($content, $tags) = $this->replaceElementTagsWithMarkers($content);
if ($continue) {
// First we operate in text mode, replacing any shortcodes with marker elements so that later we can
// use a proper DOM
list($content, $tags) = $this->replaceElementTagsWithMarkers($content);
$htmlvalue = Injector::inst()->create('HTMLValue', $content);
$htmlvalue = Injector::inst()->create('HTMLValue', $content);
// Now parse the result into a DOM
if (!$htmlvalue->isValid()){
if(self::$error_behavior == self::ERROR) {
user_error('Couldn\'t decode HTML when processing short codes', E_USER_ERRROR);
}
else {
return $content;
}
}
// First, replace any shortcodes that are in attributes
$this->replaceAttributeTagsWithContent($htmlvalue);
// Find all the element scoped shortcode markers
$shortcodes = $htmlvalue->query('//img[@class="'.self::$marker_class.'"]');
// Find the parents. Do this before DOM modification, since SPLIT might cause parents to move otherwise
$parents = $this->findParentsForMarkers($shortcodes);
foreach($shortcodes as $shortcode) {
$tag = $tags[$shortcode->getAttribute('data-tagid')];
$parent = $parents[$shortcode->getAttribute('data-parentid')];
$class = null;
if(!empty($tag['attrs']['location'])) $class = $tag['attrs']['location'];
else if(!empty($tag['attrs']['class'])) $class = $tag['attrs']['class'];
$location = self::INLINE;
if($class == 'left' || $class == 'right') $location = self::BEFORE;
if($class == 'center' || $class == 'leftALone') $location = self::SPLIT;
if(!$parent) {
if($location !== self::INLINE) {
user_error("Parent block for shortcode couldn't be found, but location wasn't INLINE",
E_USER_ERROR);
// Now parse the result into a DOM
if (!$htmlvalue->isValid()){
if(self::$error_behavior == self::ERROR) {
user_error('Couldn\'t decode HTML when processing short codes', E_USER_ERRROR);
}
else {
$continue = false;
}
}
else {
$this->moveMarkerToCompliantHome($shortcode, $parent, $location);
}
$this->replaceMarkerWithContent($shortcode, $tag);
}
$content = $htmlvalue->getContent();
if ($continue) {
// First, replace any shortcodes that are in attributes
$this->replaceAttributeTagsWithContent($htmlvalue);
// Clean up any marker classes left over, for example, those injected into <script> tags
$parser = $this;
$content = preg_replace_callback(
// Not a general-case parser; assumes that the HTML generated in replaceElementTagsWithMarkers()
// hasn't been heavily modified
'/<img[^>]+class="'.preg_quote(self::$marker_class).'"[^>]+data-tagid="([^"]+)"[^>]+>/i',
function ($matches) use ($tags, $parser) {
$tag = $tags[$matches[1]];
return $parser->getShortcodeReplacementText($tag);
},
$content
);
// Find all the element scoped shortcode markers
$shortcodes = $htmlvalue->query('//img[@class="'.self::$marker_class.'"]');
// Find the parents. Do this before DOM modification, since SPLIT might cause parents to move otherwise
$parents = $this->findParentsForMarkers($shortcodes);
foreach($shortcodes as $shortcode) {
$tag = $tags[$shortcode->getAttribute('data-tagid')];
$parent = $parents[$shortcode->getAttribute('data-parentid')];
$class = null;
if(!empty($tag['attrs']['location'])) $class = $tag['attrs']['location'];
else if(!empty($tag['attrs']['class'])) $class = $tag['attrs']['class'];
$location = self::INLINE;
if($class == 'left' || $class == 'right') $location = self::BEFORE;
if($class == 'center' || $class == 'leftALone') $location = self::SPLIT;
if(!$parent) {
if($location !== self::INLINE) {
user_error("Parent block for shortcode couldn't be found, but location wasn't INLINE",
E_USER_ERROR);
}
}
else {
$this->moveMarkerToCompliantHome($shortcode, $parent, $location);
}
$this->replaceMarkerWithContent($shortcode, $tag);
}
$content = $htmlvalue->getContent();
// Clean up any marker classes left over, for example, those injected into <script> tags
$parser = $this;
$content = preg_replace_callback(
// Not a general-case parser; assumes that the HTML generated in replaceElementTagsWithMarkers()
// hasn't been heavily modified
'/<img[^>]+class="'.preg_quote(self::$marker_class).'"[^>]+data-tagid="([^"]+)"[^>]+>/i',
function ($matches) use ($tags, $parser) {
$tag = $tags[$matches[1]];
return $parser->getShortcodeReplacementText($tag);
},
$content
);
}
$this->extend('onAfterParse', $content);
return $content;
}