Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

allow disabling version banner #123

Merged
merged 2 commits into from
Oct 24, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion CHANGES.rst
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,10 @@ Unreleased

- When getting the canonical URL on Read the Docs, replace the path with
``/en/stable/`` instead of ``/page/``. This can be configured with
``rtd_canonical_path``. :pr:`119`
``rtd_canonical_path``. :pr:`122`
- The version banner can be disabled by setting ``version_banner = False``.
On Read the Docs, it is disabled when building the ``stable`` version or
PRs. :pr:`123`


Version 2.2.0
Expand Down
30 changes: 20 additions & 10 deletions src/pallets_sphinx_themes/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ def setup(app):

app.add_config_value("is_pallets_theme", None, "html")
app.add_config_value("rtd_canonical_path", "/en/stable/", "html")
app.add_config_value("version_banner", True, "html")

# Use the sphinx-notfound-page extension to generate a 404 page with valid
# URLs. Only configure it if it's not already configured.
Expand Down Expand Up @@ -82,16 +83,25 @@ def find_base_canonical_url(app: Sphinx) -> None:

@only_pallets_theme()
def add_theme_files(app: Sphinx) -> None:
# Add the JavaScript for the version warning banner. Include the project and
# version as data attributes that the script will access. The project name
# is assumed to be the PyPI name, and is normalized to avoid a redirect.
app.add_js_file(
"describe_version.js",
**{
"data-project": re.sub(r"[-_.]+", "-", app.config.project).lower(),
"data-version": app.config.version,
},
)
# Add the JavaScript for the version warning banner if ``version_banner`` is
# enabled. On Read the Docs, don't include it for stable or PR builds.
# Include the project and version as data attributes that the script will
# access. The project name is assumed to be the PyPI name, and is normalized
# to avoid a redirect.
rtd_version = os.environ.get("READTHEDOCS_VERSION")
rtd_version_type = os.environ.get("READTHEDOCS_VERSION_TYPE")

if app.config.version_banner and (
rtd_version is None # not on read the docs
or (rtd_version != "stable" and rtd_version_type in {"branch", "tag"})
):
app.add_js_file(
"describe_version.js",
**{
"data-project": re.sub(r"[-_.]+", "-", app.config.project).lower(),
"data-version": app.config.version,
},
)


@only_pallets_theme()
Expand Down
55 changes: 32 additions & 23 deletions src/pallets_sphinx_themes/themes/pocoo/static/describe_version.js
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ function compareVersions(a, b) {
/**
* Get the list of released versions for the project from PyPI. Prerelease and
* development versions are discarded. The list is sorted in descending order,
* latest version first.
* highest version first.
*
* This will be called on every page load. To avoid making excessive requests to
* PyPI, the result is cached for 1 day. PyPI also sends cache headers, so a
Expand Down Expand Up @@ -103,8 +103,9 @@ async function getReleasedVersions(name) {
}

/**
* Get the latest released version of the project from PyPI, and compare the
* version being documented. Return the
* Get the highest released version of the project from PyPI, and compare the
* version being documented. Returns a list of two values, the comparison
* result and the highest version.
*
* @param name The normalized PyPI project name.
* @param value The version being documented.
Expand All @@ -122,55 +123,63 @@ async function describeVersion(name, value) {
return [1, null]
}

let latestVersion = releasedVersions[0]
let compared = compareVersions(currentVersion, latestVersion)
let highestVersion = releasedVersions[0]
let compared = compareVersions(currentVersion, highestVersion)

if (compared === 1) {
return [1, latestVersion]
return [1, highestVersion]
}

// If the current version including trailing zeros is a prefix of the latest
// version, then these are the latest docs. For example, 2.0.x becomes 2.0,
// If the current version including trailing zeros is a prefix of the highest
// version, then these are the stable docs. For example, 2.0.x becomes 2.0,
// which is a prefix of 2.0.3. If we were just looking at the compare result,
// it would incorrectly be marked as an old version.
if (currentVersion.parts.every((n, i) => n === latestVersion.parts[i])) {
return [0, latestVersion]
if (currentVersion.parts.every((n, i) => n === highestVersion.parts[i])) {
return [0, highestVersion]
}

return [-1, latestVersion]
return [-1, highestVersion]
}

/**
* Compare the version being documented to the latest version, and display a
* warning banner if it is not the latest version.
* Compare the version being documented to the highest released version, and
* display a warning banner if it is not the highest version.
*
* @param project The normalized PyPI project name.
* @param version The version being documented.
* @returns {Promise<void>}
*/
async function createBanner(project, version) {
let [desc, latest] = await describeVersion(project, version)
let [compared, stable] = await describeVersion(project, version)

// No banner if this is the latest version or there are no other versions.
if (desc === 0 || latest === null) {
// No banner if this is the highest version or there are no other versions.
if (compared === 0 || stable === null) {
return
}

let banner = document.createElement("p")
banner.className = "version-warning"

if (desc === 1) {
if (compared === 1) {
banner.textContent = "This is the development version. The stable version is "
} else if (desc === -1) {
} else if (compared === -1) {
banner.textContent = "This is an old version. The current version is "
}

let link = document.createElement("a")
link.href = document.querySelector('link[rel="canonical"]').href
link.textContent = latest.value
banner.append(link, ".")
document.getElementsByClassName("document")[0].prepend(banner)
let canonical = document.querySelector('link[rel="canonical"]')

if (canonical !== null) {
// If a canonical URL is available, the version is a link to it.
let link = document.createElement("a")
link.href = canonical.href
link.textContent = stable.value
banner.append(link, ".")
} else {
// Otherwise, the version is text only.
banner.append(stable.value, ".")
}

document.getElementsByClassName("document")[0].prepend(banner)
// Set scroll-padding-top to prevent the banner from overlapping anchors.
// It's also set in CSS assuming the banner text is only 1 line.
let bannerStyle = window.getComputedStyle(banner)
Expand Down