From 4b827e11872d7cd9dbc387d0dcc33b6dbc7f2579 Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade Date: Fri, 21 Oct 2022 01:00:32 +0200 Subject: [PATCH 1/6] Don't overwrite a page's existing manual meta description tag --- docs/source/conf.py | 2 ++ sphinxext/opengraph/__init__.py | 14 +++++++-- sphinxext/opengraph/metaparser.py | 29 +++++++++++++++++++ .../conf.py | 10 +++++++ .../index.rst | 4 +++ .../roots/test-meta-name-description/conf.py | 10 +++++++ .../test-meta-name-description/index.rst | 1 + tests/test_options.py | 23 +++++++++++++++ 8 files changed, 91 insertions(+), 2 deletions(-) create mode 100644 sphinxext/opengraph/metaparser.py create mode 100644 tests/roots/test-meta-name-description-no-override-manual/conf.py create mode 100644 tests/roots/test-meta-name-description-no-override-manual/index.rst create mode 100644 tests/roots/test-meta-name-description/conf.py create mode 100644 tests/roots/test-meta-name-description/index.rst diff --git a/docs/source/conf.py b/docs/source/conf.py index ee638f3..ec9cf32 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -44,6 +44,8 @@ # This pattern also affects html_static_path and html_extra_path. exclude_patterns = [] +# Generate tags. +ogp_enable_meta_description = True # -- Options for HTML output ------------------------------------------------- diff --git a/sphinxext/opengraph/__init__.py b/sphinxext/opengraph/__init__.py index e68ed2f..251d376 100644 --- a/sphinxext/opengraph/__init__.py +++ b/sphinxext/opengraph/__init__.py @@ -6,6 +6,7 @@ from sphinx.application import Sphinx from .descriptionparser import get_description +from .metaparser import get_meta_description from .titleparser import get_title import os @@ -28,7 +29,7 @@ } -def make_tag(property: str, content: str) -> str: +def make_tag(property: str, content: str, type_: str = "property") -> str: # Parse quotation, so they won't break html tags if smart quotes are disabled content = content.replace('"', """) return f'' @@ -45,6 +46,7 @@ def get_tags( if fields is None: fields = {} tags = {} + meta_tags = {} # For non-og meta tags # Set length of description try: @@ -105,6 +107,11 @@ def get_tags( if description: tags["og:description"] = description + if config["ogp_enable_meta_description"] and not get_meta_description( + context["metatags"] + ): + meta_tags["description"] = description + # image tag # Get basic values from config if "og:image" in fields: @@ -160,7 +167,9 @@ def get_tags( return ( "\n".join( - [make_tag(p, c) for p, c in tags.items()] + config["ogp_custom_meta_tags"] + [make_tag(p, c) for p, c in tags.items()] + + [make_tag(p, c, "name") for p, c in meta_tags.items()] + + config["ogp_custom_meta_tags"] ) + "\n" ) @@ -186,6 +195,7 @@ def setup(app: Sphinx) -> Dict[str, Any]: app.add_config_value("ogp_type", "website", "html") app.add_config_value("ogp_site_name", None, "html") app.add_config_value("ogp_custom_meta_tags", [], "html") + app.add_config_value("ogp_enable_meta_description", False, "html") app.connect("html-page-context", html_page_context) diff --git a/sphinxext/opengraph/metaparser.py b/sphinxext/opengraph/metaparser.py new file mode 100644 index 0000000..77d06a4 --- /dev/null +++ b/sphinxext/opengraph/metaparser.py @@ -0,0 +1,29 @@ +from html.parser import HTMLParser + + +class HTMLTextParser(HTMLParser): + """ + Parse HTML into text + """ + + def __init__(self): + super().__init__() + self.meta_description = None + + def handle_starttag(self, tag, attrs) -> None: + # For example: + # attrs = [("content", "My manual description"), ("name", "description")] + if ("name", "description") in attrs: + self.meta_description = True + for name, value in attrs: + if name == "content": + self.meta_description = value + break + + +def get_meta_description(meta_tags: str) -> bool: + htp = HTMLTextParser() + htp.feed(meta_tags) + htp.close() + + return htp.meta_description diff --git a/tests/roots/test-meta-name-description-no-override-manual/conf.py b/tests/roots/test-meta-name-description-no-override-manual/conf.py new file mode 100644 index 0000000..8a6134e --- /dev/null +++ b/tests/roots/test-meta-name-description-no-override-manual/conf.py @@ -0,0 +1,10 @@ +extensions = ["sphinxext.opengraph"] + +master_doc = "index" +exclude_patterns = ["_build"] + +html_theme = "basic" + +ogp_site_url = "http://example.org/en/latest/" + +ogp_enable_meta_description = True diff --git a/tests/roots/test-meta-name-description-no-override-manual/index.rst b/tests/roots/test-meta-name-description-no-override-manual/index.rst new file mode 100644 index 0000000..4f7058d --- /dev/null +++ b/tests/roots/test-meta-name-description-no-override-manual/index.rst @@ -0,0 +1,4 @@ +.. meta:: + :description: My manual description + +Lorem ipsum dolor sit amet, consectetur adipiscing elit. Suspendisse at lorem ornare, fringilla massa nec, venenatis mi. Donec erat sapien, tincidunt nec rhoncus nec, scelerisque id diam. Orci varius natoque penatibus et magnis dis parturient mauris. \ No newline at end of file diff --git a/tests/roots/test-meta-name-description/conf.py b/tests/roots/test-meta-name-description/conf.py new file mode 100644 index 0000000..b31eaac --- /dev/null +++ b/tests/roots/test-meta-name-description/conf.py @@ -0,0 +1,10 @@ +extensions = ["sphinxext.opengraph"] + +master_doc = "index" +exclude_patterns = ["_build"] + +html_theme = "basic" + +ogp_site_url = "http://example.org/en/latest/" + +enable_meta_description = True diff --git a/tests/roots/test-meta-name-description/index.rst b/tests/roots/test-meta-name-description/index.rst new file mode 100644 index 0000000..a33871d --- /dev/null +++ b/tests/roots/test-meta-name-description/index.rst @@ -0,0 +1 @@ +Lorem ipsum dolor sit amet, consectetur adipiscing elit. Suspendisse at lorem ornare, fringilla massa nec, venenatis mi. Donec erat sapien, tincidunt nec rhoncus nec, scelerisque id diam. Orci varius natoque penatibus et magnis dis parturient mauris. \ No newline at end of file diff --git a/tests/test_options.py b/tests/test_options.py index 60d856c..b51218a 100644 --- a/tests/test_options.py +++ b/tests/test_options.py @@ -13,6 +13,12 @@ def get_tag_content(tags, tag_type): return get_tag(tags, tag_type).get("content", "") +def get_meta_description(tags): + return [tag for tag in tags if tag.get("name") == "description"][0].get( + "content", "" + ) + + @pytest.mark.sphinx("html", testroot="simple") def test_simple(og_meta_tags): description = get_tag_content(og_meta_tags, "description") @@ -26,6 +32,23 @@ def test_simple(og_meta_tags): ) +@pytest.mark.sphinx("html", testroot="meta-name-description") +def test_meta_name_description(meta_tags): + og_description = get_tag_content(meta_tags, "description") + description = get_meta_description(meta_tags) + + assert description == og_description + + +@pytest.mark.sphinx("html", testroot="meta-name-description-no-override-manual") +def test_meta_name_description(meta_tags): + og_description = get_tag_content(meta_tags, "description") + description = get_meta_description(meta_tags) + + assert description != og_description + assert description == "My manual description" + + @pytest.mark.sphinx("html", testroot="simple") def test_site_url(og_meta_tags): # Uses the same directory as simple, because it already contains url for a minimal config From de9eccc4d1e47b509d5ef3c225cf83a8b9d69ba3 Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade Date: Fri, 21 Oct 2022 01:02:25 +0200 Subject: [PATCH 2/6] Allow testing feature branches --- .github/workflows/workflow.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/.github/workflows/workflow.yml b/.github/workflows/workflow.yml index 954685b..3c0a859 100644 --- a/.github/workflows/workflow.yml +++ b/.github/workflows/workflow.yml @@ -4,8 +4,6 @@ on: branches: - main push: - branches: - - main create: tags: - '*' From 161f2b5a5b2c8d90b26f06fe4dd88a5e592237a4 Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade Date: Sat, 22 Oct 2022 18:55:54 +0200 Subject: [PATCH 3/6] Don't overwrite a page's existing manual meta og:description tag --- sphinxext/opengraph/__init__.py | 2 +- .../conf.py | 0 .../index.rst | 0 .../conf.py | 10 ++++++++++ .../index.rst | 3 +++ tests/test_options.py | 12 ++++++++++-- 6 files changed, 24 insertions(+), 3 deletions(-) rename tests/roots/{test-meta-name-description-no-override-manual => test-meta-name-description-manual-description}/conf.py (100%) rename tests/roots/{test-meta-name-description-no-override-manual => test-meta-name-description-manual-description}/index.rst (100%) create mode 100644 tests/roots/test-meta-name-description-manual-og-description/conf.py create mode 100644 tests/roots/test-meta-name-description-manual-og-description/index.rst diff --git a/sphinxext/opengraph/__init__.py b/sphinxext/opengraph/__init__.py index 251d376..f4b21c4 100644 --- a/sphinxext/opengraph/__init__.py +++ b/sphinxext/opengraph/__init__.py @@ -32,7 +32,7 @@ def make_tag(property: str, content: str, type_: str = "property") -> str: # Parse quotation, so they won't break html tags if smart quotes are disabled content = content.replace('"', """) - return f'' + return f'' def get_tags( diff --git a/tests/roots/test-meta-name-description-no-override-manual/conf.py b/tests/roots/test-meta-name-description-manual-description/conf.py similarity index 100% rename from tests/roots/test-meta-name-description-no-override-manual/conf.py rename to tests/roots/test-meta-name-description-manual-description/conf.py diff --git a/tests/roots/test-meta-name-description-no-override-manual/index.rst b/tests/roots/test-meta-name-description-manual-description/index.rst similarity index 100% rename from tests/roots/test-meta-name-description-no-override-manual/index.rst rename to tests/roots/test-meta-name-description-manual-description/index.rst diff --git a/tests/roots/test-meta-name-description-manual-og-description/conf.py b/tests/roots/test-meta-name-description-manual-og-description/conf.py new file mode 100644 index 0000000..8a6134e --- /dev/null +++ b/tests/roots/test-meta-name-description-manual-og-description/conf.py @@ -0,0 +1,10 @@ +extensions = ["sphinxext.opengraph"] + +master_doc = "index" +exclude_patterns = ["_build"] + +html_theme = "basic" + +ogp_site_url = "http://example.org/en/latest/" + +ogp_enable_meta_description = True diff --git a/tests/roots/test-meta-name-description-manual-og-description/index.rst b/tests/roots/test-meta-name-description-manual-og-description/index.rst new file mode 100644 index 0000000..19f5583 --- /dev/null +++ b/tests/roots/test-meta-name-description-manual-og-description/index.rst @@ -0,0 +1,3 @@ +:og:description: My manual og:description + +Lorem ipsum dolor sit amet, consectetur adipiscing elit. Suspendisse at lorem ornare, fringilla massa nec, venenatis mi. Donec erat sapien, tincidunt nec rhoncus nec, scelerisque id diam. Orci varius natoque penatibus et magnis dis parturient mauris. \ No newline at end of file diff --git a/tests/test_options.py b/tests/test_options.py index b51218a..cc0db17 100644 --- a/tests/test_options.py +++ b/tests/test_options.py @@ -1,7 +1,6 @@ import pytest from sphinx.application import Sphinx import conftest -import os def get_tag(tags, tag_type): @@ -40,7 +39,7 @@ def test_meta_name_description(meta_tags): assert description == og_description -@pytest.mark.sphinx("html", testroot="meta-name-description-no-override-manual") +@pytest.mark.sphinx("html", testroot="meta-name-description-manual-description") def test_meta_name_description(meta_tags): og_description = get_tag_content(meta_tags, "description") description = get_meta_description(meta_tags) @@ -49,6 +48,15 @@ def test_meta_name_description(meta_tags): assert description == "My manual description" +@pytest.mark.sphinx("html", testroot="meta-name-description-manual-og-description") +def test_meta_name_description(meta_tags): + og_description = get_tag_content(meta_tags, "description") + description = get_meta_description(meta_tags) + + assert og_description != description + assert og_description == "My manual og:description" + + @pytest.mark.sphinx("html", testroot="simple") def test_site_url(og_meta_tags): # Uses the same directory as simple, because it already contains url for a minimal config From c6eccdf61325ebd83afae83b79a1b52329e556b3 Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade Date: Sat, 22 Oct 2022 22:27:05 +0200 Subject: [PATCH 4/6] Update documentation --- README.md | 34 +++++++++++++++++++++------------- 1 file changed, 21 insertions(+), 13 deletions(-) diff --git a/README.md b/README.md index 41594bb..e561ea9 100644 --- a/README.md +++ b/README.md @@ -1,12 +1,15 @@ # sphinxext-opengraph -![Build](https://github.com/wpilibsuite/sphinxext-opengraph/workflows/Test%20and%20Deploy/badge.svg) -[![Code style: black](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/psf/black) -Sphinx extension to generate OpenGraph metadata (https://ogp.me/) +[![Build](https://github.com/wpilibsuite/sphinxext-opengraph/workflows/Test%20and%20Deploy/badge.svg)](https://github.com/wpilibsuite/sphinxext-opengraph/actions) +[![Code style: Black](https://img.shields.io/badge/code%20style-Black-000000.svg)](https://github.com/psf/black) + +Sphinx extension to generate [Open Graph metadata](https://ogp.me/). ## Installation -`python -m pip install sphinxext-opengraph` +```sh +python -m pip install sphinxext-opengraph +``` ## Usage Just add `sphinxext.opengraph` to your extensions list in your `conf.py` @@ -17,9 +20,9 @@ extensions = [ ] ``` ## Options -These values are placed in the conf.py of your sphinx project. +These values are placed in the `conf.py` of your Sphinx project. -Users hosting documentation on Read The Docs *do not* need to set any of the following unless custom configuration is wanted. The extension will automatically retrieve your site url. +Users hosting documentation on Read The Docs *do not* need to set any of the following unless custom configuration is wanted. The extension will automatically retrieve your site URL. * `ogp_site_url` * This config option is very important, set it to the URL the site is being hosted on. @@ -32,12 +35,14 @@ Users hosting documentation on Read The Docs *do not* need to set any of the fol * `ogp_image_alt` * This is not required. Alt text for image. Defaults to using `ogp_site_name` or the document's title as alt text, if available. Set to `False` if you want to turn off alt text completely. * `ogp_use_first_image` - * This is not required. Set to True to use each page's first image, if available. If set to True but no image is found, Sphinx will use `ogp_image` instead. + * This is not required. Set to `True` to use each page's first image, if available. If set to `True` but no image is found, Sphinx will use `ogp_image` instead. * `ogp_type` - * This sets the ogp type attribute, for more information on the types available please take a look at https://ogp.me/#types. By default it is set to `website`, which should be fine for most use cases. + * This sets the ogp type attribute, for more information on the types available please take a look at [https://ogp.me/#types](https://ogp.me/#types). By default it is set to `website`, which should be fine for most use cases. * `ogp_custom_meta_tags` * This is not required. List of custom html snippets to insert. - +* `ogp_enable_meta_description` + * This is not required. When `True`, generates `` from the page. + ## Example Config ### Simple Config @@ -59,20 +64,23 @@ ogp_custom_meta_tags = [ '', ] +ogp_enable_meta_description = True ``` ## Per Page Overrides -[Field lists](https://www.sphinx-doc.org/en/master/usage/restructuredtext/field-lists.html) are used to allow you to override certain settings on each page and set unsupported arbitrary OpenGraph tags. +[Field lists](https://www.sphinx-doc.org/en/master/usage/restructuredtext/field-lists.html) are used to allow you to override certain settings on each page and set unsupported arbitrary Open Graph tags. Make sure you place the fields at the very start of the document such that Sphinx will pick them up and also won't build them into the html. ### Overrides -These are some overrides that can be used, you can actually override any tag and field lists will always take priority. +These are some overrides that can be used on individual pages, you can actually override any tag and field lists will always take priority. * `:og_description_length:` - * Configure the amount of characters to grab for the description of the page. If the value isn't a number it will fall back to `ogp_description_length`. Note the slightly different syntax because this isn't directly an OpenGraph tag. + * Configure the amount of characters to grab for the description of the page. If the value isn't a number it will fall back to `ogp_description_length`. Note the slightly different syntax because this isn't directly an Open Graph tag. * `:og:description:` * Lets you override the description of the page. +* `:description:` or `.. meta::\n :description:` + * Sets the `` description. * `:og:title:` * Lets you override the title of the page. * `:og:type:` @@ -95,7 +103,7 @@ Page contents ``` ### Arbitrary Tags[^1] -Additionally, you can use field lists to add any arbitrary OpenGraph tag not supported by the extension. The syntax for arbitrary tags is the same with `:og:tag: content`. For Example: +Additionally, you can use field lists to add any arbitrary Open Graph tag not supported by the extension. The syntax for arbitrary tags is the same with `:og:tag: content`. For example: ```rst :og:video: http://example.org/video.mp4 From b95a988b8280bafec62af8d4e5ee5b6608ae63ea Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade Date: Tue, 25 Oct 2022 22:01:57 +0300 Subject: [PATCH 5/6] Default ogp_enable_meta_description to True --- docs/source/conf.py | 3 --- sphinxext/opengraph/__init__.py | 2 +- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/docs/source/conf.py b/docs/source/conf.py index ec9cf32..56d293c 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -44,9 +44,6 @@ # This pattern also affects html_static_path and html_extra_path. exclude_patterns = [] -# Generate tags. -ogp_enable_meta_description = True - # -- Options for HTML output ------------------------------------------------- # The theme to use for HTML and HTML Help pages. See the documentation for diff --git a/sphinxext/opengraph/__init__.py b/sphinxext/opengraph/__init__.py index f4b21c4..ab8b8f9 100644 --- a/sphinxext/opengraph/__init__.py +++ b/sphinxext/opengraph/__init__.py @@ -195,7 +195,7 @@ def setup(app: Sphinx) -> Dict[str, Any]: app.add_config_value("ogp_type", "website", "html") app.add_config_value("ogp_site_name", None, "html") app.add_config_value("ogp_custom_meta_tags", [], "html") - app.add_config_value("ogp_enable_meta_description", False, "html") + app.add_config_value("ogp_enable_meta_description", True, "html") app.connect("html-page-context", html_page_context) From 8f4b96603ef5b173e400996fe65228ab8504afec Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade Date: Wed, 26 Oct 2022 16:13:41 +0300 Subject: [PATCH 6/6] Run on external PRs but not internal PRs --- .github/workflows/workflow.yml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.github/workflows/workflow.yml b/.github/workflows/workflow.yml index 3c0a859..9a10c32 100644 --- a/.github/workflows/workflow.yml +++ b/.github/workflows/workflow.yml @@ -10,6 +10,11 @@ on: jobs: check: runs-on: ubuntu-latest + + # We want to run on external PRs, but not on our own internal PRs as they'll be run + # by the push to the branch. + if: github.event_name == 'push' || github.event.pull_request.head.repo.full_name != github.repository + steps: - uses: actions/checkout@v3 - uses: actions/setup-python@v4