diff --git a/CHANGELOG.md b/CHANGELOG.md index 17de5d3..a3de0cb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ # Release Notes for Anchors +## Unreleased + +- Fixed a couple of URL formatting bugs around case sensitivity and punctuation. ([#31](https://github.com/craftcms/anchors/issues/31)) + ## 3.2.0 - 2023-01-09 - Added the `@anchors` GraphQL directive. ([#18](https://github.com/craftcms/anchors/issues/18)) @@ -16,6 +20,7 @@ - The `parser` component can now be configured via `craft\services\Plugins::$pluginConfigs`. ## 2.4.0 - 2022-12-16 + - Removed unnecessary tab spots. ([#21](https://github.com/craftcms/anchors/issues/21)) - Fixed a bug where headings that spanned multiple lines were ignored. ([#20](https://github.com/craftcms/anchors/issues/20)) diff --git a/README.md b/README.md index 1045c24..2003c88 100644 --- a/README.md +++ b/README.md @@ -53,6 +53,14 @@ If you are displaying content in a different language than the current site, use {{ entry.body|anchors(language=entry.site.language) }} ``` +By default, `anchors` filter will lowercase words that are all uppercase and lowercase the first letter in any other case. + +If you want the anchors to always be lowercase, you can use the `lowercase` argument: + +```twig +{{ entry.body|anchors(lowercase=true) }} +``` + ## Configuration To configure Anchors, create a new `anchors.php` file within the `config/` folder, which returns an array. diff --git a/src/Parser.php b/src/Parser.php index 156e433..d9409b0 100644 --- a/src/Parser.php +++ b/src/Parser.php @@ -54,16 +54,17 @@ class Parser extends Component * @param string $html The HTML to parse * @param string|string[] $tags The tags to add anchor links to. * @param string|null $language The content language, used when converting non-ASCII characters to ASCII + * @param bool $lowercase Whether to always lowercase the entire anchor name * @return string The parsed HTML. */ - public function parseHtml(string $html, $tags = 'h1,h2,h3', ?string $language = null): string + public function parseHtml(string $html, $tags = 'h1,h2,h3', ?string $language = null, bool $lowercase = false): string { if (is_string($tags)) { $tags = StringHelper::split($tags); } - return preg_replace_callback('/<(' . implode('|', $tags) . ')([^>]*)>\s*([\w\W]+?)\s*<\/\1>/', function(array $match) use ($language) { - $anchorName = $this->generateAnchorName($match[3], $language); + return preg_replace_callback('/<(' . implode('|', $tags) . ')([^>]*)>\s*([\w\W]+?)\s*<\/\1>/', function(array $match) use ($language, $lowercase) { + $anchorName = $this->generateAnchorName($match[3], $language, $lowercase); $heading = preg_replace('/\s+/', ' ', strip_tags(str_replace([' ', ' '], ' ', $match[3]))); $link = Html::tag('a', $this->anchorLinkText, [ 'class' => $this->anchorLinkClass, @@ -88,10 +89,15 @@ public function parseHtml(string $html, $tags = 'h1,h2,h3', ?string $language = * * @param string $heading * @param string|null $language + * @param bool $lowercase * @return string The generated anchor name. */ - public function generateAnchorName(string $heading, string $language = null): string + public function generateAnchorName(string $heading, string $language = null, bool $lowercase = false): string { + // decode html entities into chars + // see https://github.com/craftcms/anchors/issues/31 for details + $heading = htmlspecialchars_decode($heading, ENT_QUOTES); + // Remove HTML tags $heading = preg_replace('/<(.*?)>/', '', $heading); @@ -110,11 +116,15 @@ public function generateAnchorName(string $heading, string $language = null): st // Turn them into camelCase foreach ($words as $i => $word) { - // Special case if the whole word is capitalized - if (strtoupper($word) === $word) { + if ($lowercase === true) { $words[$i] = strtolower($word); } else { - $words[$i] = lcfirst($word); + // Special case if the whole word is capitalized + if (strtoupper($word) === $word) { + $words[$i] = strtolower($word); + } else { + $words[$i] = lcfirst($word); + } } } diff --git a/src/TwigExtension.php b/src/TwigExtension.php index eeeb862..12dbebf 100644 --- a/src/TwigExtension.php +++ b/src/TwigExtension.php @@ -41,10 +41,11 @@ public function getFilters(): array * @param mixed $html The HTML to parse. * @param string|string[] $tags The HTML tags to check for. * @param string|null $language The content language, used when converting non-ASCII characters to ASCII + * @param bool $lowercase Whether to always lowercase the entire anchor name * @return string The parsed string. */ - public function anchorsFilter($html, $tags = 'h1,h2,h3', ?string $language = null): string + public function anchorsFilter($html, $tags = 'h1,h2,h3', ?string $language = null, bool $lowercase = false): string { - return Plugin::getInstance()->getParser()->parseHtml((string)$html, $tags, $language); + return Plugin::getInstance()->getParser()->parseHtml((string)$html, $tags, $language, $lowercase); } }