diff --git a/.github/workflows/legacy-build.yml b/.github/workflows/legacy-build.yml index 8a4b75a..baa8400 100644 --- a/.github/workflows/legacy-build.yml +++ b/.github/workflows/legacy-build.yml @@ -37,6 +37,9 @@ jobs: - 'legacy/demo/**' - 'legacy/Makefile' - '.github/workflows/legacy-build.yml' + v1.0: + - *shared + - 'legacy/1.0/**' v2.0: - *shared - 'legacy/2.0/**' @@ -76,7 +79,7 @@ jobs: BUILD_ALL: ${{ github.event_name != 'pull_request' }} run: | # Implemented versions; append when a new directory lands. - ALL='["2.0","2.1","2.5","3.0","3.1","3.2","3.3","4.0","4.1","4.2"]' + ALL='["1.0","2.0","2.1","2.5","3.0","3.1","3.2","3.3","4.0","4.1","4.2"]' if [ "${BUILD_ALL}" = "true" ]; then VERSIONS="${ALL}" else diff --git a/CHANGES.md b/CHANGES.md index 2be8aa2..23e466c 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,4 +1,6 @@ # Changelog +- Add Plone 1.0 image to the legacy matrix, completing it. Refs #197 + [@ericof] - Add Plone 2.0 image to the legacy matrix. Refs #196 [@ericof] - Add Plone 2.1 image to the legacy matrix. Refs #195 diff --git a/README.md b/README.md index d065b09..ceac8c7 100644 --- a/README.md +++ b/README.md @@ -54,6 +54,7 @@ listed; earlier point releases remain in the repository and on Docker Hub. | 2.5.4-2 | 2.4.6 | Debian *(legacy)* | [`legacy/2.5/Dockerfile`](legacy/2.5/Dockerfile) | `2.5`, `2.5.4-2`, `2.5-demo`, `2.5.4-2-demo` | | 2.1.4 | 2.4.6 | Debian *(legacy)* | [`legacy/2.1/Dockerfile`](legacy/2.1/Dockerfile) | `2.1`, `2.1.4`, `2.1-demo`, `2.1.4-demo` | | 2.0.5 | 2.3.7 | Debian *(legacy)* | [`legacy/2.0/Dockerfile`](legacy/2.0/Dockerfile) | `2.0`, `2.0.5`, `2.0-demo`, `2.0.5-demo` | +| 1.0.6 | 2.3.7 | Debian *(legacy)* | [`legacy/1.0/Dockerfile`](legacy/1.0/Dockerfile) | `1.0`, `1.0.6`, `1.0-demo`, `1.0.6-demo` | ### Where the tags live diff --git a/legacy/1.0/Dockerfile b/legacy/1.0/Dockerfile new file mode 100644 index 0000000..e2480de --- /dev/null +++ b/legacy/1.0/Dockerfile @@ -0,0 +1,250 @@ +# syntax=docker/dockerfile:1 +# +# Plone 1.0.x — tarball era (Zope 2.7.x / Python 2.3.x) +# +# The oldest Plone in the matrix, and the only image that patches upstream +# source. Strategy is otherwise identical to 2.0: +# Stage 1 (fetch): modern userland downloads everything (old TLS never touches network) +# Stage 2 (build): compile Python 2.3 + PIL + Zope 2.7 on bookworm (gcc 12: warns, doesn't error) +# Stage 3 (runtime): slim image, non-root, /data volume, port 8080 +# +# NOT FOR PRODUCTION. Zope 2.7 has known, unpatched security issues. +# Intended for content extraction, migration, and archaeology only. + +# [V 2026-08-20] Plone 1.0.6 targets Zope 2.6.x, whose CMFPlone/INSTALL.txt +# asks for "Zope 2.5.1 or later" and "CMF 1.3 or later". Zope 2.6.4 requires +# Python 2.1 and uses a different build system entirely (wo_pcgi.py, and +# inst/make_instance.py rather than mkzopeinstance.py), so it would have meant +# a new interpreter and a new template. It is not needed: Plone 1.0.6 runs on +# Zope 2.7.8 / Python 2.3.7 — the exact stack 2.0 already ships — once two +# upstream files are ported. See patches/ and README.md. +ARG PYTHON_VERSION=2.3.7 +ARG ZOPE_VERSION=2.7.8 +ARG PLONE_VERSION=1.0.6 +# [V 2026-08-20] The 1.0.5 bundle is the source of DCWorkflow and Formulator; +# see PLONE_BUNDLE_URL below. +ARG PLONE_BUNDLE_VERSION=1.0.5 +# [V 2026-08-20] Last of the CMF 1.3 line, which is what 1.0.6 asks for. CMF +# 1.4 is a Zope 2.7-era API and predates nothing Plone 1.0 knows about. +ARG CMF_VERSION=1.3.3 +# [V 2026-08-20] Plone 1.0.6 never imports PIL — unlike 2.1.4 and 2.5.4, and +# like 2.0.5, whose only use site is guarded. Bundled anyway for the same +# reason as 2.0: image scaling works rather than silently degrading during a +# migration rehearsal, and every tarball-era image keeps the same shape. +ARG PIL_VERSION=1.1.6 + +# [V 2026-08-20] Verified: Zope-2.7.8-final.tgz (2.9 MB) exists at old.zope.dev +# and unpacks to Zope-2.7.8-final/ with a top-level ./configure. +ARG ZOPE_URL=https://old.zope.dev/Products/Zope/${ZOPE_VERSION}/Zope-${ZOPE_VERSION}-final.tgz +# [V] python.org keeps all historical releases +ARG PYTHON_URL=https://www.python.org/ftp/python/${PYTHON_VERSION}/Python-${PYTHON_VERSION}.tgz +# [V 2026-08-20] Plone-1.0.6.tar.gz (406 KB) exists at dist.plone.org/archive/ +# and unpacks to a bare CMFPlone/ — it is the CMFPlone product ALONE, not a +# bundle like every 2.x tarball. Note also that Plone-1.0.tar.gz, -1.0.1 and +# -1.0.2 are listed in that index but all 404, serving a 3 kB HTML error page. +ARG PLONE_URL=https://dist.plone.org/archive/Plone-${PLONE_VERSION}.tar.gz +# [V 2026-08-20] CMFPlone1.0.5.tar.gz (1.4 MB) is the real 1.0 release bundle: +# CMFPlone-1.0.5/{CMFPlone,DCWorkflow,Formulator,i18n}. INSTALL.txt names +# exactly those three products, and DCWorkflow 0.5+ / Formulator 1.4.1 are not +# separately downloadable anywhere — old.zope.dev 404s both. So 1.0.6 supplies +# CMFPlone and the 1.0.5 bundle supplies its two companions. +ARG PLONE_BUNDLE_URL=https://dist.plone.org/archive/CMFPlone${PLONE_BUNDLE_VERSION}.tar.gz +# [V 2026-08-20] Verified: CMF-1.3.3.tar.gz unpacks to CMF-1.3.3/ carrying +# CMFCore, CMFDefault, CMFTopic and CMFCalendar. +ARG CMF_URL=https://old.zope.dev/Products/CMF/CMF-${CMF_VERSION}/CMF-${CMF_VERSION}.tar.gz +# [V 2026-08-20] effbot.org is dead (404) and PyPI lists PIL but serves no +# files; dist.plone.org/thirdparty is the surviving canonical source. +ARG PIL_URL=https://dist.plone.org/thirdparty/PIL-${PIL_VERSION}.tar.gz + +# --- simplejson ------------------------------------------------------------- +# Python has no stdlib `json` before 2.6, so every image below 4.0 ships +# simplejson instead. The release is NOT interchangeable between interpreters +# — see the header of shared/install-simplejson.sh, and note that the install +# gate will fail the build on a wrong pairing rather than let it through. +ARG SIMPLEJSON_VERSION=1.9.3 +ARG SIMPLEJSON_URL=https://files.pythonhosted.org/packages/source/s/simplejson/simplejson-${SIMPLEJSON_VERSION}.tar.gz + +# --------------------------------------------------------------------------- +# Stage 1: fetch — modern TLS, nothing else +# --------------------------------------------------------------------------- +FROM debian:bookworm-slim AS fetch +ARG ZOPE_URL +ARG PYTHON_URL +ARG PLONE_URL +ARG PLONE_BUNDLE_URL +ARG CMF_URL +ARG PIL_URL +RUN apt-get update \ + && apt-get install -y --no-install-recommends ca-certificates curl \ + && rm -rf /var/lib/apt/lists/* +WORKDIR /dist +RUN curl -fSL "${PYTHON_URL}" -o python.tgz \ + && curl -fSL "${ZOPE_URL}" -o zope.tgz \ + && curl -fSL "${PLONE_URL}" -o plone.tar.gz \ + && curl -fSL "${PLONE_BUNDLE_URL}" -o plone-bundle.tar.gz \ + && curl -fSL "${CMF_URL}" -o cmf.tar.gz \ + && curl -fSL "${PIL_URL}" -o pil.tar.gz + +# --------------------------------------------------------------------------- +# Stage 1b: fetch-simplejson — derived from fetch, deliberately not part of it +# --------------------------------------------------------------------------- +# pybuild does `COPY --from=fetch /dist /dist`, so folding this download into +# stage 1 would invalidate the interpreter build every time the simplejson pin +# moves and recompile CPython from source — hours, under emulation on an arm64 +# host. Deriving a stage leaves `fetch` byte-identical, so pybuild stays cached, +# and reuses the curl and ca-certificates already installed there. WORKDIR +# /dist is inherited, so the tarball lands beside the others. +FROM fetch AS fetch-simplejson +ARG SIMPLEJSON_URL +RUN curl -fSL --retry 5 --retry-delay 5 "${SIMPLEJSON_URL}" -o simplejson.tar.gz + +# --------------------------------------------------------------------------- +# Stage 2a: pybuild — Python 2.3 only, so `--target pybuild` is exactly gate G1 +# and a Zope failure cannot deny us a testable interpreter. +# --------------------------------------------------------------------------- +FROM debian:bookworm AS pybuild +ARG PYTHON_VERSION + +RUN apt-get update \ + && apt-get install -y --no-install-recommends \ + gcc make libc6-dev zlib1g-dev libcrypt-dev \ + && rm -rf /var/lib/apt/lists/* + +COPY --from=fetch /dist /dist +COPY shared/build-python2.sh /usr/local/bin/build-python2.sh + +RUN build-python2.sh /dist/python.tgz "${PYTHON_VERSION}" /opt/python2.3 + +# --------------------------------------------------------------------------- +# Stage 2b: build — PIL, Zope 2.7, the Zope instance, and the Plone products. +# --------------------------------------------------------------------------- +FROM pybuild AS build +ARG ZOPE_VERSION +ARG PLONE_VERSION +ARG PLONE_BUNDLE_VERSION +ARG CMF_VERSION +ARG PIL_VERSION + +# --- PIL -------------------------------------------------------------------- +# `patch` is needed here and nowhere else in the matrix: see the port step below. +RUN apt-get update \ + && apt-get install -y --no-install-recommends libjpeg62-turbo-dev patch \ + && rm -rf /var/lib/apt/lists/* + +COPY shared/build-pil.sh /usr/local/bin/build-pil.sh +RUN build-pil.sh /dist/pil.tar.gz "${PIL_VERSION}" /opt/python2.3/bin/python2.3 + +# --- Zope 2.7 --------------------------------------------------------------- +# Oldest C in the matrix: this is the pre-rewrite ExtensionClass. -fcommon +# avoids gcc>=10 "multiple definition" errors from tentative definitions. +RUN tar -xzf /dist/zope.tgz -C /tmp \ + && cd /tmp/Zope-${ZOPE_VERSION}-final \ + && ./configure --prefix=/opt/zope \ + --with-python=/opt/python2.3/bin/python2.3 \ + && CFLAGS="-fcommon -fno-strict-aliasing" make \ + && make install \ + && rm -rf /tmp/Zope-${ZOPE_VERSION}-final + +# --- Zope instance ---------------------------------------------------------- +# Placeholder inituser; the real one is written by the entrypoint at runtime. +RUN /opt/python2.3/bin/python2.3 /opt/zope/bin/mkzopeinstance.py \ + --dir /app/instance --user "admin:placeholder" + +# --- Plone products --------------------------------------------------------- +# Three sources, because no single 1.0.6 artifact carries the whole set: +# Plone-1.0.6.tar.gz -> CMFPlone (bare, no prefix) +# CMFPlone1.0.5.tar.gz -> DCWorkflow, Formulator (strip 1) +# CMF-1.3.3.tar.gz -> CMFCore, CMFDefault, CMFTopic, CMFCalendar +# +# [V 2026-08-20] CMFPlone's only other imports are Products.Localizer and +# Products.CMFEventService, and BOTH sit behind try/except (PloneInitialize.py:8 +# and WorkflowTool.py:15), so neither is required. Everything else it imports — +# ExternalMethod, SiteAccess, PageTemplates, PythonScripts — ships with Zope. +RUN mkdir -p /tmp/plone /tmp/bundle /tmp/cmf \ + && tar -xzf /dist/plone.tar.gz -C /tmp/plone \ + && tar -xzf /dist/plone-bundle.tar.gz -C /tmp/bundle --strip-components=1 \ + && tar -xzf /dist/cmf.tar.gz -C /tmp/cmf --strip-components=1 \ + && cp -a /tmp/plone/CMFPlone /app/instance/Products/ \ + && cp -a /tmp/bundle/DCWorkflow /tmp/bundle/Formulator /app/instance/Products/ \ + && cp -a /tmp/cmf/CMFCore /tmp/cmf/CMFDefault \ + /tmp/cmf/CMFTopic /tmp/cmf/CMFCalendar /app/instance/Products/ \ + && test "$(cat /app/instance/Products/CMFPlone/version.txt)" = "${PLONE_VERSION}" \ + && test -d /app/instance/Products/CMFCore \ + && test -d /app/instance/Products/DCWorkflow \ + && test -d /app/instance/Products/Formulator \ + && rm -rf /tmp/plone /tmp/bundle /tmp/cmf + +# --- Port to Zope 2.7 ------------------------------------------------------- +# The ONLY image in this matrix that modifies upstream source. Both patches are +# consequences of running a Zope 2.6-era Plone on Zope 2.7; each is documented +# in README.md with the exact error it fixes. --forward makes a patch that has +# already applied a hard failure rather than a silent re-application, and the +# build stops if either is rejected. +COPY 1.0/patches /tmp/patches +RUN for p in /tmp/patches/*.patch; do \ + echo "applying $(basename "$p")"; \ + patch -p1 --forward --batch -d /app/instance/Products < "$p"; \ + done \ + && rm -rf /tmp/patches + +# Point the filestorage and logs at /data (populated at runtime). +RUN sed -i \ + -e 's|\$INSTANCE/var/Data.fs|/data/filestorage/Data.fs|' \ + -e 's|\$INSTANCE/log|/data/log|' \ + /app/instance/etc/zope.conf + +# --- simplejson ------------------------------------------------------------- +# Last in the stage, and with its ARG declared here rather than at the top, so +# that moving the pin does not invalidate the PIL and Plone layers above. +# site-packages lives under /opt/python2.3, which stage 3 copies wholesale, so +# nothing further is needed at runtime. +ARG SIMPLEJSON_VERSION +COPY --from=fetch-simplejson /dist/simplejson.tar.gz /dist/simplejson.tar.gz +COPY shared/install-simplejson.sh /usr/local/bin/install-simplejson.sh +RUN install-simplejson.sh /dist/simplejson.tar.gz "${SIMPLEJSON_VERSION}" \ + /opt/python2.3/bin/python2.3 + +# --------------------------------------------------------------------------- +# Stage 3: runtime +# --------------------------------------------------------------------------- +FROM debian:bookworm-slim +ARG PLONE_VERSION +ARG ZOPE_VERSION +ARG PYTHON_VERSION + +LABEL org.opencontainers.image.title="Plone ${PLONE_VERSION} (legacy)" \ + org.opencontainers.image.description="Plone ${PLONE_VERSION} on Zope ${ZOPE_VERSION} / Python ${PYTHON_VERSION}, with two source patches for Zope 2.7. Migration and content-extraction use only — NOT for production." \ + org.opencontainers.image.vendor="Simples Consultoria" + +RUN apt-get update \ + && apt-get install -y --no-install-recommends zlib1g libcrypt1 libjpeg62-turbo \ + && rm -rf /var/lib/apt/lists/* \ + && useradd --system -m -d /app -U -u 500 plone \ + && mkdir -p /data \ + && chown -R plone:plone /data + +COPY --from=build --chown=plone:plone /opt/python2.3 /opt/python2.3 +COPY --from=build --chown=plone:plone /opt/zope /opt/zope +COPY --from=build --chown=plone:plone /app /app +COPY --chown=plone:plone shared/docker-entrypoint.sh /docker-entrypoint.sh +RUN chmod +x /docker-entrypoint.sh +COPY --chown=plone:plone shared/upgrade-plone.py /app/upgrade-plone.py +COPY --chown=plone:plone shared/configure-zeo.sh /app/configure-zeo.sh +RUN chmod +x /app/configure-zeo.sh + +ENV ZOPE_HOME=/opt/zope \ + INSTANCE_HOME=/app/instance \ + PYTHON=/opt/python2.3/bin/python2.3 \ + ZOPE_HTTP_PORT=8080 + +VOLUME /data +EXPOSE 8080 +WORKDIR /app +USER plone + +# ZServer is slow to warm up on first start (Data.fs creation + product init) +HEALTHCHECK --interval=10s --timeout=5s --start-period=120s --retries=5 \ + CMD ["/opt/python2.3/bin/python2.3", "-c", "import urllib; urllib.urlopen('http://127.0.0.1:8080/')"] + +ENTRYPOINT ["/docker-entrypoint.sh"] +CMD ["start"] diff --git a/legacy/1.0/README.md b/legacy/1.0/README.md new file mode 100644 index 0000000..e864713 --- /dev/null +++ b/legacy/1.0/README.md @@ -0,0 +1,174 @@ +# Plone 1.0 legacy image + +Plone 1.0.6 on Zope 2.7.8 / Python 2.3.7, with CMF 1.3.3. Tarball-era template +(no buildout). The oldest Plone in the matrix, and **the only image that +patches upstream source** — see "The two patches" below. + +**Not for production.** Zope 2.7 has known, unpatched vulnerabilities. This +image exists for content extraction, migration rehearsal, and historical +inspection. + +## Usage + +```sh +docker build -f 1.0/Dockerfile -t plone-legacy:1.0.6 . +docker run -d -p 8080:8080 -e ADMIN_PASSWORD=secret \ + -v plone10-data:/data plone-legacy:1.0.6 +``` + +Creating a site through the ZMI: **pick a customisation policy.** The `Add +Plone Site` form has a `Customization Policy` dropdown that defaults to +nothing, and a site created with it empty is broken (see below). Choose +`Default Plone`. + +| | | +|---|---| +| Port | 8080 (`ZOPE_HTTP_PORT` to change) | +| FTP | 8021, **not** `EXPOSE`d — publish with `-p 8021:8021` if wanted | +| Volume | `/data` (`filestorage/Data.fs`, `log/`) | +| User | `plone` (uid 500) | +| Env | `ADMIN_USER`, `ADMIN_PASSWORD` (first run only) | +| PIL | 1.1.6, with JPEG + PNG (no freetype/`_imagingft`) | + +Runtime shape, entrypoint and FTP behaviour are identical to +[2.0](../2.0/README.md), which shares the same Zope. + +## Where the code comes from + +Unlike every 2.x release, **no single 1.0.6 artifact carries a working Plone**. +The image assembles it from three: + +| Source | Supplies | +|---|---| +| `dist.plone.org/archive/Plone-1.0.6.tar.gz` | `CMFPlone` — and nothing else | +| `dist.plone.org/archive/CMFPlone1.0.5.tar.gz` | `DCWorkflow` 0.5+, `Formulator` 1.4.1 | +| `old.zope.dev/.../CMF-1.3.3.tar.gz` | `CMFCore`, `CMFDefault`, `CMFTopic`, `CMFCalendar` | + +`CMFPlone/INSTALL.txt` asks for exactly three Plone products — CMFPlone, +DCWorkflow and Formulator — plus CMF 1.3 or later. Only the first is in the +1.0.6 tarball, and **neither DCWorkflow nor Formulator is separately +downloadable anywhere**: `old.zope.dev/Products/DCWorkflow/` and +`.../Formulator/` both 404. The 1.0.5 release bundle +(`CMFPlone-1.0.5/{CMFPlone,DCWorkflow,Formulator,i18n}`) is the only surviving +source, so 1.0.6 supplies CMFPlone and 1.0.5 supplies its two companions. + +## Validated facts / hypotheses + +- **[V 2026-08-20]** **1.0.6 is the only downloadable 1.0.x.** + `Plone-1.0.tar.gz`, `Plone-1.0.1.tar.gz` and `Plone-1.0.2.tar.gz` are all + listed in the `dist.plone.org/archive/` index and all **404**, serving a + 3,056-byte HTML error page — which `tar` reports as "Unrecognized archive + format" rather than as a failed download. 1.0.3, 1.0.4 and 1.0.5 published no + `.tar.gz` at all under that name (only RPM, `.exe` and MacOS `.pkg`). +- **[V 2026-08-20]** **Zope 2.6.4 was not needed, and would have been much more + expensive.** Plone 1.0.6 is written for Zope 2.6.x, which + `Zope-2.6.4-src/README.txt` states requires **Python 2.1** ("This release + requires Python 2.1."). Zope 2.6 also has no `./configure` — it builds via + `wo_pcgi.py`, and creates instances with `inst/make_instance.py` rather than + `mkzopeinstance.py` — so it would have meant a new interpreter *and* a new + template. Running on Zope 2.7.8 / Python 2.3.7 instead reuses 2.0's Dockerfile + almost verbatim, at a cost of two patched files. +- **[V 2026-08-20]** **CMFPlone 1.0.6 has only two optional dependencies, and + both are genuinely optional.** Its complete set of `Products.*` imports is + CMFCore, CMFDefault, CMFCalendar, DCWorkflow, Formulator, ExternalMethod, + SiteAccess, PageTemplates, PythonScripts, Localizer and CMFEventService. The + last two sit behind `try/except` — `PloneInitialize.py:8` and + `WorkflowTool.py:15` — and the four before them ship with Zope. So nothing + beyond the table above is required, and **zero products fail to import or + install**. +- **[V 2026-08-20]** **PIL is not imported at all by 1.0.6**, as in 2.0.5 (where + the single use site is guarded) and unlike 2.1.4 and 2.5.4. Bundled anyway by + the same decision, so scaling works and every tarball-era image has the same + shape. +- **[V 2026-08-20]** The site factory matches 2.0 and 2.1: `/manage_main` links + `CMFPlone/addSite`, whose form posts to `manage_addSite`. The smoke test + discovers this rather than assuming it. +- **[V 2026-08-20]** **A site created without `custom_policy` is accepted and + then broken.** The ZMI factory defaults the argument to empty — + `CMFPlone/Portal.py:289 custom_policy='',` — while + `CustomizationPolicy.py:50` is what calls + `portal._setProperty('left_slots', ...)`. So the POST returns its usual 302 + and the site root then 500s on every render: + + ``` + Module None, line 11, in prepare_slots + AttributeError: left_slots + ``` + + Plone 2.0.5 and 2.1.4 take the same argument but default it to + `'Default Plone'`, which is why this never surfaced before 1.0. The smoke + test now posts the policy whenever the add-form offers the control, rather + than branching on a version number. +- **[V 2026-08-20]** **Plone 1.0.6 emits no `generator` meta tag** — the marker + every version from 2.0 up carries, and the one the smoke test asserted. Its + only `` is `imagetoolbar`. The site root does carry + `plone_javascripts`, an `@import` of `plone.css`, `… - Welcome to + Plone` and 68 occurrences of the word Plone, so the gate now accepts + those skin markers as a documented fallback and prints which marker matched. +- **[V 2026-08-20]** A Plone 1.0.6 site can be created and rendered: the site + root serves **21,167 bytes** of real Plone markup, and the image passes + `SMOKE_CREATE_SITE=1` **twice consecutively**. +- **[V 2026-08-20]** Beyond the site root, `login_form`, `folder_contents`, + `search`, `personalize_form`, `join_form`, `news`, `plone.css` and + `portal_types/manage_main` all answer 200. `contact-info` 404s; it is a + Plone 2 page and not part of 1.0. + +## The two patches + +Both live in [`patches/`](patches/), are applied with `patch -p1 --forward +--batch` in the build stage, and fail the build if rejected. Both exist for the +same reason — a Zope 2.6-era Plone running on Zope 2.7 — and **neither is a bug +in Plone 1.0.6**: each is correct against the Zope it was written for. + +### `0001-isExpired-datetime-syntaxerror.patch` + +`CMFPlone/skins/plone_scripts/isExpired.py` guards its date parse with +`except IndexError`. CMF returns the literal string `'None'` from +`ExpirationDate()` when no expiry is set, and the two DateTime +implementations disagree about what that raises. Verified by running both +modules side by side under the image's own Python 2.3.7: + +``` +Zope 2.6.4 DateTime('None') -> exceptions.IndexError <- what Plone catches +Zope 2.7.8 DateTime('None') -> DateTime.DateTime.SyntaxError <- escapes +``` + +So on 2.7 the exception escapes and **every page render 500s**: + +``` +Module None, line 8, in isExpired +Module DateTime.DateTime, line 513, in __init__ +SyntaxError: Unable to parse ('None',), {} +``` + +The patch widens the handler. Upstream intent is unchanged: a date that will +not parse has not expired. + +### `0002-login_form-tal-nesting.patch` + +`CMFPlone/skins/plone_forms/login_form.pt` builds its cookie warning with a +`document.writeln('

……')` inside a ` diff --git a/legacy/2.0/README.md b/legacy/2.0/README.md index 48ed669..4abe104 100644 --- a/legacy/2.0/README.md +++ b/legacy/2.0/README.md @@ -1,7 +1,8 @@ # Plone 2.0 legacy image Plone 2.0.5 on Zope 2.7.8 / Python 2.3.7. Tarball-era template (no buildout). -The oldest image in the matrix. +The oldest image that runs upstream source unmodified: 1.0 is older, but needs +two patches to run on this same Zope 2.7.8 stack. **Not for production.** Zope 2.7 has known, unpatched vulnerabilities. This image exists for content extraction, migration rehearsal, and historical inspection. diff --git a/legacy/Makefile b/legacy/Makefile index 6b68d0e..84ea2de 100644 --- a/legacy/Makefile +++ b/legacy/Makefile @@ -14,7 +14,7 @@ PLATFORM ?= linux/amd64 # the matrix in README.md, and the ALL list in # ../.github/workflows/legacy-build.yml. One series lands per pull request; # append here as each one is ported. -SERIES := 2.0 2.1 2.5 3.0 3.1 3.2 3.3 4.0 4.1 4.2 +SERIES := 1.0 2.0 2.1 2.5 3.0 3.1 3.2 3.3 4.0 4.1 4.2 # Builder used for the -demo images, which MUST be a `docker`-driver one: # demo/Dockerfile's FROM reads the base image out of the local image store, and @@ -42,6 +42,11 @@ DEMO_TEST_PASSWORD ?= smoke-not-admin # these images are built from. # [V 2026-08-20] 4.1.6 is both the final 4.1.x release and the last # UnifiedInstaller published for the series, so this row needs no compromise. +# [V 2026-08-20] 1.0.6 is the last 1.0.x with a working download, and the only +# one: Plone-1.0.tar.gz, -1.0.1 and -1.0.2 are listed at dist.plone.org/archive +# but every one of them 404s. Note it ships CMFPlone ALONE -- DCWorkflow and +# Formulator come from CMFPlone1.0.5.tar.gz, and CMF from old.zope.dev. +FULL_VERSION_1.0 := 1.0.6 FULL_VERSION_2.0 := 2.0.5 FULL_VERSION_2.1 := 2.1.4 # [V 2026-08-20] There is no Plone 2.5.5 on dist.plone.org (nor /download/ diff --git a/legacy/README.md b/legacy/README.md index 2450501..531962b 100644 --- a/legacy/README.md +++ b/legacy/README.md @@ -22,9 +22,11 @@ They exist for content extraction, migration rehearsal, and archaeology. | 2.5 | 2.5.4-2 | 2.9.12 | 2.4.6 | tarball | `simplejson` 2.0.9 (installed) | **done** | | 2.1 | 2.1.4 | 2.8.12 | 2.4.6 | tarball | `simplejson` 2.0.9 (installed) | **done** | | 2.0 | 2.0.5 | 2.7.8 | 2.3.7 | tarball | `simplejson` 1.9.3 (installed) | **done** | +| 1.0 | 1.0.6 | 2.7.8 | 2.3.7 | tarball | `simplejson` 1.9.3 (installed) | **done**, [patched](1.0/README.md#the-two-patches) | -The matrix extends down to Plone 1.0. One series lands per pull request; the -remaining one follows these. +The matrix is **complete**: every series from 1.0 to 4.2 is ported. 1.0 is the +only image that patches upstream source, because it targets Zope 2.6 and has +to run on 2.7. Adding a series touches eight places. Miss one and the build still passes — the image is simply never built, or the docs quietly disagree with what ships — so