Skip to content

Commit

Permalink
“Edit this page” → “Edit on GitHub/GitLab/Bitbucket” (#1177)
Browse files Browse the repository at this point in the history
* “Edit this page” → “Edit on GitHub/GitLab/Bitbucket”

Fixes #1172

* Add tests

* Fix typo
  • Loading branch information
jeanas authored Feb 14, 2023
1 parent 30b2c52 commit 8d8bcb8
Show file tree
Hide file tree
Showing 4 changed files with 102 additions and 35 deletions.
35 changes: 33 additions & 2 deletions docs/user_guide/source-buttons.rst
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ Source Buttons

Source buttons are links to the source of your page's content (either on your site, or on hosting sites like GitHub).

Add an Edit this Page button
============================
Add an edit button
==================

You can add a button to each page that will allow users to edit the page text
directly and submit a pull request to update the documentation. To include this
Expand Down Expand Up @@ -80,6 +80,37 @@ any other context values.
"some_other_arg": "?some-other-arg"
}
With the predefined providers, the link text reads "Edit on GitHub/GitLab/Bitbucket".
By default, a simple "Edit" is used if you use a custom URL. However, you can set
a provider name like this:

.. code:: python
html_context = {
"edit_page_url_template": "...",
"edit_page_provider_name": "Provider",
}
This will turn the link into "Edit on Provider".


Custom link text
----------------

You can change the default text by extending the ``edit-this-page.html``
template. For example, if you have ``templates_path = ["_templates"]``
in your Sphinx configuration, you could put this code in
`_templates/edit-this-page.html`:

.. code:: html+jinja

{% extends "!components/edit-this-page.html" %}

{% block edit_this_page_text %}
Edit this page
{% endblock %}


View Source link
================

Expand Down
29 changes: 16 additions & 13 deletions src/pydata_sphinx_theme/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -779,8 +779,8 @@ def extract_level_recursive(ul, navs_list):
def setup_edit_url(app, pagename, templatename, context, doctree):
"""Add a function that jinja can access for returning the edit URL of a page."""

def get_edit_url():
"""Return a URL for an "edit this page" link."""
def get_edit_provider_and_url():
"""Return a provider name and a URL for an "edit this page" link."""
file_name = f"{pagename}{context['page_source_suffix']}"

# Make sure that doc_path has a path separator only if it exists (to avoid //)
Expand All @@ -794,7 +794,7 @@ def get_edit_url():
"gitlab_url": "https://gitlab.com",
}

edit_url_attrs = {}
edit_attrs = {}

# ensure custom URL is checked first, if given
url_template = context.get("edit_page_url_template")
Expand All @@ -806,23 +806,26 @@ def get_edit_url():
"Ensure `file_name` appears in `edit_page_url_template`: "
f"{url_template}"
)
provider_name = context.get("edit_page_provider_name")
edit_attrs[("edit_page_url_template",)] = (provider_name, url_template)

edit_url_attrs[("edit_page_url_template",)] = url_template

edit_url_attrs.update(
edit_attrs.update(
{
("bitbucket_user", "bitbucket_repo", "bitbucket_version"): (
"Bitbucket",
"{{ bitbucket_url }}/{{ bitbucket_user }}/{{ bitbucket_repo }}"
"/src/{{ bitbucket_version }}"
"/{{ doc_path }}{{ file_name }}?mode=edit"
"/{{ doc_path }}{{ file_name }}?mode=edit",
),
("github_user", "github_repo", "github_version"): (
"GitHub",
"{{ github_url }}/{{ github_user }}/{{ github_repo }}"
"/edit/{{ github_version }}/{{ doc_path }}{{ file_name }}"
"/edit/{{ github_version }}/{{ doc_path }}{{ file_name }}",
),
("gitlab_user", "gitlab_repo", "gitlab_version"): (
"GitLab",
"{{ gitlab_url }}/{{ gitlab_user }}/{{ gitlab_repo }}"
"/-/edit/{{ gitlab_version }}/{{ doc_path }}{{ file_name }}"
"/-/edit/{{ gitlab_version }}/{{ doc_path }}{{ file_name }}",
),
}
)
Expand All @@ -831,17 +834,17 @@ def get_edit_url():
doc_context.update(context)
doc_context.update(doc_path=doc_path, file_name=file_name)

for attrs, url_template in edit_url_attrs.items():
for attrs, (provider, url_template) in edit_attrs.items():
if all(doc_context.get(attr) not in [None, "None"] for attr in attrs):
return jinja2.Template(url_template).render(**doc_context)
return provider, jinja2.Template(url_template).render(**doc_context)

raise ExtensionError(
"Missing required value for `use_edit_page_button`. "
"Ensure one set of the following in your `html_context` "
f"configuration: {sorted(edit_url_attrs.keys())}"
f"configuration: {sorted(edit_attrs.keys())}"
)

context["get_edit_url"] = get_edit_url
context["get_edit_provider_and_url"] = get_edit_provider_and_url

# Ensure that the max TOC level is an integer
context["theme_show_toc_level"] = int(context.get("theme_show_toc_level", 1))
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,16 @@
{% if sourcename is defined and theme_use_edit_page_button==true and page_source_suffix %}
{% set src = sourcename.split('.') %}
<div class="tocsection editthispage">
<a href="{{ get_edit_url() }}">
<i class="fa-solid fa-pencil"></i> {{ _("Edit this page") }}
<a href="{{ get_edit_provider_and_url()[1] }}">
<i class="fa-solid fa-pencil"></i>
{% set provider = get_edit_provider_and_url()[0] %}
{% block edit_this_page_text %}
{% if provider %}
{% trans provider=provider %}Edit on {{ provider }}{% endtrans %}
{% else %}
{% trans %}Edit{% endtrans %}
{% endif %}
{% endblock %}
</a>
</div>
{% endif %}
61 changes: 43 additions & 18 deletions tests/test_build.py
Original file line number Diff line number Diff line change
Expand Up @@ -495,7 +495,7 @@ def test_included_toc(sphinx_build_factory):
"github_version": "HEAD",
"doc_path": "docs",
},
"https://github.com/foo/bar/edit/HEAD/docs/index.rst",
("Edit on GitHub", "https://github.com/foo/bar/edit/HEAD/docs/index.rst"),
],
[
{
Expand All @@ -504,7 +504,7 @@ def test_included_toc(sphinx_build_factory):
"gitlab_version": "HEAD",
"doc_path": "docs",
},
"https://gitlab.com/foo/bar/-/edit/HEAD/docs/index.rst",
("Edit on GitLab", "https://gitlab.com/foo/bar/-/edit/HEAD/docs/index.rst"),
],
[
{
Expand All @@ -513,7 +513,10 @@ def test_included_toc(sphinx_build_factory):
"bitbucket_version": "HEAD",
"doc_path": "docs",
},
"https://bitbucket.org/foo/bar/src/HEAD/docs/index.rst?mode=edit",
(
"Edit on Bitbucket",
"https://bitbucket.org/foo/bar/src/HEAD/docs/index.rst?mode=edit",
),
],
]

Expand All @@ -526,10 +529,10 @@ def test_included_toc(sphinx_build_factory):
key: f"{value}/" if key == "doc_path" else value
for key, value in html_context.items()
},
# the URL does not change
url,
# the text and URL do not change
text_and_url,
]
for html_context, url in good_edits
for html_context, text_and_url in good_edits
]

# copy the "good" ones, provide a `<whatever>_url` based off the default
Expand All @@ -541,11 +544,14 @@ def test_included_toc(sphinx_build_factory):
# add a provider url
**{f"{provider}_url": f"https://{provider}.example.com"},
),
f"""https://{provider}.example.com/foo/{url.split("/foo/")[1]}""",
(
text,
f"""https://{provider}.example.com/foo/{url.split("/foo/")[1]}""",
),
]
for html_context, url in good_edits
for provider in ["gitlab", "bitbucket", "github"]
if provider in url
for html_context, (text, url) in good_edits
for provider in ["github", "gitlab", "bitbucket"]
if provider in text.lower()
]

# missing any of the values should fail
Expand All @@ -560,7 +566,7 @@ def test_included_toc(sphinx_build_factory):
},
None,
]
for html_context, url in good_edits
for html_context, _ in good_edits
]

# a good custom URL template
Expand All @@ -571,7 +577,20 @@ def test_included_toc(sphinx_build_factory):
"https://dvcs.example.com/foo/bar/edit/HEAD/{{ file_name }}"
)
},
"https://dvcs.example.com/foo/bar/edit/HEAD/index.rst",
("Edit", "https://dvcs.example.com/foo/bar/edit/HEAD/index.rst"),
]
]

# a good custom URL template with an additional provider name
good_custom_with_provider = [
[
{
"edit_page_url_template": (
"https://dvcs.example.com/foo/bar/edit/HEAD/{{ file_name }}"
),
"edit_page_provider_name": "FooProvider",
},
("Edit on FooProvider", "https://dvcs.example.com/foo/bar/edit/HEAD/index.rst"),
]
]

Expand All @@ -594,24 +613,31 @@ def test_included_toc(sphinx_build_factory):
]


@pytest.mark.parametrize("html_context,edit_url", all_edits)
def test_edit_page_url(sphinx_build_factory, html_context, edit_url):
@pytest.mark.parametrize("html_context,edit_text_and_url", all_edits)
def test_edit_page_url(sphinx_build_factory, html_context, edit_text_and_url):
confoverrides = {
"html_theme_options.use_edit_page_button": True,
"html_context": html_context,
}
sphinx_build = sphinx_build_factory("base", confoverrides=confoverrides)

if edit_url is None:
with pytest.raises(sphinx.errors.ExtensionError):
if edit_text_and_url is None:
with pytest.raises(
sphinx.errors.ExtensionError, match="Missing required value"
):
sphinx_build.build()
return

edit_text, edit_url = edit_text_and_url
sphinx_build.build()
index_html = sphinx_build.html_tree("index.html")
edit_link = index_html.select(".editthispage a")
assert edit_link, "no edit link found"
assert edit_link[0].attrs["href"] == edit_url, f"edit link didn't match {edit_link}"
# First child is the icon
assert (
list(edit_link[0].strings)[1].strip() == edit_text
), f"edit text didn't match {edit_text}"


@pytest.mark.parametrize(
Expand All @@ -636,7 +662,6 @@ def test_edit_page_url(sphinx_build_factory, html_context, edit_url):
],
)
def test_analytics(sphinx_build_factory, provider, tags):

confoverrides = provider
sphinx_build = sphinx_build_factory("base", confoverrides=confoverrides)
sphinx_build.build()
Expand Down Expand Up @@ -895,7 +920,7 @@ def test_translations(sphinx_build_factory):
# TODO: Add translations where there are english phrases below
sidebar_secondary = index.select(".bd-sidebar-secondary")[0]
assert "Montrer le code source" in str(sidebar_secondary)
assert "Edit this page" in str(sidebar_secondary)
assert "Edit on GitHub" in str(sidebar_secondary)

# TODO: Add translations where there are english phrases below
header = index.select(".bd-header")[0]
Expand Down

0 comments on commit 8d8bcb8

Please sign in to comment.