diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 534d2fa6..c7e6c020 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,56 +1,57 @@ exclude: "^swh/web/tests/resources/" repos: - repo: https://github.com/pre-commit/pre-commit-hooks rev: v4.1.0 hooks: - id: trailing-whitespace + exclude: '.eml$' - id: check-json - id: check-yaml - repo: https://gitlab.com/pycqa/flake8 rev: 4.0.1 hooks: - id: flake8 - repo: https://github.com/codespell-project/codespell rev: v2.1.0 hooks: - id: codespell name: Check source code spelling exclude: > (?x)^( cypress/integration/directory.spec.js| yarn.lock| package.json )$ args: [-L edn] stages: [commit] - id: codespell name: Check commit message spelling stages: [commit-msg] - repo: local hooks: - id: mypy name: mypy entry: env DJANGO_SETTINGS_MODULE=swh.web.settings.development mypy args: [swh] pass_filenames: false language: system types: [python] - id: eslint name: eslint entry: node_modules/.bin/eslint -c assets/config/.eslintrc language: system types: [javascript] - repo: https://github.com/PyCQA/isort rev: 5.10.1 hooks: - id: isort - repo: https://github.com/python/black rev: 19.10b0 hooks: - id: black diff --git a/MANIFEST.in b/MANIFEST.in index 1e16964a..ea19ad89 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -1,13 +1,14 @@ include pytest.ini include README.md include requirements*.txt include tox.ini include version.txt recursive-include swh py.typed recursive-include assets * recursive-include swh/web/templates * recursive-include swh/web/tests/resources * +recursive-include swh/web/tests/inbound_email/resources *.eml include package.json include yarn.lock diff --git a/PKG-INFO b/PKG-INFO index 3beeb4c1..120a2158 100644 --- a/PKG-INFO +++ b/PKG-INFO @@ -1,206 +1,206 @@ Metadata-Version: 2.1 Name: swh.web -Version: 0.0.379 +Version: 0.0.380 Summary: Software Heritage Web UI Home-page: https://forge.softwareheritage.org/diffusion/DWUI/ Author: Software Heritage developers Author-email: swh-devel@inria.fr License: UNKNOWN Project-URL: Bug Reports, https://forge.softwareheritage.org/maniphest Project-URL: Funding, https://www.softwareheritage.org/donate Project-URL: Source, https://forge.softwareheritage.org/source/swh-web Project-URL: Documentation, https://docs.softwareheritage.org/devel/swh-web/ Platform: UNKNOWN Classifier: Programming Language :: Python :: 3 Classifier: Intended Audience :: Developers Classifier: License :: OSI Approved :: GNU Affero General Public License v3 or later (AGPLv3+) Classifier: Operating System :: OS Independent Classifier: Development Status :: 5 - Production/Stable Classifier: Framework :: Django Requires-Python: >=3.7 Description-Content-Type: text/markdown Provides-Extra: testing License-File: LICENSE License-File: AUTHORS # swh-web This repository holds the development of Software Heritage web applications: * swh-web API (https://archive.softwareheritage.org/api): enables to query the content of the archive through HTTP requests and get responses in JSON or YAML. * swh-web browse (https://archive.softwareheritage.org/browse): graphical interface that eases the navigation in the archive. Documentation about how to use these components but also the details of their URI schemes can be found in the docs folder. The produced HTML documentation can be read and browsed at https://docs.softwareheritage.org/devel/swh-web/index.html. ## Technical details Those applications are powered by: * [Django Web Framework](https://www.djangoproject.com/) on the backend side with the following extensions enabled: * [django-rest-framework](http://www.django-rest-framework.org/) * [django-webpack-loader](https://github.com/owais/django-webpack-loader) * [django-js-reverse](http://django-js-reverse.readthedocs.io/en/latest/) * [webpack](https://webpack.js.org/) on the frontend side for better static assets management, including: * assets dependencies management and retrieval through [yarn](https://yarnpkg.com/en/) * linting of custom javascript code (through [eslint](https://eslint.org/)) and stylesheets (through [stylelint](https://stylelint.io/)) * use of [es6](http://es6-features.org) syntax and advanced javascript feature like [async/await](https://javascript.info/async-await) or [fetch](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API) thanks to [babel](https://babeljs.io/) (es6 to es5 transpiler and polyfills provider) * assets minification (using [terser](https://github.com/terser-js/terser) and [cssnano](http://cssnano.co/)) but also dead code elimination for production use ## How to build, run and test ### Backend requirements First you will need [Python 3](https://www.python.org) and a complete [swh development environment](https://forge.softwareheritage.org/source/swh-environment/) installed. To run the backend, you need to have the following [Python 3 modules](requirements.txt) installed. To run the backend tests, the following [Python 3 modules](requirements-test.txt) are also required to be installed. One easy way to install them is to use the `pip` tool: ``` $ pip install -r requirements.txt -r requirements-test.txt ``` ### Frontend requirements To compile the frontend assets, you need to have [nodejs](https://nodejs.org/en/) >= 12.0.0 and [yarn](https://yarnpkg.com/en/) installed. If you are on Debian, you can easily install an up to date nodejs from the [nodesource](https://github.com/nodesource/distributions/blob/master/README.md) repository. Packages for yarn can be installed by following [these instructions](https://yarnpkg.com/en/docs/install#debian-stable). Alternatively, you can install yarn with `npm install yarn`, and add `YARN=node_modules/yarn/bin/yarn` as argument whenever you run `make`. Please note that the static assets bundles generated by webpack are not stored in the git repository. Follow the instructions below in order to generate them in order to be able to run the frontend part of the web applications. ### Make targets to execute the applications Below is the list of available make targets that can be executed from the root directory of swh-web in order to build and/or execute the web applications under various configurations: * **run-django-webpack-devserver**: Compile and serve not optimized (without mignification and dead code elimination) frontend static assets using [webpack-dev-server](https://github.com/webpack/webpack-dev-server) and run django server with development settings. This is the recommended target to use when developing swh-web as it enables automatic reloading of backend and frontend part of the applications when modifying source files (*.py, *.js, *.css, *.html). * **run-django-webpack-dev**: Compile not optimized (no minification, no dead code elimination) frontend static assets using webpack and run django server with development settings. This is the recommended target when one only wants to develop the backend side of the application. * **run-django-webpack-prod**: Compile optimized (with minification and dead code elimination) frontend static assets using webpack and run django server with production settings. This is useful to test the applications in production mode (with the difference that static assets are served by django). Production settings notably enable advanced django caching and you will need to have [memcached](https://memcached.org/) installed for that feature to work. * **run-django-server-dev**: Run the django server with development settings but without compiling frontend static assets through webpack. * **run-django-server-prod**: Run the django server with production settings but without compiling frontend static assets through webpack. * **run-gunicorn-server**: Run the web applications with production settings in a [gunicorn](http://gunicorn.org/) worker as they will be in real production environment. Once one of these targets executed, the web applications can be executed by pointing your browser to http://localhost:5004. ### Make targets to test the applications Some make targets are also available to easily execute the backend and frontend tests of the Software Heritage web applications. The backend tests are powered by the [pytest](https://docs.pytest.org/en/latest/) and [hypothesis](https://hypothesis.readthedocs.io/en/latest/) frameworks while the frontend ones rely on the use of the [cypress](https://www.cypress.io/) tool. Below is the exhaustive list of those targets: * **test**: execute the backend tests using a fast hypothesis profile (only one input example will be provided for each test) * **test-full**: execute the backend tests using a slower hypothesis profile (one hundred of input examples will be provided for each test which helps spotting possible bugs) * **test-frontend**: execute the frontend tests using cypress in headless mode but with some slow test suites disabled * **test-frontend-full**: execute the frontend tests using cypress in headless mode with all test suites enabled * **test-frontend-ui**: execute the frontend tests using the cypress GUI but with some slow test suites disabled * **test-frontend-full-ui**: execute the frontend tests using the cypress GUI with all test suites enabled ### Yarn targets Below is a list of available yarn targets in order to only execute the frontend static assets compilation (no web server will be executed): * **build-dev**: compile not optimized (without mignification and dead code elimination) frontend static assets and store the results in the `swh/web/static` folder. * **build**: compile optimized (with mignification and dead code elimination) frontend static assets and store the results in the `swh/web/static` folder. **The build target must be executed prior performing the Debian packaging of swh-web** in order for the package to contain the optimized assets dedicated to production environment. To execute these targets, issue the following command: ``` $ yarn ``` diff --git a/requirements-test.txt b/requirements-test.txt index 7818d678..551c3920 100644 --- a/requirements-test.txt +++ b/requirements-test.txt @@ -1,19 +1,19 @@ decorator # dependency of swh.core[http] djangorestframework-stubs django-stubs django-test-migrations hypothesis -pytest < 7.0.0 # v7.0.0 removed _pytest.tmpdir.TempdirFactory, which is used by some of the pytest plugins we use +pytest pytest-django pytest-mock pytest-postgresql < 4.0.0 requests-mock != 1.9.0, != 1.9.1 swh.core[http] >= 0.0.95 swh.loader.git >= 0.8.0 swh-scheduler[testing] >= 0.5.0 swh.storage >= 0.1.1 types-chardet types-docutils types-psycopg2 types-pyyaml types-requests diff --git a/static/webpack-stats.json b/static/webpack-stats.json index 6a881ce6..10587305 100644 --- a/static/webpack-stats.json +++ b/static/webpack-stats.json @@ -1,805 +1,805 @@ { "status": "done", "assets": { - "img/thirdParty/chosen-sprite.png": { - "name": "img/thirdParty/chosen-sprite.png", - "path": "/var/lib/jenkins/workspace/DWAPPS/pypi-upload/static/img/thirdParty/chosen-sprite.png", - "publicPath": "/static/img/thirdParty/chosen-sprite.png" - }, "img/thirdParty/chosen-sprite@2x.png": { "name": "img/thirdParty/chosen-sprite@2x.png", "path": "/var/lib/jenkins/workspace/DWAPPS/pypi-upload/static/img/thirdParty/chosen-sprite@2x.png", "publicPath": "/static/img/thirdParty/chosen-sprite@2x.png" }, - "fonts/materialdesignicons-webfont.eot?v=6.6.96": { - "name": "fonts/materialdesignicons-webfont.eot?v=6.6.96", - "path": "/var/lib/jenkins/workspace/DWAPPS/pypi-upload/static/fonts/materialdesignicons-webfont.eot", - "publicPath": "/static/fonts/materialdesignicons-webfont.eot?v=6.6.96" - }, - "fonts/materialdesignicons-webfont.eot": { - "name": "fonts/materialdesignicons-webfont.eot", - "path": "/var/lib/jenkins/workspace/DWAPPS/pypi-upload/static/fonts/materialdesignicons-webfont.eot", - "publicPath": "/static/fonts/materialdesignicons-webfont.eot" + "img/thirdParty/chosen-sprite.png": { + "name": "img/thirdParty/chosen-sprite.png", + "path": "/var/lib/jenkins/workspace/DWAPPS/pypi-upload/static/img/thirdParty/chosen-sprite.png", + "publicPath": "/static/img/thirdParty/chosen-sprite.png" }, "fonts/materialdesignicons-webfont.woff2?v=6.6.96": { "name": "fonts/materialdesignicons-webfont.woff2?v=6.6.96", "path": "/var/lib/jenkins/workspace/DWAPPS/pypi-upload/static/fonts/materialdesignicons-webfont.woff2", "publicPath": "/static/fonts/materialdesignicons-webfont.woff2?v=6.6.96" }, "fonts/materialdesignicons-webfont.woff?v=6.6.96": { "name": "fonts/materialdesignicons-webfont.woff?v=6.6.96", "path": "/var/lib/jenkins/workspace/DWAPPS/pypi-upload/static/fonts/materialdesignicons-webfont.woff", "publicPath": "/static/fonts/materialdesignicons-webfont.woff?v=6.6.96" }, + "fonts/materialdesignicons-webfont.eot?v=6.6.96": { + "name": "fonts/materialdesignicons-webfont.eot?v=6.6.96", + "path": "/var/lib/jenkins/workspace/DWAPPS/pypi-upload/static/fonts/materialdesignicons-webfont.eot", + "publicPath": "/static/fonts/materialdesignicons-webfont.eot?v=6.6.96" + }, + "fonts/materialdesignicons-webfont.eot": { + "name": "fonts/materialdesignicons-webfont.eot", + "path": "/var/lib/jenkins/workspace/DWAPPS/pypi-upload/static/fonts/materialdesignicons-webfont.eot", + "publicPath": "/static/fonts/materialdesignicons-webfont.eot" + }, "fonts/materialdesignicons-webfont.ttf?v=6.6.96": { "name": "fonts/materialdesignicons-webfont.ttf?v=6.6.96", "path": "/var/lib/jenkins/workspace/DWAPPS/pypi-upload/static/fonts/materialdesignicons-webfont.ttf", "publicPath": "/static/fonts/materialdesignicons-webfont.ttf?v=6.6.96" }, "fonts/alegreya-latin-400.woff2": { "name": "fonts/alegreya-latin-400.woff2", "path": "/var/lib/jenkins/workspace/DWAPPS/pypi-upload/static/fonts/alegreya-latin-400.woff2", "publicPath": "/static/fonts/alegreya-latin-400.woff2" }, "fonts/alegreya-latin-400.woff": { "name": "fonts/alegreya-latin-400.woff", "path": "/var/lib/jenkins/workspace/DWAPPS/pypi-upload/static/fonts/alegreya-latin-400.woff", "publicPath": "/static/fonts/alegreya-latin-400.woff" }, "fonts/alegreya-latin-400italic.woff2": { "name": "fonts/alegreya-latin-400italic.woff2", "path": "/var/lib/jenkins/workspace/DWAPPS/pypi-upload/static/fonts/alegreya-latin-400italic.woff2", "publicPath": "/static/fonts/alegreya-latin-400italic.woff2" }, - "fonts/alegreya-latin-500italic.woff2": { - "name": "fonts/alegreya-latin-500italic.woff2", - "path": "/var/lib/jenkins/workspace/DWAPPS/pypi-upload/static/fonts/alegreya-latin-500italic.woff2", - "publicPath": "/static/fonts/alegreya-latin-500italic.woff2" - }, "fonts/alegreya-latin-400italic.woff": { "name": "fonts/alegreya-latin-400italic.woff", "path": "/var/lib/jenkins/workspace/DWAPPS/pypi-upload/static/fonts/alegreya-latin-400italic.woff", "publicPath": "/static/fonts/alegreya-latin-400italic.woff" }, + "fonts/alegreya-latin-500.woff2": { + "name": "fonts/alegreya-latin-500.woff2", + "path": "/var/lib/jenkins/workspace/DWAPPS/pypi-upload/static/fonts/alegreya-latin-500.woff2", + "publicPath": "/static/fonts/alegreya-latin-500.woff2" + }, "fonts/alegreya-latin-500.woff": { "name": "fonts/alegreya-latin-500.woff", "path": "/var/lib/jenkins/workspace/DWAPPS/pypi-upload/static/fonts/alegreya-latin-500.woff", "publicPath": "/static/fonts/alegreya-latin-500.woff" }, - "fonts/alegreya-latin-500.woff2": { - "name": "fonts/alegreya-latin-500.woff2", - "path": "/var/lib/jenkins/workspace/DWAPPS/pypi-upload/static/fonts/alegreya-latin-500.woff2", - "publicPath": "/static/fonts/alegreya-latin-500.woff2" + "fonts/alegreya-latin-500italic.woff2": { + "name": "fonts/alegreya-latin-500italic.woff2", + "path": "/var/lib/jenkins/workspace/DWAPPS/pypi-upload/static/fonts/alegreya-latin-500italic.woff2", + "publicPath": "/static/fonts/alegreya-latin-500italic.woff2" }, "fonts/alegreya-latin-500italic.woff": { "name": "fonts/alegreya-latin-500italic.woff", "path": "/var/lib/jenkins/workspace/DWAPPS/pypi-upload/static/fonts/alegreya-latin-500italic.woff", "publicPath": "/static/fonts/alegreya-latin-500italic.woff" }, "fonts/alegreya-latin-700.woff2": { "name": "fonts/alegreya-latin-700.woff2", "path": "/var/lib/jenkins/workspace/DWAPPS/pypi-upload/static/fonts/alegreya-latin-700.woff2", "publicPath": "/static/fonts/alegreya-latin-700.woff2" }, "fonts/alegreya-latin-700.woff": { "name": "fonts/alegreya-latin-700.woff", "path": "/var/lib/jenkins/workspace/DWAPPS/pypi-upload/static/fonts/alegreya-latin-700.woff", "publicPath": "/static/fonts/alegreya-latin-700.woff" }, "fonts/alegreya-latin-700italic.woff2": { "name": "fonts/alegreya-latin-700italic.woff2", "path": "/var/lib/jenkins/workspace/DWAPPS/pypi-upload/static/fonts/alegreya-latin-700italic.woff2", "publicPath": "/static/fonts/alegreya-latin-700italic.woff2" }, "fonts/alegreya-latin-700italic.woff": { "name": "fonts/alegreya-latin-700italic.woff", "path": "/var/lib/jenkins/workspace/DWAPPS/pypi-upload/static/fonts/alegreya-latin-700italic.woff", "publicPath": "/static/fonts/alegreya-latin-700italic.woff" }, - "fonts/alegreya-latin-800.woff2": { - "name": "fonts/alegreya-latin-800.woff2", - "path": "/var/lib/jenkins/workspace/DWAPPS/pypi-upload/static/fonts/alegreya-latin-800.woff2", - "publicPath": "/static/fonts/alegreya-latin-800.woff2" - }, "fonts/alegreya-latin-800.woff": { "name": "fonts/alegreya-latin-800.woff", "path": "/var/lib/jenkins/workspace/DWAPPS/pypi-upload/static/fonts/alegreya-latin-800.woff", "publicPath": "/static/fonts/alegreya-latin-800.woff" }, + "fonts/alegreya-latin-800.woff2": { + "name": "fonts/alegreya-latin-800.woff2", + "path": "/var/lib/jenkins/workspace/DWAPPS/pypi-upload/static/fonts/alegreya-latin-800.woff2", + "publicPath": "/static/fonts/alegreya-latin-800.woff2" + }, "fonts/alegreya-latin-800italic.woff2": { "name": "fonts/alegreya-latin-800italic.woff2", "path": "/var/lib/jenkins/workspace/DWAPPS/pypi-upload/static/fonts/alegreya-latin-800italic.woff2", "publicPath": "/static/fonts/alegreya-latin-800italic.woff2" }, "fonts/alegreya-latin-800italic.woff": { "name": "fonts/alegreya-latin-800italic.woff", "path": "/var/lib/jenkins/workspace/DWAPPS/pypi-upload/static/fonts/alegreya-latin-800italic.woff", "publicPath": "/static/fonts/alegreya-latin-800italic.woff" }, "fonts/alegreya-latin-900.woff2": { "name": "fonts/alegreya-latin-900.woff2", "path": "/var/lib/jenkins/workspace/DWAPPS/pypi-upload/static/fonts/alegreya-latin-900.woff2", "publicPath": "/static/fonts/alegreya-latin-900.woff2" }, "fonts/alegreya-latin-900.woff": { "name": "fonts/alegreya-latin-900.woff", "path": "/var/lib/jenkins/workspace/DWAPPS/pypi-upload/static/fonts/alegreya-latin-900.woff", "publicPath": "/static/fonts/alegreya-latin-900.woff" }, "fonts/alegreya-latin-900italic.woff2": { "name": "fonts/alegreya-latin-900italic.woff2", "path": "/var/lib/jenkins/workspace/DWAPPS/pypi-upload/static/fonts/alegreya-latin-900italic.woff2", "publicPath": "/static/fonts/alegreya-latin-900italic.woff2" }, "fonts/alegreya-latin-900italic.woff": { "name": "fonts/alegreya-latin-900italic.woff", "path": "/var/lib/jenkins/workspace/DWAPPS/pypi-upload/static/fonts/alegreya-latin-900italic.woff", "publicPath": "/static/fonts/alegreya-latin-900italic.woff" }, "fonts/alegreya-sans-latin-100.woff2": { "name": "fonts/alegreya-sans-latin-100.woff2", "path": "/var/lib/jenkins/workspace/DWAPPS/pypi-upload/static/fonts/alegreya-sans-latin-100.woff2", "publicPath": "/static/fonts/alegreya-sans-latin-100.woff2" }, "fonts/alegreya-sans-latin-100.woff": { "name": "fonts/alegreya-sans-latin-100.woff", "path": "/var/lib/jenkins/workspace/DWAPPS/pypi-upload/static/fonts/alegreya-sans-latin-100.woff", "publicPath": "/static/fonts/alegreya-sans-latin-100.woff" }, "fonts/alegreya-sans-latin-100italic.woff2": { "name": "fonts/alegreya-sans-latin-100italic.woff2", "path": "/var/lib/jenkins/workspace/DWAPPS/pypi-upload/static/fonts/alegreya-sans-latin-100italic.woff2", "publicPath": "/static/fonts/alegreya-sans-latin-100italic.woff2" }, "fonts/alegreya-sans-latin-100italic.woff": { "name": "fonts/alegreya-sans-latin-100italic.woff", "path": "/var/lib/jenkins/workspace/DWAPPS/pypi-upload/static/fonts/alegreya-sans-latin-100italic.woff", "publicPath": "/static/fonts/alegreya-sans-latin-100italic.woff" }, "fonts/alegreya-sans-latin-300.woff2": { "name": "fonts/alegreya-sans-latin-300.woff2", "path": "/var/lib/jenkins/workspace/DWAPPS/pypi-upload/static/fonts/alegreya-sans-latin-300.woff2", "publicPath": "/static/fonts/alegreya-sans-latin-300.woff2" }, - "fonts/alegreya-sans-latin-300italic.woff2": { - "name": "fonts/alegreya-sans-latin-300italic.woff2", - "path": "/var/lib/jenkins/workspace/DWAPPS/pypi-upload/static/fonts/alegreya-sans-latin-300italic.woff2", - "publicPath": "/static/fonts/alegreya-sans-latin-300italic.woff2" - }, "fonts/alegreya-sans-latin-300.woff": { "name": "fonts/alegreya-sans-latin-300.woff", "path": "/var/lib/jenkins/workspace/DWAPPS/pypi-upload/static/fonts/alegreya-sans-latin-300.woff", "publicPath": "/static/fonts/alegreya-sans-latin-300.woff" }, + "fonts/alegreya-sans-latin-300italic.woff2": { + "name": "fonts/alegreya-sans-latin-300italic.woff2", + "path": "/var/lib/jenkins/workspace/DWAPPS/pypi-upload/static/fonts/alegreya-sans-latin-300italic.woff2", + "publicPath": "/static/fonts/alegreya-sans-latin-300italic.woff2" + }, "fonts/alegreya-sans-latin-300italic.woff": { "name": "fonts/alegreya-sans-latin-300italic.woff", "path": "/var/lib/jenkins/workspace/DWAPPS/pypi-upload/static/fonts/alegreya-sans-latin-300italic.woff", "publicPath": "/static/fonts/alegreya-sans-latin-300italic.woff" }, "fonts/alegreya-sans-latin-400.woff2": { "name": "fonts/alegreya-sans-latin-400.woff2", "path": "/var/lib/jenkins/workspace/DWAPPS/pypi-upload/static/fonts/alegreya-sans-latin-400.woff2", "publicPath": "/static/fonts/alegreya-sans-latin-400.woff2" }, "fonts/alegreya-sans-latin-400.woff": { "name": "fonts/alegreya-sans-latin-400.woff", "path": "/var/lib/jenkins/workspace/DWAPPS/pypi-upload/static/fonts/alegreya-sans-latin-400.woff", "publicPath": "/static/fonts/alegreya-sans-latin-400.woff" }, "fonts/alegreya-sans-latin-400italic.woff2": { "name": "fonts/alegreya-sans-latin-400italic.woff2", "path": "/var/lib/jenkins/workspace/DWAPPS/pypi-upload/static/fonts/alegreya-sans-latin-400italic.woff2", "publicPath": "/static/fonts/alegreya-sans-latin-400italic.woff2" }, "fonts/alegreya-sans-latin-400italic.woff": { "name": "fonts/alegreya-sans-latin-400italic.woff", "path": "/var/lib/jenkins/workspace/DWAPPS/pypi-upload/static/fonts/alegreya-sans-latin-400italic.woff", "publicPath": "/static/fonts/alegreya-sans-latin-400italic.woff" }, "fonts/alegreya-sans-latin-500.woff2": { "name": "fonts/alegreya-sans-latin-500.woff2", "path": "/var/lib/jenkins/workspace/DWAPPS/pypi-upload/static/fonts/alegreya-sans-latin-500.woff2", "publicPath": "/static/fonts/alegreya-sans-latin-500.woff2" }, - "fonts/alegreya-sans-latin-500italic.woff2": { - "name": "fonts/alegreya-sans-latin-500italic.woff2", - "path": "/var/lib/jenkins/workspace/DWAPPS/pypi-upload/static/fonts/alegreya-sans-latin-500italic.woff2", - "publicPath": "/static/fonts/alegreya-sans-latin-500italic.woff2" - }, "fonts/alegreya-sans-latin-500.woff": { "name": "fonts/alegreya-sans-latin-500.woff", "path": "/var/lib/jenkins/workspace/DWAPPS/pypi-upload/static/fonts/alegreya-sans-latin-500.woff", "publicPath": "/static/fonts/alegreya-sans-latin-500.woff" }, + "fonts/alegreya-sans-latin-500italic.woff2": { + "name": "fonts/alegreya-sans-latin-500italic.woff2", + "path": "/var/lib/jenkins/workspace/DWAPPS/pypi-upload/static/fonts/alegreya-sans-latin-500italic.woff2", + "publicPath": "/static/fonts/alegreya-sans-latin-500italic.woff2" + }, "fonts/alegreya-sans-latin-500italic.woff": { "name": "fonts/alegreya-sans-latin-500italic.woff", "path": "/var/lib/jenkins/workspace/DWAPPS/pypi-upload/static/fonts/alegreya-sans-latin-500italic.woff", "publicPath": "/static/fonts/alegreya-sans-latin-500italic.woff" }, - "fonts/alegreya-sans-latin-700.woff": { - "name": "fonts/alegreya-sans-latin-700.woff", - "path": "/var/lib/jenkins/workspace/DWAPPS/pypi-upload/static/fonts/alegreya-sans-latin-700.woff", - "publicPath": "/static/fonts/alegreya-sans-latin-700.woff" - }, "fonts/alegreya-sans-latin-700.woff2": { "name": "fonts/alegreya-sans-latin-700.woff2", "path": "/var/lib/jenkins/workspace/DWAPPS/pypi-upload/static/fonts/alegreya-sans-latin-700.woff2", "publicPath": "/static/fonts/alegreya-sans-latin-700.woff2" }, + "fonts/alegreya-sans-latin-700.woff": { + "name": "fonts/alegreya-sans-latin-700.woff", + "path": "/var/lib/jenkins/workspace/DWAPPS/pypi-upload/static/fonts/alegreya-sans-latin-700.woff", + "publicPath": "/static/fonts/alegreya-sans-latin-700.woff" + }, "fonts/alegreya-sans-latin-700italic.woff2": { "name": "fonts/alegreya-sans-latin-700italic.woff2", "path": "/var/lib/jenkins/workspace/DWAPPS/pypi-upload/static/fonts/alegreya-sans-latin-700italic.woff2", "publicPath": "/static/fonts/alegreya-sans-latin-700italic.woff2" }, - "fonts/alegreya-sans-latin-800.woff2": { - "name": "fonts/alegreya-sans-latin-800.woff2", - "path": "/var/lib/jenkins/workspace/DWAPPS/pypi-upload/static/fonts/alegreya-sans-latin-800.woff2", - "publicPath": "/static/fonts/alegreya-sans-latin-800.woff2" - }, "fonts/alegreya-sans-latin-700italic.woff": { "name": "fonts/alegreya-sans-latin-700italic.woff", "path": "/var/lib/jenkins/workspace/DWAPPS/pypi-upload/static/fonts/alegreya-sans-latin-700italic.woff", "publicPath": "/static/fonts/alegreya-sans-latin-700italic.woff" }, + "fonts/alegreya-sans-latin-800.woff2": { + "name": "fonts/alegreya-sans-latin-800.woff2", + "path": "/var/lib/jenkins/workspace/DWAPPS/pypi-upload/static/fonts/alegreya-sans-latin-800.woff2", + "publicPath": "/static/fonts/alegreya-sans-latin-800.woff2" + }, "fonts/alegreya-sans-latin-800.woff": { "name": "fonts/alegreya-sans-latin-800.woff", "path": "/var/lib/jenkins/workspace/DWAPPS/pypi-upload/static/fonts/alegreya-sans-latin-800.woff", "publicPath": "/static/fonts/alegreya-sans-latin-800.woff" }, + "fonts/alegreya-sans-latin-800italic.woff2": { + "name": "fonts/alegreya-sans-latin-800italic.woff2", + "path": "/var/lib/jenkins/workspace/DWAPPS/pypi-upload/static/fonts/alegreya-sans-latin-800italic.woff2", + "publicPath": "/static/fonts/alegreya-sans-latin-800italic.woff2" + }, "fonts/alegreya-sans-latin-800italic.woff": { "name": "fonts/alegreya-sans-latin-800italic.woff", "path": "/var/lib/jenkins/workspace/DWAPPS/pypi-upload/static/fonts/alegreya-sans-latin-800italic.woff", "publicPath": "/static/fonts/alegreya-sans-latin-800italic.woff" }, "fonts/alegreya-sans-latin-900.woff2": { "name": "fonts/alegreya-sans-latin-900.woff2", "path": "/var/lib/jenkins/workspace/DWAPPS/pypi-upload/static/fonts/alegreya-sans-latin-900.woff2", "publicPath": "/static/fonts/alegreya-sans-latin-900.woff2" }, - "fonts/alegreya-sans-latin-800italic.woff2": { - "name": "fonts/alegreya-sans-latin-800italic.woff2", - "path": "/var/lib/jenkins/workspace/DWAPPS/pypi-upload/static/fonts/alegreya-sans-latin-800italic.woff2", - "publicPath": "/static/fonts/alegreya-sans-latin-800italic.woff2" - }, "fonts/alegreya-sans-latin-900.woff": { "name": "fonts/alegreya-sans-latin-900.woff", "path": "/var/lib/jenkins/workspace/DWAPPS/pypi-upload/static/fonts/alegreya-sans-latin-900.woff", "publicPath": "/static/fonts/alegreya-sans-latin-900.woff" }, "fonts/alegreya-sans-latin-900italic.woff2": { "name": "fonts/alegreya-sans-latin-900italic.woff2", "path": "/var/lib/jenkins/workspace/DWAPPS/pypi-upload/static/fonts/alegreya-sans-latin-900italic.woff2", "publicPath": "/static/fonts/alegreya-sans-latin-900italic.woff2" }, "fonts/alegreya-sans-latin-900italic.woff": { "name": "fonts/alegreya-sans-latin-900italic.woff", "path": "/var/lib/jenkins/workspace/DWAPPS/pypi-upload/static/fonts/alegreya-sans-latin-900italic.woff", "publicPath": "/static/fonts/alegreya-sans-latin-900italic.woff" }, + "js/pdf.worker.min.js": { + "name": "js/pdf.worker.min.js", + "path": "/var/lib/jenkins/workspace/DWAPPS/pypi-upload/static/js/pdf.worker.min.js", + "publicPath": "/static/js/pdf.worker.min.js" + }, "fonts/MathJax_AMS-Regular.woff": { "name": "fonts/MathJax_AMS-Regular.woff", "path": "/var/lib/jenkins/workspace/DWAPPS/pypi-upload/static/fonts/MathJax_AMS-Regular.woff", "publicPath": "/static/fonts/MathJax_AMS-Regular.woff" }, "fonts/MathJax_Calligraphic-Bold.woff": { "name": "fonts/MathJax_Calligraphic-Bold.woff", "path": "/var/lib/jenkins/workspace/DWAPPS/pypi-upload/static/fonts/MathJax_Calligraphic-Bold.woff", "publicPath": "/static/fonts/MathJax_Calligraphic-Bold.woff" }, "fonts/MathJax_Calligraphic-Regular.woff": { "name": "fonts/MathJax_Calligraphic-Regular.woff", "path": "/var/lib/jenkins/workspace/DWAPPS/pypi-upload/static/fonts/MathJax_Calligraphic-Regular.woff", "publicPath": "/static/fonts/MathJax_Calligraphic-Regular.woff" }, "fonts/MathJax_Fraktur-Bold.woff": { "name": "fonts/MathJax_Fraktur-Bold.woff", "path": "/var/lib/jenkins/workspace/DWAPPS/pypi-upload/static/fonts/MathJax_Fraktur-Bold.woff", "publicPath": "/static/fonts/MathJax_Fraktur-Bold.woff" }, "fonts/MathJax_Fraktur-Regular.woff": { "name": "fonts/MathJax_Fraktur-Regular.woff", "path": "/var/lib/jenkins/workspace/DWAPPS/pypi-upload/static/fonts/MathJax_Fraktur-Regular.woff", "publicPath": "/static/fonts/MathJax_Fraktur-Regular.woff" }, "fonts/MathJax_Main-Bold.woff": { "name": "fonts/MathJax_Main-Bold.woff", "path": "/var/lib/jenkins/workspace/DWAPPS/pypi-upload/static/fonts/MathJax_Main-Bold.woff", "publicPath": "/static/fonts/MathJax_Main-Bold.woff" }, "fonts/MathJax_Main-Italic.woff": { "name": "fonts/MathJax_Main-Italic.woff", "path": "/var/lib/jenkins/workspace/DWAPPS/pypi-upload/static/fonts/MathJax_Main-Italic.woff", "publicPath": "/static/fonts/MathJax_Main-Italic.woff" }, "fonts/MathJax_Main-Regular.woff": { "name": "fonts/MathJax_Main-Regular.woff", "path": "/var/lib/jenkins/workspace/DWAPPS/pypi-upload/static/fonts/MathJax_Main-Regular.woff", "publicPath": "/static/fonts/MathJax_Main-Regular.woff" }, "fonts/MathJax_Math-BoldItalic.woff": { "name": "fonts/MathJax_Math-BoldItalic.woff", "path": "/var/lib/jenkins/workspace/DWAPPS/pypi-upload/static/fonts/MathJax_Math-BoldItalic.woff", "publicPath": "/static/fonts/MathJax_Math-BoldItalic.woff" }, "fonts/MathJax_Math-Italic.woff": { "name": "fonts/MathJax_Math-Italic.woff", "path": "/var/lib/jenkins/workspace/DWAPPS/pypi-upload/static/fonts/MathJax_Math-Italic.woff", "publicPath": "/static/fonts/MathJax_Math-Italic.woff" }, "fonts/MathJax_Math-Regular.woff": { "name": "fonts/MathJax_Math-Regular.woff", "path": "/var/lib/jenkins/workspace/DWAPPS/pypi-upload/static/fonts/MathJax_Math-Regular.woff", "publicPath": "/static/fonts/MathJax_Math-Regular.woff" }, "fonts/MathJax_SansSerif-Bold.woff": { "name": "fonts/MathJax_SansSerif-Bold.woff", "path": "/var/lib/jenkins/workspace/DWAPPS/pypi-upload/static/fonts/MathJax_SansSerif-Bold.woff", "publicPath": "/static/fonts/MathJax_SansSerif-Bold.woff" }, "fonts/MathJax_SansSerif-Italic.woff": { "name": "fonts/MathJax_SansSerif-Italic.woff", "path": "/var/lib/jenkins/workspace/DWAPPS/pypi-upload/static/fonts/MathJax_SansSerif-Italic.woff", "publicPath": "/static/fonts/MathJax_SansSerif-Italic.woff" }, "fonts/MathJax_SansSerif-Regular.woff": { "name": "fonts/MathJax_SansSerif-Regular.woff", "path": "/var/lib/jenkins/workspace/DWAPPS/pypi-upload/static/fonts/MathJax_SansSerif-Regular.woff", "publicPath": "/static/fonts/MathJax_SansSerif-Regular.woff" }, "fonts/MathJax_Script-Regular.woff": { "name": "fonts/MathJax_Script-Regular.woff", "path": "/var/lib/jenkins/workspace/DWAPPS/pypi-upload/static/fonts/MathJax_Script-Regular.woff", "publicPath": "/static/fonts/MathJax_Script-Regular.woff" }, "fonts/MathJax_Size1-Regular.woff": { "name": "fonts/MathJax_Size1-Regular.woff", "path": "/var/lib/jenkins/workspace/DWAPPS/pypi-upload/static/fonts/MathJax_Size1-Regular.woff", "publicPath": "/static/fonts/MathJax_Size1-Regular.woff" }, "fonts/MathJax_Size2-Regular.woff": { "name": "fonts/MathJax_Size2-Regular.woff", "path": "/var/lib/jenkins/workspace/DWAPPS/pypi-upload/static/fonts/MathJax_Size2-Regular.woff", "publicPath": "/static/fonts/MathJax_Size2-Regular.woff" }, "fonts/MathJax_Size3-Regular.woff": { "name": "fonts/MathJax_Size3-Regular.woff", "path": "/var/lib/jenkins/workspace/DWAPPS/pypi-upload/static/fonts/MathJax_Size3-Regular.woff", "publicPath": "/static/fonts/MathJax_Size3-Regular.woff" }, "fonts/MathJax_Size4-Regular.woff": { "name": "fonts/MathJax_Size4-Regular.woff", "path": "/var/lib/jenkins/workspace/DWAPPS/pypi-upload/static/fonts/MathJax_Size4-Regular.woff", "publicPath": "/static/fonts/MathJax_Size4-Regular.woff" }, "fonts/MathJax_Typewriter-Regular.woff": { "name": "fonts/MathJax_Typewriter-Regular.woff", "path": "/var/lib/jenkins/workspace/DWAPPS/pypi-upload/static/fonts/MathJax_Typewriter-Regular.woff", "publicPath": "/static/fonts/MathJax_Typewriter-Regular.woff" }, "fonts/MathJax_Vector-Bold.woff": { "name": "fonts/MathJax_Vector-Bold.woff", "path": "/var/lib/jenkins/workspace/DWAPPS/pypi-upload/static/fonts/MathJax_Vector-Bold.woff", "publicPath": "/static/fonts/MathJax_Vector-Bold.woff" }, "fonts/MathJax_Vector-Regular.woff": { "name": "fonts/MathJax_Vector-Regular.woff", "path": "/var/lib/jenkins/workspace/DWAPPS/pypi-upload/static/fonts/MathJax_Vector-Regular.woff", "publicPath": "/static/fonts/MathJax_Vector-Regular.woff" }, "fonts/MathJax_Zero.woff": { "name": "fonts/MathJax_Zero.woff", "path": "/var/lib/jenkins/workspace/DWAPPS/pypi-upload/static/fonts/MathJax_Zero.woff", "publicPath": "/static/fonts/MathJax_Zero.woff" }, - "js/pdf.worker.min.js": { - "name": "js/pdf.worker.min.js", - "path": "/var/lib/jenkins/workspace/DWAPPS/pypi-upload/static/js/pdf.worker.min.js", - "publicPath": "/static/js/pdf.worker.min.js" - }, "robots.txt": { "name": "robots.txt", "path": "/var/lib/jenkins/workspace/DWAPPS/pypi-upload/static/robots.txt", "publicPath": "/static/robots.txt" }, "js/pdf.worker.min.js.LICENSE.txt": { "name": "js/pdf.worker.min.js.LICENSE.txt", "path": "/var/lib/jenkins/workspace/DWAPPS/pypi-upload/static/js/pdf.worker.min.js.LICENSE.txt", "publicPath": "/static/js/pdf.worker.min.js.LICENSE.txt" }, "js/add_forge.bcbbc1be5ab34eede65c.js.LICENSE.txt": { "name": "js/add_forge.bcbbc1be5ab34eede65c.js.LICENSE.txt", "path": "/var/lib/jenkins/workspace/DWAPPS/pypi-upload/static/js/add_forge.bcbbc1be5ab34eede65c.js.LICENSE.txt", "publicPath": "/static/js/add_forge.bcbbc1be5ab34eede65c.js.LICENSE.txt" }, "js/admin.e1bdd6f126d315dc57e6.js.LICENSE.txt": { "name": "js/admin.e1bdd6f126d315dc57e6.js.LICENSE.txt", "path": "/var/lib/jenkins/workspace/DWAPPS/pypi-upload/static/js/admin.e1bdd6f126d315dc57e6.js.LICENSE.txt", "publicPath": "/static/js/admin.e1bdd6f126d315dc57e6.js.LICENSE.txt" }, "js/auth.7a6ba4612115126f9b59.js.LICENSE.txt": { "name": "js/auth.7a6ba4612115126f9b59.js.LICENSE.txt", "path": "/var/lib/jenkins/workspace/DWAPPS/pypi-upload/static/js/auth.7a6ba4612115126f9b59.js.LICENSE.txt", "publicPath": "/static/js/auth.7a6ba4612115126f9b59.js.LICENSE.txt" }, "js/browse.89ce9f7581cb4a43fa54.js.LICENSE.txt": { "name": "js/browse.89ce9f7581cb4a43fa54.js.LICENSE.txt", "path": "/var/lib/jenkins/workspace/DWAPPS/pypi-upload/static/js/browse.89ce9f7581cb4a43fa54.js.LICENSE.txt", "publicPath": "/static/js/browse.89ce9f7581cb4a43fa54.js.LICENSE.txt" }, "js/guided_tour.6fdb762b8416936b3c84.js.LICENSE.txt": { "name": "js/guided_tour.6fdb762b8416936b3c84.js.LICENSE.txt", "path": "/var/lib/jenkins/workspace/DWAPPS/pypi-upload/static/js/guided_tour.6fdb762b8416936b3c84.js.LICENSE.txt", "publicPath": "/static/js/guided_tour.6fdb762b8416936b3c84.js.LICENSE.txt" }, "js/highlightjs.ccd71c93a63af0249728.js.LICENSE.txt": { "name": "js/highlightjs.ccd71c93a63af0249728.js.LICENSE.txt", "path": "/var/lib/jenkins/workspace/DWAPPS/pypi-upload/static/js/highlightjs.ccd71c93a63af0249728.js.LICENSE.txt", "publicPath": "/static/js/highlightjs.ccd71c93a63af0249728.js.LICENSE.txt" }, "js/revision.530bd3980955c9cce3fb.js.LICENSE.txt": { "name": "js/revision.530bd3980955c9cce3fb.js.LICENSE.txt", "path": "/var/lib/jenkins/workspace/DWAPPS/pypi-upload/static/js/revision.530bd3980955c9cce3fb.js.LICENSE.txt", "publicPath": "/static/js/revision.530bd3980955c9cce3fb.js.LICENSE.txt" }, "js/save.78a0e5b182ba60ef8cba.js.LICENSE.txt": { "name": "js/save.78a0e5b182ba60ef8cba.js.LICENSE.txt", "path": "/var/lib/jenkins/workspace/DWAPPS/pypi-upload/static/js/save.78a0e5b182ba60ef8cba.js.LICENSE.txt", "publicPath": "/static/js/save.78a0e5b182ba60ef8cba.js.LICENSE.txt" }, "js/showdown.b448e42a34c43d6a7de5.js.LICENSE.txt": { "name": "js/showdown.b448e42a34c43d6a7de5.js.LICENSE.txt", "path": "/var/lib/jenkins/workspace/DWAPPS/pypi-upload/static/js/showdown.b448e42a34c43d6a7de5.js.LICENSE.txt", "publicPath": "/static/js/showdown.b448e42a34c43d6a7de5.js.LICENSE.txt" }, "js/vault.e2e8f042b9c742390872.js.LICENSE.txt": { "name": "js/vault.e2e8f042b9c742390872.js.LICENSE.txt", "path": "/var/lib/jenkins/workspace/DWAPPS/pypi-upload/static/js/vault.e2e8f042b9c742390872.js.LICENSE.txt", "publicPath": "/static/js/vault.e2e8f042b9c742390872.js.LICENSE.txt" }, "js/vendors.4d86327911239b43ce67.js.LICENSE.txt": { "name": "js/vendors.4d86327911239b43ce67.js.LICENSE.txt", "path": "/var/lib/jenkins/workspace/DWAPPS/pypi-upload/static/js/vendors.4d86327911239b43ce67.js.LICENSE.txt", "publicPath": "/static/js/vendors.4d86327911239b43ce67.js.LICENSE.txt" }, "js/webapp.ec1d7512d2628f39df9b.js.LICENSE.txt": { "name": "js/webapp.ec1d7512d2628f39df9b.js.LICENSE.txt", "path": "/var/lib/jenkins/workspace/DWAPPS/pypi-upload/static/js/webapp.ec1d7512d2628f39df9b.js.LICENSE.txt", "publicPath": "/static/js/webapp.ec1d7512d2628f39df9b.js.LICENSE.txt" }, "css/add_forge.737aa359fb467b5670bc.css": { "name": "css/add_forge.737aa359fb467b5670bc.css", "path": "/var/lib/jenkins/workspace/DWAPPS/pypi-upload/static/css/add_forge.737aa359fb467b5670bc.css", "publicPath": "/static/css/add_forge.737aa359fb467b5670bc.css" }, "js/add_forge.bcbbc1be5ab34eede65c.js": { "name": "js/add_forge.bcbbc1be5ab34eede65c.js", "path": "/var/lib/jenkins/workspace/DWAPPS/pypi-upload/static/js/add_forge.bcbbc1be5ab34eede65c.js", "publicPath": "/static/js/add_forge.bcbbc1be5ab34eede65c.js" }, "js/admin.e1bdd6f126d315dc57e6.js": { "name": "js/admin.e1bdd6f126d315dc57e6.js", "path": "/var/lib/jenkins/workspace/DWAPPS/pypi-upload/static/js/admin.e1bdd6f126d315dc57e6.js", "publicPath": "/static/js/admin.e1bdd6f126d315dc57e6.js" }, "css/auth.0336a94c2c02b4b2a4f4.css": { "name": "css/auth.0336a94c2c02b4b2a4f4.css", "path": "/var/lib/jenkins/workspace/DWAPPS/pypi-upload/static/css/auth.0336a94c2c02b4b2a4f4.css", "publicPath": "/static/css/auth.0336a94c2c02b4b2a4f4.css" }, "js/auth.7a6ba4612115126f9b59.js": { "name": "js/auth.7a6ba4612115126f9b59.js", "path": "/var/lib/jenkins/workspace/DWAPPS/pypi-upload/static/js/auth.7a6ba4612115126f9b59.js", "publicPath": "/static/js/auth.7a6ba4612115126f9b59.js" }, "css/browse.6315ef52ed73df532bed.css": { "name": "css/browse.6315ef52ed73df532bed.css", "path": "/var/lib/jenkins/workspace/DWAPPS/pypi-upload/static/css/browse.6315ef52ed73df532bed.css", "publicPath": "/static/css/browse.6315ef52ed73df532bed.css" }, "js/browse.89ce9f7581cb4a43fa54.js": { "name": "js/browse.89ce9f7581cb4a43fa54.js", "path": "/var/lib/jenkins/workspace/DWAPPS/pypi-upload/static/js/browse.89ce9f7581cb4a43fa54.js", "publicPath": "/static/js/browse.89ce9f7581cb4a43fa54.js" }, "css/guided_tour.00d493abd76b55df7e25.css": { "name": "css/guided_tour.00d493abd76b55df7e25.css", "path": "/var/lib/jenkins/workspace/DWAPPS/pypi-upload/static/css/guided_tour.00d493abd76b55df7e25.css", "publicPath": "/static/css/guided_tour.00d493abd76b55df7e25.css" }, "js/guided_tour.6fdb762b8416936b3c84.js": { "name": "js/guided_tour.6fdb762b8416936b3c84.js", "path": "/var/lib/jenkins/workspace/DWAPPS/pypi-upload/static/js/guided_tour.6fdb762b8416936b3c84.js", "publicPath": "/static/js/guided_tour.6fdb762b8416936b3c84.js" }, "css/origin.5b45e9e6e54fd51ee886.css": { "name": "css/origin.5b45e9e6e54fd51ee886.css", "path": "/var/lib/jenkins/workspace/DWAPPS/pypi-upload/static/css/origin.5b45e9e6e54fd51ee886.css", "publicPath": "/static/css/origin.5b45e9e6e54fd51ee886.css" }, "js/origin.751694c0f651d0e204de.js": { "name": "js/origin.751694c0f651d0e204de.js", "path": "/var/lib/jenkins/workspace/DWAPPS/pypi-upload/static/js/origin.751694c0f651d0e204de.js", "publicPath": "/static/js/origin.751694c0f651d0e204de.js" }, "css/revision.5ddd36d69e1760bfa29d.css": { "name": "css/revision.5ddd36d69e1760bfa29d.css", "path": "/var/lib/jenkins/workspace/DWAPPS/pypi-upload/static/css/revision.5ddd36d69e1760bfa29d.css", "publicPath": "/static/css/revision.5ddd36d69e1760bfa29d.css" }, "js/revision.530bd3980955c9cce3fb.js": { "name": "js/revision.530bd3980955c9cce3fb.js", "path": "/var/lib/jenkins/workspace/DWAPPS/pypi-upload/static/js/revision.530bd3980955c9cce3fb.js", "publicPath": "/static/js/revision.530bd3980955c9cce3fb.js" }, "js/save.78a0e5b182ba60ef8cba.js": { "name": "js/save.78a0e5b182ba60ef8cba.js", "path": "/var/lib/jenkins/workspace/DWAPPS/pypi-upload/static/js/save.78a0e5b182ba60ef8cba.js", "publicPath": "/static/js/save.78a0e5b182ba60ef8cba.js" }, "css/vault.25fc5883f848b48ffa5b.css": { "name": "css/vault.25fc5883f848b48ffa5b.css", "path": "/var/lib/jenkins/workspace/DWAPPS/pypi-upload/static/css/vault.25fc5883f848b48ffa5b.css", "publicPath": "/static/css/vault.25fc5883f848b48ffa5b.css" }, "js/vault.e2e8f042b9c742390872.js": { "name": "js/vault.e2e8f042b9c742390872.js", "path": "/var/lib/jenkins/workspace/DWAPPS/pypi-upload/static/js/vault.e2e8f042b9c742390872.js", "publicPath": "/static/js/vault.e2e8f042b9c742390872.js" }, "css/vendors.e9b3239bd4aa43cec2c2.css": { "name": "css/vendors.e9b3239bd4aa43cec2c2.css", "path": "/var/lib/jenkins/workspace/DWAPPS/pypi-upload/static/css/vendors.e9b3239bd4aa43cec2c2.css", "publicPath": "/static/css/vendors.e9b3239bd4aa43cec2c2.css" }, "js/vendors.4d86327911239b43ce67.js": { "name": "js/vendors.4d86327911239b43ce67.js", "path": "/var/lib/jenkins/workspace/DWAPPS/pypi-upload/static/js/vendors.4d86327911239b43ce67.js", "publicPath": "/static/js/vendors.4d86327911239b43ce67.js" }, "css/webapp.dd439fd586ffe21d20dc.css": { "name": "css/webapp.dd439fd586ffe21d20dc.css", "path": "/var/lib/jenkins/workspace/DWAPPS/pypi-upload/static/css/webapp.dd439fd586ffe21d20dc.css", "publicPath": "/static/css/webapp.dd439fd586ffe21d20dc.css" }, "js/webapp.ec1d7512d2628f39df9b.js": { "name": "js/webapp.ec1d7512d2628f39df9b.js", "path": "/var/lib/jenkins/workspace/DWAPPS/pypi-upload/static/js/webapp.ec1d7512d2628f39df9b.js", "publicPath": "/static/js/webapp.ec1d7512d2628f39df9b.js" }, "js/d3.f989183a810fb9dbb086.js": { "name": "js/d3.f989183a810fb9dbb086.js", "path": "/var/lib/jenkins/workspace/DWAPPS/pypi-upload/static/js/d3.f989183a810fb9dbb086.js", "publicPath": "/static/js/d3.f989183a810fb9dbb086.js" }, "css/highlightjs.ae43064ab38a65a04d81.css": { "name": "css/highlightjs.ae43064ab38a65a04d81.css", "path": "/var/lib/jenkins/workspace/DWAPPS/pypi-upload/static/css/highlightjs.ae43064ab38a65a04d81.css", "publicPath": "/static/css/highlightjs.ae43064ab38a65a04d81.css" }, "js/highlightjs.ccd71c93a63af0249728.js": { "name": "js/highlightjs.ccd71c93a63af0249728.js", "path": "/var/lib/jenkins/workspace/DWAPPS/pypi-upload/static/js/highlightjs.ccd71c93a63af0249728.js", "publicPath": "/static/js/highlightjs.ccd71c93a63af0249728.js" }, "css/showdown.426fbf6a7a6653fd4cbb.css": { "name": "css/showdown.426fbf6a7a6653fd4cbb.css", "path": "/var/lib/jenkins/workspace/DWAPPS/pypi-upload/static/css/showdown.426fbf6a7a6653fd4cbb.css", "publicPath": "/static/css/showdown.426fbf6a7a6653fd4cbb.css" }, "js/showdown.b448e42a34c43d6a7de5.js": { "name": "js/showdown.b448e42a34c43d6a7de5.js", "path": "/var/lib/jenkins/workspace/DWAPPS/pypi-upload/static/js/showdown.b448e42a34c43d6a7de5.js", "publicPath": "/static/js/showdown.b448e42a34c43d6a7de5.js" }, "css/org.6851b70c924e28f6bf51.css": { "name": "css/org.6851b70c924e28f6bf51.css", "path": "/var/lib/jenkins/workspace/DWAPPS/pypi-upload/static/css/org.6851b70c924e28f6bf51.css", "publicPath": "/static/css/org.6851b70c924e28f6bf51.css" }, "js/org.9adc9a39a210c2fb4be0.js": { "name": "js/org.9adc9a39a210c2fb4be0.js", "path": "/var/lib/jenkins/workspace/DWAPPS/pypi-upload/static/js/org.9adc9a39a210c2fb4be0.js", "publicPath": "/static/js/org.9adc9a39a210c2fb4be0.js" }, "js/pdfjs.7fa499e22fea56527058.js": { "name": "js/pdfjs.7fa499e22fea56527058.js", "path": "/var/lib/jenkins/workspace/DWAPPS/pypi-upload/static/js/pdfjs.7fa499e22fea56527058.js", "publicPath": "/static/js/pdfjs.7fa499e22fea56527058.js" }, "js/mathjax.a0046af7afe82fd10728.js": { "name": "js/mathjax.a0046af7afe82fd10728.js", "path": "/var/lib/jenkins/workspace/DWAPPS/pypi-upload/static/js/mathjax.a0046af7afe82fd10728.js", "publicPath": "/static/js/mathjax.a0046af7afe82fd10728.js" }, "css/add_forge.737aa359fb467b5670bc.css.map": { "name": "css/add_forge.737aa359fb467b5670bc.css.map", "path": "/var/lib/jenkins/workspace/DWAPPS/pypi-upload/static/css/add_forge.737aa359fb467b5670bc.css.map", "publicPath": "/static/css/add_forge.737aa359fb467b5670bc.css.map" }, + "css/auth.0336a94c2c02b4b2a4f4.css.map": { + "name": "css/auth.0336a94c2c02b4b2a4f4.css.map", + "path": "/var/lib/jenkins/workspace/DWAPPS/pypi-upload/static/css/auth.0336a94c2c02b4b2a4f4.css.map", + "publicPath": "/static/css/auth.0336a94c2c02b4b2a4f4.css.map" + }, "css/guided_tour.00d493abd76b55df7e25.css.map": { "name": "css/guided_tour.00d493abd76b55df7e25.css.map", "path": "/var/lib/jenkins/workspace/DWAPPS/pypi-upload/static/css/guided_tour.00d493abd76b55df7e25.css.map", "publicPath": "/static/css/guided_tour.00d493abd76b55df7e25.css.map" }, "css/origin.5b45e9e6e54fd51ee886.css.map": { "name": "css/origin.5b45e9e6e54fd51ee886.css.map", "path": "/var/lib/jenkins/workspace/DWAPPS/pypi-upload/static/css/origin.5b45e9e6e54fd51ee886.css.map", "publicPath": "/static/css/origin.5b45e9e6e54fd51ee886.css.map" }, + "css/browse.6315ef52ed73df532bed.css.map": { + "name": "css/browse.6315ef52ed73df532bed.css.map", + "path": "/var/lib/jenkins/workspace/DWAPPS/pypi-upload/static/css/browse.6315ef52ed73df532bed.css.map", + "publicPath": "/static/css/browse.6315ef52ed73df532bed.css.map" + }, "css/revision.5ddd36d69e1760bfa29d.css.map": { "name": "css/revision.5ddd36d69e1760bfa29d.css.map", "path": "/var/lib/jenkins/workspace/DWAPPS/pypi-upload/static/css/revision.5ddd36d69e1760bfa29d.css.map", "publicPath": "/static/css/revision.5ddd36d69e1760bfa29d.css.map" }, "css/vault.25fc5883f848b48ffa5b.css.map": { "name": "css/vault.25fc5883f848b48ffa5b.css.map", "path": "/var/lib/jenkins/workspace/DWAPPS/pypi-upload/static/css/vault.25fc5883f848b48ffa5b.css.map", "publicPath": "/static/css/vault.25fc5883f848b48ffa5b.css.map" }, - "css/auth.0336a94c2c02b4b2a4f4.css.map": { - "name": "css/auth.0336a94c2c02b4b2a4f4.css.map", - "path": "/var/lib/jenkins/workspace/DWAPPS/pypi-upload/static/css/auth.0336a94c2c02b4b2a4f4.css.map", - "publicPath": "/static/css/auth.0336a94c2c02b4b2a4f4.css.map" - }, "css/vendors.e9b3239bd4aa43cec2c2.css.map": { "name": "css/vendors.e9b3239bd4aa43cec2c2.css.map", "path": "/var/lib/jenkins/workspace/DWAPPS/pypi-upload/static/css/vendors.e9b3239bd4aa43cec2c2.css.map", "publicPath": "/static/css/vendors.e9b3239bd4aa43cec2c2.css.map" }, - "css/browse.6315ef52ed73df532bed.css.map": { - "name": "css/browse.6315ef52ed73df532bed.css.map", - "path": "/var/lib/jenkins/workspace/DWAPPS/pypi-upload/static/css/browse.6315ef52ed73df532bed.css.map", - "publicPath": "/static/css/browse.6315ef52ed73df532bed.css.map" - }, "css/webapp.dd439fd586ffe21d20dc.css.map": { "name": "css/webapp.dd439fd586ffe21d20dc.css.map", "path": "/var/lib/jenkins/workspace/DWAPPS/pypi-upload/static/css/webapp.dd439fd586ffe21d20dc.css.map", "publicPath": "/static/css/webapp.dd439fd586ffe21d20dc.css.map" }, "css/highlightjs.ae43064ab38a65a04d81.css.map": { "name": "css/highlightjs.ae43064ab38a65a04d81.css.map", "path": "/var/lib/jenkins/workspace/DWAPPS/pypi-upload/static/css/highlightjs.ae43064ab38a65a04d81.css.map", "publicPath": "/static/css/highlightjs.ae43064ab38a65a04d81.css.map" }, "css/showdown.426fbf6a7a6653fd4cbb.css.map": { "name": "css/showdown.426fbf6a7a6653fd4cbb.css.map", "path": "/var/lib/jenkins/workspace/DWAPPS/pypi-upload/static/css/showdown.426fbf6a7a6653fd4cbb.css.map", "publicPath": "/static/css/showdown.426fbf6a7a6653fd4cbb.css.map" }, "css/org.6851b70c924e28f6bf51.css.map": { "name": "css/org.6851b70c924e28f6bf51.css.map", "path": "/var/lib/jenkins/workspace/DWAPPS/pypi-upload/static/css/org.6851b70c924e28f6bf51.css.map", "publicPath": "/static/css/org.6851b70c924e28f6bf51.css.map" }, "js/add_forge.bcbbc1be5ab34eede65c.js.map": { "name": "js/add_forge.bcbbc1be5ab34eede65c.js.map", "path": "/var/lib/jenkins/workspace/DWAPPS/pypi-upload/static/js/add_forge.bcbbc1be5ab34eede65c.js.map", "publicPath": "/static/js/add_forge.bcbbc1be5ab34eede65c.js.map" }, "js/admin.e1bdd6f126d315dc57e6.js.map": { "name": "js/admin.e1bdd6f126d315dc57e6.js.map", "path": "/var/lib/jenkins/workspace/DWAPPS/pypi-upload/static/js/admin.e1bdd6f126d315dc57e6.js.map", "publicPath": "/static/js/admin.e1bdd6f126d315dc57e6.js.map" }, "js/auth.7a6ba4612115126f9b59.js.map": { "name": "js/auth.7a6ba4612115126f9b59.js.map", "path": "/var/lib/jenkins/workspace/DWAPPS/pypi-upload/static/js/auth.7a6ba4612115126f9b59.js.map", "publicPath": "/static/js/auth.7a6ba4612115126f9b59.js.map" }, "js/browse.89ce9f7581cb4a43fa54.js.map": { "name": "js/browse.89ce9f7581cb4a43fa54.js.map", "path": "/var/lib/jenkins/workspace/DWAPPS/pypi-upload/static/js/browse.89ce9f7581cb4a43fa54.js.map", "publicPath": "/static/js/browse.89ce9f7581cb4a43fa54.js.map" }, "js/guided_tour.6fdb762b8416936b3c84.js.map": { "name": "js/guided_tour.6fdb762b8416936b3c84.js.map", "path": "/var/lib/jenkins/workspace/DWAPPS/pypi-upload/static/js/guided_tour.6fdb762b8416936b3c84.js.map", "publicPath": "/static/js/guided_tour.6fdb762b8416936b3c84.js.map" }, "js/origin.751694c0f651d0e204de.js.map": { "name": "js/origin.751694c0f651d0e204de.js.map", "path": "/var/lib/jenkins/workspace/DWAPPS/pypi-upload/static/js/origin.751694c0f651d0e204de.js.map", "publicPath": "/static/js/origin.751694c0f651d0e204de.js.map" }, "js/revision.530bd3980955c9cce3fb.js.map": { "name": "js/revision.530bd3980955c9cce3fb.js.map", "path": "/var/lib/jenkins/workspace/DWAPPS/pypi-upload/static/js/revision.530bd3980955c9cce3fb.js.map", "publicPath": "/static/js/revision.530bd3980955c9cce3fb.js.map" }, "js/save.78a0e5b182ba60ef8cba.js.map": { "name": "js/save.78a0e5b182ba60ef8cba.js.map", "path": "/var/lib/jenkins/workspace/DWAPPS/pypi-upload/static/js/save.78a0e5b182ba60ef8cba.js.map", "publicPath": "/static/js/save.78a0e5b182ba60ef8cba.js.map" }, "js/vault.e2e8f042b9c742390872.js.map": { "name": "js/vault.e2e8f042b9c742390872.js.map", "path": "/var/lib/jenkins/workspace/DWAPPS/pypi-upload/static/js/vault.e2e8f042b9c742390872.js.map", "publicPath": "/static/js/vault.e2e8f042b9c742390872.js.map" }, "js/vendors.4d86327911239b43ce67.js.map": { "name": "js/vendors.4d86327911239b43ce67.js.map", "path": "/var/lib/jenkins/workspace/DWAPPS/pypi-upload/static/js/vendors.4d86327911239b43ce67.js.map", "publicPath": "/static/js/vendors.4d86327911239b43ce67.js.map" }, "js/webapp.ec1d7512d2628f39df9b.js.map": { "name": "js/webapp.ec1d7512d2628f39df9b.js.map", "path": "/var/lib/jenkins/workspace/DWAPPS/pypi-upload/static/js/webapp.ec1d7512d2628f39df9b.js.map", "publicPath": "/static/js/webapp.ec1d7512d2628f39df9b.js.map" }, "js/d3.f989183a810fb9dbb086.js.map": { "name": "js/d3.f989183a810fb9dbb086.js.map", "path": "/var/lib/jenkins/workspace/DWAPPS/pypi-upload/static/js/d3.f989183a810fb9dbb086.js.map", "publicPath": "/static/js/d3.f989183a810fb9dbb086.js.map" }, "js/highlightjs.ccd71c93a63af0249728.js.map": { "name": "js/highlightjs.ccd71c93a63af0249728.js.map", "path": "/var/lib/jenkins/workspace/DWAPPS/pypi-upload/static/js/highlightjs.ccd71c93a63af0249728.js.map", "publicPath": "/static/js/highlightjs.ccd71c93a63af0249728.js.map" }, "js/showdown.b448e42a34c43d6a7de5.js.map": { "name": "js/showdown.b448e42a34c43d6a7de5.js.map", "path": "/var/lib/jenkins/workspace/DWAPPS/pypi-upload/static/js/showdown.b448e42a34c43d6a7de5.js.map", "publicPath": "/static/js/showdown.b448e42a34c43d6a7de5.js.map" }, "js/org.9adc9a39a210c2fb4be0.js.map": { "name": "js/org.9adc9a39a210c2fb4be0.js.map", "path": "/var/lib/jenkins/workspace/DWAPPS/pypi-upload/static/js/org.9adc9a39a210c2fb4be0.js.map", "publicPath": "/static/js/org.9adc9a39a210c2fb4be0.js.map" }, "js/pdfjs.7fa499e22fea56527058.js.map": { "name": "js/pdfjs.7fa499e22fea56527058.js.map", "path": "/var/lib/jenkins/workspace/DWAPPS/pypi-upload/static/js/pdfjs.7fa499e22fea56527058.js.map", "publicPath": "/static/js/pdfjs.7fa499e22fea56527058.js.map" }, "js/mathjax.a0046af7afe82fd10728.js.map": { "name": "js/mathjax.a0046af7afe82fd10728.js.map", "path": "/var/lib/jenkins/workspace/DWAPPS/pypi-upload/static/js/mathjax.a0046af7afe82fd10728.js.map", "publicPath": "/static/js/mathjax.a0046af7afe82fd10728.js.map" } }, "chunks": { "add_forge": [ "css/add_forge.737aa359fb467b5670bc.css", "js/add_forge.bcbbc1be5ab34eede65c.js" ], "admin": [ "js/admin.e1bdd6f126d315dc57e6.js" ], "auth": [ "css/auth.0336a94c2c02b4b2a4f4.css", "js/auth.7a6ba4612115126f9b59.js" ], "browse": [ "css/browse.6315ef52ed73df532bed.css", "js/browse.89ce9f7581cb4a43fa54.js" ], "guided_tour": [ "css/guided_tour.00d493abd76b55df7e25.css", "js/guided_tour.6fdb762b8416936b3c84.js" ], "origin": [ "css/origin.5b45e9e6e54fd51ee886.css", "js/origin.751694c0f651d0e204de.js" ], "revision": [ "css/revision.5ddd36d69e1760bfa29d.css", "js/revision.530bd3980955c9cce3fb.js" ], "save": [ "js/save.78a0e5b182ba60ef8cba.js" ], "vault": [ "css/vault.25fc5883f848b48ffa5b.css", "js/vault.e2e8f042b9c742390872.js" ], "vendors": [ "css/vendors.e9b3239bd4aa43cec2c2.css", "js/vendors.4d86327911239b43ce67.js" ], "webapp": [ "css/webapp.dd439fd586ffe21d20dc.css", "js/webapp.ec1d7512d2628f39df9b.js" ] }, "publicPath": "/static/" } \ No newline at end of file diff --git a/swh.web.egg-info/PKG-INFO b/swh.web.egg-info/PKG-INFO index 3beeb4c1..120a2158 100644 --- a/swh.web.egg-info/PKG-INFO +++ b/swh.web.egg-info/PKG-INFO @@ -1,206 +1,206 @@ Metadata-Version: 2.1 Name: swh.web -Version: 0.0.379 +Version: 0.0.380 Summary: Software Heritage Web UI Home-page: https://forge.softwareheritage.org/diffusion/DWUI/ Author: Software Heritage developers Author-email: swh-devel@inria.fr License: UNKNOWN Project-URL: Bug Reports, https://forge.softwareheritage.org/maniphest Project-URL: Funding, https://www.softwareheritage.org/donate Project-URL: Source, https://forge.softwareheritage.org/source/swh-web Project-URL: Documentation, https://docs.softwareheritage.org/devel/swh-web/ Platform: UNKNOWN Classifier: Programming Language :: Python :: 3 Classifier: Intended Audience :: Developers Classifier: License :: OSI Approved :: GNU Affero General Public License v3 or later (AGPLv3+) Classifier: Operating System :: OS Independent Classifier: Development Status :: 5 - Production/Stable Classifier: Framework :: Django Requires-Python: >=3.7 Description-Content-Type: text/markdown Provides-Extra: testing License-File: LICENSE License-File: AUTHORS # swh-web This repository holds the development of Software Heritage web applications: * swh-web API (https://archive.softwareheritage.org/api): enables to query the content of the archive through HTTP requests and get responses in JSON or YAML. * swh-web browse (https://archive.softwareheritage.org/browse): graphical interface that eases the navigation in the archive. Documentation about how to use these components but also the details of their URI schemes can be found in the docs folder. The produced HTML documentation can be read and browsed at https://docs.softwareheritage.org/devel/swh-web/index.html. ## Technical details Those applications are powered by: * [Django Web Framework](https://www.djangoproject.com/) on the backend side with the following extensions enabled: * [django-rest-framework](http://www.django-rest-framework.org/) * [django-webpack-loader](https://github.com/owais/django-webpack-loader) * [django-js-reverse](http://django-js-reverse.readthedocs.io/en/latest/) * [webpack](https://webpack.js.org/) on the frontend side for better static assets management, including: * assets dependencies management and retrieval through [yarn](https://yarnpkg.com/en/) * linting of custom javascript code (through [eslint](https://eslint.org/)) and stylesheets (through [stylelint](https://stylelint.io/)) * use of [es6](http://es6-features.org) syntax and advanced javascript feature like [async/await](https://javascript.info/async-await) or [fetch](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API) thanks to [babel](https://babeljs.io/) (es6 to es5 transpiler and polyfills provider) * assets minification (using [terser](https://github.com/terser-js/terser) and [cssnano](http://cssnano.co/)) but also dead code elimination for production use ## How to build, run and test ### Backend requirements First you will need [Python 3](https://www.python.org) and a complete [swh development environment](https://forge.softwareheritage.org/source/swh-environment/) installed. To run the backend, you need to have the following [Python 3 modules](requirements.txt) installed. To run the backend tests, the following [Python 3 modules](requirements-test.txt) are also required to be installed. One easy way to install them is to use the `pip` tool: ``` $ pip install -r requirements.txt -r requirements-test.txt ``` ### Frontend requirements To compile the frontend assets, you need to have [nodejs](https://nodejs.org/en/) >= 12.0.0 and [yarn](https://yarnpkg.com/en/) installed. If you are on Debian, you can easily install an up to date nodejs from the [nodesource](https://github.com/nodesource/distributions/blob/master/README.md) repository. Packages for yarn can be installed by following [these instructions](https://yarnpkg.com/en/docs/install#debian-stable). Alternatively, you can install yarn with `npm install yarn`, and add `YARN=node_modules/yarn/bin/yarn` as argument whenever you run `make`. Please note that the static assets bundles generated by webpack are not stored in the git repository. Follow the instructions below in order to generate them in order to be able to run the frontend part of the web applications. ### Make targets to execute the applications Below is the list of available make targets that can be executed from the root directory of swh-web in order to build and/or execute the web applications under various configurations: * **run-django-webpack-devserver**: Compile and serve not optimized (without mignification and dead code elimination) frontend static assets using [webpack-dev-server](https://github.com/webpack/webpack-dev-server) and run django server with development settings. This is the recommended target to use when developing swh-web as it enables automatic reloading of backend and frontend part of the applications when modifying source files (*.py, *.js, *.css, *.html). * **run-django-webpack-dev**: Compile not optimized (no minification, no dead code elimination) frontend static assets using webpack and run django server with development settings. This is the recommended target when one only wants to develop the backend side of the application. * **run-django-webpack-prod**: Compile optimized (with minification and dead code elimination) frontend static assets using webpack and run django server with production settings. This is useful to test the applications in production mode (with the difference that static assets are served by django). Production settings notably enable advanced django caching and you will need to have [memcached](https://memcached.org/) installed for that feature to work. * **run-django-server-dev**: Run the django server with development settings but without compiling frontend static assets through webpack. * **run-django-server-prod**: Run the django server with production settings but without compiling frontend static assets through webpack. * **run-gunicorn-server**: Run the web applications with production settings in a [gunicorn](http://gunicorn.org/) worker as they will be in real production environment. Once one of these targets executed, the web applications can be executed by pointing your browser to http://localhost:5004. ### Make targets to test the applications Some make targets are also available to easily execute the backend and frontend tests of the Software Heritage web applications. The backend tests are powered by the [pytest](https://docs.pytest.org/en/latest/) and [hypothesis](https://hypothesis.readthedocs.io/en/latest/) frameworks while the frontend ones rely on the use of the [cypress](https://www.cypress.io/) tool. Below is the exhaustive list of those targets: * **test**: execute the backend tests using a fast hypothesis profile (only one input example will be provided for each test) * **test-full**: execute the backend tests using a slower hypothesis profile (one hundred of input examples will be provided for each test which helps spotting possible bugs) * **test-frontend**: execute the frontend tests using cypress in headless mode but with some slow test suites disabled * **test-frontend-full**: execute the frontend tests using cypress in headless mode with all test suites enabled * **test-frontend-ui**: execute the frontend tests using the cypress GUI but with some slow test suites disabled * **test-frontend-full-ui**: execute the frontend tests using the cypress GUI with all test suites enabled ### Yarn targets Below is a list of available yarn targets in order to only execute the frontend static assets compilation (no web server will be executed): * **build-dev**: compile not optimized (without mignification and dead code elimination) frontend static assets and store the results in the `swh/web/static` folder. * **build**: compile optimized (with mignification and dead code elimination) frontend static assets and store the results in the `swh/web/static` folder. **The build target must be executed prior performing the Debian packaging of swh-web** in order for the package to contain the optimized assets dedicated to production environment. To execute these targets, issue the following command: ``` $ yarn ``` diff --git a/swh.web.egg-info/SOURCES.txt b/swh.web.egg-info/SOURCES.txt index 61d8c425..b66be8c1 100644 --- a/swh.web.egg-info/SOURCES.txt +++ b/swh.web.egg-info/SOURCES.txt @@ -1,2038 +1,2044 @@ .gitignore .pre-commit-config.yaml AUTHORS CODE_OF_CONDUCT.md CONTRIBUTORS LICENSE MANIFEST.in Makefile Makefile.local README.md conftest.py cypress.json mypy.ini package.json pyproject.toml pytest.ini requirements-swh.txt requirements-test.txt requirements.txt setup.cfg setup.py tox.ini yarn.lock assets/config/.bootstraprc assets/config/.eslintignore assets/config/.eslintrc assets/config/bootstrap-pre-customize.scss assets/config/webpack.config.development.js assets/config/webpack.config.production.js assets/config/webpack-plugins/dump-highlightjs-languages-data-plugin.js assets/config/webpack-plugins/fix-swh-source-maps-webpack-plugin.js assets/config/webpack-plugins/generate-weblabels-webpack-plugin/README.md assets/config/webpack-plugins/generate-weblabels-webpack-plugin/index.js assets/config/webpack-plugins/generate-weblabels-webpack-plugin/jslicenses.ejs assets/config/webpack-plugins/generate-weblabels-webpack-plugin/plugin-options-schema.json assets/config/webpack-plugins/generate-weblabels-webpack-plugin/spdx-licenses-mapping.js assets/src/bundles/add_forge/add-forge.css assets/src/bundles/add_forge/add-request-history-item.ejs assets/src/bundles/add_forge/create-request.js assets/src/bundles/add_forge/forge-admin-email.ejs assets/src/bundles/add_forge/index.js assets/src/bundles/add_forge/moderation-dashboard.js assets/src/bundles/add_forge/request-dashboard.js assets/src/bundles/admin/deposit.js assets/src/bundles/admin/index.js assets/src/bundles/admin/mailmap-form.ejs assets/src/bundles/admin/mailmap.js assets/src/bundles/admin/origin-save.js assets/src/bundles/auth/auth.css assets/src/bundles/auth/index.js assets/src/bundles/browse/breadcrumbs.css assets/src/bundles/browse/browse-utils.js assets/src/bundles/browse/browse.css assets/src/bundles/browse/content.css assets/src/bundles/browse/index.js assets/src/bundles/browse/origin-search.js assets/src/bundles/browse/snapshot-navigation.css assets/src/bundles/browse/snapshot-navigation.js assets/src/bundles/browse/swhid-utils.js assets/src/bundles/guided_tour/guided-tour-steps.yaml assets/src/bundles/guided_tour/index.js assets/src/bundles/guided_tour/swh-introjs.css assets/src/bundles/origin/index.js assets/src/bundles/origin/utils.js assets/src/bundles/origin/visits-calendar.js assets/src/bundles/origin/visits-histogram.js assets/src/bundles/origin/visits-reporting.css assets/src/bundles/origin/visits-reporting.js assets/src/bundles/revision/diff-panel.ejs assets/src/bundles/revision/diff-utils.js assets/src/bundles/revision/index.js assets/src/bundles/revision/log-utils.js assets/src/bundles/revision/revision.css assets/src/bundles/save/artifact-form-row.ejs assets/src/bundles/save/index.js assets/src/bundles/vault/index.js assets/src/bundles/vault/vault-create-tasks.js assets/src/bundles/vault/vault-table-row.ejs assets/src/bundles/vault/vault-ui.js assets/src/bundles/vault/vault.css assets/src/bundles/vendors/datatables.css assets/src/bundles/vendors/elementsfrompoint-polyfill.js assets/src/bundles/vendors/index.js assets/src/bundles/webapp/badges.js assets/src/bundles/webapp/breadcrumbs.css assets/src/bundles/webapp/code-highlighting.js assets/src/bundles/webapp/coverage.css assets/src/bundles/webapp/history-counters.css assets/src/bundles/webapp/history-counters.js assets/src/bundles/webapp/iframes.js assets/src/bundles/webapp/index.js assets/src/bundles/webapp/math-typesetting.js assets/src/bundles/webapp/notebook-rendering.js assets/src/bundles/webapp/notebook.css assets/src/bundles/webapp/pdf-rendering.js assets/src/bundles/webapp/readme-rendering.js assets/src/bundles/webapp/sentry.js assets/src/bundles/webapp/status-widget.css assets/src/bundles/webapp/status-widget.js assets/src/bundles/webapp/webapp-utils.js assets/src/bundles/webapp/webapp.css assets/src/bundles/webapp/xss-filtering.js assets/src/thirdparty/jquery.tabSlideOut/LICENSE assets/src/thirdparty/jquery.tabSlideOut/jquery.tabSlideOut.css assets/src/thirdparty/jquery.tabSlideOut/jquery.tabSlideOut.js assets/src/utils/constants.js assets/src/utils/d3.js assets/src/utils/functions.js assets/src/utils/highlightjs.css assets/src/utils/highlightjs.js assets/src/utils/hljs-swh-theme.css assets/src/utils/mathjax.js assets/src/utils/org.css assets/src/utils/org.js assets/src/utils/requests-filter-checkbox.ejs assets/src/utils/scrolling.js assets/src/utils/showdown.css assets/src/utils/showdown.js cypress/fixtures/add-forge-now-requests.json cypress/fixtures/example.json cypress/fixtures/origin-save.json cypress/fixtures/save-task-info.json cypress/fixtures/swh_1_dir_cd19126d815470b28919d64b2a8e6a3e37f900dd.tar.gz cypress/fixtures/swh_1_rev_1c480a4573d2a003fc2630c21c2b25829de49972.git.tar cypress/integration/add-forge-now-request-create.spec.js cypress/integration/add-forge-now-request-dashboard.spec.js cypress/integration/add-forge-now-requests-moderation.spec.js cypress/integration/admin.spec.js cypress/integration/api-tokens.spec.js cypress/integration/back-to-top.spec.js cypress/integration/code-highlighting.spec.js cypress/integration/content-display.spec.js cypress/integration/content-rendering.spec.js cypress/integration/deposit-admin.spec.js cypress/integration/directory.spec.js cypress/integration/errors.spec.js cypress/integration/guided-tour.spec.js cypress/integration/home.spec.js cypress/integration/language-select.spec.js cypress/integration/layout.spec.js cypress/integration/mailmap.spec.js cypress/integration/origin-browse.spec.js cypress/integration/origin-save.spec.js cypress/integration/origin-search.spec.js cypress/integration/origin-visits.spec.js cypress/integration/persistent-identifiers.spec.js cypress/integration/revision-diff.spec.js cypress/integration/sidebar.spec.js cypress/integration/vault.spec.js cypress/plugins/index.js cypress/support/index.js cypress/utils/index.js docs/.gitignore docs/Makefile docs/conf.py docs/developers-info.rst docs/index.rst docs/uri-scheme-api-content.rst docs/uri-scheme-api-directory.rst docs/uri-scheme-api-graph.rst docs/uri-scheme-api-identifiers.rst docs/uri-scheme-api-origin.rst docs/uri-scheme-api-release.rst docs/uri-scheme-api-revision.rst docs/uri-scheme-api-snapshot.rst docs/uri-scheme-api-stat.rst docs/uri-scheme-api-vault.rst docs/uri-scheme-api.rst docs/uri-scheme-browse-content.rst docs/uri-scheme-browse-directory.rst docs/uri-scheme-browse-origin.rst docs/uri-scheme-browse-release.rst docs/uri-scheme-browse-revision.rst docs/uri-scheme-browse-snapshot.rst docs/uri-scheme-browse.rst docs/uri-scheme-identifiers.rst docs/uri-scheme-misc.rst docs/_static/.placeholder docs/_templates/.placeholder static/robots.txt static/webpack-stats.json static/css/add_forge.737aa359fb467b5670bc.css static/css/add_forge.737aa359fb467b5670bc.css.map static/css/auth.0336a94c2c02b4b2a4f4.css static/css/auth.0336a94c2c02b4b2a4f4.css.map static/css/browse.6315ef52ed73df532bed.css static/css/browse.6315ef52ed73df532bed.css.map static/css/guided_tour.00d493abd76b55df7e25.css static/css/guided_tour.00d493abd76b55df7e25.css.map static/css/highlightjs.ae43064ab38a65a04d81.css static/css/highlightjs.ae43064ab38a65a04d81.css.map static/css/org.6851b70c924e28f6bf51.css static/css/org.6851b70c924e28f6bf51.css.map static/css/origin.5b45e9e6e54fd51ee886.css static/css/origin.5b45e9e6e54fd51ee886.css.map static/css/revision.5ddd36d69e1760bfa29d.css static/css/revision.5ddd36d69e1760bfa29d.css.map static/css/showdown.426fbf6a7a6653fd4cbb.css static/css/showdown.426fbf6a7a6653fd4cbb.css.map static/css/vault.25fc5883f848b48ffa5b.css static/css/vault.25fc5883f848b48ffa5b.css.map static/css/vendors.e9b3239bd4aa43cec2c2.css static/css/vendors.e9b3239bd4aa43cec2c2.css.map static/css/webapp.dd439fd586ffe21d20dc.css static/css/webapp.dd439fd586ffe21d20dc.css.map static/fonts/MathJax_AMS-Regular.woff static/fonts/MathJax_Calligraphic-Bold.woff static/fonts/MathJax_Calligraphic-Regular.woff static/fonts/MathJax_Fraktur-Bold.woff static/fonts/MathJax_Fraktur-Regular.woff static/fonts/MathJax_Main-Bold.woff static/fonts/MathJax_Main-Italic.woff static/fonts/MathJax_Main-Regular.woff static/fonts/MathJax_Math-BoldItalic.woff static/fonts/MathJax_Math-Italic.woff static/fonts/MathJax_Math-Regular.woff static/fonts/MathJax_SansSerif-Bold.woff static/fonts/MathJax_SansSerif-Italic.woff static/fonts/MathJax_SansSerif-Regular.woff static/fonts/MathJax_Script-Regular.woff static/fonts/MathJax_Size1-Regular.woff static/fonts/MathJax_Size2-Regular.woff static/fonts/MathJax_Size3-Regular.woff static/fonts/MathJax_Size4-Regular.woff static/fonts/MathJax_Typewriter-Regular.woff static/fonts/MathJax_Vector-Bold.woff static/fonts/MathJax_Vector-Regular.woff static/fonts/MathJax_Zero.woff static/fonts/alegreya-latin-400.woff static/fonts/alegreya-latin-400.woff2 static/fonts/alegreya-latin-400italic.woff static/fonts/alegreya-latin-400italic.woff2 static/fonts/alegreya-latin-500.woff static/fonts/alegreya-latin-500.woff2 static/fonts/alegreya-latin-500italic.woff static/fonts/alegreya-latin-500italic.woff2 static/fonts/alegreya-latin-700.woff static/fonts/alegreya-latin-700.woff2 static/fonts/alegreya-latin-700italic.woff static/fonts/alegreya-latin-700italic.woff2 static/fonts/alegreya-latin-800.woff static/fonts/alegreya-latin-800.woff2 static/fonts/alegreya-latin-800italic.woff static/fonts/alegreya-latin-800italic.woff2 static/fonts/alegreya-latin-900.woff static/fonts/alegreya-latin-900.woff2 static/fonts/alegreya-latin-900italic.woff static/fonts/alegreya-latin-900italic.woff2 static/fonts/alegreya-sans-latin-100.woff static/fonts/alegreya-sans-latin-100.woff2 static/fonts/alegreya-sans-latin-100italic.woff static/fonts/alegreya-sans-latin-100italic.woff2 static/fonts/alegreya-sans-latin-300.woff static/fonts/alegreya-sans-latin-300.woff2 static/fonts/alegreya-sans-latin-300italic.woff static/fonts/alegreya-sans-latin-300italic.woff2 static/fonts/alegreya-sans-latin-400.woff static/fonts/alegreya-sans-latin-400.woff2 static/fonts/alegreya-sans-latin-400italic.woff static/fonts/alegreya-sans-latin-400italic.woff2 static/fonts/alegreya-sans-latin-500.woff static/fonts/alegreya-sans-latin-500.woff2 static/fonts/alegreya-sans-latin-500italic.woff static/fonts/alegreya-sans-latin-500italic.woff2 static/fonts/alegreya-sans-latin-700.woff static/fonts/alegreya-sans-latin-700.woff2 static/fonts/alegreya-sans-latin-700italic.woff static/fonts/alegreya-sans-latin-700italic.woff2 static/fonts/alegreya-sans-latin-800.woff static/fonts/alegreya-sans-latin-800.woff2 static/fonts/alegreya-sans-latin-800italic.woff static/fonts/alegreya-sans-latin-800italic.woff2 static/fonts/alegreya-sans-latin-900.woff static/fonts/alegreya-sans-latin-900.woff2 static/fonts/alegreya-sans-latin-900italic.woff static/fonts/alegreya-sans-latin-900italic.woff2 static/fonts/materialdesignicons-webfont.eot static/fonts/materialdesignicons-webfont.ttf static/fonts/materialdesignicons-webfont.woff static/fonts/materialdesignicons-webfont.woff2 static/img/arrow-up-small.png static/img/swh-donor.png static/img/swh-logo-white.svg static/img/swh-logo.png static/img/swh-logo.svg static/img/swh-spinner-small.gif static/img/swh-spinner.gif static/img/swh-vault.png static/img/icons/swh-logo-32x32.png static/img/icons/swh-logo-archive-180x180.png static/img/icons/swh-logo-archive-192x192.png static/img/icons/swh-logo-archive-270x270.png static/img/logos/bitbucket.png static/img/logos/cgit.png static/img/logos/cran.png static/img/logos/debian.png static/img/logos/elife.png static/img/logos/gitea.png static/img/logos/github.png static/img/logos/gitlab.png static/img/logos/gitorious.png static/img/logos/gnu.png static/img/logos/googlecode.png static/img/logos/guix.png static/img/logos/hal.png static/img/logos/heptapod.png static/img/logos/inria.jpg static/img/logos/ipol.png static/img/logos/launchpad.png static/img/logos/nixos.png static/img/logos/npm.png static/img/logos/opam.png static/img/logos/phabricator.png static/img/logos/pypi.png static/img/logos/sourceforge.png static/img/thirdParty/chosen-sprite.png static/img/thirdParty/chosen-sprite@2x.png static/js/add_forge.bcbbc1be5ab34eede65c.js static/js/add_forge.bcbbc1be5ab34eede65c.js.LICENSE.txt static/js/add_forge.bcbbc1be5ab34eede65c.js.map static/js/admin.e1bdd6f126d315dc57e6.js static/js/admin.e1bdd6f126d315dc57e6.js.LICENSE.txt static/js/admin.e1bdd6f126d315dc57e6.js.map static/js/auth.7a6ba4612115126f9b59.js static/js/auth.7a6ba4612115126f9b59.js.LICENSE.txt static/js/auth.7a6ba4612115126f9b59.js.map static/js/browse.89ce9f7581cb4a43fa54.js static/js/browse.89ce9f7581cb4a43fa54.js.LICENSE.txt static/js/browse.89ce9f7581cb4a43fa54.js.map static/js/d3.f989183a810fb9dbb086.js static/js/d3.f989183a810fb9dbb086.js.map static/js/guided_tour.6fdb762b8416936b3c84.js static/js/guided_tour.6fdb762b8416936b3c84.js.LICENSE.txt static/js/guided_tour.6fdb762b8416936b3c84.js.map static/js/highlightjs.ccd71c93a63af0249728.js static/js/highlightjs.ccd71c93a63af0249728.js.LICENSE.txt static/js/highlightjs.ccd71c93a63af0249728.js.map static/js/mathjax.a0046af7afe82fd10728.js static/js/mathjax.a0046af7afe82fd10728.js.map static/js/org.9adc9a39a210c2fb4be0.js static/js/org.9adc9a39a210c2fb4be0.js.map static/js/origin.751694c0f651d0e204de.js static/js/origin.751694c0f651d0e204de.js.map static/js/pdf.worker.min.js static/js/pdf.worker.min.js.LICENSE.txt static/js/pdfjs.7fa499e22fea56527058.js static/js/pdfjs.7fa499e22fea56527058.js.map static/js/revision.530bd3980955c9cce3fb.js static/js/revision.530bd3980955c9cce3fb.js.LICENSE.txt static/js/revision.530bd3980955c9cce3fb.js.map static/js/save.78a0e5b182ba60ef8cba.js static/js/save.78a0e5b182ba60ef8cba.js.LICENSE.txt static/js/save.78a0e5b182ba60ef8cba.js.map static/js/showdown.b448e42a34c43d6a7de5.js static/js/showdown.b448e42a34c43d6a7de5.js.LICENSE.txt static/js/showdown.b448e42a34c43d6a7de5.js.map static/js/vault.e2e8f042b9c742390872.js static/js/vault.e2e8f042b9c742390872.js.LICENSE.txt static/js/vault.e2e8f042b9c742390872.js.map static/js/vendors.4d86327911239b43ce67.js static/js/vendors.4d86327911239b43ce67.js.LICENSE.txt static/js/vendors.4d86327911239b43ce67.js.map static/js/webapp.ec1d7512d2628f39df9b.js static/js/webapp.ec1d7512d2628f39df9b.js.LICENSE.txt static/js/webapp.ec1d7512d2628f39df9b.js.map static/json/highlightjs-languages.json static/jssources/LICENSE.txt static/jssources/jslicenses.json static/jssources/@babel/runtime/LICENSE.txt static/jssources/@babel/runtime/helpers/esm/asyncToGenerator.js static/jssources/@babel/runtime/helpers/esm/inherits.js static/jssources/@babel/runtime/helpers/esm/setPrototypeOf.js static/jssources/@babel/runtime/regenerator/index.js static/jssources/@sentry/browser/LICENSE.txt static/jssources/@sentry/browser/esm/backend.js static/jssources/@sentry/browser/esm/client.js static/jssources/@sentry/browser/esm/eventbuilder.js static/jssources/@sentry/browser/esm/helpers.js static/jssources/@sentry/browser/esm/sdk.js static/jssources/@sentry/browser/esm/stack-parsers.js static/jssources/@sentry/browser/esm/integrations/breadcrumbs.js static/jssources/@sentry/browser/esm/integrations/dedupe.js static/jssources/@sentry/browser/esm/integrations/globalhandlers.js static/jssources/@sentry/browser/esm/integrations/linkederrors.js static/jssources/@sentry/browser/esm/integrations/trycatch.js static/jssources/@sentry/browser/esm/integrations/useragent.js static/jssources/@sentry/browser/esm/transports/base.js static/jssources/@sentry/browser/esm/transports/fetch.js static/jssources/@sentry/browser/esm/transports/new-fetch.js static/jssources/@sentry/browser/esm/transports/new-xhr.js static/jssources/@sentry/browser/esm/transports/utils.js static/jssources/@sentry/browser/esm/transports/xhr.js static/jssources/@sentry/core/LICENSE.txt static/jssources/@sentry/core/esm/api.js static/jssources/@sentry/core/esm/basebackend.js static/jssources/@sentry/core/esm/baseclient.js static/jssources/@sentry/core/esm/integration.js static/jssources/@sentry/core/esm/request.js static/jssources/@sentry/core/esm/sdk.js static/jssources/@sentry/core/esm/version.js static/jssources/@sentry/core/esm/integrations/functiontostring.js static/jssources/@sentry/core/esm/integrations/inboundfilters.js static/jssources/@sentry/core/esm/transports/base.js static/jssources/@sentry/core/esm/transports/noop.js static/jssources/@sentry/hub/LICENSE.txt static/jssources/@sentry/hub/esm/hub.js static/jssources/@sentry/hub/esm/scope.js static/jssources/@sentry/hub/esm/session.js static/jssources/@sentry/minimal/LICENSE.txt static/jssources/@sentry/minimal/esm/index.js static/jssources/@sentry/types/LICENSE.txt static/jssources/@sentry/types/esm/severity.js static/jssources/@sentry/utils/LICENSE.txt static/jssources/@sentry/utils/esm/async.js static/jssources/@sentry/utils/esm/browser.js static/jssources/@sentry/utils/esm/clientreport.js static/jssources/@sentry/utils/esm/dsn.js static/jssources/@sentry/utils/esm/enums.js static/jssources/@sentry/utils/esm/env.js static/jssources/@sentry/utils/esm/envelope.js static/jssources/@sentry/utils/esm/error.js static/jssources/@sentry/utils/esm/global.js static/jssources/@sentry/utils/esm/instrument.js static/jssources/@sentry/utils/esm/is.js static/jssources/@sentry/utils/esm/logger.js static/jssources/@sentry/utils/esm/memo.js static/jssources/@sentry/utils/esm/misc.js static/jssources/@sentry/utils/esm/node.js static/jssources/@sentry/utils/esm/normalize.js static/jssources/@sentry/utils/esm/object.js static/jssources/@sentry/utils/esm/polyfill.js static/jssources/@sentry/utils/esm/promisebuffer.js static/jssources/@sentry/utils/esm/ratelimit.js static/jssources/@sentry/utils/esm/severity.js static/jssources/@sentry/utils/esm/stacktrace.js static/jssources/@sentry/utils/esm/status.js static/jssources/@sentry/utils/esm/string.js static/jssources/@sentry/utils/esm/supports.js static/jssources/@sentry/utils/esm/syncpromise.js static/jssources/@sentry/utils/esm/time.js static/jssources/admin-lte/LICENSE.txt static/jssources/admin-lte/dist/js/adminlte.js static/jssources/ansi_up/LICENSE.txt static/jssources/ansi_up/ansi_up.js static/jssources/assets/src/bundles/add_forge/add-request-history-item.ejs static/jssources/assets/src/bundles/add_forge/create-request.js static/jssources/assets/src/bundles/add_forge/forge-admin-email.ejs static/jssources/assets/src/bundles/add_forge/index.js static/jssources/assets/src/bundles/add_forge/moderation-dashboard.js static/jssources/assets/src/bundles/add_forge/request-dashboard.js static/jssources/assets/src/bundles/admin/deposit.js static/jssources/assets/src/bundles/admin/index.js static/jssources/assets/src/bundles/admin/mailmap-form.ejs static/jssources/assets/src/bundles/admin/mailmap.js static/jssources/assets/src/bundles/admin/origin-save.js static/jssources/assets/src/bundles/auth/index.js static/jssources/assets/src/bundles/browse/browse-utils.js static/jssources/assets/src/bundles/browse/index.js static/jssources/assets/src/bundles/browse/origin-search.js static/jssources/assets/src/bundles/browse/snapshot-navigation.js static/jssources/assets/src/bundles/browse/swhid-utils.js static/jssources/assets/src/bundles/guided_tour/index.js static/jssources/assets/src/bundles/origin/index.js static/jssources/assets/src/bundles/origin/utils.js static/jssources/assets/src/bundles/origin/visits-calendar.js static/jssources/assets/src/bundles/origin/visits-histogram.js static/jssources/assets/src/bundles/origin/visits-reporting.js static/jssources/assets/src/bundles/revision/diff-panel.ejs static/jssources/assets/src/bundles/revision/diff-utils.js static/jssources/assets/src/bundles/revision/index.js static/jssources/assets/src/bundles/revision/log-utils.js static/jssources/assets/src/bundles/save/artifact-form-row.ejs static/jssources/assets/src/bundles/save/index.js static/jssources/assets/src/bundles/vault/index.js static/jssources/assets/src/bundles/vault/vault-create-tasks.js static/jssources/assets/src/bundles/vault/vault-table-row.ejs static/jssources/assets/src/bundles/vault/vault-ui.js static/jssources/assets/src/bundles/vendors/elementsfrompoint-polyfill.js static/jssources/assets/src/bundles/vendors/index.js static/jssources/assets/src/bundles/webapp/badges.js static/jssources/assets/src/bundles/webapp/code-highlighting.js static/jssources/assets/src/bundles/webapp/history-counters.js static/jssources/assets/src/bundles/webapp/iframes.js static/jssources/assets/src/bundles/webapp/index.js static/jssources/assets/src/bundles/webapp/math-typesetting.js static/jssources/assets/src/bundles/webapp/notebook-rendering.js static/jssources/assets/src/bundles/webapp/pdf-rendering.js static/jssources/assets/src/bundles/webapp/readme-rendering.js static/jssources/assets/src/bundles/webapp/sentry.js static/jssources/assets/src/bundles/webapp/status-widget.js static/jssources/assets/src/bundles/webapp/webapp-utils.js static/jssources/assets/src/bundles/webapp/xss-filtering.js static/jssources/assets/src/thirdparty/jquery.tabSlideOut/LICENSE.txt static/jssources/assets/src/thirdparty/jquery.tabSlideOut/jquery.tabSlideOut.js static/jssources/assets/src/utils/constants.js static/jssources/assets/src/utils/d3.js static/jssources/assets/src/utils/functions.js static/jssources/assets/src/utils/highlightjs.js static/jssources/assets/src/utils/mathjax.js static/jssources/assets/src/utils/org.js static/jssources/assets/src/utils/requests-filter-checkbox.ejs static/jssources/assets/src/utils/scrolling.js static/jssources/assets/src/utils/showdown.js static/jssources/bootstrap/LICENSE.txt static/jssources/bootstrap/js/dist/alert.js static/jssources/bootstrap/js/dist/button.js static/jssources/bootstrap/js/dist/carousel.js static/jssources/bootstrap/js/dist/collapse.js static/jssources/bootstrap/js/dist/dropdown.js static/jssources/bootstrap/js/dist/modal.js static/jssources/bootstrap/js/dist/popover.js static/jssources/bootstrap/js/dist/scrollspy.js static/jssources/bootstrap/js/dist/tab.js static/jssources/bootstrap/js/dist/tooltip.js static/jssources/bootstrap/js/dist/util.js static/jssources/chosen-js/LICENSE.md static/jssources/chosen-js/chosen.jquery.js static/jssources/clipboard/LICENSE.txt static/jssources/clipboard/dist/clipboard.js static/jssources/core-js/LICENSE.txt static/jssources/core-js/internals/a-callable.js static/jssources/core-js/internals/a-constructor.js static/jssources/core-js/internals/a-possible-prototype.js static/jssources/core-js/internals/add-to-unscopables.js static/jssources/core-js/internals/advance-string-index.js static/jssources/core-js/internals/an-instance.js static/jssources/core-js/internals/an-object.js static/jssources/core-js/internals/array-buffer-native.js static/jssources/core-js/internals/array-buffer-non-extensible.js static/jssources/core-js/internals/array-buffer-view-core.js static/jssources/core-js/internals/array-buffer.js static/jssources/core-js/internals/array-copy-within.js static/jssources/core-js/internals/array-fill.js static/jssources/core-js/internals/array-for-each.js static/jssources/core-js/internals/array-from-constructor-and-list.js static/jssources/core-js/internals/array-from.js static/jssources/core-js/internals/array-includes.js static/jssources/core-js/internals/array-iteration.js static/jssources/core-js/internals/array-last-index-of.js static/jssources/core-js/internals/array-method-has-species-support.js static/jssources/core-js/internals/array-method-is-strict.js static/jssources/core-js/internals/array-reduce.js static/jssources/core-js/internals/array-slice-simple.js static/jssources/core-js/internals/array-slice.js static/jssources/core-js/internals/array-sort.js static/jssources/core-js/internals/array-species-constructor.js static/jssources/core-js/internals/array-species-create.js static/jssources/core-js/internals/base64-map.js static/jssources/core-js/internals/call-with-safe-iteration-closing.js static/jssources/core-js/internals/check-correctness-of-iteration.js static/jssources/core-js/internals/classof-raw.js static/jssources/core-js/internals/classof.js static/jssources/core-js/internals/clear-error-stack.js static/jssources/core-js/internals/collection-strong.js static/jssources/core-js/internals/collection-weak.js static/jssources/core-js/internals/collection.js static/jssources/core-js/internals/copy-constructor-properties.js static/jssources/core-js/internals/correct-is-regexp-logic.js static/jssources/core-js/internals/correct-prototype-getter.js static/jssources/core-js/internals/create-html.js static/jssources/core-js/internals/create-iterator-constructor.js static/jssources/core-js/internals/create-non-enumerable-property.js static/jssources/core-js/internals/create-property-descriptor.js static/jssources/core-js/internals/create-property.js static/jssources/core-js/internals/date-to-iso-string.js static/jssources/core-js/internals/date-to-primitive.js static/jssources/core-js/internals/define-iterator.js static/jssources/core-js/internals/define-well-known-symbol.js static/jssources/core-js/internals/descriptors.js static/jssources/core-js/internals/document-create-element.js static/jssources/core-js/internals/dom-exception-constants.js static/jssources/core-js/internals/dom-iterables.js static/jssources/core-js/internals/dom-token-list-prototype.js static/jssources/core-js/internals/engine-ff-version.js static/jssources/core-js/internals/engine-is-browser.js static/jssources/core-js/internals/engine-is-ie-or-edge.js static/jssources/core-js/internals/engine-is-ios-pebble.js static/jssources/core-js/internals/engine-is-ios.js static/jssources/core-js/internals/engine-is-node.js static/jssources/core-js/internals/engine-is-webos-webkit.js static/jssources/core-js/internals/engine-user-agent.js static/jssources/core-js/internals/engine-v8-version.js static/jssources/core-js/internals/engine-webkit-version.js static/jssources/core-js/internals/enum-bug-keys.js static/jssources/core-js/internals/error-stack-installable.js static/jssources/core-js/internals/error-to-string.js static/jssources/core-js/internals/export.js static/jssources/core-js/internals/fails.js static/jssources/core-js/internals/fix-regexp-well-known-symbol-logic.js static/jssources/core-js/internals/flatten-into-array.js static/jssources/core-js/internals/freezing.js static/jssources/core-js/internals/function-apply.js static/jssources/core-js/internals/function-bind-context.js static/jssources/core-js/internals/function-bind-native.js static/jssources/core-js/internals/function-bind.js static/jssources/core-js/internals/function-call.js static/jssources/core-js/internals/function-name.js static/jssources/core-js/internals/function-uncurry-this.js static/jssources/core-js/internals/get-built-in.js static/jssources/core-js/internals/get-iterator-method.js static/jssources/core-js/internals/get-iterator.js static/jssources/core-js/internals/get-method.js static/jssources/core-js/internals/get-substitution.js static/jssources/core-js/internals/global.js static/jssources/core-js/internals/has-own-property.js static/jssources/core-js/internals/hidden-keys.js static/jssources/core-js/internals/host-report-errors.js static/jssources/core-js/internals/html.js static/jssources/core-js/internals/ie8-dom-define.js static/jssources/core-js/internals/ieee754.js static/jssources/core-js/internals/indexed-object.js static/jssources/core-js/internals/inherit-if-required.js static/jssources/core-js/internals/inspect-source.js static/jssources/core-js/internals/install-error-cause.js static/jssources/core-js/internals/internal-metadata.js static/jssources/core-js/internals/internal-state.js static/jssources/core-js/internals/is-array-iterator-method.js static/jssources/core-js/internals/is-array.js static/jssources/core-js/internals/is-callable.js static/jssources/core-js/internals/is-constructor.js static/jssources/core-js/internals/is-data-descriptor.js static/jssources/core-js/internals/is-forced.js static/jssources/core-js/internals/is-integral-number.js static/jssources/core-js/internals/is-object.js static/jssources/core-js/internals/is-pure.js static/jssources/core-js/internals/is-regexp.js static/jssources/core-js/internals/is-symbol.js static/jssources/core-js/internals/iterate.js static/jssources/core-js/internals/iterator-close.js static/jssources/core-js/internals/iterators-core.js static/jssources/core-js/internals/iterators.js static/jssources/core-js/internals/length-of-array-like.js static/jssources/core-js/internals/math-expm1.js static/jssources/core-js/internals/math-fround.js static/jssources/core-js/internals/math-log10.js static/jssources/core-js/internals/math-log1p.js static/jssources/core-js/internals/math-sign.js static/jssources/core-js/internals/microtask.js static/jssources/core-js/internals/native-promise-constructor.js static/jssources/core-js/internals/native-symbol.js static/jssources/core-js/internals/native-url.js static/jssources/core-js/internals/native-weak-map.js static/jssources/core-js/internals/new-promise-capability.js static/jssources/core-js/internals/normalize-string-argument.js static/jssources/core-js/internals/not-a-regexp.js static/jssources/core-js/internals/number-is-finite.js static/jssources/core-js/internals/number-parse-float.js static/jssources/core-js/internals/number-parse-int.js static/jssources/core-js/internals/object-assign.js static/jssources/core-js/internals/object-create.js static/jssources/core-js/internals/object-define-properties.js static/jssources/core-js/internals/object-define-property.js static/jssources/core-js/internals/object-get-own-property-descriptor.js static/jssources/core-js/internals/object-get-own-property-names-external.js static/jssources/core-js/internals/object-get-own-property-names.js static/jssources/core-js/internals/object-get-own-property-symbols.js static/jssources/core-js/internals/object-get-prototype-of.js static/jssources/core-js/internals/object-is-extensible.js static/jssources/core-js/internals/object-is-prototype-of.js static/jssources/core-js/internals/object-keys-internal.js static/jssources/core-js/internals/object-keys.js static/jssources/core-js/internals/object-property-is-enumerable.js static/jssources/core-js/internals/object-prototype-accessors-forced.js static/jssources/core-js/internals/object-set-prototype-of.js static/jssources/core-js/internals/object-to-array.js static/jssources/core-js/internals/object-to-string.js static/jssources/core-js/internals/ordinary-to-primitive.js static/jssources/core-js/internals/own-keys.js static/jssources/core-js/internals/path.js static/jssources/core-js/internals/perform.js static/jssources/core-js/internals/promise-resolve.js static/jssources/core-js/internals/queue.js static/jssources/core-js/internals/redefine-all.js static/jssources/core-js/internals/redefine.js static/jssources/core-js/internals/regexp-exec-abstract.js static/jssources/core-js/internals/regexp-exec.js static/jssources/core-js/internals/regexp-flags.js static/jssources/core-js/internals/regexp-sticky-helpers.js static/jssources/core-js/internals/regexp-unsupported-dot-all.js static/jssources/core-js/internals/regexp-unsupported-ncg.js static/jssources/core-js/internals/require-object-coercible.js static/jssources/core-js/internals/same-value.js static/jssources/core-js/internals/set-global.js static/jssources/core-js/internals/set-species.js static/jssources/core-js/internals/set-to-string-tag.js static/jssources/core-js/internals/shared-key.js static/jssources/core-js/internals/shared-store.js static/jssources/core-js/internals/shared.js static/jssources/core-js/internals/species-constructor.js static/jssources/core-js/internals/string-html-forced.js static/jssources/core-js/internals/string-multibyte.js static/jssources/core-js/internals/string-pad-webkit-bug.js static/jssources/core-js/internals/string-pad.js static/jssources/core-js/internals/string-punycode-to-ascii.js static/jssources/core-js/internals/string-repeat.js static/jssources/core-js/internals/string-trim-forced.js static/jssources/core-js/internals/string-trim.js static/jssources/core-js/internals/task.js static/jssources/core-js/internals/this-number-value.js static/jssources/core-js/internals/to-absolute-index.js static/jssources/core-js/internals/to-index.js static/jssources/core-js/internals/to-indexed-object.js static/jssources/core-js/internals/to-integer-or-infinity.js static/jssources/core-js/internals/to-length.js static/jssources/core-js/internals/to-object.js static/jssources/core-js/internals/to-offset.js static/jssources/core-js/internals/to-positive-integer.js static/jssources/core-js/internals/to-primitive.js static/jssources/core-js/internals/to-property-key.js static/jssources/core-js/internals/to-string-tag-support.js static/jssources/core-js/internals/to-string.js static/jssources/core-js/internals/try-node-require.js static/jssources/core-js/internals/try-to-string.js static/jssources/core-js/internals/typed-array-constructor.js static/jssources/core-js/internals/typed-array-constructors-require-wrappers.js static/jssources/core-js/internals/typed-array-from-species-and-list.js static/jssources/core-js/internals/typed-array-from.js static/jssources/core-js/internals/typed-array-species-constructor.js static/jssources/core-js/internals/uid.js static/jssources/core-js/internals/use-symbol-as-uid.js static/jssources/core-js/internals/v8-prototype-define-bug.js static/jssources/core-js/internals/validate-arguments-length.js static/jssources/core-js/internals/well-known-symbol-wrapped.js static/jssources/core-js/internals/well-known-symbol.js static/jssources/core-js/internals/whitespaces.js static/jssources/core-js/internals/wrap-error-constructor-with-cause.js static/jssources/core-js/modules/es.aggregate-error.cause.js static/jssources/core-js/modules/es.aggregate-error.js static/jssources/core-js/modules/es.array-buffer.constructor.js static/jssources/core-js/modules/es.array-buffer.is-view.js static/jssources/core-js/modules/es.array-buffer.slice.js static/jssources/core-js/modules/es.array.at.js static/jssources/core-js/modules/es.array.concat.js static/jssources/core-js/modules/es.array.copy-within.js static/jssources/core-js/modules/es.array.every.js static/jssources/core-js/modules/es.array.fill.js static/jssources/core-js/modules/es.array.filter.js static/jssources/core-js/modules/es.array.find-index.js static/jssources/core-js/modules/es.array.find.js static/jssources/core-js/modules/es.array.flat-map.js static/jssources/core-js/modules/es.array.flat.js static/jssources/core-js/modules/es.array.for-each.js static/jssources/core-js/modules/es.array.from.js static/jssources/core-js/modules/es.array.includes.js static/jssources/core-js/modules/es.array.index-of.js static/jssources/core-js/modules/es.array.is-array.js static/jssources/core-js/modules/es.array.iterator.js static/jssources/core-js/modules/es.array.join.js static/jssources/core-js/modules/es.array.last-index-of.js static/jssources/core-js/modules/es.array.map.js static/jssources/core-js/modules/es.array.of.js static/jssources/core-js/modules/es.array.reduce-right.js static/jssources/core-js/modules/es.array.reduce.js static/jssources/core-js/modules/es.array.reverse.js static/jssources/core-js/modules/es.array.slice.js static/jssources/core-js/modules/es.array.some.js static/jssources/core-js/modules/es.array.sort.js static/jssources/core-js/modules/es.array.species.js static/jssources/core-js/modules/es.array.splice.js static/jssources/core-js/modules/es.array.unscopables.flat-map.js static/jssources/core-js/modules/es.array.unscopables.flat.js static/jssources/core-js/modules/es.data-view.js static/jssources/core-js/modules/es.date.get-year.js static/jssources/core-js/modules/es.date.now.js static/jssources/core-js/modules/es.date.set-year.js static/jssources/core-js/modules/es.date.to-gmt-string.js static/jssources/core-js/modules/es.date.to-iso-string.js static/jssources/core-js/modules/es.date.to-json.js static/jssources/core-js/modules/es.date.to-primitive.js static/jssources/core-js/modules/es.date.to-string.js static/jssources/core-js/modules/es.error.cause.js static/jssources/core-js/modules/es.error.to-string.js static/jssources/core-js/modules/es.escape.js static/jssources/core-js/modules/es.function.bind.js static/jssources/core-js/modules/es.function.has-instance.js static/jssources/core-js/modules/es.function.name.js static/jssources/core-js/modules/es.global-this.js static/jssources/core-js/modules/es.json.stringify.js static/jssources/core-js/modules/es.json.to-string-tag.js static/jssources/core-js/modules/es.map.js static/jssources/core-js/modules/es.math.acosh.js static/jssources/core-js/modules/es.math.asinh.js static/jssources/core-js/modules/es.math.atanh.js static/jssources/core-js/modules/es.math.cbrt.js static/jssources/core-js/modules/es.math.clz32.js static/jssources/core-js/modules/es.math.cosh.js static/jssources/core-js/modules/es.math.expm1.js static/jssources/core-js/modules/es.math.fround.js static/jssources/core-js/modules/es.math.hypot.js static/jssources/core-js/modules/es.math.imul.js static/jssources/core-js/modules/es.math.log10.js static/jssources/core-js/modules/es.math.log1p.js static/jssources/core-js/modules/es.math.log2.js static/jssources/core-js/modules/es.math.sign.js static/jssources/core-js/modules/es.math.sinh.js static/jssources/core-js/modules/es.math.tanh.js static/jssources/core-js/modules/es.math.to-string-tag.js static/jssources/core-js/modules/es.math.trunc.js static/jssources/core-js/modules/es.number.constructor.js static/jssources/core-js/modules/es.number.epsilon.js static/jssources/core-js/modules/es.number.is-finite.js static/jssources/core-js/modules/es.number.is-integer.js static/jssources/core-js/modules/es.number.is-nan.js static/jssources/core-js/modules/es.number.is-safe-integer.js static/jssources/core-js/modules/es.number.max-safe-integer.js static/jssources/core-js/modules/es.number.min-safe-integer.js static/jssources/core-js/modules/es.number.parse-float.js static/jssources/core-js/modules/es.number.parse-int.js static/jssources/core-js/modules/es.number.to-exponential.js static/jssources/core-js/modules/es.number.to-fixed.js static/jssources/core-js/modules/es.number.to-precision.js static/jssources/core-js/modules/es.object.assign.js static/jssources/core-js/modules/es.object.create.js static/jssources/core-js/modules/es.object.define-getter.js static/jssources/core-js/modules/es.object.define-properties.js static/jssources/core-js/modules/es.object.define-property.js static/jssources/core-js/modules/es.object.define-setter.js static/jssources/core-js/modules/es.object.entries.js static/jssources/core-js/modules/es.object.freeze.js static/jssources/core-js/modules/es.object.from-entries.js static/jssources/core-js/modules/es.object.get-own-property-descriptor.js static/jssources/core-js/modules/es.object.get-own-property-descriptors.js static/jssources/core-js/modules/es.object.get-own-property-names.js static/jssources/core-js/modules/es.object.get-prototype-of.js static/jssources/core-js/modules/es.object.has-own.js static/jssources/core-js/modules/es.object.is-extensible.js static/jssources/core-js/modules/es.object.is-frozen.js static/jssources/core-js/modules/es.object.is-sealed.js static/jssources/core-js/modules/es.object.is.js static/jssources/core-js/modules/es.object.keys.js static/jssources/core-js/modules/es.object.lookup-getter.js static/jssources/core-js/modules/es.object.lookup-setter.js static/jssources/core-js/modules/es.object.prevent-extensions.js static/jssources/core-js/modules/es.object.seal.js static/jssources/core-js/modules/es.object.set-prototype-of.js static/jssources/core-js/modules/es.object.to-string.js static/jssources/core-js/modules/es.object.values.js static/jssources/core-js/modules/es.parse-float.js static/jssources/core-js/modules/es.parse-int.js static/jssources/core-js/modules/es.promise.all-settled.js static/jssources/core-js/modules/es.promise.any.js static/jssources/core-js/modules/es.promise.finally.js static/jssources/core-js/modules/es.promise.js static/jssources/core-js/modules/es.reflect.apply.js static/jssources/core-js/modules/es.reflect.construct.js static/jssources/core-js/modules/es.reflect.define-property.js static/jssources/core-js/modules/es.reflect.delete-property.js static/jssources/core-js/modules/es.reflect.get-own-property-descriptor.js static/jssources/core-js/modules/es.reflect.get-prototype-of.js static/jssources/core-js/modules/es.reflect.get.js static/jssources/core-js/modules/es.reflect.has.js static/jssources/core-js/modules/es.reflect.is-extensible.js static/jssources/core-js/modules/es.reflect.own-keys.js static/jssources/core-js/modules/es.reflect.prevent-extensions.js static/jssources/core-js/modules/es.reflect.set-prototype-of.js static/jssources/core-js/modules/es.reflect.set.js static/jssources/core-js/modules/es.reflect.to-string-tag.js static/jssources/core-js/modules/es.regexp.constructor.js static/jssources/core-js/modules/es.regexp.dot-all.js static/jssources/core-js/modules/es.regexp.exec.js static/jssources/core-js/modules/es.regexp.flags.js static/jssources/core-js/modules/es.regexp.sticky.js static/jssources/core-js/modules/es.regexp.test.js static/jssources/core-js/modules/es.regexp.to-string.js static/jssources/core-js/modules/es.set.js static/jssources/core-js/modules/es.string.anchor.js static/jssources/core-js/modules/es.string.at-alternative.js static/jssources/core-js/modules/es.string.big.js static/jssources/core-js/modules/es.string.blink.js static/jssources/core-js/modules/es.string.bold.js static/jssources/core-js/modules/es.string.code-point-at.js static/jssources/core-js/modules/es.string.ends-with.js static/jssources/core-js/modules/es.string.fixed.js static/jssources/core-js/modules/es.string.fontcolor.js static/jssources/core-js/modules/es.string.fontsize.js static/jssources/core-js/modules/es.string.from-code-point.js static/jssources/core-js/modules/es.string.includes.js static/jssources/core-js/modules/es.string.italics.js static/jssources/core-js/modules/es.string.iterator.js static/jssources/core-js/modules/es.string.link.js static/jssources/core-js/modules/es.string.match-all.js static/jssources/core-js/modules/es.string.match.js static/jssources/core-js/modules/es.string.pad-end.js static/jssources/core-js/modules/es.string.pad-start.js static/jssources/core-js/modules/es.string.raw.js static/jssources/core-js/modules/es.string.repeat.js static/jssources/core-js/modules/es.string.replace-all.js static/jssources/core-js/modules/es.string.replace.js static/jssources/core-js/modules/es.string.search.js static/jssources/core-js/modules/es.string.small.js static/jssources/core-js/modules/es.string.split.js static/jssources/core-js/modules/es.string.starts-with.js static/jssources/core-js/modules/es.string.strike.js static/jssources/core-js/modules/es.string.sub.js static/jssources/core-js/modules/es.string.substr.js static/jssources/core-js/modules/es.string.sup.js static/jssources/core-js/modules/es.string.trim-end.js static/jssources/core-js/modules/es.string.trim-start.js static/jssources/core-js/modules/es.string.trim.js static/jssources/core-js/modules/es.symbol.async-iterator.js static/jssources/core-js/modules/es.symbol.description.js static/jssources/core-js/modules/es.symbol.has-instance.js static/jssources/core-js/modules/es.symbol.is-concat-spreadable.js static/jssources/core-js/modules/es.symbol.iterator.js static/jssources/core-js/modules/es.symbol.js static/jssources/core-js/modules/es.symbol.match-all.js static/jssources/core-js/modules/es.symbol.match.js static/jssources/core-js/modules/es.symbol.replace.js static/jssources/core-js/modules/es.symbol.search.js static/jssources/core-js/modules/es.symbol.species.js static/jssources/core-js/modules/es.symbol.split.js static/jssources/core-js/modules/es.symbol.to-primitive.js static/jssources/core-js/modules/es.symbol.to-string-tag.js static/jssources/core-js/modules/es.symbol.unscopables.js static/jssources/core-js/modules/es.typed-array.at.js static/jssources/core-js/modules/es.typed-array.copy-within.js static/jssources/core-js/modules/es.typed-array.every.js static/jssources/core-js/modules/es.typed-array.fill.js static/jssources/core-js/modules/es.typed-array.filter.js static/jssources/core-js/modules/es.typed-array.find-index.js static/jssources/core-js/modules/es.typed-array.find.js static/jssources/core-js/modules/es.typed-array.float32-array.js static/jssources/core-js/modules/es.typed-array.float64-array.js static/jssources/core-js/modules/es.typed-array.for-each.js static/jssources/core-js/modules/es.typed-array.from.js static/jssources/core-js/modules/es.typed-array.includes.js static/jssources/core-js/modules/es.typed-array.index-of.js static/jssources/core-js/modules/es.typed-array.int16-array.js static/jssources/core-js/modules/es.typed-array.int32-array.js static/jssources/core-js/modules/es.typed-array.int8-array.js static/jssources/core-js/modules/es.typed-array.iterator.js static/jssources/core-js/modules/es.typed-array.join.js static/jssources/core-js/modules/es.typed-array.last-index-of.js static/jssources/core-js/modules/es.typed-array.map.js static/jssources/core-js/modules/es.typed-array.of.js static/jssources/core-js/modules/es.typed-array.reduce-right.js static/jssources/core-js/modules/es.typed-array.reduce.js static/jssources/core-js/modules/es.typed-array.reverse.js static/jssources/core-js/modules/es.typed-array.set.js static/jssources/core-js/modules/es.typed-array.slice.js static/jssources/core-js/modules/es.typed-array.some.js static/jssources/core-js/modules/es.typed-array.sort.js static/jssources/core-js/modules/es.typed-array.subarray.js static/jssources/core-js/modules/es.typed-array.to-locale-string.js static/jssources/core-js/modules/es.typed-array.to-string.js static/jssources/core-js/modules/es.typed-array.uint16-array.js static/jssources/core-js/modules/es.typed-array.uint32-array.js static/jssources/core-js/modules/es.typed-array.uint8-array.js static/jssources/core-js/modules/es.typed-array.uint8-clamped-array.js static/jssources/core-js/modules/es.unescape.js static/jssources/core-js/modules/es.weak-map.js static/jssources/core-js/modules/es.weak-set.js static/jssources/core-js/modules/web.atob.js static/jssources/core-js/modules/web.btoa.js static/jssources/core-js/modules/web.dom-collections.for-each.js static/jssources/core-js/modules/web.dom-collections.iterator.js static/jssources/core-js/modules/web.dom-exception.constructor.js static/jssources/core-js/modules/web.dom-exception.stack.js static/jssources/core-js/modules/web.dom-exception.to-string-tag.js static/jssources/core-js/modules/web.immediate.js static/jssources/core-js/modules/web.queue-microtask.js static/jssources/core-js/modules/web.structured-clone.js static/jssources/core-js/modules/web.timers.js static/jssources/core-js/modules/web.url-search-params.js static/jssources/core-js/modules/web.url.js static/jssources/core-js/modules/web.url.to-json.js static/jssources/core-js/stable/index.js static/jssources/d3-array/LICENSE.txt static/jssources/d3-array/src/array.js static/jssources/d3-array/src/ascending.js static/jssources/d3-array/src/bin.js static/jssources/d3-array/src/bisect.js static/jssources/d3-array/src/bisector.js static/jssources/d3-array/src/constant.js static/jssources/d3-array/src/count.js static/jssources/d3-array/src/cross.js static/jssources/d3-array/src/cumsum.js static/jssources/d3-array/src/descending.js static/jssources/d3-array/src/deviation.js static/jssources/d3-array/src/difference.js static/jssources/d3-array/src/disjoint.js static/jssources/d3-array/src/every.js static/jssources/d3-array/src/extent.js static/jssources/d3-array/src/filter.js static/jssources/d3-array/src/fsum.js static/jssources/d3-array/src/greatest.js static/jssources/d3-array/src/greatestIndex.js static/jssources/d3-array/src/group.js static/jssources/d3-array/src/groupSort.js static/jssources/d3-array/src/identity.js static/jssources/d3-array/src/index.js static/jssources/d3-array/src/intersection.js static/jssources/d3-array/src/least.js static/jssources/d3-array/src/leastIndex.js static/jssources/d3-array/src/map.js static/jssources/d3-array/src/max.js static/jssources/d3-array/src/maxIndex.js static/jssources/d3-array/src/mean.js static/jssources/d3-array/src/median.js static/jssources/d3-array/src/merge.js static/jssources/d3-array/src/min.js static/jssources/d3-array/src/minIndex.js static/jssources/d3-array/src/mode.js static/jssources/d3-array/src/nice.js static/jssources/d3-array/src/number.js static/jssources/d3-array/src/pairs.js static/jssources/d3-array/src/permute.js static/jssources/d3-array/src/quantile.js static/jssources/d3-array/src/quickselect.js static/jssources/d3-array/src/range.js static/jssources/d3-array/src/reduce.js static/jssources/d3-array/src/reverse.js static/jssources/d3-array/src/scan.js static/jssources/d3-array/src/set.js static/jssources/d3-array/src/shuffle.js static/jssources/d3-array/src/some.js static/jssources/d3-array/src/sort.js static/jssources/d3-array/src/subset.js static/jssources/d3-array/src/sum.js static/jssources/d3-array/src/superset.js static/jssources/d3-array/src/ticks.js static/jssources/d3-array/src/transpose.js static/jssources/d3-array/src/union.js static/jssources/d3-array/src/variance.js static/jssources/d3-array/src/zip.js static/jssources/d3-array/src/threshold/freedmanDiaconis.js static/jssources/d3-array/src/threshold/scott.js static/jssources/d3-array/src/threshold/sturges.js static/jssources/d3-axis/LICENSE.txt static/jssources/d3-axis/src/axis.js static/jssources/d3-axis/src/identity.js static/jssources/d3-axis/src/index.js static/jssources/d3-color/LICENSE.txt static/jssources/d3-color/src/color.js static/jssources/d3-color/src/define.js static/jssources/d3-dispatch/LICENSE.txt static/jssources/d3-dispatch/src/dispatch.js static/jssources/d3-ease/LICENSE.txt static/jssources/d3-ease/src/cubic.js static/jssources/d3-format/LICENSE.txt static/jssources/d3-format/src/defaultLocale.js static/jssources/d3-format/src/exponent.js static/jssources/d3-format/src/formatDecimal.js static/jssources/d3-format/src/formatGroup.js static/jssources/d3-format/src/formatNumerals.js static/jssources/d3-format/src/formatPrefixAuto.js static/jssources/d3-format/src/formatRounded.js static/jssources/d3-format/src/formatSpecifier.js static/jssources/d3-format/src/formatTrim.js static/jssources/d3-format/src/formatTypes.js static/jssources/d3-format/src/identity.js static/jssources/d3-format/src/index.js static/jssources/d3-format/src/locale.js static/jssources/d3-format/src/precisionFixed.js static/jssources/d3-format/src/precisionPrefix.js static/jssources/d3-format/src/precisionRound.js static/jssources/d3-interpolate/LICENSE.txt static/jssources/d3-interpolate/src/array.js static/jssources/d3-interpolate/src/basis.js static/jssources/d3-interpolate/src/basisClosed.js static/jssources/d3-interpolate/src/color.js static/jssources/d3-interpolate/src/constant.js static/jssources/d3-interpolate/src/date.js static/jssources/d3-interpolate/src/number.js static/jssources/d3-interpolate/src/numberArray.js static/jssources/d3-interpolate/src/object.js static/jssources/d3-interpolate/src/piecewise.js static/jssources/d3-interpolate/src/rgb.js static/jssources/d3-interpolate/src/round.js static/jssources/d3-interpolate/src/string.js static/jssources/d3-interpolate/src/value.js static/jssources/d3-interpolate/src/transform/decompose.js static/jssources/d3-interpolate/src/transform/index.js static/jssources/d3-interpolate/src/transform/parse.js static/jssources/d3-path/LICENSE.txt static/jssources/d3-path/src/path.js static/jssources/d3-scale/LICENSE.txt static/jssources/d3-scale/src/band.js static/jssources/d3-scale/src/constant.js static/jssources/d3-scale/src/continuous.js static/jssources/d3-scale/src/diverging.js static/jssources/d3-scale/src/identity.js static/jssources/d3-scale/src/index.js static/jssources/d3-scale/src/init.js static/jssources/d3-scale/src/linear.js static/jssources/d3-scale/src/log.js static/jssources/d3-scale/src/nice.js static/jssources/d3-scale/src/number.js static/jssources/d3-scale/src/ordinal.js static/jssources/d3-scale/src/pow.js static/jssources/d3-scale/src/quantile.js static/jssources/d3-scale/src/quantize.js static/jssources/d3-scale/src/radial.js static/jssources/d3-scale/src/sequential.js static/jssources/d3-scale/src/sequentialQuantile.js static/jssources/d3-scale/src/symlog.js static/jssources/d3-scale/src/threshold.js static/jssources/d3-scale/src/tickFormat.js static/jssources/d3-scale/src/time.js static/jssources/d3-scale/src/utcTime.js static/jssources/d3-selection/LICENSE.txt static/jssources/d3-selection/src/array.js static/jssources/d3-selection/src/constant.js static/jssources/d3-selection/src/create.js static/jssources/d3-selection/src/creator.js static/jssources/d3-selection/src/index.js static/jssources/d3-selection/src/local.js static/jssources/d3-selection/src/matcher.js static/jssources/d3-selection/src/namespace.js static/jssources/d3-selection/src/namespaces.js static/jssources/d3-selection/src/pointer.js static/jssources/d3-selection/src/pointers.js static/jssources/d3-selection/src/select.js static/jssources/d3-selection/src/selectAll.js static/jssources/d3-selection/src/selector.js static/jssources/d3-selection/src/selectorAll.js static/jssources/d3-selection/src/sourceEvent.js static/jssources/d3-selection/src/window.js static/jssources/d3-selection/src/selection/append.js static/jssources/d3-selection/src/selection/attr.js static/jssources/d3-selection/src/selection/call.js static/jssources/d3-selection/src/selection/classed.js static/jssources/d3-selection/src/selection/clone.js static/jssources/d3-selection/src/selection/data.js static/jssources/d3-selection/src/selection/datum.js static/jssources/d3-selection/src/selection/dispatch.js static/jssources/d3-selection/src/selection/each.js static/jssources/d3-selection/src/selection/empty.js static/jssources/d3-selection/src/selection/enter.js static/jssources/d3-selection/src/selection/exit.js static/jssources/d3-selection/src/selection/filter.js static/jssources/d3-selection/src/selection/html.js static/jssources/d3-selection/src/selection/index.js static/jssources/d3-selection/src/selection/insert.js static/jssources/d3-selection/src/selection/iterator.js static/jssources/d3-selection/src/selection/join.js static/jssources/d3-selection/src/selection/lower.js static/jssources/d3-selection/src/selection/merge.js static/jssources/d3-selection/src/selection/node.js static/jssources/d3-selection/src/selection/nodes.js static/jssources/d3-selection/src/selection/on.js static/jssources/d3-selection/src/selection/order.js static/jssources/d3-selection/src/selection/property.js static/jssources/d3-selection/src/selection/raise.js static/jssources/d3-selection/src/selection/remove.js static/jssources/d3-selection/src/selection/select.js static/jssources/d3-selection/src/selection/selectAll.js static/jssources/d3-selection/src/selection/selectChild.js static/jssources/d3-selection/src/selection/selectChildren.js static/jssources/d3-selection/src/selection/size.js static/jssources/d3-selection/src/selection/sort.js static/jssources/d3-selection/src/selection/sparse.js static/jssources/d3-selection/src/selection/style.js static/jssources/d3-selection/src/selection/text.js static/jssources/d3-shape/LICENSE.txt static/jssources/d3-shape/src/arc.js static/jssources/d3-shape/src/area.js static/jssources/d3-shape/src/areaRadial.js static/jssources/d3-shape/src/array.js static/jssources/d3-shape/src/constant.js static/jssources/d3-shape/src/descending.js static/jssources/d3-shape/src/identity.js static/jssources/d3-shape/src/index.js static/jssources/d3-shape/src/line.js static/jssources/d3-shape/src/lineRadial.js static/jssources/d3-shape/src/math.js static/jssources/d3-shape/src/noop.js static/jssources/d3-shape/src/pie.js static/jssources/d3-shape/src/point.js static/jssources/d3-shape/src/pointRadial.js static/jssources/d3-shape/src/stack.js static/jssources/d3-shape/src/symbol.js static/jssources/d3-shape/src/curve/basis.js static/jssources/d3-shape/src/curve/basisClosed.js static/jssources/d3-shape/src/curve/basisOpen.js static/jssources/d3-shape/src/curve/bump.js static/jssources/d3-shape/src/curve/bundle.js static/jssources/d3-shape/src/curve/cardinal.js static/jssources/d3-shape/src/curve/cardinalClosed.js static/jssources/d3-shape/src/curve/cardinalOpen.js static/jssources/d3-shape/src/curve/catmullRom.js static/jssources/d3-shape/src/curve/catmullRomClosed.js static/jssources/d3-shape/src/curve/catmullRomOpen.js static/jssources/d3-shape/src/curve/linear.js static/jssources/d3-shape/src/curve/linearClosed.js static/jssources/d3-shape/src/curve/monotone.js static/jssources/d3-shape/src/curve/natural.js static/jssources/d3-shape/src/curve/radial.js static/jssources/d3-shape/src/curve/step.js static/jssources/d3-shape/src/link/index.js static/jssources/d3-shape/src/offset/diverging.js static/jssources/d3-shape/src/offset/expand.js static/jssources/d3-shape/src/offset/none.js static/jssources/d3-shape/src/offset/silhouette.js static/jssources/d3-shape/src/offset/wiggle.js static/jssources/d3-shape/src/order/appearance.js static/jssources/d3-shape/src/order/ascending.js static/jssources/d3-shape/src/order/descending.js static/jssources/d3-shape/src/order/insideOut.js static/jssources/d3-shape/src/order/none.js static/jssources/d3-shape/src/order/reverse.js static/jssources/d3-shape/src/symbol/circle.js static/jssources/d3-shape/src/symbol/cross.js static/jssources/d3-shape/src/symbol/diamond.js static/jssources/d3-shape/src/symbol/square.js static/jssources/d3-shape/src/symbol/star.js static/jssources/d3-shape/src/symbol/triangle.js static/jssources/d3-shape/src/symbol/wye.js static/jssources/d3-time/LICENSE.txt static/jssources/d3-time-format/LICENSE.txt static/jssources/d3-time-format/src/defaultLocale.js static/jssources/d3-time-format/src/index.js static/jssources/d3-time-format/src/isoFormat.js static/jssources/d3-time-format/src/isoParse.js static/jssources/d3-time-format/src/locale.js static/jssources/d3-time/src/day.js static/jssources/d3-time/src/duration.js static/jssources/d3-time/src/hour.js static/jssources/d3-time/src/index.js static/jssources/d3-time/src/interval.js static/jssources/d3-time/src/millisecond.js static/jssources/d3-time/src/minute.js static/jssources/d3-time/src/month.js static/jssources/d3-time/src/second.js static/jssources/d3-time/src/ticks.js static/jssources/d3-time/src/utcDay.js static/jssources/d3-time/src/utcHour.js static/jssources/d3-time/src/utcMinute.js static/jssources/d3-time/src/utcMonth.js static/jssources/d3-time/src/utcWeek.js static/jssources/d3-time/src/utcYear.js static/jssources/d3-time/src/week.js static/jssources/d3-time/src/year.js static/jssources/d3-timer/LICENSE.txt static/jssources/d3-timer/src/timeout.js static/jssources/d3-timer/src/timer.js static/jssources/d3-transition/LICENSE.txt static/jssources/d3-transition/src/active.js static/jssources/d3-transition/src/index.js static/jssources/d3-transition/src/interrupt.js static/jssources/d3-transition/src/selection/index.js static/jssources/d3-transition/src/selection/interrupt.js static/jssources/d3-transition/src/selection/transition.js static/jssources/d3-transition/src/transition/attr.js static/jssources/d3-transition/src/transition/attrTween.js static/jssources/d3-transition/src/transition/delay.js static/jssources/d3-transition/src/transition/duration.js static/jssources/d3-transition/src/transition/ease.js static/jssources/d3-transition/src/transition/easeVarying.js static/jssources/d3-transition/src/transition/end.js static/jssources/d3-transition/src/transition/filter.js static/jssources/d3-transition/src/transition/index.js static/jssources/d3-transition/src/transition/interpolate.js static/jssources/d3-transition/src/transition/merge.js static/jssources/d3-transition/src/transition/on.js static/jssources/d3-transition/src/transition/remove.js static/jssources/d3-transition/src/transition/schedule.js static/jssources/d3-transition/src/transition/select.js static/jssources/d3-transition/src/transition/selectAll.js static/jssources/d3-transition/src/transition/selection.js static/jssources/d3-transition/src/transition/style.js static/jssources/d3-transition/src/transition/styleTween.js static/jssources/d3-transition/src/transition/text.js static/jssources/d3-transition/src/transition/textTween.js static/jssources/d3-transition/src/transition/transition.js static/jssources/d3-transition/src/transition/tween.js static/jssources/datatables.net/License.txt static/jssources/datatables.net-bs4/License.txt static/jssources/datatables.net-bs4/js/dataTables.bootstrap4.js static/jssources/datatables.net/js/jquery.dataTables.js static/jssources/dompurify/LICENSE.txt static/jssources/dompurify/dist/purify.js static/jssources/email-validator/LICENSE.txt static/jssources/email-validator/index.js static/jssources/expose-loader/LICENSE.txt static/jssources/expose-loader/dist/runtime/getGlobalThis.js static/jssources/he/LICENSE-MIT.txt.txt static/jssources/he/he.js static/jssources/hex-rgb/index.js static/jssources/hex-rgb/license.txt static/jssources/highlight.js/LICENSE.txt static/jssources/highlight.js/es/index.js static/jssources/highlight.js/lib/core.js static/jssources/highlight.js/lib/index.js static/jssources/highlight.js/lib/languages/1c.js static/jssources/highlight.js/lib/languages/abnf.js static/jssources/highlight.js/lib/languages/accesslog.js static/jssources/highlight.js/lib/languages/actionscript.js static/jssources/highlight.js/lib/languages/ada.js static/jssources/highlight.js/lib/languages/angelscript.js static/jssources/highlight.js/lib/languages/apache.js static/jssources/highlight.js/lib/languages/applescript.js static/jssources/highlight.js/lib/languages/arcade.js static/jssources/highlight.js/lib/languages/arduino.js static/jssources/highlight.js/lib/languages/armasm.js static/jssources/highlight.js/lib/languages/asciidoc.js static/jssources/highlight.js/lib/languages/aspectj.js static/jssources/highlight.js/lib/languages/autohotkey.js static/jssources/highlight.js/lib/languages/autoit.js static/jssources/highlight.js/lib/languages/avrasm.js static/jssources/highlight.js/lib/languages/awk.js static/jssources/highlight.js/lib/languages/axapta.js static/jssources/highlight.js/lib/languages/bash.js static/jssources/highlight.js/lib/languages/basic.js static/jssources/highlight.js/lib/languages/bnf.js static/jssources/highlight.js/lib/languages/brainfuck.js static/jssources/highlight.js/lib/languages/c.js static/jssources/highlight.js/lib/languages/cal.js static/jssources/highlight.js/lib/languages/capnproto.js static/jssources/highlight.js/lib/languages/ceylon.js static/jssources/highlight.js/lib/languages/clean.js static/jssources/highlight.js/lib/languages/clojure-repl.js static/jssources/highlight.js/lib/languages/clojure.js static/jssources/highlight.js/lib/languages/cmake.js static/jssources/highlight.js/lib/languages/coffeescript.js static/jssources/highlight.js/lib/languages/coq.js static/jssources/highlight.js/lib/languages/cos.js static/jssources/highlight.js/lib/languages/cpp.js static/jssources/highlight.js/lib/languages/crmsh.js static/jssources/highlight.js/lib/languages/crystal.js static/jssources/highlight.js/lib/languages/csharp.js static/jssources/highlight.js/lib/languages/csp.js static/jssources/highlight.js/lib/languages/css.js static/jssources/highlight.js/lib/languages/d.js static/jssources/highlight.js/lib/languages/dart.js static/jssources/highlight.js/lib/languages/delphi.js static/jssources/highlight.js/lib/languages/diff.js static/jssources/highlight.js/lib/languages/django.js static/jssources/highlight.js/lib/languages/dns.js static/jssources/highlight.js/lib/languages/dockerfile.js static/jssources/highlight.js/lib/languages/dos.js static/jssources/highlight.js/lib/languages/dsconfig.js static/jssources/highlight.js/lib/languages/dts.js static/jssources/highlight.js/lib/languages/dust.js static/jssources/highlight.js/lib/languages/ebnf.js static/jssources/highlight.js/lib/languages/elixir.js static/jssources/highlight.js/lib/languages/elm.js static/jssources/highlight.js/lib/languages/erb.js static/jssources/highlight.js/lib/languages/erlang-repl.js static/jssources/highlight.js/lib/languages/erlang.js static/jssources/highlight.js/lib/languages/excel.js static/jssources/highlight.js/lib/languages/fix.js static/jssources/highlight.js/lib/languages/flix.js static/jssources/highlight.js/lib/languages/fortran.js static/jssources/highlight.js/lib/languages/fsharp.js static/jssources/highlight.js/lib/languages/gams.js static/jssources/highlight.js/lib/languages/gauss.js static/jssources/highlight.js/lib/languages/gcode.js static/jssources/highlight.js/lib/languages/gherkin.js static/jssources/highlight.js/lib/languages/glsl.js static/jssources/highlight.js/lib/languages/gml.js static/jssources/highlight.js/lib/languages/go.js static/jssources/highlight.js/lib/languages/golo.js static/jssources/highlight.js/lib/languages/gradle.js static/jssources/highlight.js/lib/languages/graphql.js static/jssources/highlight.js/lib/languages/groovy.js static/jssources/highlight.js/lib/languages/haml.js static/jssources/highlight.js/lib/languages/handlebars.js static/jssources/highlight.js/lib/languages/haskell.js static/jssources/highlight.js/lib/languages/haxe.js static/jssources/highlight.js/lib/languages/hsp.js static/jssources/highlight.js/lib/languages/http.js static/jssources/highlight.js/lib/languages/hy.js static/jssources/highlight.js/lib/languages/inform7.js static/jssources/highlight.js/lib/languages/ini.js static/jssources/highlight.js/lib/languages/irpf90.js static/jssources/highlight.js/lib/languages/isbl.js static/jssources/highlight.js/lib/languages/java.js static/jssources/highlight.js/lib/languages/javascript.js static/jssources/highlight.js/lib/languages/jboss-cli.js static/jssources/highlight.js/lib/languages/json.js static/jssources/highlight.js/lib/languages/julia-repl.js static/jssources/highlight.js/lib/languages/julia.js static/jssources/highlight.js/lib/languages/kotlin.js static/jssources/highlight.js/lib/languages/lasso.js static/jssources/highlight.js/lib/languages/latex.js static/jssources/highlight.js/lib/languages/ldif.js static/jssources/highlight.js/lib/languages/leaf.js static/jssources/highlight.js/lib/languages/less.js static/jssources/highlight.js/lib/languages/lisp.js static/jssources/highlight.js/lib/languages/livecodeserver.js static/jssources/highlight.js/lib/languages/livescript.js static/jssources/highlight.js/lib/languages/llvm.js static/jssources/highlight.js/lib/languages/lsl.js static/jssources/highlight.js/lib/languages/lua.js static/jssources/highlight.js/lib/languages/makefile.js static/jssources/highlight.js/lib/languages/markdown.js static/jssources/highlight.js/lib/languages/mathematica.js static/jssources/highlight.js/lib/languages/matlab.js static/jssources/highlight.js/lib/languages/maxima.js static/jssources/highlight.js/lib/languages/mel.js static/jssources/highlight.js/lib/languages/mercury.js static/jssources/highlight.js/lib/languages/mipsasm.js static/jssources/highlight.js/lib/languages/mizar.js static/jssources/highlight.js/lib/languages/mojolicious.js static/jssources/highlight.js/lib/languages/monkey.js static/jssources/highlight.js/lib/languages/moonscript.js static/jssources/highlight.js/lib/languages/n1ql.js static/jssources/highlight.js/lib/languages/nestedtext.js static/jssources/highlight.js/lib/languages/nginx.js static/jssources/highlight.js/lib/languages/nim.js static/jssources/highlight.js/lib/languages/nix.js static/jssources/highlight.js/lib/languages/node-repl.js static/jssources/highlight.js/lib/languages/nsis.js static/jssources/highlight.js/lib/languages/objectivec.js static/jssources/highlight.js/lib/languages/ocaml.js static/jssources/highlight.js/lib/languages/openscad.js static/jssources/highlight.js/lib/languages/oxygene.js static/jssources/highlight.js/lib/languages/parser3.js static/jssources/highlight.js/lib/languages/perl.js static/jssources/highlight.js/lib/languages/pf.js static/jssources/highlight.js/lib/languages/pgsql.js static/jssources/highlight.js/lib/languages/php-template.js static/jssources/highlight.js/lib/languages/php.js static/jssources/highlight.js/lib/languages/plaintext.js static/jssources/highlight.js/lib/languages/pony.js static/jssources/highlight.js/lib/languages/powershell.js static/jssources/highlight.js/lib/languages/processing.js static/jssources/highlight.js/lib/languages/profile.js static/jssources/highlight.js/lib/languages/prolog.js static/jssources/highlight.js/lib/languages/properties.js static/jssources/highlight.js/lib/languages/protobuf.js static/jssources/highlight.js/lib/languages/puppet.js static/jssources/highlight.js/lib/languages/purebasic.js static/jssources/highlight.js/lib/languages/python-repl.js static/jssources/highlight.js/lib/languages/python.js static/jssources/highlight.js/lib/languages/q.js static/jssources/highlight.js/lib/languages/qml.js static/jssources/highlight.js/lib/languages/r.js static/jssources/highlight.js/lib/languages/reasonml.js static/jssources/highlight.js/lib/languages/rib.js static/jssources/highlight.js/lib/languages/roboconf.js static/jssources/highlight.js/lib/languages/routeros.js static/jssources/highlight.js/lib/languages/rsl.js static/jssources/highlight.js/lib/languages/ruby.js static/jssources/highlight.js/lib/languages/ruleslanguage.js static/jssources/highlight.js/lib/languages/rust.js static/jssources/highlight.js/lib/languages/sas.js static/jssources/highlight.js/lib/languages/scala.js static/jssources/highlight.js/lib/languages/scheme.js static/jssources/highlight.js/lib/languages/scilab.js static/jssources/highlight.js/lib/languages/scss.js static/jssources/highlight.js/lib/languages/shell.js static/jssources/highlight.js/lib/languages/smali.js static/jssources/highlight.js/lib/languages/smalltalk.js static/jssources/highlight.js/lib/languages/sml.js static/jssources/highlight.js/lib/languages/sqf.js static/jssources/highlight.js/lib/languages/sql.js static/jssources/highlight.js/lib/languages/stan.js static/jssources/highlight.js/lib/languages/stata.js static/jssources/highlight.js/lib/languages/step21.js static/jssources/highlight.js/lib/languages/stylus.js static/jssources/highlight.js/lib/languages/subunit.js static/jssources/highlight.js/lib/languages/swift.js static/jssources/highlight.js/lib/languages/taggerscript.js static/jssources/highlight.js/lib/languages/tap.js static/jssources/highlight.js/lib/languages/tcl.js static/jssources/highlight.js/lib/languages/thrift.js static/jssources/highlight.js/lib/languages/tp.js static/jssources/highlight.js/lib/languages/twig.js static/jssources/highlight.js/lib/languages/typescript.js static/jssources/highlight.js/lib/languages/vala.js static/jssources/highlight.js/lib/languages/vbnet.js static/jssources/highlight.js/lib/languages/vbscript-html.js static/jssources/highlight.js/lib/languages/vbscript.js static/jssources/highlight.js/lib/languages/verilog.js static/jssources/highlight.js/lib/languages/vhdl.js static/jssources/highlight.js/lib/languages/vim.js static/jssources/highlight.js/lib/languages/wasm.js static/jssources/highlight.js/lib/languages/wren.js static/jssources/highlight.js/lib/languages/x86asm.js static/jssources/highlight.js/lib/languages/xl.js static/jssources/highlight.js/lib/languages/xml.js static/jssources/highlight.js/lib/languages/xquery.js static/jssources/highlight.js/lib/languages/yaml.js static/jssources/highlight.js/lib/languages/zephir.js static/jssources/highlightjs-4d/LICENSE.txt static/jssources/highlightjs-4d/dist/4d.min.js static/jssources/highlightjs-alan/LICENSE.txt static/jssources/highlightjs-alan/dist/alan.min.js static/jssources/highlightjs-blade/LICENSE.txt static/jssources/highlightjs-blade/dist/blade.min.js static/jssources/highlightjs-chaos/LICENSE.txt static/jssources/highlightjs-chaos/dist/chaos.min.js static/jssources/highlightjs-chapel/LICENSE.txt static/jssources/highlightjs-chapel/dist/chapel.min.js static/jssources/highlightjs-cpcdos/LICENSE.txt static/jssources/highlightjs-cpcdos/dist/cpc-highlight.min.js static/jssources/highlightjs-cshtml-razor/LICENSE.txt static/jssources/highlightjs-cshtml-razor/dist/cshtml-razor.min.js static/jssources/highlightjs-cypher/LICENSE.txt static/jssources/highlightjs-cypher/dist/cypher.min.js static/jssources/highlightjs-dafny/LICENSE.txt static/jssources/highlightjs-dafny/dist/dafny.min.js static/jssources/highlightjs-dylan/LICENSE.txt static/jssources/highlightjs-dylan/dist/dylan.min.js static/jssources/highlightjs-eta/dist/eta.min.js static/jssources/highlightjs-extempore/LICENSE.txt static/jssources/highlightjs-extempore/dist/extempore.min.js static/jssources/highlightjs-gdscript/LICENSE.txt static/jssources/highlightjs-gdscript/dist/gdscript.min.js static/jssources/highlightjs-gf/LICENSE.txt static/jssources/highlightjs-gf/dist/gf.min.js static/jssources/highlightjs-gsql/dist/gsql.min.js static/jssources/highlightjs-hlsl/LICENSE.txt static/jssources/highlightjs-hlsl/dist/hlsl.min.js static/jssources/highlightjs-jolie/LICENSE.txt static/jssources/highlightjs-jolie/dist/jolie.min.js static/jssources/highlightjs-lean/LICENSE.txt static/jssources/highlightjs-lean/src/lean.js static/jssources/highlightjs-line-numbers.js/LICENSE.txt static/jssources/highlightjs-line-numbers.js/src/highlightjs-line-numbers.js static/jssources/highlightjs-lox/LICENSE.md static/jssources/highlightjs-lox/dist/es/lox.mjs static/jssources/highlightjs-mirc/LICENSE.txt static/jssources/highlightjs-mirc/mirc.js static/jssources/highlightjs-modelica/LICENSE.txt static/jssources/highlightjs-modelica/modelica.js static/jssources/highlightjs-never/LICENSE.txt static/jssources/highlightjs-never/dist/never.min.js static/jssources/highlightjs-octave/LICENSE.txt static/jssources/highlightjs-octave/dist/highlightjs-octave.cjs.js static/jssources/highlightjs-oz/LICENSE.txt static/jssources/highlightjs-oz/dist/oz.min.js static/jssources/highlightjs-qsharp/dist/qsharp.min.js static/jssources/highlightjs-redbol/LICENSE.txt static/jssources/highlightjs-redbol/dist/redbol.min.js static/jssources/highlightjs-robot/LICENSE.txt static/jssources/highlightjs-robot/robot.js static/jssources/highlightjs-robots-txt/LICENSE.txt static/jssources/highlightjs-robots-txt/dist/robots-txt.min.js static/jssources/highlightjs-rpm-specfile/LICENSE.txt static/jssources/highlightjs-rpm-specfile/rpm-specfile.js static/jssources/highlightjs-sap-abap/LICENSE.txt static/jssources/highlightjs-sap-abap/dist/abap.min.js static/jssources/highlightjs-solidity/LICENSE.txt static/jssources/highlightjs-solidity/dist/solidity.min.js static/jssources/highlightjs-svelte/LICENSE.txt static/jssources/highlightjs-svelte/dist/svelte.min.js static/jssources/highlightjs-terraform/LICENSE.txt static/jssources/highlightjs-terraform/terraform.js static/jssources/highlightjs-xsharp/LICENSE.txt static/jssources/highlightjs-xsharp/dist/xsharp.min.js static/jssources/highlightjs-zenscript/LICENSE.txt static/jssources/highlightjs-zenscript/dist/zenscript.min.js static/jssources/highlightjs-zig/LICENSE.txt static/jssources/highlightjs-zig/dist/zig.min.js static/jssources/hightlightjs-papyrus/LICENSE.txt static/jssources/hightlightjs-papyrus/dist/papyrus.min.js static/jssources/html-encoder-decoder/LICENSE.txt static/jssources/html-encoder-decoder/lib/index.js static/jssources/iframe-resizer/LICENSE.txt static/jssources/iframe-resizer/index.js static/jssources/iframe-resizer/js/iframeResizer.contentWindow.js static/jssources/iframe-resizer/js/iframeResizer.js static/jssources/iframe-resizer/js/index.js static/jssources/internmap/LICENSE.txt static/jssources/internmap/src/index.js static/jssources/intro.js/intro.module.js static/jssources/intro.js/license.md static/jssources/iterate-object/LICENSE.txt static/jssources/iterate-object/lib/index.js static/jssources/jquery/LICENSE.txt static/jssources/jquery/dist/jquery.js static/jssources/js-cookie/LICENSE.txt static/jssources/js-cookie/dist/js.cookie.mjs static/jssources/js-year-calendar/LICENSE.txt static/jssources/js-year-calendar/dist/js-year-calendar.js static/jssources/mathjax/LICENSE.txt static/jssources/mathjax/es5/tex-mml-chtml.js static/jssources/notebookjs/LICENSE.txt static/jssources/notebookjs/notebook.js static/jssources/object-fit-images/license.txt static/jssources/object-fit-images/dist/ofi.common-js.js static/jssources/org/LICENSE.txt static/jssources/org/lib/org.js static/jssources/org/lib/org/lexer.js static/jssources/org/lib/org/node.js static/jssources/org/lib/org/parser.js static/jssources/org/lib/org/stream.js static/jssources/org/lib/org/converter/converter.js static/jssources/org/lib/org/converter/html.js static/jssources/pdfjs-dist/LICENSE.txt static/jssources/pdfjs-dist/build/pdf.js static/jssources/pdfjs-dist/build/pdf.worker.js static/jssources/popper.js/dist/esm/popper.js static/jssources/regenerator-runtime/LICENSE.txt static/jssources/regenerator-runtime/runtime.js static/jssources/regex-escape/LICENSE.txt static/jssources/regex-escape/lib/index.js static/jssources/script-loader/LICENSE.txt static/jssources/script-loader/addScript.js static/jssources/showdown/LICENSE.txt static/jssources/showdown/dist/showdown.js static/jssources/tslib/LICENSE.txt static/jssources/tslib/tslib.es6.js static/jssources/waypoints/licenses.txt.txt static/jssources/waypoints/lib/jquery.waypoints.js static/jssources/whatwg-fetch/LICENSE.txt static/jssources/whatwg-fetch/dist/fetch.umd.js static/xml/swh-opensearch.xml swh/__init__.py swh.web.egg-info/PKG-INFO swh.web.egg-info/SOURCES.txt swh.web.egg-info/dependency_links.txt swh.web.egg-info/requires.txt swh.web.egg-info/top_level.txt swh/web/__init__.py swh/web/config.py swh/web/gunicorn_config.py swh/web/manage.py swh/web/py.typed swh/web/urls.py swh/web/add_forge_now/__init__.py swh/web/add_forge_now/apps.py swh/web/add_forge_now/models.py swh/web/add_forge_now/views.py swh/web/add_forge_now/migrations/0001_initial.py swh/web/add_forge_now/migrations/0002_authorized_null_comment.py swh/web/add_forge_now/migrations/0003_request_submitter_forward_username.py swh/web/add_forge_now/migrations/0004_rename_tables.py swh/web/add_forge_now/migrations/__init__.py swh/web/admin/__init__.py swh/web/admin/add_forge_now.py swh/web/admin/adminurls.py swh/web/admin/deposit.py swh/web/admin/mailmap.py swh/web/admin/origin_save.py swh/web/admin/urls.py swh/web/api/__init__.py swh/web/api/apidoc.py swh/web/api/apiresponse.py swh/web/api/apiurls.py swh/web/api/renderers.py swh/web/api/throttling.py swh/web/api/urls.py swh/web/api/utils.py swh/web/api/views/__init__.py swh/web/api/views/add_forge_now.py swh/web/api/views/content.py swh/web/api/views/directory.py swh/web/api/views/graph.py swh/web/api/views/identifiers.py swh/web/api/views/metadata.py swh/web/api/views/origin.py swh/web/api/views/origin_save.py swh/web/api/views/ping.py swh/web/api/views/release.py swh/web/api/views/revision.py swh/web/api/views/snapshot.py swh/web/api/views/stat.py swh/web/api/views/utils.py swh/web/api/views/vault.py swh/web/auth/__init__.py swh/web/auth/apps.py swh/web/auth/mailmap.py swh/web/auth/models.py swh/web/auth/utils.py swh/web/auth/views.py swh/web/auth/management/__init__.py swh/web/auth/management/commands/__init__.py swh/web/auth/management/commands/sync_mailmaps.py swh/web/auth/migrations/0001_initial.py swh/web/auth/migrations/0002_remove_stored_tokens.py swh/web/auth/migrations/0003_delete_oidcuser.py swh/web/auth/migrations/0004_usermailmap.py swh/web/auth/migrations/0005_usermailmapevent.py swh/web/auth/migrations/0006_fix_mailmap_admin_user_id.py swh/web/auth/migrations/__init__.py swh/web/browse/__init__.py swh/web/browse/browseurls.py swh/web/browse/identifiers.py swh/web/browse/snapshot_context.py swh/web/browse/urls.py swh/web/browse/utils.py swh/web/browse/views/__init__.py swh/web/browse/views/content.py swh/web/browse/views/directory.py swh/web/browse/views/origin.py swh/web/browse/views/release.py swh/web/browse/views/revision.py swh/web/browse/views/snapshot.py swh/web/common/__init__.py swh/web/common/apps.py swh/web/common/archive.py swh/web/common/converters.py swh/web/common/exc.py swh/web/common/highlightjs.py swh/web/common/identifiers.py swh/web/common/middlewares.py swh/web/common/models.py swh/web/common/origin_save.py swh/web/common/origin_visits.py swh/web/common/query.py swh/web/common/swh_templatetags.py swh/web/common/typing.py swh/web/common/urlsindex.py swh/web/common/utils.py swh/web/common/management/__init__.py swh/web/common/management/commands/__init__.py swh/web/common/management/commands/refresh_savecodenow_statuses.py swh/web/common/migrations/0001_initial.py swh/web/common/migrations/0002_saveoriginrequest_visit_date.py swh/web/common/migrations/0003_saveoriginrequest_loading_task_status.py swh/web/common/migrations/0004_auto_20190204_1324.py swh/web/common/migrations/0005_remove_duplicated_authorized_origins.py swh/web/common/migrations/0006_rename_origin_type.py swh/web/common/migrations/0007_save_request_task_status_fix_typo.py swh/web/common/migrations/0008_save-code-now_indexes_20210106_1327.py swh/web/common/migrations/0009_saveoriginrequest_visit_status.py swh/web/common/migrations/0010_saveoriginrequest_user_id.py swh/web/common/migrations/0011_saveoriginrequest_user_ids.py swh/web/common/migrations/0012_saveoriginrequest_note.py swh/web/common/migrations/__init__.py swh/web/inbound_email/__init__.py swh/web/inbound_email/apps.py swh/web/inbound_email/signals.py swh/web/inbound_email/utils.py swh/web/inbound_email/management/__init__.py swh/web/inbound_email/management/commands/__init__.py swh/web/inbound_email/management/commands/process_inbound_email.py swh/web/misc/__init__.py swh/web/misc/badges.py swh/web/misc/coverage.py swh/web/misc/fundraising.py swh/web/misc/iframe.py swh/web/misc/metrics.py swh/web/misc/origin_save.py swh/web/misc/urls.py swh/web/settings/__init__.py swh/web/settings/common.py swh/web/settings/development.py swh/web/settings/production.py swh/web/settings/tests.py swh/web/templates/error.html swh/web/templates/homepage.html swh/web/templates/layout.html swh/web/templates/login.html swh/web/templates/logout.html swh/web/templates/add_forge_now/common.html swh/web/templates/add_forge_now/creation_form.html swh/web/templates/add_forge_now/help.html swh/web/templates/add_forge_now/list.html swh/web/templates/add_forge_now/request-dashboard.html swh/web/templates/add_forge_now/requests-moderation.html swh/web/templates/admin/deposit.html swh/web/templates/admin/mailmap.html swh/web/templates/admin/origin-save/common.html swh/web/templates/admin/origin-save/filters.html swh/web/templates/admin/origin-save/requests.html swh/web/templates/api/api.html swh/web/templates/api/apidoc.html swh/web/templates/api/endpoints.html swh/web/templates/auth/profile.html swh/web/templates/browse/branches.html swh/web/templates/browse/browse.html swh/web/templates/browse/content.html swh/web/templates/browse/directory.html swh/web/templates/browse/help.html swh/web/templates/browse/layout.html swh/web/templates/browse/origin-visits.html swh/web/templates/browse/release.html swh/web/templates/browse/releases.html swh/web/templates/browse/revision-log.html swh/web/templates/browse/revision.html swh/web/templates/browse/search.html swh/web/templates/browse/vault-ui.html swh/web/templates/includes/branch-search.html swh/web/templates/includes/breadcrumbs.html swh/web/templates/includes/content-display.html swh/web/templates/includes/directory-display.html swh/web/templates/includes/empty-snapshot.html swh/web/templates/includes/global-modals.html swh/web/templates/includes/http-error.html swh/web/templates/includes/origin-search-form.html swh/web/templates/includes/readme-display.html swh/web/templates/includes/revision-info.html swh/web/templates/includes/show-metadata.html swh/web/templates/includes/show-swhids.html swh/web/templates/includes/snapshot-context.html swh/web/templates/includes/take-new-snapshot.html swh/web/templates/includes/top-navigation.html swh/web/templates/includes/vault-common.html swh/web/templates/includes/vault-create-tasks.html swh/web/templates/misc/coverage.html swh/web/templates/misc/fundraising-banner.html swh/web/templates/misc/iframe.html swh/web/templates/misc/jslicenses.html swh/web/templates/misc/origin-save-help.html swh/web/templates/misc/origin-save-list.html swh/web/templates/misc/origin-save.html swh/web/tests/__init__.py swh/web/tests/conftest.py swh/web/tests/create_test_admin.py swh/web/tests/create_test_users.py swh/web/tests/data.py swh/web/tests/django_asserts.py swh/web/tests/random_fixtures_test.py swh/web/tests/strategies.py swh/web/tests/test_config.py swh/web/tests/test_create_users.py swh/web/tests/test_gunicorn_config.py swh/web/tests/test_migrations.py swh/web/tests/test_random_fixtures.py swh/web/tests/test_templates.py swh/web/tests/test_urls.py swh/web/tests/utils.py swh/web/tests/views.py swh/web/tests/add_forge_now/test_migration.py swh/web/tests/add_forge_now/test_models.py swh/web/tests/add_forge_now/test_views.py swh/web/tests/admin/__init__.py swh/web/tests/admin/test_deposit.py swh/web/tests/admin/test_origin_save.py swh/web/tests/api/__init__.py swh/web/tests/api/test_api_lookup.py swh/web/tests/api/test_apidoc.py swh/web/tests/api/test_apiresponse.py swh/web/tests/api/test_apiurls.py swh/web/tests/api/test_throttling.py swh/web/tests/api/test_utils.py swh/web/tests/api/views/__init__.py swh/web/tests/api/views/test_add_forge_now.py swh/web/tests/api/views/test_content.py swh/web/tests/api/views/test_directory.py swh/web/tests/api/views/test_graph.py swh/web/tests/api/views/test_identifiers.py swh/web/tests/api/views/test_metadata.py swh/web/tests/api/views/test_origin.py swh/web/tests/api/views/test_origin_save.py swh/web/tests/api/views/test_ping.py swh/web/tests/api/views/test_release.py swh/web/tests/api/views/test_revision.py swh/web/tests/api/views/test_snapshot.py swh/web/tests/api/views/test_stat.py swh/web/tests/api/views/test_vault.py swh/web/tests/api/views/utils.py swh/web/tests/auth/__init__.py swh/web/tests/auth/test_mailmap.py swh/web/tests/auth/test_migrations.py swh/web/tests/auth/test_utils.py swh/web/tests/auth/test_views.py swh/web/tests/browse/__init__.py swh/web/tests/browse/test_snapshot_context.py swh/web/tests/browse/test_utils.py swh/web/tests/browse/views/__init__.py swh/web/tests/browse/views/test_content.py swh/web/tests/browse/views/test_directory.py swh/web/tests/browse/views/test_identifiers.py swh/web/tests/browse/views/test_origin.py swh/web/tests/browse/views/test_release.py swh/web/tests/browse/views/test_revision.py swh/web/tests/browse/views/test_snapshot.py swh/web/tests/common/__init__.py swh/web/tests/common/test_archive.py swh/web/tests/common/test_converters.py swh/web/tests/common/test_django_command.py swh/web/tests/common/test_highlightjs.py swh/web/tests/common/test_identifiers.py swh/web/tests/common/test_middlewares.py swh/web/tests/common/test_origin_save.py swh/web/tests/common/test_origin_visits.py swh/web/tests/common/test_query.py swh/web/tests/common/test_templatetags.py swh/web/tests/common/test_utils.py +swh/web/tests/inbound_email/__init__.py swh/web/tests/inbound_email/test_management_command.py swh/web/tests/inbound_email/test_utils.py +swh/web/tests/inbound_email/resources/__init__.py +swh/web/tests/inbound_email/resources/multipart.eml +swh/web/tests/inbound_email/resources/multipart_html_only.eml +swh/web/tests/inbound_email/resources/multipart_text_only.eml +swh/web/tests/inbound_email/resources/plaintext.eml swh/web/tests/misc/__init__.py swh/web/tests/misc/test_badges.py swh/web/tests/misc/test_coverage.py swh/web/tests/misc/test_fundraising.py swh/web/tests/misc/test_iframe.py swh/web/tests/misc/test_metrics.py swh/web/tests/misc/test_origin_save.py swh/web/tests/resources/contents/code/LICENSE swh/web/tests/resources/contents/code/extensions/test.R swh/web/tests/resources/contents/code/extensions/test.abnf swh/web/tests/resources/contents/code/extensions/test.adb swh/web/tests/resources/contents/code/extensions/test.adoc swh/web/tests/resources/contents/code/extensions/test.ahk swh/web/tests/resources/contents/code/extensions/test.aj swh/web/tests/resources/contents/code/extensions/test.applescript swh/web/tests/resources/contents/code/extensions/test.as swh/web/tests/resources/contents/code/extensions/test.au3 swh/web/tests/resources/contents/code/extensions/test.awk swh/web/tests/resources/contents/code/extensions/test.bas swh/web/tests/resources/contents/code/extensions/test.bat swh/web/tests/resources/contents/code/extensions/test.bf swh/web/tests/resources/contents/code/extensions/test.bnf swh/web/tests/resources/contents/code/extensions/test.bsl swh/web/tests/resources/contents/code/extensions/test.cal swh/web/tests/resources/contents/code/extensions/test.capnp swh/web/tests/resources/contents/code/extensions/test.ceylon swh/web/tests/resources/contents/code/extensions/test.clj swh/web/tests/resources/contents/code/extensions/test.cls swh/web/tests/resources/contents/code/extensions/test.cmake swh/web/tests/resources/contents/code/extensions/test.coffee swh/web/tests/resources/contents/code/extensions/test.cpp swh/web/tests/resources/contents/code/extensions/test.cr swh/web/tests/resources/contents/code/extensions/test.cs swh/web/tests/resources/contents/code/extensions/test.css swh/web/tests/resources/contents/code/extensions/test.d swh/web/tests/resources/contents/code/extensions/test.dart swh/web/tests/resources/contents/code/extensions/test.dcl swh/web/tests/resources/contents/code/extensions/test.dfm swh/web/tests/resources/contents/code/extensions/test.diff swh/web/tests/resources/contents/code/extensions/test.do swh/web/tests/resources/contents/code/extensions/test.dts swh/web/tests/resources/contents/code/extensions/test.dust swh/web/tests/resources/contents/code/extensions/test.ebnf swh/web/tests/resources/contents/code/extensions/test.elm swh/web/tests/resources/contents/code/extensions/test.ep swh/web/tests/resources/contents/code/extensions/test.erb swh/web/tests/resources/contents/code/extensions/test.erl swh/web/tests/resources/contents/code/extensions/test.ex swh/web/tests/resources/contents/code/extensions/test.f90 swh/web/tests/resources/contents/code/extensions/test.feature swh/web/tests/resources/contents/code/extensions/test.flix swh/web/tests/resources/contents/code/extensions/test.fs swh/web/tests/resources/contents/code/extensions/test.gcode swh/web/tests/resources/contents/code/extensions/test.glsl swh/web/tests/resources/contents/code/extensions/test.gml swh/web/tests/resources/contents/code/extensions/test.gms swh/web/tests/resources/contents/code/extensions/test.go swh/web/tests/resources/contents/code/extensions/test.golo swh/web/tests/resources/contents/code/extensions/test.gradle swh/web/tests/resources/contents/code/extensions/test.groovy swh/web/tests/resources/contents/code/extensions/test.gss swh/web/tests/resources/contents/code/extensions/test.haml swh/web/tests/resources/contents/code/extensions/test.hbs swh/web/tests/resources/contents/code/extensions/test.hs swh/web/tests/resources/contents/code/extensions/test.hsp swh/web/tests/resources/contents/code/extensions/test.html swh/web/tests/resources/contents/code/extensions/test.hx swh/web/tests/resources/contents/code/extensions/test.hy swh/web/tests/resources/contents/code/extensions/test.ini swh/web/tests/resources/contents/code/extensions/test.ino swh/web/tests/resources/contents/code/extensions/test.java swh/web/tests/resources/contents/code/extensions/test.jl swh/web/tests/resources/contents/code/extensions/test.js swh/web/tests/resources/contents/code/extensions/test.json swh/web/tests/resources/contents/code/extensions/test.kt swh/web/tests/resources/contents/code/extensions/test.lasso swh/web/tests/resources/contents/code/extensions/test.lc swh/web/tests/resources/contents/code/extensions/test.ldif swh/web/tests/resources/contents/code/extensions/test.leaf swh/web/tests/resources/contents/code/extensions/test.less swh/web/tests/resources/contents/code/extensions/test.lisp swh/web/tests/resources/contents/code/extensions/test.ll swh/web/tests/resources/contents/code/extensions/test.ls swh/web/tests/resources/contents/code/extensions/test.lsl swh/web/tests/resources/contents/code/extensions/test.lua swh/web/tests/resources/contents/code/extensions/test.m swh/web/tests/resources/contents/code/extensions/test.md swh/web/tests/resources/contents/code/extensions/test.mel swh/web/tests/resources/contents/code/extensions/test.mk swh/web/tests/resources/contents/code/extensions/test.ml swh/web/tests/resources/contents/code/extensions/test.moon swh/web/tests/resources/contents/code/extensions/test.nim swh/web/tests/resources/contents/code/extensions/test.nix swh/web/tests/resources/contents/code/extensions/test.nsi swh/web/tests/resources/contents/code/extensions/test.p swh/web/tests/resources/contents/code/extensions/test.pbi swh/web/tests/resources/contents/code/extensions/test.pde swh/web/tests/resources/contents/code/extensions/test.php swh/web/tests/resources/contents/code/extensions/test.pl swh/web/tests/resources/contents/code/extensions/test.pony swh/web/tests/resources/contents/code/extensions/test.pp swh/web/tests/resources/contents/code/extensions/test.properties swh/web/tests/resources/contents/code/extensions/test.proto swh/web/tests/resources/contents/code/extensions/test.ps1 swh/web/tests/resources/contents/code/extensions/test.py swh/web/tests/resources/contents/code/extensions/test.q swh/web/tests/resources/contents/code/extensions/test.qml swh/web/tests/resources/contents/code/extensions/test.rb swh/web/tests/resources/contents/code/extensions/test.re swh/web/tests/resources/contents/code/extensions/test.rib swh/web/tests/resources/contents/code/extensions/test.rs swh/web/tests/resources/contents/code/extensions/test.rsc swh/web/tests/resources/contents/code/extensions/test.s swh/web/tests/resources/contents/code/extensions/test.sas swh/web/tests/resources/contents/code/extensions/test.scad swh/web/tests/resources/contents/code/extensions/test.scala swh/web/tests/resources/contents/code/extensions/test.sci swh/web/tests/resources/contents/code/extensions/test.scm swh/web/tests/resources/contents/code/extensions/test.scss swh/web/tests/resources/contents/code/extensions/test.sh swh/web/tests/resources/contents/code/extensions/test.sl swh/web/tests/resources/contents/code/extensions/test.smali swh/web/tests/resources/contents/code/extensions/test.sml swh/web/tests/resources/contents/code/extensions/test.sqf swh/web/tests/resources/contents/code/extensions/test.st swh/web/tests/resources/contents/code/extensions/test.stan swh/web/tests/resources/contents/code/extensions/test.styl swh/web/tests/resources/contents/code/extensions/test.subunit swh/web/tests/resources/contents/code/extensions/test.swift swh/web/tests/resources/contents/code/extensions/test.tap swh/web/tests/resources/contents/code/extensions/test.tcl swh/web/tests/resources/contents/code/extensions/test.tex swh/web/tests/resources/contents/code/extensions/test.thrift swh/web/tests/resources/contents/code/extensions/test.ts swh/web/tests/resources/contents/code/extensions/test.v swh/web/tests/resources/contents/code/extensions/test.vala swh/web/tests/resources/contents/code/extensions/test.vb swh/web/tests/resources/contents/code/extensions/test.vbs swh/web/tests/resources/contents/code/extensions/test.vhd swh/web/tests/resources/contents/code/extensions/test.vim swh/web/tests/resources/contents/code/extensions/test.wl swh/web/tests/resources/contents/code/extensions/test.xml swh/web/tests/resources/contents/code/extensions/test.xqy swh/web/tests/resources/contents/code/extensions/test.yml swh/web/tests/resources/contents/code/extensions/test.zep swh/web/tests/resources/contents/code/filenames/.htaccess swh/web/tests/resources/contents/code/filenames/CMakeLists.txt swh/web/tests/resources/contents/code/filenames/Dockerfile swh/web/tests/resources/contents/code/filenames/Makefile swh/web/tests/resources/contents/code/filenames/access.log swh/web/tests/resources/contents/code/filenames/httpd.conf swh/web/tests/resources/contents/code/filenames/nginx.conf swh/web/tests/resources/contents/code/filenames/nginx.log swh/web/tests/resources/contents/code/filenames/pf.conf swh/web/tests/resources/contents/code/filenames/resolv.conf swh/web/tests/resources/contents/other/extensions/bash-cheatsheet.pdf swh/web/tests/resources/contents/other/extensions/public.gpg swh/web/tests/resources/contents/other/extensions/swh-logo.jpeg swh/web/tests/resources/contents/other/extensions/swh-logo.png swh/web/tests/resources/contents/other/extensions/swh-logo.webp swh/web/tests/resources/contents/other/extensions/swh-spinner.gif swh/web/tests/resources/contents/other/extensions/word2vec.ipynb swh/web/tests/resources/deposit/raw-metadata-add-to-origin.xml swh/web/tests/resources/deposit/raw-metadata-create-origin.xml swh/web/tests/resources/deposit/raw-metadata-no-swh.xml swh/web/tests/resources/deposit/raw-metadata-provenance.xml swh/web/tests/resources/http_esnode1.internal.softwareheritage.org/swh_workers-*__search swh/web/tests/resources/repos/highlightjs-line-numbers.js.zip swh/web/tests/resources/repos/highlightjs-line-numbers.js_visit2.zip swh/web/tests/resources/repos/libtess2.zip swh/web/tests/resources/repos/repo_with_submodules.tgz \ No newline at end of file diff --git a/swh.web.egg-info/requires.txt b/swh.web.egg-info/requires.txt index e103f418..47fe7924 100644 --- a/swh.web.egg-info/requires.txt +++ b/swh.web.egg-info/requires.txt @@ -1,52 +1,52 @@ beautifulsoup4 chardet cryptography django<3 django-cors-headers django-js-reverse djangorestframework django-webpack-loader docutils htmlmin iso8601 lxml prometheus-client pybadges>=2.2.1 pygments python-magic>=0.4.0 python-memcached pyyaml requests sentry-sdk typing-extensions psycopg2<2.9 swh.auth[django]>=0.5.3 swh.core>=0.0.95 swh.counters>=0.5.1 swh.indexer>=0.4.1 swh.model>=2.6.3 swh.scheduler>=0.7.0 swh.search>=0.2.0 swh.storage>=0.31.0 swh.vault>=1.0.0 [testing] decorator djangorestframework-stubs django-stubs django-test-migrations hypothesis -pytest<7.0.0 +pytest pytest-django pytest-mock pytest-postgresql<4.0.0 requests-mock!=1.9.0,!=1.9.1 swh.core[http]>=0.0.95 swh.loader.git>=0.8.0 swh-scheduler[testing]>=0.5.0 swh.storage>=0.1.1 types-chardet types-docutils types-psycopg2 types-pyyaml types-requests diff --git a/swh/web/common/utils.py b/swh/web/common/utils.py index 098bbad1..da65d01b 100644 --- a/swh/web/common/utils.py +++ b/swh/web/common/utils.py @@ -1,549 +1,549 @@ # Copyright (C) 2017-2022 The Software Heritage developers # See the AUTHORS file at the top-level directory of this distribution # License: GNU Affero General Public License version 3, or any later version # See top-level LICENSE file for more information from datetime import datetime, timezone import os import re from typing import Any, Dict, List, Optional import urllib.parse from xml.etree import ElementTree from bs4 import BeautifulSoup from docutils.core import publish_parts import docutils.parsers.rst import docutils.utils from docutils.writers.html5_polyglot import HTMLTranslator, Writer from iso8601 import ParseError, parse_date from pkg_resources import get_distribution from prometheus_client.registry import CollectorRegistry import requests from requests.auth import HTTPBasicAuth from django.core.cache import cache from django.http import HttpRequest, QueryDict from django.shortcuts import redirect from django.urls import resolve from django.urls import reverse as django_reverse from swh.web.auth.utils import ( ADD_FORGE_MODERATOR_PERMISSION, ADMIN_LIST_DEPOSIT_PERMISSION, MAILMAP_ADMIN_PERMISSION, ) from swh.web.common.exc import BadInputExc from swh.web.common.typing import QueryParameters from swh.web.config import SWH_WEB_SERVER_NAME, get_config, search SWH_WEB_METRICS_REGISTRY = CollectorRegistry(auto_describe=True) swh_object_icons = { "alias": "mdi mdi-star", "branch": "mdi mdi-source-branch", "branches": "mdi mdi-source-branch", "content": "mdi mdi-file-document", "cnt": "mdi mdi-file-document", "directory": "mdi mdi-folder", "dir": "mdi mdi-folder", "origin": "mdi mdi-source-repository", "ori": "mdi mdi-source-repository", "person": "mdi mdi-account", "revisions history": "mdi mdi-history", "release": "mdi mdi-tag", "rel": "mdi mdi-tag", "releases": "mdi mdi-tag", "revision": "mdi mdi-rotate-90 mdi-source-commit", "rev": "mdi mdi-rotate-90 mdi-source-commit", "snapshot": "mdi mdi-camera", "snp": "mdi mdi-camera", "visits": "mdi mdi-calendar-month", } def reverse( viewname: str, url_args: Optional[Dict[str, Any]] = None, query_params: Optional[QueryParameters] = None, current_app: Optional[str] = None, urlconf: Optional[str] = None, request: Optional[HttpRequest] = None, ) -> str: """An override of django reverse function supporting query parameters. Args: viewname: the name of the django view from which to compute a url url_args: dictionary of url arguments indexed by their names query_params: dictionary of query parameters to append to the reversed url current_app: the name of the django app tighten to the view urlconf: url configuration module request: build an absolute URI if provided Returns: str: the url of the requested view with processed arguments and query parameters """ if url_args: url_args = {k: v for k, v in url_args.items() if v is not None} url = django_reverse( viewname, urlconf=urlconf, kwargs=url_args, current_app=current_app ) if query_params: query_params = {k: v for k, v in query_params.items() if v is not None} if query_params and len(query_params) > 0: query_dict = QueryDict("", mutable=True) for k in sorted(query_params.keys()): query_dict[k] = query_params[k] url += "?" + query_dict.urlencode(safe="/;:") if request is not None: url = request.build_absolute_uri(url) return url def datetime_to_utc(date): """Returns datetime in UTC without timezone info Args: date (datetime.datetime): input datetime with timezone info Returns: datetime.datetime: datetime in UTC without timezone info """ if date.tzinfo and date.tzinfo != timezone.utc: return date.astimezone(tz=timezone.utc) else: return date def parse_iso8601_date_to_utc(iso_date: str) -> datetime: """Given an ISO 8601 datetime string, parse the result as UTC datetime. Returns: a timezone-aware datetime representing the parsed date Raises: swh.web.common.exc.BadInputExc: provided date does not respect ISO 8601 format Samples: - 2016-01-12 - 2016-01-12T09:19:12+0100 - 2007-01-14T20:34:22Z """ try: date = parse_date(iso_date) return datetime_to_utc(date) except ParseError as e: raise BadInputExc(e) def shorten_path(path): """Shorten the given path: for each hash present, only return the first 8 characters followed by an ellipsis""" sha256_re = r"([0-9a-f]{8})[0-9a-z]{56}" sha1_re = r"([0-9a-f]{8})[0-9a-f]{32}" ret = re.sub(sha256_re, r"\1...", path) return re.sub(sha1_re, r"\1...", ret) def format_utc_iso_date(iso_date, fmt="%d %B %Y, %H:%M UTC"): """Turns a string representation of an ISO 8601 datetime string to UTC and format it into a more human readable one. For instance, from the following input string: '2017-05-04T13:27:13+02:00' the following one is returned: '04 May 2017, 11:27 UTC'. Custom format string may also be provided as parameter Args: iso_date (str): a string representation of an ISO 8601 date fmt (str): optional date formatting string Returns: str: a formatted string representation of the input iso date """ if not iso_date: return iso_date date = parse_iso8601_date_to_utc(iso_date) return date.strftime(fmt) def gen_path_info(path): """Function to generate path data navigation for use with a breadcrumb in the swh web ui. For instance, from a path /folder1/folder2/folder3, it returns the following list:: [{'name': 'folder1', 'path': 'folder1'}, {'name': 'folder2', 'path': 'folder1/folder2'}, {'name': 'folder3', 'path': 'folder1/folder2/folder3'}] Args: path: a filesystem path Returns: list: a list of path data for navigation as illustrated above. """ path_info = [] if path: sub_paths = path.strip("/").split("/") path_from_root = "" for p in sub_paths: path_from_root += "/" + p path_info.append({"name": p, "path": path_from_root.strip("/")}) return path_info def parse_rst(text, report_level=2): """ Parse a reStructuredText string with docutils. Args: text (str): string with reStructuredText markups in it report_level (int): level of docutils report messages to print (1 info 2 warning 3 error 4 severe 5 none) Returns: docutils.nodes.document: a parsed docutils document """ parser = docutils.parsers.rst.Parser() components = (docutils.parsers.rst.Parser,) settings = docutils.frontend.OptionParser( components=components ).get_default_values() settings.report_level = report_level document = docutils.utils.new_document("rst-doc", settings=settings) parser.parse(text, document) return document def get_client_ip(request): """ Return the client IP address from an incoming HTTP request. Args: request (django.http.HttpRequest): the incoming HTTP request Returns: str: The client IP address """ x_forwarded_for = request.META.get("HTTP_X_FORWARDED_FOR") if x_forwarded_for: ip = x_forwarded_for.split(",")[0] else: ip = request.META.get("REMOTE_ADDR") return ip def is_swh_web_development(request: HttpRequest) -> bool: """Indicate if we are running a development version of swh-web. """ site_base_url = request.build_absolute_uri("/") return any( host in site_base_url for host in ("localhost", "127.0.0.1", "testserver") ) def is_swh_web_staging(request: HttpRequest) -> bool: """Indicate if we are running a staging version of swh-web. """ config = get_config() site_base_url = request.build_absolute_uri("/") return any( server_name in site_base_url for server_name in config["staging_server_names"] ) def is_swh_web_production(request: HttpRequest) -> bool: """Indicate if we are running the public production version of swh-web. """ return SWH_WEB_SERVER_NAME in request.build_absolute_uri("/") browsers_supported_image_mimes = set( [ "image/gif", "image/png", "image/jpeg", "image/bmp", "image/webp", "image/svg", "image/svg+xml", ] ) def context_processor(request): """ Django context processor used to inject variables in all swh-web templates. """ config = get_config() if ( hasattr(request, "user") and request.user.is_authenticated and not hasattr(request.user, "backend") ): # To avoid django.template.base.VariableDoesNotExist errors # when rendering templates when standard Django user is logged in. request.user.backend = "django.contrib.auth.backends.ModelBackend" return { "swh_object_icons": swh_object_icons, "available_languages": None, "swh_client_config": config["client_config"], "oidc_enabled": bool(config["keycloak"]["server_url"]), "browsers_supported_image_mimes": browsers_supported_image_mimes, "keycloak": config["keycloak"], "site_base_url": request.build_absolute_uri("/"), "DJANGO_SETTINGS_MODULE": os.environ["DJANGO_SETTINGS_MODULE"], "status": config["status"], "swh_web_dev": is_swh_web_development(request), "swh_web_staging": is_swh_web_staging(request), "swh_web_version": get_distribution("swh.web").version, "iframe_mode": False, "ADMIN_LIST_DEPOSIT_PERMISSION": ADMIN_LIST_DEPOSIT_PERMISSION, "ADD_FORGE_MODERATOR_PERMISSION": ADD_FORGE_MODERATOR_PERMISSION, "FEATURES": get_config()["features"], "MAILMAP_ADMIN_PERMISSION": MAILMAP_ADMIN_PERMISSION, } def resolve_branch_alias( snapshot: Dict[str, Any], branch: Optional[Dict[str, Any]] ) -> Optional[Dict[str, Any]]: """ Resolve branch alias in snapshot content. Args: snapshot: a full snapshot content branch: a branch alias contained in the snapshot Returns: The real snapshot branch that got aliased. """ while branch and branch["target_type"] == "alias": if branch["target"] in snapshot["branches"]: branch = snapshot["branches"][branch["target"]] else: from swh.web.common import archive snp = archive.lookup_snapshot( snapshot["id"], branches_from=branch["target"], branches_count=1 ) if snp and branch["target"] in snp["branches"]: branch = snp["branches"][branch["target"]] else: branch = None return branch class _NoHeaderHTMLTranslator(HTMLTranslator): """ Docutils translator subclass to customize the generation of HTML from reST-formatted docstrings """ def __init__(self, document): super().__init__(document) self.body_prefix = [] self.body_suffix = [] _HTML_WRITER = Writer() _HTML_WRITER.translator_class = _NoHeaderHTMLTranslator def rst_to_html(rst: str) -> str: """ Convert reStructuredText document into HTML. Args: rst: A string containing a reStructuredText document Returns: Body content of the produced HTML conversion. """ settings = { "initial_header_level": 2, "halt_level": 4, "traceback": True, "file_insertion_enabled": False, "raw_enabled": False, } pp = publish_parts(rst, writer=_HTML_WRITER, settings_overrides=settings) return f'
{pp["html_body"]}
' def prettify_html(html: str) -> str: """ Prettify an HTML document. Args: html: Input HTML document Returns: The prettified HTML document """ return BeautifulSoup(html, "lxml").prettify() def _deposits_list_url( deposits_list_base_url: str, page_size: int, username: Optional[str] ) -> str: params = {"page_size": str(page_size)} if username is not None: params["username"] = username return f"{deposits_list_base_url}?{urllib.parse.urlencode(params)}" def get_deposits_list(username: Optional[str] = None) -> List[Dict[str, Any]]: """Return the list of software deposits using swh-deposit API """ config = get_config()["deposit"] deposits_list_base_url = config["private_api_url"] + "deposits" deposits_list_auth = HTTPBasicAuth( config["private_api_user"], config["private_api_password"] ) deposits_list_url = _deposits_list_url( deposits_list_base_url, page_size=1, username=username ) nb_deposits = requests.get( deposits_list_url, auth=deposits_list_auth, timeout=30 ).json()["count"] cache_key = f"swh-deposit-list-{username}" deposits_data = cache.get(cache_key) if not deposits_data or deposits_data["count"] != nb_deposits: deposits_list_url = _deposits_list_url( deposits_list_base_url, page_size=nb_deposits, username=username ) deposits_data = requests.get( deposits_list_url, auth=deposits_list_auth, timeout=30, ).json() cache.set(cache_key, deposits_data) return deposits_data["results"] def get_deposit_raw_metadata(deposit_id: int) -> Optional[str]: cache_key = f"swh-deposit-raw-metadata-{deposit_id}" metadata = cache.get(cache_key) if metadata is None: config = get_config()["deposit"] url = f"{config['private_api_url']}/{deposit_id}/meta" - metadata = requests.get(url).json()["metadata_raw"] + metadata = requests.get(url).json()["raw_metadata"] cache.set(cache_key, metadata) return metadata def origin_visit_types() -> List[str]: """Return the exhaustive list of visit types for origins ingested into the archive. """ try: return sorted(search().visit_types_count().keys()) except Exception: return [] def redirect_to_new_route(request, new_route, permanent=True): """Redirect a request to another route with url args and query parameters eg: /origin//log?path=test can be redirected as /log?url=&path=test. This can be used to deprecate routes """ request_path = resolve(request.path_info) args = {**request_path.kwargs, **request.GET.dict()} return redirect(reverse(new_route, query_params=args), permanent=permanent,) NAMESPACES = { "swh": "https://www.softwareheritage.org/schema/2018/deposit", "schema": "http://schema.org/", } def parse_swh_metadata_provenance(raw_metadata: str) -> Optional[str]: """Parse swh metadata-provenance out of the raw metadata deposit. If found, returns the value, None otherwise. .. code-block:: xml https://example.org/metadata/url Args: raw_metadata: raw metadata out of deposits received Returns: Either the metadata provenance url if any or None otherwise """ metadata = ElementTree.fromstring(raw_metadata) url = metadata.findtext( "swh:deposit/swh:metadata-provenance/schema:url", namespaces=NAMESPACES, ) return url or None def parse_swh_deposit_origin(raw_metadata: str) -> Optional[str]: """Parses and from metadata document, if any. They are mutually exclusive and tested as such in the deposit. .. code-block:: xml .. code-block:: xml Returns: The one not null if any, None otherwise """ metadata = ElementTree.fromstring(raw_metadata) for origin_tag in ["create_origin", "add_to_origin"]: elt = metadata.find( f"swh:deposit/swh:{origin_tag}/swh:origin[@url]", namespaces=NAMESPACES ) if elt is not None: return elt.attrib["url"] return None def has_add_forge_now_permission(user) -> bool: """Is a user considered an add-forge-now moderator? Returns True if a user is staff or has add forge now moderator permission """ return user.is_staff or user.has_perm(ADD_FORGE_MODERATOR_PERMISSION) diff --git a/swh/web/inbound_email/utils.py b/swh/web/inbound_email/utils.py index b469e40f..9a210626 100644 --- a/swh/web/inbound_email/utils.py +++ b/swh/web/inbound_email/utils.py @@ -1,80 +1,204 @@ # Copyright (C) 2022 The Software Heritage developers # See the AUTHORS file at the top-level directory of this distribution # License: GNU Affero General Public License version 3, or any later version # See top-level LICENSE file for more information from dataclasses import dataclass from email.headerregistry import Address from email.message import EmailMessage -from typing import List, Optional +import logging +from typing import List, Optional, Set + +from django.core.signing import Signer +from django.utils.crypto import constant_time_compare + +logger = logging.getLogger(__name__) def extract_recipients(message: EmailMessage) -> List[Address]: """Extract a list of recipients of the `message`. This uses the ``To`` and ``Cc`` fields. """ ret = [] for header_name in ("to", "cc"): for header in message.get_all(header_name, []): ret.extend(header.addresses) return ret @dataclass class AddressMatch: """Data related to a recipient match""" recipient: Address """The original recipient that matched the expected address""" extension: Optional[str] """The parsed +-extension of the matched recipient address""" def single_recipient_matches( recipient: Address, address: str ) -> Optional[AddressMatch]: """Check whether a single address matches the provided base address. The match is case-insensitive, which is not really RFC-compliant but is consistent with what most people would expect. This function supports "+-addressing", where the local part of the email address is appended with a `+`. """ parsed_address = Address(addr_spec=address.lower()) if recipient.domain.lower() != parsed_address.domain: return None base_username, _, extension = recipient.username.partition("+") if base_username.lower() != parsed_address.username: return None return AddressMatch(recipient=recipient, extension=extension or None) def recipient_matches(message: EmailMessage, address: str) -> List[AddressMatch]: """Check whether any of the message recipients match the given address. The match is case-insensitive, which is not really RFC-compliant but matches what most people would expect. This function supports "+-addressing", where the local part of the email address is appended with a `+`. """ ret = [] for recipient in extract_recipients(message): match = single_recipient_matches(recipient, address) if match: ret.append(match) return ret + + +ADDRESS_SIGNER_SEP = "." +"""Separator for email address signatures""" + + +def get_address_signer(salt: str) -> Signer: + """Get a signer for the given seed""" + return Signer(salt=salt, sep=ADDRESS_SIGNER_SEP) + + +def get_address_for_pk(salt: str, base_address: str, pk: int) -> str: + """Get the email address that will be able to receive messages to be logged in + this request.""" + if "@" not in base_address: + raise ValueError("Base address needs to contain an @") + + username, domain = base_address.split("@") + + extension = get_address_signer(salt).sign(str(pk)) + + return f"{username}+{extension}@{domain}" + + +def get_pk_from_extension(salt: str, extension: str) -> int: + """Retrieve the primary key for the given inbound address extension. + + We reimplement `Signer.unsign`, because the extension can be casemapped at any + point in the email chain (even though email is, theoretically, case sensitive), + so we have to compare lowercase versions of both the extension and the + signature... + + Raises ValueError if the signature couldn't be verified. + + """ + + value, signature = extension.rsplit(ADDRESS_SIGNER_SEP, 1) + expected_signature = get_address_signer(salt).signature(value) + if not constant_time_compare(signature.lower(), expected_signature.lower()): + raise ValueError(f"Invalid signature for extension {extension}") + + return int(value) + + +def get_pks_from_message( + salt: str, base_address: str, message: EmailMessage +) -> Set[int]: + """Retrieve the set of primary keys that were successfully decoded from the + recipients of the ``message`` matching ``base_address``. + + This uses :func:`recipient_matches` to retrieve all the recipient addresses matching + ``base_address``, then :func:`get_pk_from_extension` to decode the primary key and + verify the signature for every extension. To generate relevant email addresses, use + :func:`get_address_for_pk` with the same ``base_address`` and ``salt``. + + Returns: + the set of primary keys that were successfully decoded from the recipients of the + ``message`` + + """ + ret: Set[int] = set() + + for match in recipient_matches(message, base_address): + extension = match.extension + if extension is None: + logger.debug( + "Recipient address %s cannot be matched to a request, ignoring", + match.recipient.addr_spec, + ) + continue + + try: + ret.add(get_pk_from_extension(salt, extension)) + except ValueError: + logger.debug( + "Recipient address %s failed validation", match.recipient.addr_spec + ) + continue + + return ret + + +def get_message_plaintext(message: EmailMessage) -> Optional[bytes]: + """Get the plaintext body for a given message, if any such part exists. If only a html + part exists, return that instead. + + If there are multiple, ambiguous plain text or html parts in the message, this + function will return the largest of them. + + """ + if not message.is_multipart(): + single_part = message.get_payload(decode=True).rstrip(b"\n") + return single_part or None + + text_parts: List[bytes] = [] + fallback_parts: List[bytes] = [] + + for part in message.walk(): + content_type = part.get_content_type() + content_disposition = str(part.get("Content-Disposition")) + if "attachment" in content_disposition: + continue + if content_type == "text/plain": + current_part = part.get_payload(decode=True).rstrip(b"\n") + if current_part: + text_parts.append(current_part) + elif not text_parts and content_type == "text/html": + current_part = part.get_payload(decode=True).rstrip(b"\n") + if current_part: + fallback_parts.append(current_part) + + if text_parts: + return max(text_parts, key=len) + + if fallback_parts: + return max(fallback_parts, key=len) + + return None diff --git a/swh/web/tests/inbound_email/__init__.py b/swh/web/tests/inbound_email/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/swh/web/tests/inbound_email/resources/__init__.py b/swh/web/tests/inbound_email/resources/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/swh/web/tests/inbound_email/resources/multipart.eml b/swh/web/tests/inbound_email/resources/multipart.eml new file mode 100644 index 00000000..b148efe4 --- /dev/null +++ b/swh/web/tests/inbound_email/resources/multipart.eml @@ -0,0 +1,24 @@ +Return-Path: +X-Mailer: MessagingEngine.com Webmail Interface +User-Agent: Cyrus-JMAP/3.7.0-alpha0-382-g88b93171a9-fm-20220330.001-g88b93171 +Mime-Version: 1.0 +Message-Id: +Date: Mon, 04 Apr 2022 17:10:00 +0200 +From: "Test User" +To: test@example.com +Subject: Multipart email +Content-Type: multipart/alternative; + boundary=67575b1b68b24603a2d00f02e032c975 + +--67575b1b68b24603a2d00f02e032c975 +Content-Type: text/plain + +*Multipart email.* + +-- +Test User +--67575b1b68b24603a2d00f02e032c975 +Content-Type: text/html + +
Multipart email.

-- 
Test User
+--67575b1b68b24603a2d00f02e032c975-- diff --git a/swh/web/tests/inbound_email/resources/multipart_html_only.eml b/swh/web/tests/inbound_email/resources/multipart_html_only.eml new file mode 100644 index 00000000..664d39b5 --- /dev/null +++ b/swh/web/tests/inbound_email/resources/multipart_html_only.eml @@ -0,0 +1,21 @@ +Return-Path: +X-Mailer: MessagingEngine.com Webmail Interface +User-Agent: Cyrus-JMAP/3.7.0-alpha0-382-g88b93171a9-fm-20220330.001-g88b93171 +Mime-Version: 1.0 +Message-Id: +Date: Mon, 04 Apr 2022 17:10:00 +0200 +From: "Test User" +To: test@example.com +Subject: Multipart email +Content-Type: multipart/alternative; + boundary=67575b1b68b24603a2d00f02e032c975 + +--67575b1b68b24603a2d00f02e032c975 +Content-Type: text/html + +
Multipart email (short html part).

-- 
Test User
+--67575b1b68b24603a2d00f02e032c975 +Content-Type: text/html + +
Multipart email (a much longer html part).

-- 
Test User
+--67575b1b68b24603a2d00f02e032c975-- diff --git a/swh/web/tests/inbound_email/resources/multipart_text_only.eml b/swh/web/tests/inbound_email/resources/multipart_text_only.eml new file mode 100644 index 00000000..9430e013 --- /dev/null +++ b/swh/web/tests/inbound_email/resources/multipart_text_only.eml @@ -0,0 +1,27 @@ +Return-Path: +X-Mailer: MessagingEngine.com Webmail Interface +User-Agent: Cyrus-JMAP/3.7.0-alpha0-382-g88b93171a9-fm-20220330.001-g88b93171 +Mime-Version: 1.0 +Message-Id: +Date: Mon, 04 Apr 2022 17:10:00 +0200 +From: "Test User" +To: test@example.com +Subject: Multipart email +Content-Type: multipart/alternative; + boundary=67575b1b68b24603a2d00f02e032c975 + +--67575b1b68b24603a2d00f02e032c975 +Content-Type: text/plain + +*Multipart email.* + +-- +Test User +--67575b1b68b24603a2d00f02e032c975 +Content-Type: text/plain + +*Multipart email, but a longer text part.* + +-- +Test User +--67575b1b68b24603a2d00f02e032c975-- diff --git a/swh/web/tests/inbound_email/resources/plaintext.eml b/swh/web/tests/inbound_email/resources/plaintext.eml new file mode 100644 index 00000000..77588407 --- /dev/null +++ b/swh/web/tests/inbound_email/resources/plaintext.eml @@ -0,0 +1,15 @@ +Return-Path: +X-Mailer: MessagingEngine.com Webmail Interface +User-Agent: Cyrus-JMAP/3.7.0-alpha0-382-g88b93171a9-fm-20220330.001-g88b93171 +Mime-Version: 1.0 +Message-Id: <21e17bca-d6a7-40fb-bab8-5dadd939835b@www.fastmail.com> +Date: Mon, 04 Apr 2022 17:08:04 +0200 +From: "Test User" +To: test@example.com +Subject: Plain text email +Content-Type: text/plain + +Plain text email. + +-- +Test User diff --git a/swh/web/tests/inbound_email/test_utils.py b/swh/web/tests/inbound_email/test_utils.py index 83af934c..9a1ed6e5 100644 --- a/swh/web/tests/inbound_email/test_utils.py +++ b/swh/web/tests/inbound_email/test_utils.py @@ -1,132 +1,298 @@ # Copyright (C) 2022 The Software Heritage developers # See the AUTHORS file at the top-level directory of this distribution # License: GNU Affero General Public License version 3, or any later version # See top-level LICENSE file for more information +import email from email.headerregistry import Address from email.message import EmailMessage +import email.policy +from importlib.resources import open_binary +from typing import List + +import pytest from swh.web.inbound_email import utils def test_extract_recipients(): message = EmailMessage() assert utils.extract_recipients(message) == [] message["To"] = "Test Recipient " assert utils.extract_recipients(message) == [ Address(display_name="Test Recipient", addr_spec="test-recipient@example.com") ] message["Cc"] = ( "test-recipient-2@example.com, " "Another Test Recipient " ) assert utils.extract_recipients(message) == [ Address(display_name="Test Recipient", addr_spec="test-recipient@example.com"), Address(addr_spec="test-recipient-2@example.com"), Address( display_name="Another Test Recipient", addr_spec="test-recipient-3@example.com", ), ] del message["To"] assert utils.extract_recipients(message) == [ Address(addr_spec="test-recipient-2@example.com"), Address( display_name="Another Test Recipient", addr_spec="test-recipient-3@example.com", ), ] def test_single_recipient_matches(): assert ( utils.single_recipient_matches( Address(addr_spec="test@example.com"), "match@example.com" ) is None ) assert utils.single_recipient_matches( Address(addr_spec="match@example.com"), "match@example.com" ) == utils.AddressMatch( recipient=Address(addr_spec="match@example.com"), extension=None ) assert utils.single_recipient_matches( Address(addr_spec="MaTch+12345AbC@exaMple.Com"), "match@example.com" ) == utils.AddressMatch( recipient=Address(addr_spec="MaTch+12345AbC@exaMple.Com"), extension="12345AbC" ) def test_recipient_matches(): message = EmailMessage() assert utils.recipient_matches(message, "match@example.com") == [] message = EmailMessage() message["to"] = "nomatch@example.com" assert utils.recipient_matches(message, "match@example.com") == [] message = EmailMessage() message["to"] = "match@example.com" assert utils.recipient_matches(message, "match@example.com") == [ utils.AddressMatch( recipient=Address(addr_spec="match@example.com"), extension=None ) ] message = EmailMessage() message["to"] = "match+extension@example.com" assert utils.recipient_matches(message, "match@example.com") == [ utils.AddressMatch( recipient=Address(addr_spec="match+extension@example.com"), extension="extension", ) ] message = EmailMessage() message["to"] = "match+weird+plussed+extension@example.com" assert utils.recipient_matches(message, "match@example.com") == [ utils.AddressMatch( recipient=Address(addr_spec="match+weird+plussed+extension@example.com"), extension="weird+plussed+extension", ) ] message = EmailMessage() message["to"] = "nomatch@example.com" message["cc"] = ", ".join( ( "match@example.com", "match@notamatch.example.com", "Another Match ", ) ) assert utils.recipient_matches(message, "match@example.com") == [ utils.AddressMatch( recipient=Address(addr_spec="match@example.com"), extension=None, ), utils.AddressMatch( recipient=Address( display_name="Another Match", addr_spec="match+extension@example.com" ), extension="extension", ), ] def test_recipient_matches_casemapping(): message = EmailMessage() message["to"] = "match@example.com" assert utils.recipient_matches(message, "Match@Example.Com") assert utils.recipient_matches(message, "match@example.com") message = EmailMessage() message["to"] = "Match+weirdCaseMapping@Example.Com" matches = utils.recipient_matches(message, "match@example.com") assert matches assert matches[0].extension == "weirdCaseMapping" + + +def test_get_address_for_pk(): + salt = "test_salt" + pks = [1, 10, 1000] + base_address = "base@example.com" + + addresses = { + pk: utils.get_address_for_pk(salt=salt, base_address=base_address, pk=pk) + for pk in pks + } + + assert len(set(addresses.values())) == len(addresses) + + for pk, address in addresses.items(): + localpart, _, domain = address.partition("@") + base_localpart, _, extension = localpart.partition("+") + assert domain == "example.com" + assert base_localpart == "base" + assert extension.startswith(f"{pk}.") + + +def test_get_address_for_pk_salt(): + pk = 1000 + base_address = "base@example.com" + addresses = [ + utils.get_address_for_pk(salt=salt, base_address=base_address, pk=pk) + for salt in ["salt1", "salt2"] + ] + + assert len(addresses) == len(set(addresses)) + + +def test_get_pks_from_message(): + salt = "test_salt" + pks = [1, 10, 1000] + base_address = "base@example.com" + + addresses = { + pk: utils.get_address_for_pk(salt=salt, base_address=base_address, pk=pk) + for pk in pks + } + + message = EmailMessage() + message["To"] = "test@example.com" + + assert utils.get_pks_from_message(salt, base_address, message) == set() + + message = EmailMessage() + message["To"] = f"Test Address <{addresses[1]}>" + + assert utils.get_pks_from_message(salt, base_address, message) == {1} + + message = EmailMessage() + message["To"] = f"Test Address <{addresses[1]}>" + message["Cc"] = ", ".join( + [ + f"Test Address <{addresses[1]}>", + f"Another Test Address <{addresses[10].lower()}>", + "A Third Address ", + ] + ) + + assert utils.get_pks_from_message(salt, base_address, message) == {1, 10} + + +def test_get_pks_from_message_logging(caplog): + salt = "test_salt" + pks = [1, 10, 1000] + base_address = "base@example.com" + + addresses = { + pk: utils.get_address_for_pk(salt=salt, base_address=base_address, pk=pk) + for pk in pks + } + + message = EmailMessage() + message["To"] = f"Test Address <{base_address}>" + + assert utils.get_pks_from_message(salt, base_address, message) == set() + + relevant_records = [ + record + for record in caplog.records + if record.name == "swh.web.inbound_email.utils" + ] + assert len(relevant_records) == 1 + assert relevant_records[0].levelname == "DEBUG" + assert ( + f"{base_address} cannot be matched to a request" + in relevant_records[0].getMessage() + ) + + # Replace the signature with "mangle{signature}" + mangled_address = addresses[1].replace(".", ".mangle", 1) + + message = EmailMessage() + message["To"] = f"Test Address <{mangled_address}>" + + assert utils.get_pks_from_message(salt, base_address, message) == set() + + relevant_records = [ + record + for record in caplog.records + if record.name == "swh.web.inbound_email.utils" + ] + assert len(relevant_records) == 2 + assert relevant_records[0].levelname == "DEBUG" + assert relevant_records[1].levelname == "DEBUG" + + assert f"{mangled_address} failed" in relevant_records[1].getMessage() + + +@pytest.mark.parametrize( + "filename,expected_parts,expected_absent", + ( + pytest.param( + "plaintext.eml", + [b"Plain text email.\n\n-- \nTest User"], + [], + id="plaintext", + ), + pytest.param( + "multipart.eml", + [b"*Multipart email.*\n\n-- \nTest User"], + [], + id="multipart", + ), + pytest.param( + "multipart_html_only.eml", + [b"", b"Multipart email (a much longer html part)."], + [b"Multipart email (short html part)"], + id="multipart_html_only", + ), + pytest.param( + "multipart_text_only.eml", + [b"*Multipart email, but a longer text part.*\n\n--\nTest User"], + [], + id="multipart_text_only", + ), + ), +) +def test_get_message_plaintext( + filename: str, expected_parts: List[bytes], expected_absent: List[bytes] +): + with open_binary("swh.web.tests.inbound_email.resources", filename) as f: + message = email.message_from_binary_file(f, policy=email.policy.default) + + assert isinstance(message, EmailMessage) + + plaintext = utils.get_message_plaintext(message) + assert plaintext is not None + + if len(expected_parts) == 1: + assert plaintext == expected_parts[0] + else: + for part in expected_parts: + assert part in plaintext + for part in expected_absent: + assert part not in plaintext