Skip to content

Commit

Permalink
allow disabling version banner (#123)
Browse files Browse the repository at this point in the history
  • Loading branch information
davidism authored Oct 24, 2024
2 parents 96537eb + 338bc4b commit e02fc95
Show file tree
Hide file tree
Showing 3 changed files with 56 additions and 34 deletions.
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

0 comments on commit e02fc95

Please sign in to comment.