changeset 7528:14a8e11f3a87 issue2550923_computed_property

merge default into branch
author John Rouillard <rouilj@ieee.org>
date Mon, 10 Jul 2023 17:31:34 -0400
parents 98d32db3b21e (current diff) b8f012c7c5a7 (diff)
children 8fb42f41ef10
files .travis.yml CHANGES.txt doc/customizing.txt roundup/cgi/templating.py roundup/hyperdb.py
diffstat 37 files changed, 573 insertions(+), 257 deletions(-) [+]
line wrap: on
line diff
--- a/.github/dependabot.yml	Sun Jul 09 17:42:40 2023 -0400
+++ b/.github/dependabot.yml	Mon Jul 10 17:31:34 2023 -0400
@@ -18,8 +18,8 @@
     directory: "/"
     schedule:
       interval: "weekly"
-  - package-ecosystem: "docker" 
-    directory: "/scripts/Docker" 
-    target-branch: "master" 
-    schedule:
-      interval: "weekly"
+#  - package-ecosystem: "docker" 
+#    directory: "/scripts/Docker" 
+#    target-branch: "master" 
+#    schedule:
+#      interval: "weekly"
--- a/.github/workflows/anchore.yml	Sun Jul 09 17:42:40 2023 -0400
+++ b/.github/workflows/anchore.yml	Mon Jul 10 17:31:34 2023 -0400
@@ -37,21 +37,21 @@
     runs-on: ubuntu-latest
     steps:
     - name: Checkout the code
-      uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab # v3.5.2
+      uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3
     - name: Build the Docker image
       run: docker pull python:3-alpine; docker build . --file scripts/Docker/Dockerfile --tag localbuild/testimage:latest
     - name: List the Docker image
       run: docker image ls
     - name: Run the Anchore scan action itself with GitHub Advanced Security code scanning integration enabled
-      uses: anchore/scan-action@4be3c24559b430723e51858969965e163b196957 # v3.3.5
+      uses: anchore/scan-action@24fd7c9060f3c96848dd1929fac8d796fb5ae4b4 # v3.3.6
       id: scan
       with:
         image: "localbuild/testimage:latest"
         fail-build: true
     - name: Upload Anchore Scan Report
       if: always()
-      uses: github/codeql-action/upload-sarif@83f0fe6c4988d98a455712a27f0255212bba9bd4
-  # v2.3.6
+      uses: github/codeql-action/upload-sarif@cdcdbb579706841c47f7063dda365e292e5cad7a
+  # v2.13.4
       with:
         sarif_file: ${{ steps.scan.outputs.sarif }}
     - name: Inspect action SARIF report
--- a/.github/workflows/ci-test.yml	Sun Jul 09 17:42:40 2023 -0400
+++ b/.github/workflows/ci-test.yml	Mon Jul 10 17:31:34 2023 -0400
@@ -48,7 +48,7 @@
       matrix:
         # Run in all these versions of Python
         python-version:
-          - "2.7"
+          # - "2.7"
           - "3.10"
           # - "3.9"
           - "3.8"
@@ -90,7 +90,7 @@
         # if: {{ false }}
           # continue running if step fails
         # continue-on-error: true
-        uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab # v3.5.2
+        uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3
 
       # Setup version of Python to use
       - name: Set Up Python ${{ matrix.python-version }}
@@ -122,6 +122,17 @@
           #grep max_allowed /etc/mysql/mysql.conf.d/mysqld.cnf
           #ls  /etc/mysql/conf.d/  /etc/mysql/mysql.conf.d/
           #sleep 5
+          # try to improve performance speed by disabling some ACID
+          # settings and change some layout defaults.
+          sudo sed -i -e '$a\innodb_flush_log_at_trx_commit = 2' /etc/mysql/mysql.conf.d/mysqld.cnf
+          sudo sed -i -e '$a\innodb_file_per_table = OFF' /etc/mysql/mysql.conf.d/mysqld.cnf
+          sudo sed -i -e '$a\innodb_doublewrite=OFF' /etc/mysql/mysql.conf.d/mysqld.cnf
+          sudo sed -i -e '$a\innodb_fast_shutdown=2' /etc/mysql/mysql.conf.d/mysqld.cnf
+          sudo sed -i -e '$a\innodb_log_file_size=1048576' /etc/mysql/mysql.conf.d/mysqld.cnf
+          sudo sed -i -e '$a\innodb_flush_method=O_DIRECT' /etc/mysql/mysql.conf.d/mysqld.cnf
+          sudo sed -i -e '$a\innodb_log_buffer_size=3M' /etc/mysql/mysql.conf.d/mysqld.cnf
+          sudo sed -i -e '$a\innodb_buffer_pool_size=180M' /etc/mysql/mysql.conf.d/mysqld.cnf
+          sleep 3
           sudo service mysql restart
           #sleep 10
           #ps -ef | grep mysqld
@@ -131,9 +142,11 @@
       - name: Install postgres
         run: |
           sudo apt-get update && sudo apt-get install postgresql
-          # Disable fsync for speed, don't care about data durability
-          #   when testing
+          # Disable fsync, full page writes for speed,
+          # don't care about data durability when testing
           sudo sed -i -e '$a\fsync = off' /etc/postgresql/*/*/postgresql.conf
+          sudo sed -i -e '$a\full_page_writes = off' /etc/postgresql/*/*/postgresql.conf
+          sudo sed -i -e '$a\synchronous_commit = off' /etc/postgresql/*/*/postgresql.conf
           sudo service postgresql restart; sleep 10
           # set up postgresql database
           sudo -u postgres psql -c "CREATE ROLE rounduptest WITH CREATEDB LOGIN PASSWORD 'rounduptest';" -U postgres
@@ -153,7 +166,7 @@
         run: |
           sudo apt-get install swig gpgsm libgpgme-dev
           # pygments for markdown2 to highlight code blocks
-          pip install markdown2 pygments
+          pip install 'markdown2<=2.4.8' pygments
           # docutils for ReStructuredText
           pip install beautifulsoup4 brotli docutils gpg jinja2 \
             mistune==0.8.4 pyjwt pytz whoosh
@@ -241,7 +254,7 @@
       - name: Upload coverage to Coveralls
         # python 2.7 and 3.6 versions of coverage can't produce lcov files.
         if: matrix.python-version != '2.7' && matrix.python-version != '3.6'
-        uses: coverallsapp/github-action@f350da2c033043742f89e8c0b7b5145a1616da6d # master
+        uses: coverallsapp/github-action@c7885c00cb7ec0b8f9f5ff3f53cddb980f7a4412 # master
         with:
           github-token: ${{ secrets.GITHUB_TOKEN }}
           path-to-lcov: coverage.lcov
@@ -277,7 +290,7 @@
     
     steps:
       - name: Coveralls Finished
-        uses: coverallsapp/github-action@f350da2c033043742f89e8c0b7b5145a1616da6d # master
+        uses: coverallsapp/github-action@c7885c00cb7ec0b8f9f5ff3f53cddb980f7a4412 # master
         with:
           github-token: ${{ secrets.github_token }}
           parallel-finished: true
--- a/.github/workflows/codeql-analysis.yml	Sun Jul 09 17:42:40 2023 -0400
+++ b/.github/workflows/codeql-analysis.yml	Mon Jul 10 17:31:34 2023 -0400
@@ -49,11 +49,11 @@
 
     steps:
     - name: Checkout repository
-      uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab # v2.6.0
+      uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v2.6.0
 
     # Initializes the CodeQL tools for scanning.
     - name: Initialize CodeQL
-      uses: github/codeql-action/init@83f0fe6c4988d98a455712a27f0255212bba9bd4 # v2.3.6
+      uses: github/codeql-action/init@cdcdbb579706841c47f7063dda365e292e5cad7a # v2.13.4
       with:
         languages: ${{ matrix.language }}
         # If you wish to specify custom queries, you can do so here or in a config file.
@@ -64,7 +64,7 @@
     # Autobuild attempts to build any compiled languages  (C/C++, C#, or Java).
     # If this step fails, then you should remove it and run the build manually (see below)
     - name: Autobuild
-      uses: github/codeql-action/autobuild@83f0fe6c4988d98a455712a27f0255212bba9bd4 # v2.3.6
+      uses: github/codeql-action/autobuild@cdcdbb579706841c47f7063dda365e292e5cad7a # v2.13.4
 
     # â„šī¸ Command-line programs to run using the OS shell.
     # 📚 https://git.io/JvXDl
@@ -78,4 +78,4 @@
     #   make release
 
     - name: Perform CodeQL Analysis
-      uses: github/codeql-action/analyze@83f0fe6c4988d98a455712a27f0255212bba9bd4 # v2.3.6
+      uses: github/codeql-action/analyze@cdcdbb579706841c47f7063dda365e292e5cad7a # v2.13.4
--- a/.github/workflows/ossf-scorecard.yml	Sun Jul 09 17:42:40 2023 -0400
+++ b/.github/workflows/ossf-scorecard.yml	Mon Jul 10 17:31:34 2023 -0400
@@ -35,12 +35,12 @@
 
     steps:
       - name: "Checkout code"
-        uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab # v3.1.0
+        uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.1.0
         with:
           persist-credentials: false
 
       - name: "Run analysis"
-        uses: ossf/scorecard-action@80e868c13c90f172d68d1f4501dee99e2479f7af # v2.1.3
+        uses: ossf/scorecard-action@08b4669551908b1024bb425080c797723083c031 # v2.2.0
         with:
           results_file: results.sarif
           results_format: sarif
@@ -70,6 +70,6 @@
 
       # Upload the results to GitHub's code scanning dashboard.
       - name: "Upload to code-scanning"
-        uses: github/codeql-action/upload-sarif@83f0fe6c4988d98a455712a27f0255212bba9bd4 # v2.1.27
+        uses: github/codeql-action/upload-sarif@cdcdbb579706841c47f7063dda365e292e5cad7a # v2.1.27
         with:
           sarif_file: results.sarif
--- a/.travis.yml	Sun Jul 09 17:42:40 2023 -0400
+++ b/.travis.yml	Mon Jul 10 17:31:34 2023 -0400
@@ -14,6 +14,8 @@
 #  maint-1.6 only python 2, install only psycopg2 version with support for
 #     psycopg1 
 branches:
+  except:
+     - /^dependabot\/.*$/
 #  only:
 #    - default
 #    - maint-1.6
@@ -22,7 +24,7 @@
 
 python:
   - 2.7
-  - 3.10.4
+#  - 3.10.4
 #  - 3.9
 #  - 3.8
 #  - 3.6
@@ -163,15 +165,15 @@
   - PATH=$VIRTUAL_ENV/bin:$PATH
   - export LD_LIBRARY_PATH=$VIRTUAL_ENV/lib:$LD_LIBRARY_PATH
   - python -c "import sys; print('python version ', sys.version)"
-  - if [[ "$TRAVIS_PYTHON_VERSION" != "2."* ]]; then
-    python -m pytest -r a
-      --durations=20
-      -W default
-      -W "ignore:SelectableGroups:DeprecationWarning" 
-      -W "ignore:the imp module:DeprecationWarning:gpg.gpgme:15" 
-      -W "ignore:'U' mode::docutils.io" 
-      -W "ignore:unclosed:ResourceWarning:roundup.roundup.demo" 
-      -W "ignore:unclosed file:ResourceWarning:enum" 
+  - set -xv; if [[ "$TRAVIS_PYTHON_VERSION" != "2."* ]]; then
+    python -m pytest -r a \
+      --durations=20 \
+      -W default \
+      -W "ignore:SelectableGroups:DeprecationWarning" \
+      -W "ignore:the imp module:DeprecationWarning:gpg.gpgme:15" \
+      -W "ignore:'U' mode::docutils.io" \
+      -W "ignore:unclosed:ResourceWarning:roundup.roundup.demo" \
+      -W "ignore:unclosed file:ResourceWarning:enum" \
       -v --maxfail=5 test/ --cov=roundup;
     fi
   - if [[ "$TRAVIS_PYTHON_VERSION" == "2."* ]]; then
--- a/CHANGES.txt	Sun Jul 09 17:42:40 2023 -0400
+++ b/CHANGES.txt	Mon Jul 10 17:31:34 2023 -0400
@@ -70,7 +70,7 @@
   'Access-Control-Allow-Credentials' when not matching '*'. Fixes
   security issue with rest when using '*'.  (John Rouillard)
 - issue2551263: In REST response expose rate limiting, sunset, allow
-  HTTP headers to calling javascript.  (John Rouillard)
+  HTTP headers to calling JavaScript.  (John Rouillard)
 - issue2551257: When downloading an attached (user supplied file),
   make sure that an 'X-Content-Type-Options: nosniff' header is sent.
   (John Rouillard)
@@ -98,6 +98,8 @@
 - Invalid item identifiers passed to REST endpoint return a 404
   rather than a 400 error. E.G. /rest/data/issue/issue4 (rather
   than .../issue/4). (John Rouillard)
+- issue2551280 - sorted() method of MultilinkHTMLProperty is broken?
+  (Gabor Nagy report and fix; commit John Rouillard)
 
 Features:
 
@@ -158,6 +160,9 @@
   reindex the first 1000 issues while reporting any missing issues
   in the range. Also completion progress is reported when indexing a
   specific class.
+- doc updates: add explanation for SQL code in 1.3.3->1.4.0 upgrade.
+  document schema table in rdbms backends and how to dump/extract
+  version from them. (John Rouillard)
 
 2022-07-13 2.2.0
 
--- a/RELEASE.txt	Sun Jul 09 17:42:40 2023 -0400
+++ b/RELEASE.txt	Mon Jul 10 17:31:34 2023 -0400
@@ -32,8 +32,9 @@
 3.  Update version in:
       CHANGES.txt (set date for version as well) 
       roundup/__init__.py
-      website/www/index.txt
-      website/www/conf.py  (also update copyright)
+      website/www/index.txt (current stable version, release highlights)
+      website/www/conf.py  (update copyright, version from __init__.py)
+      scripts/Docker/Docker update value of org.opencontainers.image.version
 3a. Update license end date in COPYING.txt
 3b. Update doc/acknowledgements.txt (add section for
     release, churn contributers etc.). (Use hg churn -c -r ####..####)
--- a/doc/_static/style.css	Sun Jul 09 17:42:40 2023 -0400
+++ b/doc/_static/style.css	Mon Jul 10 17:31:34 2023 -0400
@@ -58,7 +58,7 @@
 }
 
 h1, h2, h3, h4, h5, h6 {
-    line-height: 1.25;
+    line-height: 1.1;
     /* Larger spacing before header and smaller after to make
        header part of following section */
     margin-block-end: 0.3em;
@@ -105,10 +105,11 @@
 body > .footer { margin: 1em 0 1em -14em; clear:both;}
 body > .navigation 
 {
+  float: left;
   margin-left: -14em;
   margin-inline-start: -14em;
+  margin-block-start: unset;
   width: 14em;
-  float: left;
 }
 body > .content 
 {
@@ -176,8 +177,19 @@
 /* style */
 
 :link { color: rgb(220,0,0); text-decoration: none;}
-:link:hover {text-decoration: underline;}
-:visited { color: rgb(187,0,0); text-decoration: none;}
+:link:hover {
+    text-decoration: underline solid clamp(1px, .3ex, 4px);
+    text-underline-position: under;
+}
+:visited { color: rgb(200,0,0); text-decoration: none;}
+:visited:hover {
+    /* would like to change from solid line to dashed, but
+       because of privacy abusing people I can't change that
+       value. So settle for darkgrey underline.
+    */
+    text-decoration-color: darkgrey;
+}
+
 a.toc-backref { color: #000000; }
 
 .header h1 { margin-left: 1em; }
@@ -201,7 +213,8 @@
 }
 .menu > ul > li.current > *
 { 
-  background-color:#dddddd;
+  background-color: #f0f0f0;
+  border-block-end: solid medium #cccccc;
 }
 
 .menu ul li:first-child { margin-top:0;}
@@ -234,9 +247,15 @@
   border-top: none;
 }
 
+/* make submenu items clickable cross whole width of menu */
+.menu ul ul > li > a {
+    display: block;
+}
+
 .menu ul li.toctree-l2.current {
-  background-color: #dddddd;
-  pading-block: 0.25em;
+  background-color: #f0f0f0;
+  border-inline-start: solid 2px rgb(220, 0, 0);
+  padding-block: 1em;
 }
 
 /* related */
@@ -281,9 +300,8 @@
   background-color: #f7f7f7;
 }
 
-/* This is a little hack to inject a 'news' block into the title
-   page without having to set up a custom directive. */
-#roundup-issue-tracker .note
+/* style for the floating release_info badge block on the home page */
+#roundup-issue-tracker .release_info
 {
   float: right;
   width: auto;
@@ -292,7 +310,6 @@
   padding: 1em;
   margin: 1em;
 }
-#roundup-issue-tracker .note .admonition-title { display: none; }
 
 table
 { 
@@ -441,6 +458,7 @@
     background-color: #f5f4d8;
     background-size: 20px 100%, 20px 100%, 16px 100%, 16px 100%;
     background-attachment: local, local, scroll, scroll;
+    padding: 0.5em;
 }
 
 /* https://moderncss.dev/totally-custom-list-styles/ make a list
@@ -459,3 +477,13 @@
     background: #f3f1cc;
     padding-inline: 0.5em;
 }
+
+/* consider highlighting header element that is a target */
+/*
+:target > :is(h1,h2,h3,h4,h5,h6) {
+  background: rgb(254, 227, 227);
+  outline: rgb(254, 227, 227) solid 3px;
+  padding: 0.3em;
+}
+*/
+
--- a/doc/_templates/layout.html	Sun Jul 09 17:42:40 2023 -0400
+++ b/doc/_templates/layout.html	Mon Jul 10 17:31:34 2023 -0400
@@ -73,14 +73,30 @@
 {%- endmacro %}
 
 {%- macro css() %}
-    <link rel="stylesheet" href="{{ pathto('_static/basic.css', 1) }}" type="text/css" />
-    <link rel="stylesheet" href="{{ pathto('_static/' + style, 1) }}" type="text/css" />
+    <link rel="stylesheet" href="{{ pathto('_static/NO_basic.css', 1) }}" type="text/css" />
+    <link rel="stylesheet" href="{{ pathto('_static/' + styles[-1], 1) }}" type="text/css" />
     <link rel="stylesheet" href="{{ pathto('_static/pygments.css', 1) }}" type="text/css" />
     {%- for cssfile in css_files %}
     <link rel="stylesheet" href="{{ pathto(cssfile, 1) }}" type="text/css" />
     {%- endfor %}
 {%- endmacro %}
 
+{#
+ In newer sphinx styles is an array and style does not exist
+ In older sphinx (1.x) style is the style set in conf.py.
+
+ If style exists, assume styles doesn't exist and make styles
+ exist with style as the only value.
+
+ So we can use styles[-1] in the css() macro.
+ When sourceforge lets me build docs with something newer than sphinx 1,
+ we can delete this.
+#}
+{% if style %}
+{% set styles = [] %}
+{{ styles.append( style ) }}
+{% endif %}
+
 <html lang="en">
   <head>
     <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
--- a/doc/acknowledgements.txt	Sun Jul 09 17:42:40 2023 -0400
+++ b/doc/acknowledgements.txt	Mon Jul 10 17:31:34 2023 -0400
@@ -35,7 +35,8 @@
 
 Marcus Priesch,
 John Kristensen (jerrykan),
-Thomas Arendsen Hein
+Thomas Arendsen Hein,
+Gabor Nagy
 
 2.2
 ---
--- a/doc/admin_guide.txt	Sun Jul 09 17:42:40 2023 -0400
+++ b/doc/admin_guide.txt	Mon Jul 10 17:31:34 2023 -0400
@@ -371,7 +371,7 @@
 when invoked.
 
 More secure CSPs can also be created. However because of the ability
-to customize the web interface, it is difficult to provide guidance.
+to customise the web interface, it is difficult to provide guidance.
 
 Dynamic CSP
 -----------
@@ -1210,11 +1210,10 @@
  client id and the client secret need to be retrieved via cloud provider
  specific protocols or websites.
 
- You need the requests_ library installed for OAuth.
+ You need the requests_ library installed to ue the IMAPS_OAUTH method.
 
 .. _requests: https://requests.readthedocs.io/en/latest/
 
-
 .. index:: ! roundup-admin
    single: roundup-admin; usage
    single: roundup-admin; data formats
@@ -1233,7 +1232,7 @@
 
 * install and initialize a new tracker
 * export/import tracker data for migrating between backends
-* creating a new user fom the command line
+* creating a new user from the command line
 * list/find users in the tracker
 
 The basic usage is::
--- a/doc/announcement.txt	Sun Jul 09 17:42:40 2023 -0400
+++ b/doc/announcement.txt	Mon Jul 10 17:31:34 2023 -0400
@@ -47,7 +47,7 @@
   Security Policy (CSP)
 
 * REST now allows rate limiting headers to be accessed by client
-  javascript.
+  JavaScript.
 
 * Default number of rounds for PBKDF2 updated to 2M to account for
   improvements in password crackers and CPU power.
@@ -56,16 +56,17 @@
 
 * Deprecate SSHA password hash function.
 
-* roundup-admin reindex can be done in batches to managle load
+* roundup-admin reindex can be done in batches to manage load
   incurred by reindexing.
 
-* roundup-admin can list avaailable templates and their installed
+* roundup-admin can list available templates and their installed
   locations.
 
-* Crash fixes in detector handling, configuration handling, 
+* Crash fixes in detector handling, configuration handling, fix for
+  sorting of multilinks.
 
 The file CHANGES.txt has a detailed list of feature additions and
-bug fixes (51) for each release. The most recent changes from
+bug fixes (52) for each release. The most recent changes from
 there are at the end of this announcement. Also see the
 information in doc/upgrading.txt.
 
@@ -86,11 +87,14 @@
 this before you use the web, command-line or mail interface
 and before any users access the tracker.
 
-Roundup requires Python 2 newer than version 2.7.2 or Python 3 newer
+Roundup requires Python 2 newer than version 2.7.12 or Python 3 newer
 than or equal to version 3.6 for correct operation. (Python
-3.4 or 3.5 may work, but are not tested.)
+3.4 or 3.5 may work, but are not tested.) Note that Python 2 support
+is being removed from the CI platforms, so you should deploy new
+trackers with Python 3 and plan on upgrading older trackers from Python
+2 to Python 3. See the upgrade guide.
 
-To give Roundup a try, just download (see below), unpack and run::
+To give Roundup a try, just download (directions above), unpack and run::
 
     python demo.py
 
@@ -231,6 +235,8 @@
 - Invalid item identifiers passed to REST endpoint return a 404
   rather than a 400 error. E.G. /rest/data/issue/issue4 (rather
   than .../issue/4). (John Rouillard)
+- issue2551280 - sorted() method of MultilinkHTMLProperty is broken?
+  (Gabor Nagy report and fix; commit John Rouillard)
 
 Features:
 ---------
--- a/doc/conf.py	Sun Jul 09 17:42:40 2023 -0400
+++ b/doc/conf.py	Mon Jul 10 17:31:34 2023 -0400
@@ -23,7 +23,7 @@
 
 
 # Read Roundup version by importing it from parent directory,
-# this ensures that 'unkown version' is inserted even if
+# this ensures that 'unknown version' is inserted even if
 # `roundup` is importable from other location in sys.path
 SYSSAVE = sys.path
 DOCROOT = os.path.abspath(os.path.dirname(__file__))
@@ -84,7 +84,7 @@
 #
 # This is also used if you do content translation via gettext catalogs.
 # Usually you set "language" from the command line for these cases.
-language = None
+language = 'en'
 
 # There are two options for replacing |today|: either, you set today to some
 # non-false value, then it is used:
@@ -117,6 +117,13 @@
 # The name of the Pygments (syntax highlighting) style to use.
 pygments_style = 'sphinx'
 
+# disable permalinks
+from sphinx import version_info
+if version_info < (3,5,0):
+    html_add_permalinks = ''
+else:
+    html_permalinks = False  # when sphinx > 3.5 is used.
+
 # A list of ignored prefixes for module index sorting.
 #modindex_common_prefix = []
 
--- a/doc/customizing.txt	Sun Jul 09 17:42:40 2023 -0400
+++ b/doc/customizing.txt	Mon Jul 10 17:31:34 2023 -0400
@@ -1,6 +1,6 @@
 .. meta::
     :description:
-        How to customize and extend the Roundup Issue
+        How to customise and extend the Roundup Issue
         Tracker. Includes many cookbook and how-to
         examples.
 
@@ -2442,7 +2442,7 @@
 Examples on the Wiki
 ====================
 
-Even more examples of customization have been contributed by
+Even more examples of customisation have been contributed by
 users. They can be found on the `wiki
 <https://wiki.roundup-tracker.org/CustomisationExamples>`_.
 
--- a/doc/developers.txt	Sun Jul 09 17:42:40 2023 -0400
+++ b/doc/developers.txt	Mon Jul 10 17:31:34 2023 -0400
@@ -182,6 +182,29 @@
 instead to the internet.  This environment variable is independent of
 the python -O flag.
 
+Testing Notes
+-------------
+
+Create tests for your changes. Also run the tests in reverse to try to
+identify test dependencies. You can do this by creating a
+``conftest.py`` in the top of the source tree and include the
+following contents::
+
+   def pytest_collection_modifyitems(items): 
+       items.reverse()
+
+to run all the tests in reverse. More tips at: https://testmon.org/blog/hidden-test-dependencies/.
+
+The full test suite can take a while to run. The `pytest-testmon
+<https://pypi.org/project/pytest-testmon/>`_ package can be installed
+with pip. It analyzes changes to code and run tests that test that
+code. This can significantly reduce the time it takes to run a basic
+test suite. Use it by running::
+
+  python3 -m pytest -v --testmon test
+
+once over the whole test suite. Then subsequent calls will analyze the
+changed files/functions and run tests that cover those changes.
 
 Internationalization Notes
 --------------------------
--- a/doc/features.txt	Sun Jul 09 17:42:40 2023 -0400
+++ b/doc/features.txt	Mon Jul 10 17:31:34 2023 -0400
@@ -20,7 +20,7 @@
    your production tracker
  - requires *no* additional support software - python (2.7 or 3.6+) is
    enough to get you going
- - easy to set up higher-performance storage backends like sqlite_,
+ - supports higher-performance storage backends like sqlite_,
    mysql_ and postgresql_
 
 *simple to use*
@@ -119,7 +119,7 @@
    available at the /xmlrpc endpoint.
  - provides same access to tracker as roundup-admin, but based on
    XMLRPC calls
- - see the `xmlrpc guide`_ for more details simple clients etc.
+ - see the `xmlrpc guide`_ for more details basic and advanced clients etc.
 
 *RESTful interface*
  - accessible using basic HTTP authentication at /rest starting point
--- a/doc/glossary.txt	Sun Jul 09 17:42:40 2023 -0400
+++ b/doc/glossary.txt	Mon Jul 10 17:31:34 2023 -0400
@@ -60,7 +60,8 @@
       the schema and hyperdb that forms one issue tracker
 
    tracker home
-      the physical location on disk of a tracker
+      the physical location on disk of a tracker. It has the
+      ``config.ini``, ``schema.py`` files for the tracker.
 
 
 -----------------
--- a/doc/index.txt	Sun Jul 09 17:42:40 2023 -0400
+++ b/doc/index.txt	Mon Jul 10 17:31:34 2023 -0400
@@ -23,7 +23,7 @@
 
    user_guide
 
-   customizing
+   customising <customizing.txt>
    rest
    xmlrpc
    reference
--- a/doc/installation.txt	Sun Jul 09 17:42:40 2023 -0400
+++ b/doc/installation.txt	Mon Jul 10 17:31:34 2023 -0400
@@ -181,7 +181,7 @@
 that users install the latest patch version of Python as these contain
 many fixes to serious bugs.
 
-Some variants of Linux will need an additional "python dev" package
+Some variants of Linux will need an additional '``python-dev``' package
 installed for Roundup installation to work. Debian and derivatives, are
 known to require this.
 
@@ -256,7 +256,8 @@
   package installed.
 
 markdown, markdown2 or mistune
-  To use markdown rendering you need to have the markdown_, markdown2_
+  To use markdown rendering you need to have the markdown_,
+  markdown2_ (2.4.9 known to be broken, 2.3.3 known to work),
   or mistune_ (v0.8.4 tested) package installed.
 
 zstd, brotli
@@ -570,10 +571,16 @@
 ---------------------
 
 The actual storage of Roundup tracker information is handled by backends.
-There's several to choose from, each with benefits and limitations:
+
+Roundup supports basic full text search (FTS) for all backends. Some
+backends support a faster native FTS. Also Roundup supports using
+whoosh or xapian for FTS.
+
+There are several backends to choose from, each with benefits and
+limitations:
 
 ========== =========== ===== ==============================
-Name       Speed       Users   Support
+Name       Speed       Users Support
 ========== =========== ===== ==============================
 anydbm     Slowest     Few   Always available
 sqlite     Fastest     Few   Always available
@@ -581,8 +588,17 @@
 mysql      Fast        Many  Needs install/admin (MySQLdb_)
 ========== =========== ===== ==============================
 
+**anydbm**
+  This was one of the original database backends. It is a simple
+  key/value store and is a prototype implementation for NoSQL
+  backends. It also used to be the only backend that was
+  guaranteed to be present. These days using SQLite is preferred
+  unless you have some reason for preferring a key/value backend
+  (e.g. you are interested in adding support for MongoDB or other
+  NoSQL persistence layer).
 **sqlite**
-  This uses the embedded database engine PySQLite_ to provide a very fast
+  This uses the embedded database engine SQLite and the PySQLite_
+  driver to provide a very fast
   backend. This is not suitable for trackers which will have many
   simultaneous users, but requires much less installation and maintenance
   effort than more scalable postgresql and mysql backends.
@@ -593,7 +609,8 @@
   Installed SQLite should be the latest version available (3.3.8 is known
   to work, 3.1.3 is known to have problems).
 
-  Roundup supports using sqlite's full text search capability. This
+  Roundup supports using `SQLite's full text search capability
+  <admin_guide.html#sqlite-details>`_. This
   can improve searching if you are not installing another indexer like
   xapian or whoosh. It works best with English text.
 **postgresql**
@@ -602,6 +619,12 @@
   requirements. You must also configure
   the ``rdbms`` section of your tracker's ``config.ini``.  It is recommended
   that you use at least version 2.8 of psycopg2.
+
+  Roundup supports using `postgresql's full text search
+  capability
+  <admin_guide.html#postgresql-info>`_. This
+  can improve searching if you are not installing another indexer like
+  xapian or whoosh. It can be tuned to work with different languages.
 **mysql**
   Backend for popular RDBMS MySQL. You must read `doc/mysql.txt`_ for
   additional installation steps and requirements. You must also
@@ -1182,7 +1205,7 @@
 	# going though roundup
 	location /@@file/ {
 	  rewrite ^/@@file/(.*) /html/$1 break;
-	  # note that you can not use cache control (see customizing doc)
+	  # note that you can not use cache control (see customising doc)
           # in roundup to set the expires headers since we are
 	  # bypassing roundup. Consider using a map or different
 	  # location stanzas to vary the expiration times.  
@@ -1205,7 +1228,7 @@
 
 The Hiawatha and lighttpd web servers can run Roundup using FastCGI.
 Cherokee can run FastCGI but it also supports wsgi directly using a
-uWSGI, Gnuicorn etc.
+wsgi server like uWSGI, Gnuicorn etc.
 
 To run Roundup using FastCGI, the flup_ package can be used under
 Python 2 and Python 3. We don't have a detailed config for this, but
@@ -1532,6 +1555,10 @@
 where pop_spec is "``username:password@server``" that specifies the roundup
 submission user's POP account name, password and server.
 
+apop (authenticated) and pops (pop over ssl) methods are also
+supported. See `the section on mailgw in the admin guide
+<admin_guide.html#mail-gateway-script-command-line>`_.
+
 On windows, you would set up the command `using the windows scheduler`_.
 
 As a regular job using an IMAP source
@@ -1545,13 +1572,17 @@
 
 where imap_spec is "``username:password@server``" that specifies the roundup
 submission user's IMAP account name, password and server. You may
-optionally include a mailbox to use other than the default ``INBOX`` with
-"``imap username:password@server mailbox``".
-
-If you have a secure (ie. HTTPS) IMAP server then you may use ``imaps``
-in place of ``imap`` in the command to use a secure connection.
-
-As with the POP job, on windows, you would set up the command 
+optionally include a mailbox to use other than the default ``INBOX`` with::
+
+  imap username:password@server mailbox
+
+If you have a secure (ie. HTTPS) IMAP server then you may use
+``imaps`` in place of ``imap`` in the command to use a securnae
+connection.  To use imap with CRAM or OAUTH, see `the section on
+mailgw in the admin guide
+<admin_guide.html#mail-gateway-script-command-line>`_ .
+
+As with the POP job, on windows, you would set up the command
 `using the windows scheduler`_.
 
 
@@ -1614,22 +1645,44 @@
 `customisation documentation`_ that has a simple detector
 that will block lot of spam attempts.
 
+Also you can consider adding `reCaptcha to reduce bot logins as
+described on the wiki
+<https://wiki.roundup-tracker.org/RequireReCAPTCHAForLogin>`_. The
+wiki also documents how to add `multi-factor (MFA)/one time
+keys/password using TOTP/HOTP
+<https://wiki.roundup-tracker.org/OneTimePasswords>`_. If you
+have a single sign on system, the `wiki page on using Shibboleth
+<https://wiki.roundup-tracker.org/ShibbolethLogin>`_
+or `LDAP <https://wiki.roundup-tracker.org/LDAPLogin2_1_5_1>`_
+with Roundup. More customisation can be found under the
+Security headings of the `CustomisationExamples wiki page
+<https://wiki.roundup-tracker.org/CustomisationExamples>`_ and
+`AdminstrationExample
+<https://wiki.roundup-tracker.org/AdministrationExample>`_ pages
+on the wiki.
+
+
 Docker Support
 ==============
 
 If you don't want to install Roundup on a host, you can create a
-Docker container. This installs Roundup using the `stand-alone web
-server`_ method. This image only supports http. We suggest putting an
+Docker container or use the `pre-built container on
+hub.docker.com
+<https://hub.docker.com/r/rounduptracker/roundup>`_. Docker hub
+images support `multiple tags <#tags-for-dockerhub-docker-images>`_.
+Docker images run Roundup using the `stand-alone web
+server`_ method. The image only supports http. We suggest putting an
 https terminating proxy in front of it.
 
 This is a work in progress and patches to improve it are welcome. You
 can find the docker config files under the `scripts/Docker` directory
 of the source tree.
 
-The dockerized Roundup is based on a 64 bit Alpine distribution.  It
-includes database drivers for anydbm, sqlite, MySQL and Postgresql
-(Postgresl is untested). It also includes additional libraries that
-are listed in `scripts/Docker/requirements.txt` (including redis).
+The Roundup container uses the 64 bit Alpine Python distribution.
+It includes database drivers for anydbm, SQLite, MySQL and
+Postgresql (Postgresql is untested). It also includes additional
+libraries that are listed in `scripts/Docker/requirements.txt`
+(including redis client support).
 
 Email support is a work in progress. Outgoing email to an external
 SMTP server should work. Receiving email should work by using a
@@ -1935,8 +1988,10 @@
 tags when 2.3.0 is released will be:
 
 ``rounduptracker/roundup:latest``
-   is a moving tag that tracks the latest build
-   with the newest version of Roundup.
+   is a moving tag that tracks the latest build with the newest
+   production version of Roundup using the current newest Alpine
+   release with the versions of Python packages at the time of
+   the build.
 
 ``rounduptracker/roundup:2.3.0``
    is a moving tag that tracks the latest build
@@ -1959,12 +2014,12 @@
 ``rounduptracker/roundup:devel``
 
 You should not assume that any ``devel`` tag is static. They are
-mainly for use by Roundup developer/maintainer for testing. There
-may be alternate tags ending with ``-devel`` to indicate builds
-from specific Mercurial versions/hashes. Also the tag may be
-overwritten to change the underlying Python libraries or
-images. Unless you like the bleeding edge, these should not be
-used in production.
+mainly for use by Roundup developers/maintainers/testers for
+testing. There may be alternate tags ending with ``-devel`` to
+indicate builds from specific Mercurial versions/hashes. Also the
+tag may be overwritten to change the underlying Python libraries
+or images. Unless you like the bleeding edge, these should
+**not** be used in production.
 
 Maintenance
 ===========
--- a/doc/overview.txt	Sun Jul 09 17:42:40 2023 -0400
+++ b/doc/overview.txt	Mon Jul 10 17:31:34 2023 -0400
@@ -253,7 +253,7 @@
 property, for example, can be used to refer to related items
 or keyword categories relevant to an item.
 
-For Roundup, all items have four properties that are not customizable:
+For Roundup, all items have four properties that are not customisable:
 
 1. a *date* property named **creation**
 2. a *link* property named **creator**
--- a/doc/reference.txt	Sun Jul 09 17:42:40 2023 -0400
+++ b/doc/reference.txt	Mon Jul 10 17:31:34 2023 -0400
@@ -13,20 +13,20 @@
 
 .. admonition:: Welcome
 
-  This document used to be part of the `customization document`_.  The
-  customization document was getting large and unwieldy.  It was a
+  This document used to be part of the `customisation document`_.  The
+  customisation document was getting large and unwieldy.  It was a
   combination of examples and internal information that made finding
   information difficult. We often had questions on the mailing list that
-  were well answered in the customization document, but finding the info
+  were well answered in the customisation document, but finding the info
   was difficult.
 
   The documentation is slowly being reorganized using the `Diataxis
   framework`_. Help with the reorganization is welcome.
 
-  This is the document you should look at for background on a tutorial
-  or how-to guide in the customization document.
-
-  .. _customization document: customizing.html
+  This document provides background for the tutorials
+  or how-tos in the customisation document.
+
+  .. _customisation document: customizing.html
   .. _diataxis framework: https://diataxis.fr/
 
 .. This document borrows from the ZopeBook section on ZPT. The original is at:
@@ -170,6 +170,8 @@
   Allowed values: ``dispatcher``, ``user``, or ``both``
 
  html_version -- ``html4``
+  This setting should be left at the default value of html4.
+  Support is ending for xhtml mode.
   HTML version to generate. The templates are ``html4`` by default.
   If you wish to make them xhtml, then you'll need to change this
   var to ``xhtml`` too so all auto-generated HTML is compliant.
@@ -662,6 +664,8 @@
 Schemas are defined using Python code in the ``schema.py`` module of your
 tracker.
 
+.. index:: schema; allowed changes
+
 What you can/can't do to the schema
 -----------------------------------
 
@@ -751,7 +755,7 @@
         priority=Link("priority"))
     issue.setkey('title')
 
-.. index:: schema; allowed changes
+.. index:: schema; classes and properties
 
 Classes and Properties - creating a new information store
 ---------------------------------------------------------
@@ -797,6 +801,10 @@
 forms. However, to make your API consistent, use singular forms for
 classes that you add.
 
+.. index::
+   schema; classes
+   schema; items
+
 Class and Items
 ~~~~~~~~~~~~~~~
 
@@ -1272,6 +1280,48 @@
 .. _detectors:
 .. _Auditors and reactors:
 
+
+Schema Integrity
+----------------
+
+There is a table in all SQL based schemas called ``schema``. It
+contains a representation of the current schema and the current
+Roundup schema version. Roundup will exit the version is not supported
+by the release. E.G. Roundup 2.1.0 will not work with a database
+created by 2.3.0 as db version 8 used by 2.3.0 is not supported by
+2.1.0.
+
+The current schema representation is automatically updated whenever a
+change is made to the schema via ``schema.py``. The schema version is
+upgraded when running ``roundup-admin migrate`` although it can be
+upgraded automatically in some cases by run a Roundup process (mailgw,
+web interface). This information is kept in one large blob in the
+table. To view this in a more understandable format, you can use the
+commands below (requires the jq command):
+
+Postgres
+  .. code::
+
+    psql -tq -d 'roundup_db'  -U roundup_user -c \
+       'select schema from schema;' | \
+       python3 -c 'import json, sys; d = eval(sys.stdin.read()); \
+       print(json.dumps(d, indent=2));' | jq . | less
+
+replace ``roundup_db``, ``roundup_user`` with the values from
+``config.ini`` and use a ``~/.pgpass`` file or type the database
+password when prompted.
+
+SQLite
+  .. code::
+
+    sqlite3 demo/db/db 'select schema from schema;' | \
+      python3 -c 'import json, sys; d = eval(sys.stdin.read()); \
+      print(json.dumps(d, indent=2));' | jq . | less
+
+Something similar for MySQL can be generated as well.
+Replacing ``jq .`` with ``jq .version`` will display the schema
+version.
+
 Detectors - adding behaviour to your tracker
 ============================================
 
@@ -1503,7 +1553,7 @@
 but with great power comes great responsibility.
 
 Interfaces.py has been around since the earliest releases of Roundup
-and used to be the main way to get a lot of customization done. In
+and used to be the main way to get a lot of customisation done. In
 modern Roundup, the extensions_ mechanism is used to `add actions
 <#defining-new-web-actions>`_ and `templating utilities`_. But there are
 places where interfaces.py is still useful. Note that the tracker
@@ -2431,9 +2481,9 @@
 Default templates
 -----------------
 
-The default templates are html4 compliant. If you wish to change them to be
-xhtml compliant, you'll need to change the ``html_version`` configuration
-variable in ``config.ini`` to ``'xhtml'`` instead of ``'html4'``.
+The default templates are html4 compliant. Support for xhtml is due to
+be phased out so leave the ``html_version`` configuration
+variable in ``config.ini`` as ``'html'`` instead of ``'xhtml'``.
 
 Most customisation of the web view can be done by modifying the
 templates in the tracker ``'html'`` directory. There are several types
--- a/doc/rest.txt	Sun Jul 09 17:42:40 2023 -0400
+++ b/doc/rest.txt	Mon Jul 10 17:31:34 2023 -0400
@@ -16,7 +16,7 @@
    :depth: 3
 
 Introduction
-------------
+============
 
 After the last 1.6.0 Release, a REST-API developed in 2015 during a
 Google Summer of Code (GSOC) by Chau Nguyen, supervised by Ezio
@@ -26,7 +26,7 @@
 pagination, field embedding among others.
 
 Enabling the REST API
----------------------
+=====================
 
 The REST API can be disabled in the ``[web]`` section of ``config.ini``
 via the variable ``enable_rest`` which is ``yes`` by default.
@@ -66,7 +66,7 @@
 .. _upgrading directions: upgrading.html
 
 Preventing CSRF Attacks
-=======================
+-----------------------
 
 Clients should set the header X-REQUESTED-WITH to any value and the
 tracker's config.ini should have ``csrf_enforce_header_x-requested-with
@@ -78,7 +78,7 @@
 ``config.ini``.
 
 Rate Limiting the API
-=====================
+---------------------
 
 This is a work in progress. This version of roundup includes Rate
 Limiting for the API (which is different from rate limiting login
@@ -114,7 +114,7 @@
 up to 10% have been seen on slower hardware.
 
 Client API
-----------
+==========
 
 The top-level REST url ``/rest/`` will display the current version of
 the REST API (Version 1 as of this writing) and some links to relevant
@@ -122,7 +122,7 @@
 from relative REST-API links for brevity.
 
 Headers
-=======
+-------
 
 If rate limiting is enabled there are 3 "standard" headers:
 
@@ -153,7 +153,7 @@
    the sunset link type.
 
 Hyperdb Stats
-=============
+-------------
 
 Adding ``@stats=true`` as a GET query parameter or POST data item will
 augment the response with an ``@stats`` dictionary. Any value other
@@ -180,7 +180,7 @@
 info.
 
 Versioning
-==========
+----------
 
 Currently there is only one version of the API. Versions are simple
 integers. The current version is ``1``. Version selection is
@@ -199,7 +199,7 @@
 described above.
 
 Input Formats
-=============
+-------------
 
 For a GET or OPTIONS request, the Content-Type header should
 not be sent.
@@ -209,7 +209,7 @@
 code 415.
 
 CORS preflight requests
-^^^^^^^^^^^^^^^^^^^^^^^
+~~~~~~~~~~~~~~~~~~~~~~~
 
 CORS preflight requests are done using the OPTIONS method. They
 require that REST be enabled. These requests do not make any changes
@@ -263,7 +263,7 @@
 and craft a rate limiter that ignores anonymous OPTIONS requests.
 
 Response Formats
-================
+----------------
 
 The default response format is json.
 
@@ -300,7 +300,7 @@
 
 
 General Guidelines
-==================
+------------------
 
 Performing a ``GET`` on an item or property of an item will return an
 ETag header or an @etag property. This needs to be submitted with
@@ -405,21 +405,21 @@
 
 
 Special Endpoints
-=================
+-----------------
 
 There are a few special endpoints that provide some additional data.
 Tracker administrators can add new endpoints. See
 "Programming the REST API"_ below.
 
 /summary
-^^^^^^^^
+~~~~~~~~
 
 A Summary page can be reached via ``/summary`` via the ``GET`` method.
 This is currently hard-coded for the standard tracker schema shipped
 with roundup and will display a summary of open issues.
 
 /data
-^^^^^
+~~~~~
 
 This is the primary entry point for data from the tracker.
 
@@ -439,7 +439,7 @@
 Details are in the sections below.
 
 /data/\ *class* Collection
-==========================
+--------------------------
 
 When performing the ``GET`` method on a class (e.g. ``/data/issue``),
 the ``data`` object includes the number of items available in
@@ -474,7 +474,7 @@
 details.
 
 Searching
-^^^^^^^^^
+~~~~~~~~~
 
 Searching is done by adding roundup field names and values as query
 parameters. Using: https://.../rest/data/issue you can search using:
@@ -549,7 +549,7 @@
 .. _URL Encoding: https://en.wikipedia.org/wiki/Percent-encoding
 
 Transitive Searching
-~~~~~~~~~~~~~~~~~~~~
+^^^^^^^^^^^^^^^^^^^^
 
 In addition to searching an issue by its properties, you can search
 for issues where linked items have a certain property. For example
@@ -572,7 +572,7 @@
 the field even if they can't view it.
 
 Sorting
-^^^^^^^
+~~~~~~~
 
 Collection endpoints support sorting. This is controlled by specifying a
 ``@sort`` parameter with a list of properties of the searched class.
@@ -586,7 +586,7 @@
 
 
 Pagination
-^^^^^^^^^^
+~~~~~~~~~~
 
 Collection endpoints support pagination. This is controlled by query
 parameters ``@page_size`` and ``@page_index`` (Note the use of the
@@ -645,7 +645,7 @@
 the next URL to retrieve all of the entries.
 
 Field embedding and verbose output
-^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
 In collections, you can specify what fields should be embedded in the
 returned data. There are some shortcuts provided using the
@@ -738,7 +738,7 @@
 by these features.
 
 Getting Message and Files Content
-^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
 You can retreive a message with a url like
 ``https://.../demo/rest/data/msg/11``. This returns something like::
@@ -850,7 +850,7 @@
 retrieve an encoded copy of the data.
 
 Other query params
-^^^^^^^^^^^^^^^^^^
+~~~~~~~~~~~~~~~~~~
 
 This table lists other supported parameters:
 
@@ -869,7 +869,7 @@
       this time.
 
 Using the POST method
-^^^^^^^^^^^^^^^^^^^^^
+~~~~~~~~~~~~~~~~~~~~~
 
 Only class links support the ``POST`` method for creation of new items
 of a class, e.g., a new issue via the ``/data/issue`` link. The post
@@ -992,7 +992,7 @@
         response was lost.
 
 Other Supported Methods for Collections
-^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
 Supports the ``OPTIONS`` method for determining which methods are
 allowed on a given endpoint.
@@ -1000,7 +1000,7 @@
 Does not support PUT, DELETE or PATCH.
 
 /data/\ *class*/\ *id* item
-===========================
+---------------------------
 
 When performing the ``GET`` method on an item
 (e.g. ``/data/issue/42``), a ``link`` attribute contains the link to
@@ -1083,7 +1083,7 @@
   }
 
 Retrieve item using key value
-^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
 If the class has a key attribute, e.g. the 'status' class in the
 classic tracker, it can be used to retrieve the item.
@@ -1103,7 +1103,7 @@
 that have ``closed`` as a substring.
 
 Dealing with Messages and Files
-^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
 Using the requests library you can upload a file using::
 
@@ -1147,7 +1147,7 @@
  
 
 Other Supported Methods for Items
-^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
 The method ``PUT`` is allowed on individual items, e.g.
 ``/data/issue/42`` On success it returns the same parameters as the
@@ -1302,7 +1302,7 @@
 ``If-Match`` header.
 
 /data/\ *class*/\ *id*/\ *property* field
-=========================================
+-----------------------------------------
 
 A ``GET`` method on a property (e.g. ``/data/issue/42/title``) returns the
 link, an ``@etag``, the type of the property (e.g. "<type str>") the id
@@ -1325,7 +1325,7 @@
 methods are allowed on a given endpoint.
 
 Message and File Content
-^^^^^^^^^^^^^^^^^^^^^^^^
+~~~~~~~~~~~~~~~~~~~~~~~~
 
 Messages and files have content properties. If the data is utf-8
 compatible (e.g. an email message) you can retrieve it with
@@ -1369,7 +1369,7 @@
 
 
 Other Supported Methods for fields
-^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
 The method ``PUT`` is allowed on a property e.g.,
 ``/data/issue/42/title``. On success it returns the same parameters as
@@ -1421,7 +1421,7 @@
 ``If-Match`` header.
 
 Tunneling Methods via POST
-==========================
+--------------------------
 
 If you are working through a proxy and unable to use http methods like
 PUT, PATCH, or DELETE, you can use POST to perform the action. To tunnel
@@ -1431,10 +1431,10 @@
 without tunneling.
 
 Examples and Use Cases
-----------------------
+======================
 
 sample python client
-====================
+--------------------
 
 The client uses the python ``requests`` library for easier interaction
 with a REST API supporting JSON encoding::
@@ -1516,7 +1516,7 @@
 
 
 Searches and selection
-======================
+----------------------
 
 One difficult interface issue is selection of items from a long list.
 Using multi-item selects requires loading a lot of data (e.g. consider
@@ -1611,14 +1611,14 @@
 
 
 Programming the REST API
-------------------------
+========================
 
 You can extend the rest api for a tracker. This describes how to add
 new rest end points. At some point it will also describe the rest.py
 structure and implementation.
 
 Adding new rest endpoints
-=========================
+-------------------------
 
 Add or edit the file `interfaces.py`_ at the root of the tracker
 directory.
@@ -1692,7 +1692,7 @@
 ``/data/issue/@schema``) is left as an exercise for the reader.
 
 Redefine/move rest endpoints
-============================
+----------------------------
 
 In addition to adding new endpoints, you can redefine existing
 endpoints. Adding this as described above::
@@ -1734,7 +1734,7 @@
 
 
 Controlling Access to Backend Data
-==================================
+----------------------------------
 
 Roundup's schema is the primary access control mechanism. Roles and
 Permissions provide the ability to carefully control what data can be
@@ -1743,7 +1743,7 @@
 However the templating system can access the hyperdb directly which
 allows filtering to happen with admin privs escaping the standard
 permissions scheme. For example access to a user's roles should be
-limited to the user (read only) and an admin.  If you have customized
+limited to the user (read only) and an admin.  If you have customised
 your schema to implement `Restricting the list of
 users that are assignable to a task <customizing.html#restricting-the-list-of-users-that-are-assignable-to-a-task>`__
 so that only users with a
@@ -1846,7 +1846,7 @@
 ignored.
 
 Changing Access Roles with JSON Web Tokens
-^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+------------------------------------------
 
 As discussed above Roundup's schema is the access control mechanism.
 However you may want to integrate a third party system with roundup.
@@ -2110,7 +2110,7 @@
 .. _thoughts on JWT credentials: https://issues.roundup-tracker.org/issue2551064
 
 Final steps
-^^^^^^^^^^^
+~~~~~~~~~~~
 
 See the `upgrading directions`_ on how to use the ``updateconfig``
 command to generate an updated copy of config.ini using
@@ -2133,7 +2133,7 @@
 
 
 Creating Custom Rate Limits
-===========================
+---------------------------
 
 You can replace the default rate limiter that is configured using
 the tracker's ``config.ini``. You can return different rate
@@ -2177,7 +2177,7 @@
 the defaults from ``config.ini`` file are used.
 
 Test Examples
-^^^^^^^^^^^^^
+~~~~~~~~~~~~~
 
 Rate limit tests::
 
--- a/doc/upgrading-history.txt	Sun Jul 09 17:42:40 2023 -0400
+++ b/doc/upgrading-history.txt	Mon Jul 10 17:31:34 2023 -0400
@@ -82,7 +82,7 @@
 these messages are passed via the URL (due to roundup redirecting after
 an edit), we did have security-issues (see issue2550724).
 
-If you have customized the OK or Error messages in your
+If you have customised the OK or Error messages in your
 roundup-installation and you were using features like bold or italic
 in the message, you will have to do without this highlighting and
 remove HTML tags from messages.
@@ -182,8 +182,8 @@
 method, therefore the default search permissions will not allow
 searching and you'll have to add an explicit search permission.
 If you have modified your schema, you can check if you're missing any
-search permissions with the following script, run it in your tracker
-directory, it will list for each Class and Property the roles that may
+search permissions with the following script, run it in your :term:`tracker
+home` directory, it will list for each Class and Property the roles that may
 search for this property::
 
     #!/usr/bin/python
@@ -326,14 +326,14 @@
 Customized MailGW Class
 -----------------------
 
-If you have customized the MailGW class in your tracker: The new MailGW
+If you have customised the MailGW class in your tracker: The new MailGW
 class opens the database for each message in the method handle_message
 (instance.open) instead of passing the opened database as a parameter to
 the MailGW constructor. The old handle_message has been renamed to
 _handle_message. The new method opens the database and wraps the call to
 the old method into a try/finally.
 
-Your customized MailGW class needs to mirror this behavior.
+Your customised MailGW class needs to mirror this behavior.
 
 Fix the "remove" button in issue files and messages lists
 ---------------------------------------------------------
@@ -523,6 +523,13 @@
  CREATE INDEX words_by_id ON __words (_textid);
  CREATE UNIQUE INDEX __textids_by_props ON __textids (_class, _itemid, _prop);
 
+You can find out which database backend you are using by looking at
+the ``db/backend_name`` file in the tracker's home directory. The
+username, password, and database name can all be found in the
+``config.ini`` file if you are using ``mysql`` or ``postgresql``. You
+can run the SQL commands using the native client interface: ``psql``
+(postgres), ``mysql`` (MySQL), or ``sqlite3`` (SQLite).
+
 .. index:: upgrading; 1.2.x to 1.3.0
 
 Migrating from 1.2.x to 1.3.0
--- a/doc/upgrading.txt	Sun Jul 09 17:42:40 2023 -0400
+++ b/doc/upgrading.txt	Mon Jul 10 17:31:34 2023 -0400
@@ -1204,7 +1204,7 @@
 to "chatting" until a second user (person) adds a message.  If you
 want this functionality, you need to merge the distributed
 statusauditor.py with your tracker's statusauditor.py. If you have not
-customized your tracker's statusauditor.py, copy the one from the
+customised your tracker's statusauditor.py, copy the one from the
 distibuted template. In addition to the python file, you also must
 copy/merge the distributed ``detectors/config.ini`` into your
 tracker's detectors directory. Most people can copy
@@ -1325,9 +1325,9 @@
 Make sure that user can view labelprop on classes (required)
 ------------------------------------------------------------
 
-If you have View permissions that use ```properties=...```,
-make sure that the labelprop for the class is listed in the
-properties list.
+If you have View permissions that use ```properties=...```, make sure
+that the `labelprop <reference.html#setlabelprop-property>`_ for the
+class is listed in the properties list.
 
 The first one of these that exists must must be in the list:
 
@@ -1343,9 +1343,9 @@
 E.G. If your class does a setlabelprop("foo") you must include "foo"
 in the properties list even if the class has name or title properties.
 
-See:
-https://www.roundup-tracker.org/docs/customizing.html#setlabelprop-property
-for further details on the labelprop.
+See: `reference.html setlabelprop
+<reference.html#setlabelprop-property>`_ for further details on the
+labelprop.
 
 If you don't do this, you will find that multilinks (and possibly
 links) may not be displayed properly. E.G. templates that iterate over
@@ -1455,19 +1455,24 @@
   xmlrpclib.Fault: <Fault 1: "<class
      'roundup.exceptions.UsageError'>:Required Header Missing">
 
-change the setting of csrf_enforce_header_x-requested-with in
-config.ini to no. So it looks like::
-
-  csrf_enforce_header_x-requested-with = no
-
-Alternatively change your xmlrpc client to add appropriate headers to
+change your xmlrpc client to add appropriate headers to
 the request including the:
 
   X-Requested-With:
 
-header as well as any other required csrf headers (e.g. referer, origin)
-configured in config.ini. See the advanced python client at the end of 
-the `xmlrpc guide`_.
+header as well as any other required csrf headers (e.g. referer,
+origin) configured in config.ini. See the `advanced python client
+<xmlrpc.html#advanced-python-client-adding-anti-csrf-headers>`_ at
+the end of the xmlrpc guide.
+
+Alternatively change the setting of
+csrf_enforce_header_x-requested-with in config.ini to ``no``. So it
+looks like::
+
+  csrf_enforce_header_x-requested-with = no
+
+This is not recommended as it reduces csrf protection.
+
 
 Support for SameSite cookie option for session cookie
 -----------------------------------------------------
--- a/doc/whatsnew-0.7.txt	Sun Jul 09 17:42:40 2023 -0400
+++ b/doc/whatsnew-0.7.txt	Mon Jul 10 17:31:34 2023 -0400
@@ -137,7 +137,7 @@
 Before 0.7.0 adding or extending web actions was done by overriding or adding
 methods on the Client class. Though this approach still works to provide
 backwards compatibility, it is recommended you upgrade to the new approach, as
-described in the `Defining new web actions`__ section of the customization
+described in the `Defining new web actions`__ section of the customisation
 documentation. You might also want to take a look at the `Using an external
 password validation source`__ example.
 
--- a/doc/whatsnew-0.8.txt	Sun Jul 09 17:42:40 2023 -0400
+++ b/doc/whatsnew-0.8.txt	Mon Jul 10 17:31:34 2023 -0400
@@ -57,7 +57,7 @@
 Roundup's previously ad-hoc logging of events has been cleaned up and is
 now configured in a single place in the tracker configuration file.
 
-The `customization documentation`_ has more details on how this is
+The `customisation documentation`_ has more details on how this is
 configured.
 
 roundup-mailgw now logs fatal exceptions rather than mailing them to admin.
@@ -104,7 +104,7 @@
 =================
 
 To write extension code for Roundup you place a file in the tracker home
-``extensions`` directory. See the `customization documentation`_ for more
+``extensions`` directory. See the `customisation documentation`_ for more
 information about how this is done.
 
 
@@ -184,4 +184,4 @@
   The builtin web server may now perform HTTP Basic Authentication by
   itself.
 
-.. _`customization documentation`: reference.html
+.. _`customisation documentation`: reference.html
--- a/doc/xmlrpc.txt	Sun Jul 09 17:42:40 2023 -0400
+++ b/doc/xmlrpc.txt	Mon Jul 10 17:31:34 2023 -0400
@@ -14,7 +14,8 @@
    :local:
 
 Introduction
-------------
+============
+
 Version 1.4 of Roundup includes an XML-RPC frontend for remote access. The
 XML-RPC interface allows a limited subset of commands similar to those found in
 local `roundup-admin` tool.
@@ -27,8 +28,8 @@
 
     http://localhost:8917/demo/xmlrpc
 
-Enabling XML-RPC server 
------------------------
+Enabling XML-RPC
+================
 There are two ways to run the XML-RPC interface:
 
   through roundup itself
@@ -36,8 +37,9 @@
   stand alone roundup-xmlrpc-server
 
 
-through roundup
+Through Roundup
 ---------------
+
 The XML-RPC service is available from the roundup HTTP server under
 /xmlrpc.
 
@@ -53,9 +55,10 @@
 This is usually included near where other permissions like "Web Access"
 or "Email Access" are assigned.
 
-stand alone roundup-xmlrpc-server
----------------------------------
-Using roundup to access the xmlrpc interface is preferred. Roundup
+Standalone roundup-xmlrpc-server
+--------------------------------
+
+Using Roundup to access the xmlrpc interface is preferred. Roundup
 provides better control over who can use the interface.
 
 The Roundup XML-RPC standalone server must be started before remote
@@ -69,21 +72,24 @@
 The default port is ``8000``. An alternative port can be specified with the
 ``--port`` switch.
 
-security consideration
-----------------------
+Security Consideration
+======================
+
 Both the standalone and embedded roundup XML endpoints used the
 default python XML parser. This parser is know to have security
 issues. For details see: https://pypi.org/project/defusedxml/.
 You may wish to use the rest interface which doesn't have the same
 issues. Patches with tests to roundup to use defusedxml are welcome.
 
-Note that the current ``roundup-xmlrpc-server`` implementation does
-not support SSL. This means that usernames and passwords will be
-passed in cleartext unless the server is being proxied behind another
-server (such as Apache or lighttpd) that provide SSL.
+.. caution::
+
+   The current standalone ``roundup-xmlrpc-server`` implementation
+   does not support SSL. This means that usernames and passwords will
+   be passed in cleartext unless the server is proxied behind
+   another server (such as Apache or lighttpd) that provides SSL.
 
 Client API
-----------
+==========
 The server currently implements seven methods/commands. Each method
 requires that the user provide a username and password in the HTTP
 authorization header in order to authenticate the request against the
@@ -137,7 +143,7 @@
         :ref:`query-tracker`.
 ======= ====================================================================
 
-sample python client
+Sample Python Client
 ====================
 
 This client will work if you turn off the x-requested-with header and
@@ -171,7 +177,7 @@
         >>> roundup_server.lookup('user','admin')
         '1'
 
-advanced python client adding anti-csrf headers
+Advanced Python Client Adding anti-csrf Headers
 ===============================================
 
 The one below adds Referer and X-Requested-With headers so it can pass
--- a/roundup/cgi/client.py	Sun Jul 09 17:42:40 2023 -0400
+++ b/roundup/cgi/client.py	Mon Jul 10 17:31:34 2023 -0400
@@ -1083,7 +1083,7 @@
 
     def authenticate_bearer_token(self, challenge):
         ''' authenticate the bearer token. Refactored from determine_user()
-            to alow it to be overridden if needed.
+            to allow it to be overridden if needed.
         '''
         try:  # will jwt import?
             import jwt
@@ -1190,7 +1190,7 @@
                     try:
                         # make sure to str(token['sub']) the
                         # subject. As decoded by json, it is unicode
-                        # which thows an error when used with 'nodeid
+                        # which throws an error when used with 'nodeid
                         # in db' down the call chain.
                         user = self.db.user.get(str(token['sub']), 'username')
                     except IndexError:
--- a/roundup/cgi/templating.py	Sun Jul 09 17:42:40 2023 -0400
+++ b/roundup/cgi/templating.py	Mon Jul 10 17:31:34 2023 -0400
@@ -2733,7 +2733,7 @@
             return value
 
         # determine orderprop for property if property is a link or multilink
-        prop = self._db.getclass(self._classname).getprops()[property]
+        prop = self._db.getclass(self._prop.classname).getprops()[property]
         if type(prop) in [hyperdb.Link, hyperdb.Multilink]:
             orderprop = value[0]._db.getclass(prop.classname).orderprop()
             sort_by_link = True
--- a/roundup/hyperdb.py	Sun Jul 09 17:42:40 2023 -0400
+++ b/roundup/hyperdb.py	Mon Jul 10 17:31:34 2023 -0400
@@ -253,12 +253,16 @@
     def __init__(self, classname, do_journal='yes', try_id_parsing='yes',
                  required=False, default_value=None,
                  msg_header_property=None, quiet=False, rev_multilink=None):
-        """ Default is to journal link and unlink events.
+        """ The base class used by Link and Multilink classes.
+
+            Default is to journal link and unlink events.
+
             When try_id_parsing is false, we don't allow IDs in input
             fields (the key of the Link or Multilink property must be
             given instead). This is useful when the name of a property
             can be numeric. It will only work if the linked item has a
             key property and is a questionable feature for multilinks.
+
             The msg_header_property is used in the mail gateway when
             sending out messages: By default roundup creates headers of
             the form: 'X-Roundup-issue-prop: value' for all properties
@@ -270,6 +274,7 @@
             'msg_header_property="username"' for the assigned_to
             property will generated message headers of the form:
             'X-Roundup-issue-assigned_to: joe_user'.
+
             The rev_multilink is used to inject a reverse multilink into
             the Class linked by a Link or Multilink property. Note that
             the result is always a Multilink. The name given with
--- a/scripts/Docker/Dockerfile	Sun Jul 09 17:42:40 2023 -0400
+++ b/scripts/Docker/Dockerfile	Mon Jul 10 17:31:34 2023 -0400
@@ -27,7 +27,7 @@
 ARG pythonversion=3.11
 
 #FROM python:3-alpine
-FROM python@sha256:caafba876f841774905f73df0fcaf7fe3f55aaf9cb48a9e369a41077f860d4a7 as build
+FROM python@sha256:0a56f24afa1fc7f518aa690cb8c7be661225e40b157d9bb8c6ef402164d9faa7 as build
 
 # Inherit global values https://github.com/moby/moby/issues/37345
 ARG appdir
@@ -157,7 +157,7 @@
 # build a new smaller docker image for execution. Build image above
 # is 1G in size.
 # FROM python:3-alpine
-FROM python@sha256:caafba876f841774905f73df0fcaf7fe3f55aaf9cb48a9e369a41077f860d4a7
+FROM python@sha256:0a56f24afa1fc7f518aa690cb8c7be661225e40b157d9bb8c6ef402164d9faa7
 
 # import from global
 ARG appdir
@@ -195,10 +195,17 @@
     fi
 
 ARG source
-LABEL "org.roundup-tracker.vendor"="Roundup Issue Tracker Team" \
-      "org.roundup-tracker.description"="Roundup Issue Tracker multi-backend" \
-      "version"="2.2.0 $source" \
-      "org.opencontainers.image.authors"="roundup-devel@lists.sourceforge.net"
+ARG pythonversion
+ARG pip_mod
+LABEL "org.opencontainers.image.vendor"="Roundup Issue Tracker Team" \
+      "org.opencontainers.image.title"="Roundup Issue Tracker" \
+      "org.opencontainers.image.description"="Roundup Issue Tracker with multi-backend support installed via $source with python version $pythonversion" \
+      "org.opencontainers.image.version"="2.2.0" \
+      "org.opencontainers.image.authors"="roundup-devel@lists.sourceforge.net" \
+      "org.opencontainers.image.licenses"="MIT AND ZPL-2.0 AND Python-2.0" \
+      "org.opencontainers.image.documentation"="https://www.roundup-tracker.org/docs/installation.html" \
+      "pip-modules"="$pip_mod"
+
 
 ARG pythonversion
 # pull over built assets
--- a/website/www/Makefile	Sun Jul 09 17:42:40 2023 -0400
+++ b/website/www/Makefile	Mon Jul 10 17:31:34 2023 -0400
@@ -7,7 +7,11 @@
 	@echo "Please use \`make <target>' where <target> is one of"
 	@echo "  html      to make standalone HTML files"
 	@echo "  linkcheck to check all external links for integrity"
-
+	@echo "  sourceforge_prod_sync sync html directory to sourceforce"
+	@echo "      production website"
+	@echo "  sourceforge_dev_sync sync html directory to sourceforce"
+	@echo "      /dev_docs subdirectory"
+	@echo "  clean remove all produced files"
 clean:
 	-rm -rf $(TMP) $(HTML) docs COPYING.txt
 
@@ -40,3 +44,7 @@
 
 sourceforge_dev_sync:
 	rsync -av html/. web.sourceforge.net:/home/project-web/roundup/htdocs/dev_docs/.
+
+sourceforge_prod_sync:
+	read -p "sync to production y/N? " resp; echo "$$resp" | grep -i "^y"
+	rsync -av html/. web.sourceforge.net:/home/project-web/roundup/htdocs/.
--- a/website/www/_static/style.css	Sun Jul 09 17:42:40 2023 -0400
+++ b/website/www/_static/style.css	Mon Jul 10 17:31:34 2023 -0400
@@ -58,7 +58,7 @@
 }
 
 h1, h2, h3, h4, h5, h6 {
-    line-height: 1.25;
+    line-height: 1.1;
     /* Larger spacing before header and smaller after to make
        header part of following section */
     margin-block-end: 0.3em;
@@ -105,10 +105,11 @@
 body > .footer { margin: 1em 0 1em -14em; clear:both;}
 body > .navigation 
 {
+  float: left;
   margin-left: -14em;
   margin-inline-start: -14em;
+  margin-block-start: unset;
   width: 14em;
-  float: left;
 }
 body > .content 
 {
@@ -176,8 +177,19 @@
 /* style */
 
 :link { color: rgb(220,0,0); text-decoration: none;}
-:link:hover {text-decoration: underline;}
-:visited { color: rgb(187,0,0); text-decoration: none;}
+:link:hover {
+    text-decoration: underline solid clamp(1px, .3ex, 4px);
+    text-underline-position: under;
+}
+:visited { color: rgb(200,0,0); text-decoration: none;}
+:visited:hover {
+    /* would like to change from solid line to dashed, but
+       because of privacy abusing people I can't change that
+       value. So settle for darkgrey underline.
+    */
+    text-decoration-color: darkgrey;
+}
+
 a.toc-backref { color: #000000; }
 
 .header h1 { margin-left: 1em; }
@@ -201,7 +213,8 @@
 }
 .menu > ul > li.current > *
 { 
-  background-color:#dddddd;
+  background-color: #f0f0f0;
+  border-block-end: solid medium #cccccc;
 }
 
 .menu ul li:first-child { margin-top:0;}
@@ -234,9 +247,15 @@
   border-top: none;
 }
 
+/* make submenu items clickable cross whole width of menu */
+.menu ul ul > li > a {
+    display: block;
+}
+
 .menu ul li.toctree-l2.current {
-  background-color: #dddddd;
-  pading-block: 0.25em;
+  background-color: #f0f0f0;
+  border-inline-start: solid 2px rgb(220, 0, 0);
+  padding-block: 1em;
 }
 
 /* related */
@@ -281,9 +300,8 @@
   background-color: #f7f7f7;
 }
 
-/* This is a little hack to inject a 'news' block into the title
-   page without having to set up a custom directive. */
-#roundup-issue-tracker .note
+/* style for the floating release_info badge block on the home page */
+#roundup-issue-tracker .release_info
 {
   float: right;
   width: auto;
@@ -292,7 +310,6 @@
   padding: 1em;
   margin: 1em;
 }
-#roundup-issue-tracker .note .admonition-title { display: none; }
 
 table
 { 
@@ -441,6 +458,7 @@
     background-color: #f5f4d8;
     background-size: 20px 100%, 20px 100%, 16px 100%, 16px 100%;
     background-attachment: local, local, scroll, scroll;
+    padding: 0.5em;
 }
 
 /* https://moderncss.dev/totally-custom-list-styles/ make a list
@@ -460,6 +478,15 @@
     padding-inline: 0.5em;
 }
 
+/* consider highlighting header element that is a target */
+/*
+:target > :is(h1,h2,h3,h4,h5,h6) {
+  background: rgb(254, 227, 227);
+  outline: rgb(254, 227, 227) solid 3px;
+  padding: 0.3em;
+}
+*/
+
 /* website only */
 /* assume desktop reader for local html files, also no contact
    page for local doc files */
@@ -469,9 +496,11 @@
     body { padding-inline-start: unset; /* remove space for float menu */}
     body > .header { margin-inline-start: unset;
 	             margin-block-start: 3em; /* move down from search */}
-    body > .navigation { margin-inline-start: unset;
+    body > .navigation { float: none;
+			 margin-block-start: unset;
+			 margin-inline-start: unset;
 			 width: unset;
-			 float: none;}
+}
     body > .navigation li > ul > li { padding-block: 1em;
     /* move links away from each other */ }
     #roundup-issue-tracker .note { float: none; /* download box */}
--- a/website/www/conf.py	Sun Jul 09 17:42:40 2023 -0400
+++ b/website/www/conf.py	Mon Jul 10 17:31:34 2023 -0400
@@ -19,6 +19,20 @@
 
 import sys, os
 
+# Read Roundup version by importing it from root of checkout.
+# this ensures that 'unknown version' is inserted even if
+# `roundup` is importable from other location in sys.path
+SYSSAVE = sys.path
+DOCROOT = os.path.abspath(os.path.dirname(__file__) + "/..")
+sys.path = [os.path.dirname(DOCROOT)]
+try:
+    from roundup import __version__ as VERSION
+    SHORTVER = '.'.join(VERSION.split('.', 2)[:2])
+except ImportError:
+    VERSION = SHORTVER = '(unknown version)'
+finally:
+    sys.path = SYSSAVE
+
 # If your extensions are in another directory, add it here. If the directory
 # is relative to the documentation root, use os.path.abspath to make it
 # absolute, like shown here.
@@ -55,13 +69,13 @@
 # built documents.
 #
 # The short X.Y version.
-version = '2.3b2'
+version = SHORTVER
 # The full version, including alpha/beta/rc tags.
-release = '2.3.0b2'
+release = VERSION
 
 # The language for content autogenerated by Sphinx. Refer to documentation
 # for a list of supported languages.
-#language = None
+language = 'en'
 
 # There are two options for replacing |today|: either, you set today to some
 # non-false value, then it is used:
@@ -154,7 +168,12 @@
 # template names.
 #html_additional_pages = {}
 
-html_add_permalinks = ''
+# disable permalinks
+from sphinx import version_info
+if version_info < (3,5,0):
+    html_add_permalinks = ''
+else:
+    html_permalinks = False  # when sphinx > 3.5 is used.
 
 # If false, no module index is generated.
 #html_use_modindex = True
@@ -171,7 +190,7 @@
 # If true, an OpenSearch description file will be output, and all pages will
 # contain a <link> tag referring to it.  The value of this option must be the
 # base URL from which the finished HTML is served.
-html_use_opensearch = ''
+html_use_opensearch = html_baseurl
 
 # If nonempty, this is the file name suffix for HTML files (e.g. ".xhtml").
 #html_file_suffix = ''
--- a/website/www/index.txt	Sun Jul 09 17:42:40 2023 -0400
+++ b/website/www/index.txt	Mon Jul 10 17:31:34 2023 -0400
@@ -19,7 +19,7 @@
 
 .. raw:: html
 
-   <div class="release_info note">
+   <div class="release_info news">
      <!-- package version/pypi download -->
      <a
           href="https://pypi.org/project/roundup/#files">
@@ -74,18 +74,32 @@
 on the winning design from Ka-Ping Yee in the Software Carpentry
 "Track" design competition.
 
-The current stable version of Roundup is 2.3.0b2. It is a bug fix
-and feature release for the 2.2.0 release 
+It is designed to be customised so you can "track your issues your
+way".
+
+The current stable version of Roundup is 2.3.0b2. It is a bug fix and
+feature release for the 2.2.0 release.
+
+It runs with Python 2.7.12+ or 3.6+.
+
+.. admonition:: Python 2 Support
+
+   Although the original plan was to support Python 2 until 2025,
+   CI resources for testing with Python 2 are being phased out by a
+   number of CI services. Python 3 should be used to deploy new
+   trackers and older trackers should be `upgraded to use Python 3
+   <docs/upgrading.html#python-3-support-info>`_.
 
 Release Highlights
 ==================
 
 Some improvements from the 2.2.0 release are:
 
-* Dockerfile demo mode implemented.
+* Dockerfile demo mode implemented. This allows quick evaluation as
+  well as the ability to spin up a configured tracker to customise.
 
 * SQLite backends can use WAL mode to reduce blocking between readers
-  and writers.
+  and writers improving concurrent use.
 
 * Redis can be used for session database with SQLite and dbm
   backends. Provides a major performance improvement.
@@ -96,33 +110,36 @@
 * Postgres full text index can now be enabled.
 
 * Modifications to in-reply-to threading when there are multiple
-  matches.
+  matches results in more predictable handling of messages.
 
 * Many updates to documentation to make it scannable, useful and
   work on mobile.
 
 * Admin documentation includes a section on setting up Content
-  Security Policy (CSP)
+  Security Policy (CSP) to better secure your Roundup trackers.
 
 * REST now allows rate limiting headers to be accessed by client
-  javascript.
+  JavaScript.
 
 * Default number of rounds for PBKDF2 updated to 2M to account for
   improvements in password crackers and CPU power.
 
-* Support PBKDF2 with SHA512 for password storage
+* Support PBKDF2 with SHA512 for password storage to improve
+  resistance to password crackers.
 
 * Deprecate SSHA password hash function.
 
-* roundup-admin reindex can be done in batches to managle load
+* roundup-admin reindex can be done in batches to manage load
   incurred by reindexing.
 
-* roundup-admin can list avaailable templates and their installed
-  locations.
+* roundup-admin can list available templates and their installed
+  locations. This is useful when installing via pip or in a docker
+  container as supporting files are not stored in the usual locations
+  like /usr/share/roundup.
 
-* Crash fixes in detector handling, configuration handling, 
+* Crash fixes in detector handling and configuration handling. 
 
-More info on the 51 changes can be found in the `change note`_.
+More info on the 51 changes can be found in the `change notes`_.
 
 Roundup Use Cases
 =================
@@ -140,7 +157,8 @@
 * sales lead tracking
 * conference paper submission and double-blind referee
   management
-* weblogging (well, almost :) 
+* weblogging (well, almost :)
+* thing management using the `GTD methodology <https://gettingthingsdone.com/>`_.
 
 ...and so on. It's been designed with :doc:`flexibility
 <docs/customizing>` in mind - it's not just another bug
@@ -154,7 +172,24 @@
 install Roundup. After you've unpacked the source, just run "``python
 demo.py``" and load up the URL it prints out!
 
-You can install using a virtual environment with pip by:
+Follow the source gratification mode with these steps (change the
+``-2.3.0`` version identifier to match your downloaded file).
+
+1. ``python3 -m pip download roundup``
+2. ``tar -xzvf roundup-2.3.0.tar.gz``
+
+   * if you don't have a tar command (e.g windows), use:
+     ``python -c 'import tarfile; tarfile.open(sys.argv[1]).extractall();' roundup-2.3.0.tar.gz``
+
+
+3. ``cd roundup-2.3.0``
+4. ``python3 demo.py``
+
+(The source download can also be used to `create a custom Docker
+image <docs/installation.html#docker-support>`_.)
+
+Alternatively, you can install using a virtual environment with pip
+by:
 
 1. create a virtual environment with::
 
@@ -180,22 +215,8 @@
 6. when you are done, use `deactivate` to return your shell to using
    the system python.
 
-Or choose the source directory gratification mode to run the demo
-with these steps (change the ``-2.2.0`` version identifier to match
-your downloaded file).
-
-1. ``python3 -m pip download roundup``
-2. ``tar -xzvf roundup-2.2.0.tar.gz``
-
-   * if you don't have a tar command, ``python -c 'import tarfile, sys; tarfile.open(sys.argv[1]).extractall();' roundup-2.2.0.tar.gz`` can be used.
-
-3. ``cd roundup-2.2.0``
-4. ``python3 demo.py``
-
 Both of these methods produce the same result.
 
-(The source download can also be used to create a Docker image.)
-
 Origin Story
 ============
 Roundup was originally released as version 0.1.1 in late August, 2001.
@@ -206,5 +227,6 @@
     something different for a while. Roundup here I come... 
 
 .. _`download`: https://pypi.org/project/roundup/
+.. _`change notes`: https://sourceforge.net/p/roundup/code/ci/tip/tree/CHANGES.txt
 .. _`change note`: https://sourceforge.net/p/roundup/code/ci/tip/tree/CHANGES.txt
 .. _`its own set of docs`: https://www.roundup-tracker.org/dev-docs/docs.html
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/website/www/requirements.pip	Mon Jul 10 17:31:34 2023 -0400
@@ -0,0 +1,1 @@
+sphinx_sitemap

Roundup Issue Tracker: http://roundup-tracker.org/