diff --git a/src/Extensions/ElementalPageExtension.php b/src/Extensions/ElementalPageExtension.php index dee8010be..cdebca0a4 100644 --- a/src/Extensions/ElementalPageExtension.php +++ b/src/Extensions/ElementalPageExtension.php @@ -2,7 +2,9 @@ namespace DNADesign\Elemental\Extensions; +use DNADesign\Elemental\Models\BaseElement; use DNADesign\Elemental\Models\ElementalArea; +use SilverStripe\CMS\Model\SiteTree; use SilverStripe\Control\Controller; use SilverStripe\View\Parsers\HTML4Value; use SilverStripe\View\SSViewer; @@ -47,23 +49,14 @@ public function getElementsForSearch() SSViewer::set_themes(SSViewer::config()->get('themes')); try { $output = []; - foreach ($this->owner->hasOne() as $key => $class) { - if ($class !== ElementalArea::class) { - continue; - } - /** @var ElementalArea $area */ - $area = $this->owner->$key(); - if ($area) { - foreach ($area->Elements() as $element) { - if ($element->getSearchIndexable()) { - $content = $element->getContentForSearchIndex(); - if ($content) { - $output[] = $content; - } - } + $this->loopThroughElements(function(BaseElement $element) use (&$output) { + if ($element->getSearchIndexable()) { + $content = $element->getContentForSearchIndex(); + if ($content) { + $output[] = $content; } } - } + }); } finally { // Reset theme if an exception occurs, if you don't have a // try / finally around code that might throw an Exception, @@ -73,6 +66,21 @@ public function getElementsForSearch() return implode($this->owner->config()->get('search_index_element_delimiter') ?? '', $output); } + /** + * @param array $anchors + * + * @see SiteTree::getAnchorsOnPage() + */ + public function updateAnchorsOnPage(array &$anchors): void + { + if (!($this->owner instanceof SiteTree)) { + return; + } + $this->loopThroughElements(function(BaseElement $element) use (&$anchors) { + $anchors = array_merge($anchors, $element->getAnchorsInContent()); + }); + } + public function MetaTags(&$tags) { if (!Controller::has_curr()) { @@ -91,4 +99,23 @@ public function MetaTags(&$tags) $tags = $html->getContent(); } } + + /** + * Call some function over all elements belonging to this page + */ + private function loopThroughElements(callable $callback): void + { + foreach ($this->owner->hasOne() as $key => $class) { + if ($class !== ElementalArea::class) { + continue; + } + /** @var ElementalArea $area */ + $area = $this->owner->$key(); + if ($area) { + foreach ($area->Elements() as $element) { + $callback($element); + } + } + } + } } diff --git a/src/Models/BaseElement.php b/src/Models/BaseElement.php index 519fe9134..33ad2ca74 100644 --- a/src/Models/BaseElement.php +++ b/src/Models/BaseElement.php @@ -257,7 +257,7 @@ public function canEdit($member = null) public function canDelete($member = null) { $extended = $this->extendedCan(__FUNCTION__, $member); - + if ($extended !== null) { return $extended; } @@ -679,6 +679,37 @@ public function getAnchor() return $this->anchor = $result; } + /** + * Get anchors in this block's content. + * Used to populate the "anchor on a page" link in the WYSIWYG + * + * By default, this finds anchors in any HTMLText field on the block, but + * this method should be overridden if anchors are provided in other ways + * for this block or if not all HTMLText fields for this block are + * displayed on the front-end. + */ + public function getAnchorsInContent(): array + { + $anchors = [$this->getAnchor()]; + $anchorRegex = "/\\s+(name|id)\\s*=\\s*([\"'])([^\\2\\s>]*?)\\2|\\s+(name|id)\\s*=\\s*([^\"']+)[\\s +>]/im"; + $allFields = DataObject::getSchema()->fieldSpecs($this); + foreach ($allFields as $field => $fieldSpec) { + $fieldObj = $this->owner->dbObject($field); + if ($fieldObj instanceof DBHTMLText) { + $parseSuccess = preg_match_all($anchorRegex, $fieldObj->getValue() ?? '', $matches); + if ($parseSuccess >= 1) { + $fieldAnchors = array_values(array_filter( + array_merge($matches[3], $matches[5]) + )); + $anchors = array_unique(array_merge($anchors, $fieldAnchors)); + } + } + } + + $this->extend('updateAnchorsInContent', $anchors); + return $anchors; + } + /** * @param string|null $action * @return string|null @@ -688,7 +719,7 @@ public function getAnchor() public function AbsoluteLink($action = null) { $page = $this->getPage(); - + if ($page && ClassInfo::hasMethod($page, 'AbsoluteLink')) { $link = $page->AbsoluteLink($action) . '#' . $this->getAnchor(); $this->extend('updateAbsoluteLink', $link);