eea.api.redirector 0.1__tar.gz → 0.2__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {eea_api_redirector-0.1/eea.api.redirector.egg-info → eea_api_redirector-0.2}/PKG-INFO +42 -15
- {eea_api_redirector-0.1 → eea_api_redirector-0.2}/README.rst +23 -1
- {eea_api_redirector-0.1 → eea_api_redirector-0.2}/docs/HISTORY.txt +18 -13
- eea_api_redirector-0.2/eea/api/redirector/locales/bg/LC_MESSAGES/eea.mo +0 -0
- eea_api_redirector-0.2/eea/api/redirector/locales/bg/LC_MESSAGES/plone.mo +0 -0
- eea_api_redirector-0.2/eea/api/redirector/locales/cs/LC_MESSAGES/eea.mo +0 -0
- eea_api_redirector-0.2/eea/api/redirector/locales/cs/LC_MESSAGES/plone.mo +0 -0
- eea_api_redirector-0.2/eea/api/redirector/locales/da/LC_MESSAGES/eea.mo +0 -0
- eea_api_redirector-0.2/eea/api/redirector/locales/da/LC_MESSAGES/plone.mo +0 -0
- eea_api_redirector-0.2/eea/api/redirector/locales/de/LC_MESSAGES/eea.mo +0 -0
- eea_api_redirector-0.2/eea/api/redirector/locales/de/LC_MESSAGES/plone.mo +0 -0
- eea_api_redirector-0.2/eea/api/redirector/locales/el/LC_MESSAGES/eea.mo +0 -0
- eea_api_redirector-0.2/eea/api/redirector/locales/el/LC_MESSAGES/plone.mo +0 -0
- eea_api_redirector-0.2/eea/api/redirector/locales/en/LC_MESSAGES/eea.mo +0 -0
- eea_api_redirector-0.2/eea/api/redirector/locales/en/LC_MESSAGES/plone.mo +0 -0
- eea_api_redirector-0.2/eea/api/redirector/locales/es/LC_MESSAGES/eea.mo +0 -0
- eea_api_redirector-0.2/eea/api/redirector/locales/es/LC_MESSAGES/plone.mo +0 -0
- eea_api_redirector-0.2/eea/api/redirector/locales/et/LC_MESSAGES/eea.mo +0 -0
- eea_api_redirector-0.2/eea/api/redirector/locales/et/LC_MESSAGES/plone.mo +0 -0
- eea_api_redirector-0.2/eea/api/redirector/locales/eu/LC_MESSAGES/eea.mo +0 -0
- eea_api_redirector-0.2/eea/api/redirector/locales/eu/LC_MESSAGES/plone.mo +0 -0
- eea_api_redirector-0.2/eea/api/redirector/locales/fi/LC_MESSAGES/eea.mo +0 -0
- eea_api_redirector-0.2/eea/api/redirector/locales/fi/LC_MESSAGES/plone.mo +0 -0
- eea_api_redirector-0.2/eea/api/redirector/locales/fr/LC_MESSAGES/eea.mo +0 -0
- eea_api_redirector-0.2/eea/api/redirector/locales/fr/LC_MESSAGES/plone.mo +0 -0
- eea_api_redirector-0.2/eea/api/redirector/locales/hr/LC_MESSAGES/eea.mo +0 -0
- eea_api_redirector-0.2/eea/api/redirector/locales/hr/LC_MESSAGES/plone.mo +0 -0
- eea_api_redirector-0.2/eea/api/redirector/locales/hu/LC_MESSAGES/eea.mo +0 -0
- eea_api_redirector-0.2/eea/api/redirector/locales/hu/LC_MESSAGES/plone.mo +0 -0
- eea_api_redirector-0.2/eea/api/redirector/locales/is/LC_MESSAGES/eea.mo +0 -0
- eea_api_redirector-0.2/eea/api/redirector/locales/is/LC_MESSAGES/plone.mo +0 -0
- eea_api_redirector-0.2/eea/api/redirector/locales/it/LC_MESSAGES/eea.mo +0 -0
- eea_api_redirector-0.2/eea/api/redirector/locales/it/LC_MESSAGES/plone.mo +0 -0
- eea_api_redirector-0.2/eea/api/redirector/locales/kl/LC_MESSAGES/eea.mo +0 -0
- eea_api_redirector-0.2/eea/api/redirector/locales/kl/LC_MESSAGES/plone.mo +0 -0
- eea_api_redirector-0.2/eea/api/redirector/locales/lt/LC_MESSAGES/eea.mo +0 -0
- eea_api_redirector-0.2/eea/api/redirector/locales/lt/LC_MESSAGES/plone.mo +0 -0
- eea_api_redirector-0.2/eea/api/redirector/locales/lv/LC_MESSAGES/eea.mo +0 -0
- eea_api_redirector-0.2/eea/api/redirector/locales/lv/LC_MESSAGES/plone.mo +0 -0
- eea_api_redirector-0.2/eea/api/redirector/locales/mt/LC_MESSAGES/eea.mo +0 -0
- eea_api_redirector-0.2/eea/api/redirector/locales/mt/LC_MESSAGES/plone.mo +0 -0
- eea_api_redirector-0.2/eea/api/redirector/locales/nl/LC_MESSAGES/eea.mo +0 -0
- eea_api_redirector-0.2/eea/api/redirector/locales/nl/LC_MESSAGES/plone.mo +0 -0
- eea_api_redirector-0.2/eea/api/redirector/locales/no/LC_MESSAGES/eea.mo +0 -0
- eea_api_redirector-0.2/eea/api/redirector/locales/no/LC_MESSAGES/plone.mo +0 -0
- eea_api_redirector-0.2/eea/api/redirector/locales/pl/LC_MESSAGES/eea.mo +0 -0
- eea_api_redirector-0.2/eea/api/redirector/locales/pl/LC_MESSAGES/plone.mo +0 -0
- eea_api_redirector-0.2/eea/api/redirector/locales/pt/LC_MESSAGES/eea.mo +0 -0
- eea_api_redirector-0.2/eea/api/redirector/locales/pt/LC_MESSAGES/plone.mo +0 -0
- eea_api_redirector-0.2/eea/api/redirector/locales/pt_BR/LC_MESSAGES/eea.mo +0 -0
- eea_api_redirector-0.2/eea/api/redirector/locales/pt_BR/LC_MESSAGES/plone.mo +0 -0
- eea_api_redirector-0.2/eea/api/redirector/locales/ro/LC_MESSAGES/eea.mo +0 -0
- eea_api_redirector-0.2/eea/api/redirector/locales/ro/LC_MESSAGES/plone.mo +0 -0
- eea_api_redirector-0.2/eea/api/redirector/locales/ru/LC_MESSAGES/eea.mo +0 -0
- eea_api_redirector-0.2/eea/api/redirector/locales/ru/LC_MESSAGES/plone.mo +0 -0
- eea_api_redirector-0.2/eea/api/redirector/locales/sk/LC_MESSAGES/eea.mo +0 -0
- eea_api_redirector-0.2/eea/api/redirector/locales/sk/LC_MESSAGES/plone.mo +0 -0
- eea_api_redirector-0.2/eea/api/redirector/locales/sl/LC_MESSAGES/eea.mo +0 -0
- eea_api_redirector-0.2/eea/api/redirector/locales/sl/LC_MESSAGES/plone.mo +0 -0
- eea_api_redirector-0.2/eea/api/redirector/locales/sv/LC_MESSAGES/eea.mo +0 -0
- eea_api_redirector-0.2/eea/api/redirector/locales/sv/LC_MESSAGES/plone.mo +0 -0
- eea_api_redirector-0.2/eea/api/redirector/locales/tr/LC_MESSAGES/eea.mo +0 -0
- eea_api_redirector-0.2/eea/api/redirector/locales/tr/LC_MESSAGES/plone.mo +0 -0
- eea_api_redirector-0.2/eea/api/redirector/locales/zh_TW/LC_MESSAGES/eea.mo +0 -0
- eea_api_redirector-0.2/eea/api/redirector/locales/zh_TW/LC_MESSAGES/plone.mo +0 -0
- eea_api_redirector-0.2/eea/api/redirector/restapi/add.py +122 -0
- {eea_api_redirector-0.1 → eea_api_redirector-0.2}/eea/api/redirector/restapi/configure.zcml +9 -0
- eea_api_redirector-0.2/eea/api/redirector/restapi/import_csv.py +89 -0
- eea_api_redirector-0.2/eea/api/redirector/version.txt +1 -0
- {eea_api_redirector-0.1 → eea_api_redirector-0.2/eea.api.redirector.egg-info}/PKG-INFO +42 -15
- {eea_api_redirector-0.1 → eea_api_redirector-0.2}/eea.api.redirector.egg-info/SOURCES.txt +63 -0
- eea_api_redirector-0.1/eea/api/redirector/restapi/add.py +0 -115
- eea_api_redirector-0.1/eea/api/redirector/version.txt +0 -1
- {eea_api_redirector-0.1 → eea_api_redirector-0.2}/CONTRIBUTING.md +0 -0
- {eea_api_redirector-0.1 → eea_api_redirector-0.2}/MANIFEST.in +0 -0
- {eea_api_redirector-0.1 → eea_api_redirector-0.2}/docs/LICENSE.GPL +0 -0
- {eea_api_redirector-0.1 → eea_api_redirector-0.2}/docs/LICENSE.txt +0 -0
- {eea_api_redirector-0.1 → eea_api_redirector-0.2}/eea/__init__.py +0 -0
- {eea_api_redirector-0.1 → eea_api_redirector-0.2}/eea/api/__init__.py +0 -0
- {eea_api_redirector-0.1 → eea_api_redirector-0.2}/eea/api/redirector/README.txt +0 -0
- {eea_api_redirector-0.1 → eea_api_redirector-0.2}/eea/api/redirector/__init__.py +0 -0
- {eea_api_redirector-0.1 → eea_api_redirector-0.2}/eea/api/redirector/browser/__init__.py +0 -0
- {eea_api_redirector-0.1 → eea_api_redirector-0.2}/eea/api/redirector/browser/configure.zcml +0 -0
- {eea_api_redirector-0.1 → eea_api_redirector-0.2}/eea/api/redirector/browser/controlpanel.py +0 -0
- {eea_api_redirector-0.1 → eea_api_redirector-0.2}/eea/api/redirector/configure.zcml +0 -0
- {eea_api_redirector-0.1 → eea_api_redirector-0.2}/eea/api/redirector/interfaces.py +0 -0
- {eea_api_redirector-0.1 → eea_api_redirector-0.2}/eea/api/redirector/locales/__init__.py +0 -0
- {eea_api_redirector-0.1 → eea_api_redirector-0.2}/eea/api/redirector/locales/bg/LC_MESSAGES/eea.po +0 -0
- {eea_api_redirector-0.1 → eea_api_redirector-0.2}/eea/api/redirector/locales/bg/LC_MESSAGES/plone.po +0 -0
- {eea_api_redirector-0.1 → eea_api_redirector-0.2}/eea/api/redirector/locales/cs/LC_MESSAGES/eea.po +0 -0
- {eea_api_redirector-0.1 → eea_api_redirector-0.2}/eea/api/redirector/locales/cs/LC_MESSAGES/plone.po +0 -0
- {eea_api_redirector-0.1 → eea_api_redirector-0.2}/eea/api/redirector/locales/da/LC_MESSAGES/eea.po +0 -0
- {eea_api_redirector-0.1 → eea_api_redirector-0.2}/eea/api/redirector/locales/da/LC_MESSAGES/plone.po +0 -0
- {eea_api_redirector-0.1 → eea_api_redirector-0.2}/eea/api/redirector/locales/de/LC_MESSAGES/eea.po +0 -0
- {eea_api_redirector-0.1 → eea_api_redirector-0.2}/eea/api/redirector/locales/de/LC_MESSAGES/plone.po +0 -0
- {eea_api_redirector-0.1 → eea_api_redirector-0.2}/eea/api/redirector/locales/eea.pot +0 -0
- {eea_api_redirector-0.1 → eea_api_redirector-0.2}/eea/api/redirector/locales/el/LC_MESSAGES/eea.po +0 -0
- {eea_api_redirector-0.1 → eea_api_redirector-0.2}/eea/api/redirector/locales/el/LC_MESSAGES/plone.po +0 -0
- {eea_api_redirector-0.1 → eea_api_redirector-0.2}/eea/api/redirector/locales/en/LC_MESSAGES/eea.po +0 -0
- {eea_api_redirector-0.1 → eea_api_redirector-0.2}/eea/api/redirector/locales/en/LC_MESSAGES/plone.po +0 -0
- {eea_api_redirector-0.1 → eea_api_redirector-0.2}/eea/api/redirector/locales/es/LC_MESSAGES/eea.po +0 -0
- {eea_api_redirector-0.1 → eea_api_redirector-0.2}/eea/api/redirector/locales/es/LC_MESSAGES/plone.po +0 -0
- {eea_api_redirector-0.1 → eea_api_redirector-0.2}/eea/api/redirector/locales/et/LC_MESSAGES/eea.po +0 -0
- {eea_api_redirector-0.1 → eea_api_redirector-0.2}/eea/api/redirector/locales/et/LC_MESSAGES/plone.po +0 -0
- {eea_api_redirector-0.1 → eea_api_redirector-0.2}/eea/api/redirector/locales/eu/LC_MESSAGES/eea.po +0 -0
- {eea_api_redirector-0.1 → eea_api_redirector-0.2}/eea/api/redirector/locales/eu/LC_MESSAGES/plone.po +0 -0
- {eea_api_redirector-0.1 → eea_api_redirector-0.2}/eea/api/redirector/locales/fi/LC_MESSAGES/eea.po +0 -0
- {eea_api_redirector-0.1 → eea_api_redirector-0.2}/eea/api/redirector/locales/fi/LC_MESSAGES/plone.po +0 -0
- {eea_api_redirector-0.1 → eea_api_redirector-0.2}/eea/api/redirector/locales/fr/LC_MESSAGES/eea.po +0 -0
- {eea_api_redirector-0.1 → eea_api_redirector-0.2}/eea/api/redirector/locales/fr/LC_MESSAGES/plone.po +0 -0
- {eea_api_redirector-0.1 → eea_api_redirector-0.2}/eea/api/redirector/locales/hr/LC_MESSAGES/eea.po +0 -0
- {eea_api_redirector-0.1 → eea_api_redirector-0.2}/eea/api/redirector/locales/hr/LC_MESSAGES/plone.po +0 -0
- {eea_api_redirector-0.1 → eea_api_redirector-0.2}/eea/api/redirector/locales/hu/LC_MESSAGES/eea.po +0 -0
- {eea_api_redirector-0.1 → eea_api_redirector-0.2}/eea/api/redirector/locales/hu/LC_MESSAGES/plone.po +0 -0
- {eea_api_redirector-0.1 → eea_api_redirector-0.2}/eea/api/redirector/locales/is/LC_MESSAGES/eea.po +0 -0
- {eea_api_redirector-0.1 → eea_api_redirector-0.2}/eea/api/redirector/locales/is/LC_MESSAGES/plone.po +0 -0
- {eea_api_redirector-0.1 → eea_api_redirector-0.2}/eea/api/redirector/locales/it/LC_MESSAGES/eea.po +0 -0
- {eea_api_redirector-0.1 → eea_api_redirector-0.2}/eea/api/redirector/locales/it/LC_MESSAGES/plone.po +0 -0
- {eea_api_redirector-0.1 → eea_api_redirector-0.2}/eea/api/redirector/locales/kl/LC_MESSAGES/eea.po +0 -0
- {eea_api_redirector-0.1 → eea_api_redirector-0.2}/eea/api/redirector/locales/kl/LC_MESSAGES/plone.po +0 -0
- {eea_api_redirector-0.1 → eea_api_redirector-0.2}/eea/api/redirector/locales/lt/LC_MESSAGES/eea.po +0 -0
- {eea_api_redirector-0.1 → eea_api_redirector-0.2}/eea/api/redirector/locales/lt/LC_MESSAGES/plone.po +0 -0
- {eea_api_redirector-0.1 → eea_api_redirector-0.2}/eea/api/redirector/locales/lv/LC_MESSAGES/eea.po +0 -0
- {eea_api_redirector-0.1 → eea_api_redirector-0.2}/eea/api/redirector/locales/lv/LC_MESSAGES/plone.po +0 -0
- {eea_api_redirector-0.1 → eea_api_redirector-0.2}/eea/api/redirector/locales/mt/LC_MESSAGES/eea.po +0 -0
- {eea_api_redirector-0.1 → eea_api_redirector-0.2}/eea/api/redirector/locales/mt/LC_MESSAGES/plone.po +0 -0
- {eea_api_redirector-0.1 → eea_api_redirector-0.2}/eea/api/redirector/locales/nl/LC_MESSAGES/eea.po +0 -0
- {eea_api_redirector-0.1 → eea_api_redirector-0.2}/eea/api/redirector/locales/nl/LC_MESSAGES/plone.po +0 -0
- {eea_api_redirector-0.1 → eea_api_redirector-0.2}/eea/api/redirector/locales/no/LC_MESSAGES/eea.po +0 -0
- {eea_api_redirector-0.1 → eea_api_redirector-0.2}/eea/api/redirector/locales/no/LC_MESSAGES/plone.po +0 -0
- {eea_api_redirector-0.1 → eea_api_redirector-0.2}/eea/api/redirector/locales/pl/LC_MESSAGES/eea.po +0 -0
- {eea_api_redirector-0.1 → eea_api_redirector-0.2}/eea/api/redirector/locales/pl/LC_MESSAGES/plone.po +0 -0
- {eea_api_redirector-0.1 → eea_api_redirector-0.2}/eea/api/redirector/locales/plone-manual.pot +0 -0
- {eea_api_redirector-0.1 → eea_api_redirector-0.2}/eea/api/redirector/locales/pt/LC_MESSAGES/eea.po +0 -0
- {eea_api_redirector-0.1 → eea_api_redirector-0.2}/eea/api/redirector/locales/pt/LC_MESSAGES/plone.po +0 -0
- {eea_api_redirector-0.1 → eea_api_redirector-0.2}/eea/api/redirector/locales/pt_BR/LC_MESSAGES/eea.po +0 -0
- {eea_api_redirector-0.1 → eea_api_redirector-0.2}/eea/api/redirector/locales/pt_BR/LC_MESSAGES/plone.po +0 -0
- {eea_api_redirector-0.1 → eea_api_redirector-0.2}/eea/api/redirector/locales/ro/LC_MESSAGES/eea.po +0 -0
- {eea_api_redirector-0.1 → eea_api_redirector-0.2}/eea/api/redirector/locales/ro/LC_MESSAGES/plone.po +0 -0
- {eea_api_redirector-0.1 → eea_api_redirector-0.2}/eea/api/redirector/locales/ru/LC_MESSAGES/eea.po +0 -0
- {eea_api_redirector-0.1 → eea_api_redirector-0.2}/eea/api/redirector/locales/ru/LC_MESSAGES/plone.po +0 -0
- {eea_api_redirector-0.1 → eea_api_redirector-0.2}/eea/api/redirector/locales/sk/LC_MESSAGES/eea.po +0 -0
- {eea_api_redirector-0.1 → eea_api_redirector-0.2}/eea/api/redirector/locales/sk/LC_MESSAGES/plone.po +0 -0
- {eea_api_redirector-0.1 → eea_api_redirector-0.2}/eea/api/redirector/locales/sl/LC_MESSAGES/eea.po +0 -0
- {eea_api_redirector-0.1 → eea_api_redirector-0.2}/eea/api/redirector/locales/sl/LC_MESSAGES/plone.po +0 -0
- {eea_api_redirector-0.1 → eea_api_redirector-0.2}/eea/api/redirector/locales/sv/LC_MESSAGES/eea.po +0 -0
- {eea_api_redirector-0.1 → eea_api_redirector-0.2}/eea/api/redirector/locales/sv/LC_MESSAGES/plone.po +0 -0
- {eea_api_redirector-0.1 → eea_api_redirector-0.2}/eea/api/redirector/locales/tr/LC_MESSAGES/eea.po +0 -0
- {eea_api_redirector-0.1 → eea_api_redirector-0.2}/eea/api/redirector/locales/tr/LC_MESSAGES/plone.po +0 -0
- {eea_api_redirector-0.1 → eea_api_redirector-0.2}/eea/api/redirector/locales/update.sh +0 -0
- {eea_api_redirector-0.1 → eea_api_redirector-0.2}/eea/api/redirector/locales/zh_TW/LC_MESSAGES/eea.po +0 -0
- {eea_api_redirector-0.1 → eea_api_redirector-0.2}/eea/api/redirector/locales/zh_TW/LC_MESSAGES/plone.po +0 -0
- {eea_api_redirector-0.1 → eea_api_redirector-0.2}/eea/api/redirector/profiles/default/browserlayer.xml +0 -0
- {eea_api_redirector-0.1 → eea_api_redirector-0.2}/eea/api/redirector/profiles/default/controlpanel.xml +0 -0
- {eea_api_redirector-0.1 → eea_api_redirector-0.2}/eea/api/redirector/profiles/default/metadata.xml +0 -0
- {eea_api_redirector-0.1 → eea_api_redirector-0.2}/eea/api/redirector/profiles/default/registry.xml +0 -0
- {eea_api_redirector-0.1 → eea_api_redirector-0.2}/eea/api/redirector/profiles/uninstall/browserlayer.xml +0 -0
- {eea_api_redirector-0.1 → eea_api_redirector-0.2}/eea/api/redirector/profiles.zcml +0 -0
- {eea_api_redirector-0.1 → eea_api_redirector-0.2}/eea/api/redirector/restapi/__init__.py +0 -0
- {eea_api_redirector-0.1 → eea_api_redirector-0.2}/eea/api/redirector/restapi/controlpanel.py +0 -0
- {eea_api_redirector-0.1 → eea_api_redirector-0.2}/eea/api/redirector/restapi/delete.py +0 -0
- {eea_api_redirector-0.1 → eea_api_redirector-0.2}/eea/api/redirector/restapi/get.py +0 -0
- {eea_api_redirector-0.1 → eea_api_redirector-0.2}/eea/api/redirector/restapi/statistics.py +0 -0
- {eea_api_redirector-0.1 → eea_api_redirector-0.2}/eea/api/redirector/setuphandlers.py +0 -0
- {eea_api_redirector-0.1 → eea_api_redirector-0.2}/eea/api/redirector/tests/__init__.py +0 -0
- {eea_api_redirector-0.1 → eea_api_redirector-0.2}/eea/api/redirector/tests/base.py +0 -0
- {eea_api_redirector-0.1 → eea_api_redirector-0.2}/eea/api/redirector/tests/test_doctests.py +0 -0
- {eea_api_redirector-0.1 → eea_api_redirector-0.2}/eea/api/redirector/upgrades/__init__.py +0 -0
- {eea_api_redirector-0.1 → eea_api_redirector-0.2}/eea/api/redirector/upgrades/configure.zcml +0 -0
- {eea_api_redirector-0.1 → eea_api_redirector-0.2}/eea/api/redirector/utilities/__init__.py +0 -0
- {eea_api_redirector-0.1 → eea_api_redirector-0.2}/eea/api/redirector/utilities/configure.zcml +0 -0
- {eea_api_redirector-0.1 → eea_api_redirector-0.2}/eea/api/redirector/utilities/errors.py +0 -0
- {eea_api_redirector-0.1 → eea_api_redirector-0.2}/eea/api/redirector/utilities/storage.py +0 -0
- {eea_api_redirector-0.1 → eea_api_redirector-0.2}/eea.api.redirector.egg-info/dependency_links.txt +0 -0
- {eea_api_redirector-0.1 → eea_api_redirector-0.2}/eea.api.redirector.egg-info/entry_points.txt +0 -0
- {eea_api_redirector-0.1 → eea_api_redirector-0.2}/eea.api.redirector.egg-info/namespace_packages.txt +0 -0
- {eea_api_redirector-0.1 → eea_api_redirector-0.2}/eea.api.redirector.egg-info/not-zip-safe +0 -0
- {eea_api_redirector-0.1 → eea_api_redirector-0.2}/eea.api.redirector.egg-info/requires.txt +0 -0
- {eea_api_redirector-0.1 → eea_api_redirector-0.2}/eea.api.redirector.egg-info/top_level.txt +0 -0
- {eea_api_redirector-0.1 → eea_api_redirector-0.2}/setup.cfg +0 -0
- {eea_api_redirector-0.1 → eea_api_redirector-0.2}/setup.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: eea.api.redirector
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.2
|
|
4
4
|
Summary: Plone Redirector extension to get redirect URLs also from Redis
|
|
5
5
|
Home-page: https://github.com/eea/eea.api.redirector
|
|
6
6
|
Author: European Environment Agency: IDM2 A-Team
|
|
@@ -61,7 +61,8 @@ Main features
|
|
|
61
61
|
9. **Proper HTTP status codes**: Returns 410 Gone for permanently deleted resources (empty redirect targets)
|
|
62
62
|
10. **Redirect loop prevention**: Built-in protection against circular redirects
|
|
63
63
|
11. **Easy configuration**: Simple environment variable setup for Redis connections
|
|
64
|
-
12. **
|
|
64
|
+
12. **CSV import endpoint**: Upload CSV files directly to the backend for bulk imports
|
|
65
|
+
13. **Non-intrusive**: Extends existing Plone functionality without replacing it
|
|
65
66
|
|
|
66
67
|
|
|
67
68
|
Install
|
|
@@ -236,6 +237,27 @@ Response::
|
|
|
236
237
|
"failed": []
|
|
237
238
|
}
|
|
238
239
|
|
|
240
|
+
**POST /@redirects-import**
|
|
241
|
+
|
|
242
|
+
Import redirects from a CSV file upload.
|
|
243
|
+
|
|
244
|
+
Request:
|
|
245
|
+
|
|
246
|
+
* Multipart form-data with ``file`` field containing the CSV file
|
|
247
|
+
|
|
248
|
+
Example::
|
|
249
|
+
|
|
250
|
+
curl -u admin:admin -F "file=@redirects.csv" http://localhost:8080/Plone/@redirects-import
|
|
251
|
+
|
|
252
|
+
Response::
|
|
253
|
+
|
|
254
|
+
{
|
|
255
|
+
"type": "success",
|
|
256
|
+
"success_count": 10,
|
|
257
|
+
"failed_count": 0,
|
|
258
|
+
"failed": []
|
|
259
|
+
}
|
|
260
|
+
|
|
239
261
|
|
|
240
262
|
Performance
|
|
241
263
|
===========
|
|
@@ -321,32 +343,37 @@ EEA_ - European Environment Agency (EU)
|
|
|
321
343
|
Changelog
|
|
322
344
|
=========
|
|
323
345
|
|
|
346
|
+
0.2 - (2025-12-20)
|
|
347
|
+
------------------------
|
|
348
|
+
* Feature: CSV import endpoint (@redirects-import) for server-side processing
|
|
349
|
+
[avoinea - refs #293748]
|
|
350
|
+
|
|
324
351
|
0.1 - (2025-11-28)
|
|
325
352
|
-----------------------
|
|
326
353
|
|
|
327
354
|
* Feature: Redis-backed URL redirects with high-performance lookups
|
|
328
|
-
[avoinea - refs
|
|
355
|
+
[avoinea - refs #293748]
|
|
329
356
|
* Feature: REST API endpoints (@redirects, @redirects-statistics) for full CRUD operations
|
|
330
|
-
[avoinea - refs
|
|
357
|
+
[avoinea - refs #293748]
|
|
331
358
|
* Feature: Advanced search with regex support and search scope selector (old_url, new_url, both)
|
|
332
|
-
[avoinea - refs
|
|
359
|
+
[avoinea - refs #293748]
|
|
333
360
|
* Feature: HTTP 410 Gone support for permanently deleted content (empty redirect targets)
|
|
334
|
-
[avoinea - refs
|
|
361
|
+
[avoinea - refs #293748]
|
|
335
362
|
* Feature: High-performance statistics using Redis pipelining (~2s for 100k+ redirects)
|
|
336
|
-
[avoinea - refs
|
|
363
|
+
[avoinea - refs #293748]
|
|
337
364
|
* Feature: Pagination support with configurable batch sizes (10, 25, 50, 100, 500, 1000)
|
|
338
|
-
[avoinea - refs
|
|
365
|
+
[avoinea - refs #293748]
|
|
339
366
|
* Feature: Alphabetical sorting of redirects for consistent display
|
|
340
|
-
[avoinea - refs
|
|
367
|
+
[avoinea - refs #293748]
|
|
341
368
|
* Feature: Bulk import/export via REST API (CSV support in frontend)
|
|
342
|
-
[avoinea - refs
|
|
369
|
+
[avoinea - refs #293748]
|
|
343
370
|
* Feature: Fallback mechanism to check Redis when redirects not found in Plone storage
|
|
344
|
-
[avoinea - refs
|
|
371
|
+
[avoinea - refs #293748]
|
|
345
372
|
* Feature: Graceful error handling - Redis failures don't break redirection system
|
|
346
|
-
[avoinea - refs
|
|
373
|
+
[avoinea - refs #293748]
|
|
347
374
|
* Feature: API endpoint support with intelligent hierarchical URL matching
|
|
348
|
-
[avoinea - refs
|
|
375
|
+
[avoinea - refs #293748]
|
|
349
376
|
* Feature: Redirect loop prevention
|
|
350
|
-
[avoinea - refs
|
|
377
|
+
[avoinea - refs #293748]
|
|
351
378
|
* Feature: Easy configuration via environment variables (REDIS_SERVER, REDIS_PORT, REDIS_DB, REDIS_TIMEOUT)
|
|
352
|
-
[avoinea - refs
|
|
379
|
+
[avoinea - refs #293748]
|
|
@@ -27,7 +27,8 @@ Main features
|
|
|
27
27
|
9. **Proper HTTP status codes**: Returns 410 Gone for permanently deleted resources (empty redirect targets)
|
|
28
28
|
10. **Redirect loop prevention**: Built-in protection against circular redirects
|
|
29
29
|
11. **Easy configuration**: Simple environment variable setup for Redis connections
|
|
30
|
-
12. **
|
|
30
|
+
12. **CSV import endpoint**: Upload CSV files directly to the backend for bulk imports
|
|
31
|
+
13. **Non-intrusive**: Extends existing Plone functionality without replacing it
|
|
31
32
|
|
|
32
33
|
|
|
33
34
|
Install
|
|
@@ -202,6 +203,27 @@ Response::
|
|
|
202
203
|
"failed": []
|
|
203
204
|
}
|
|
204
205
|
|
|
206
|
+
**POST /@redirects-import**
|
|
207
|
+
|
|
208
|
+
Import redirects from a CSV file upload.
|
|
209
|
+
|
|
210
|
+
Request:
|
|
211
|
+
|
|
212
|
+
* Multipart form-data with ``file`` field containing the CSV file
|
|
213
|
+
|
|
214
|
+
Example::
|
|
215
|
+
|
|
216
|
+
curl -u admin:admin -F "file=@redirects.csv" http://localhost:8080/Plone/@redirects-import
|
|
217
|
+
|
|
218
|
+
Response::
|
|
219
|
+
|
|
220
|
+
{
|
|
221
|
+
"type": "success",
|
|
222
|
+
"success_count": 10,
|
|
223
|
+
"failed_count": 0,
|
|
224
|
+
"failed": []
|
|
225
|
+
}
|
|
226
|
+
|
|
205
227
|
|
|
206
228
|
Performance
|
|
207
229
|
===========
|
|
@@ -1,32 +1,37 @@
|
|
|
1
1
|
Changelog
|
|
2
2
|
=========
|
|
3
3
|
|
|
4
|
+
0.2 - (2025-12-20)
|
|
5
|
+
------------------------
|
|
6
|
+
* Feature: CSV import endpoint (@redirects-import) for server-side processing
|
|
7
|
+
[avoinea - refs #293748]
|
|
8
|
+
|
|
4
9
|
0.1 - (2025-11-28)
|
|
5
10
|
-----------------------
|
|
6
11
|
|
|
7
12
|
* Feature: Redis-backed URL redirects with high-performance lookups
|
|
8
|
-
[avoinea - refs
|
|
13
|
+
[avoinea - refs #293748]
|
|
9
14
|
* Feature: REST API endpoints (@redirects, @redirects-statistics) for full CRUD operations
|
|
10
|
-
[avoinea - refs
|
|
15
|
+
[avoinea - refs #293748]
|
|
11
16
|
* Feature: Advanced search with regex support and search scope selector (old_url, new_url, both)
|
|
12
|
-
[avoinea - refs
|
|
17
|
+
[avoinea - refs #293748]
|
|
13
18
|
* Feature: HTTP 410 Gone support for permanently deleted content (empty redirect targets)
|
|
14
|
-
[avoinea - refs
|
|
19
|
+
[avoinea - refs #293748]
|
|
15
20
|
* Feature: High-performance statistics using Redis pipelining (~2s for 100k+ redirects)
|
|
16
|
-
[avoinea - refs
|
|
21
|
+
[avoinea - refs #293748]
|
|
17
22
|
* Feature: Pagination support with configurable batch sizes (10, 25, 50, 100, 500, 1000)
|
|
18
|
-
[avoinea - refs
|
|
23
|
+
[avoinea - refs #293748]
|
|
19
24
|
* Feature: Alphabetical sorting of redirects for consistent display
|
|
20
|
-
[avoinea - refs
|
|
25
|
+
[avoinea - refs #293748]
|
|
21
26
|
* Feature: Bulk import/export via REST API (CSV support in frontend)
|
|
22
|
-
[avoinea - refs
|
|
27
|
+
[avoinea - refs #293748]
|
|
23
28
|
* Feature: Fallback mechanism to check Redis when redirects not found in Plone storage
|
|
24
|
-
[avoinea - refs
|
|
29
|
+
[avoinea - refs #293748]
|
|
25
30
|
* Feature: Graceful error handling - Redis failures don't break redirection system
|
|
26
|
-
[avoinea - refs
|
|
31
|
+
[avoinea - refs #293748]
|
|
27
32
|
* Feature: API endpoint support with intelligent hierarchical URL matching
|
|
28
|
-
[avoinea - refs
|
|
33
|
+
[avoinea - refs #293748]
|
|
29
34
|
* Feature: Redirect loop prevention
|
|
30
|
-
[avoinea - refs
|
|
35
|
+
[avoinea - refs #293748]
|
|
31
36
|
* Feature: Easy configuration via environment variables (REDIS_SERVER, REDIS_PORT, REDIS_DB, REDIS_TIMEOUT)
|
|
32
|
-
[avoinea - refs
|
|
37
|
+
[avoinea - refs #293748]
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
"""RestAPI endpoint @redirects POST"""
|
|
2
|
+
|
|
3
|
+
from plone.restapi.deserializer import json_body
|
|
4
|
+
from plone.restapi.services import Service
|
|
5
|
+
from zExceptions import BadRequest
|
|
6
|
+
from zope.component import getUtility
|
|
7
|
+
from zope.interface import alsoProvides
|
|
8
|
+
from zope.interface import implementer
|
|
9
|
+
from zope.publisher.interfaces import IPublishTraverse
|
|
10
|
+
from eea.api.redirector.interfaces import IStorageUtility
|
|
11
|
+
import plone.protect.interfaces
|
|
12
|
+
import logging
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
logger = logging.getLogger(__name__)
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def add_redirects(storage, redirects):
|
|
19
|
+
"""Validate and add redirects to storage."""
|
|
20
|
+
failed_redirects = []
|
|
21
|
+
success_count = 0
|
|
22
|
+
|
|
23
|
+
for redirect in redirects:
|
|
24
|
+
if not isinstance(redirect, dict):
|
|
25
|
+
failed_redirects.append(
|
|
26
|
+
{
|
|
27
|
+
"redirect": str(redirect),
|
|
28
|
+
"error": "Item must be a dictionary with 'path' and 'redirect-to' keys",
|
|
29
|
+
}
|
|
30
|
+
)
|
|
31
|
+
continue
|
|
32
|
+
|
|
33
|
+
path = redirect.get("path")
|
|
34
|
+
target = redirect.get("redirect-to")
|
|
35
|
+
|
|
36
|
+
if not path:
|
|
37
|
+
failed_redirects.append(
|
|
38
|
+
{"redirect": str(redirect), "error": "Missing 'path' field"}
|
|
39
|
+
)
|
|
40
|
+
continue
|
|
41
|
+
|
|
42
|
+
if target is None:
|
|
43
|
+
failed_redirects.append(
|
|
44
|
+
{"redirect": str(redirect), "error": "Missing 'redirect-to' field"}
|
|
45
|
+
)
|
|
46
|
+
continue
|
|
47
|
+
|
|
48
|
+
# Validate paths
|
|
49
|
+
if not path.startswith("/"):
|
|
50
|
+
failed_redirects.append(
|
|
51
|
+
{
|
|
52
|
+
"redirect": str(redirect),
|
|
53
|
+
"error": f"Path must start with '/': {path}",
|
|
54
|
+
}
|
|
55
|
+
)
|
|
56
|
+
continue
|
|
57
|
+
|
|
58
|
+
# Prevent self-redirects (but allow empty target for Gone)
|
|
59
|
+
if target and path == target:
|
|
60
|
+
failed_redirects.append(
|
|
61
|
+
{
|
|
62
|
+
"redirect": str(redirect),
|
|
63
|
+
"error": "Path and target cannot be the same",
|
|
64
|
+
}
|
|
65
|
+
)
|
|
66
|
+
continue
|
|
67
|
+
|
|
68
|
+
# Add to Redis
|
|
69
|
+
try:
|
|
70
|
+
result = storage.set(path, target)
|
|
71
|
+
if result:
|
|
72
|
+
success_count += 1
|
|
73
|
+
logger.info("Added redirect: %s -> %s", path, target)
|
|
74
|
+
else:
|
|
75
|
+
failed_redirects.append(
|
|
76
|
+
{
|
|
77
|
+
"path": path,
|
|
78
|
+
"redirect-to": target,
|
|
79
|
+
"error": "Failed to set value in Redis",
|
|
80
|
+
}
|
|
81
|
+
)
|
|
82
|
+
except Exception as err:
|
|
83
|
+
logger.exception("Error adding redirect %s -> %s: %s", path, target, err)
|
|
84
|
+
failed_redirects.append(
|
|
85
|
+
{"path": path, "redirect-to": target, "error": str(err)}
|
|
86
|
+
)
|
|
87
|
+
|
|
88
|
+
return success_count, failed_redirects
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
@implementer(IPublishTraverse)
|
|
92
|
+
class RedisRedirectsPost(Service):
|
|
93
|
+
"""Add redirects to Redis"""
|
|
94
|
+
|
|
95
|
+
def reply(self):
|
|
96
|
+
"""Add one or more redirects to Redis storage"""
|
|
97
|
+
# Disable CSRF protection
|
|
98
|
+
if "IDisableCSRFProtection" in dir(plone.protect.interfaces):
|
|
99
|
+
alsoProvides(self.request, plone.protect.interfaces.IDisableCSRFProtection)
|
|
100
|
+
|
|
101
|
+
data = json_body(self.request)
|
|
102
|
+
storage = getUtility(IStorageUtility)
|
|
103
|
+
redirects = data.get("items", [])
|
|
104
|
+
|
|
105
|
+
if not redirects:
|
|
106
|
+
raise BadRequest(
|
|
107
|
+
"No items provided. Expected format: {'items': [{'path': '/old', 'redirect-to': '/new'}]}"
|
|
108
|
+
)
|
|
109
|
+
|
|
110
|
+
success_count, failed_redirects = add_redirects(storage, redirects)
|
|
111
|
+
|
|
112
|
+
# Return appropriate response
|
|
113
|
+
if failed_redirects:
|
|
114
|
+
self.request.response.setStatus(207) # Multi-Status
|
|
115
|
+
return {
|
|
116
|
+
"type": "partial_success" if success_count > 0 else "error",
|
|
117
|
+
"success_count": success_count,
|
|
118
|
+
"failed_count": len(failed_redirects),
|
|
119
|
+
"failed": failed_redirects,
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
return self.reply_no_content()
|
|
@@ -37,6 +37,15 @@
|
|
|
37
37
|
permission="cmf.ManagePortal"
|
|
38
38
|
/>
|
|
39
39
|
|
|
40
|
+
<!-- POST endpoint to import Redis redirects from CSV -->
|
|
41
|
+
<plone:service
|
|
42
|
+
method="POST"
|
|
43
|
+
name="@redirects-import"
|
|
44
|
+
for="Products.CMFCore.interfaces.ISiteRoot"
|
|
45
|
+
factory=".import_csv.RedisRedirectsCSVImport"
|
|
46
|
+
permission="cmf.ManagePortal"
|
|
47
|
+
/>
|
|
48
|
+
|
|
40
49
|
<!-- GET endpoint for redirect statistics -->
|
|
41
50
|
<plone:service
|
|
42
51
|
method="GET"
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
"""RestAPI endpoint @redirects-import POST (CSV upload)."""
|
|
2
|
+
|
|
3
|
+
import csv
|
|
4
|
+
import io
|
|
5
|
+
import plone.protect.interfaces
|
|
6
|
+
from plone.restapi.services import Service
|
|
7
|
+
from zExceptions import BadRequest
|
|
8
|
+
from zope.component import getUtility
|
|
9
|
+
from zope.interface import alsoProvides
|
|
10
|
+
from zope.interface import implementer
|
|
11
|
+
from zope.publisher.interfaces import IPublishTraverse
|
|
12
|
+
|
|
13
|
+
from eea.api.redirector.interfaces import IStorageUtility
|
|
14
|
+
from eea.api.redirector.restapi.add import add_redirects
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
@implementer(IPublishTraverse)
|
|
18
|
+
class RedisRedirectsCSVImport(Service):
|
|
19
|
+
"""Import redirects from a CSV file upload."""
|
|
20
|
+
|
|
21
|
+
def _get_uploaded_file(self):
|
|
22
|
+
file_upload = self.request.form.get("file")
|
|
23
|
+
if not file_upload:
|
|
24
|
+
raise BadRequest(
|
|
25
|
+
"No file uploaded. Expected multipart/form-data with 'file' field."
|
|
26
|
+
)
|
|
27
|
+
return file_upload
|
|
28
|
+
|
|
29
|
+
def _parse_csv(self, file_upload):
|
|
30
|
+
data = file_upload.read()
|
|
31
|
+
if not data:
|
|
32
|
+
raise BadRequest("CSV file is empty")
|
|
33
|
+
|
|
34
|
+
try:
|
|
35
|
+
text = data.decode("utf-8-sig")
|
|
36
|
+
except Exception as err:
|
|
37
|
+
raise BadRequest(f"Failed to decode CSV file: {err}") from err
|
|
38
|
+
|
|
39
|
+
reader = csv.reader(io.StringIO(text))
|
|
40
|
+
redirects = []
|
|
41
|
+
failed_rows = []
|
|
42
|
+
|
|
43
|
+
for index, row in enumerate(reader):
|
|
44
|
+
if index == 0:
|
|
45
|
+
continue
|
|
46
|
+
if not row or all(not cell.strip() for cell in row):
|
|
47
|
+
continue
|
|
48
|
+
|
|
49
|
+
old_url = row[0].strip() if len(row) > 0 else ""
|
|
50
|
+
new_url = row[1].strip() if len(row) > 1 else ""
|
|
51
|
+
|
|
52
|
+
if not old_url:
|
|
53
|
+
failed_rows.append({"row": index + 1, "error": "Missing old URL"})
|
|
54
|
+
continue
|
|
55
|
+
|
|
56
|
+
redirects.append({"path": old_url, "redirect-to": new_url})
|
|
57
|
+
|
|
58
|
+
return redirects, failed_rows
|
|
59
|
+
|
|
60
|
+
def reply(self):
|
|
61
|
+
"""Import redirects to Redis storage from CSV."""
|
|
62
|
+
if "IDisableCSRFProtection" in dir(plone.protect.interfaces):
|
|
63
|
+
alsoProvides(self.request, plone.protect.interfaces.IDisableCSRFProtection)
|
|
64
|
+
|
|
65
|
+
file_upload = self._get_uploaded_file()
|
|
66
|
+
redirects, failed_rows = self._parse_csv(file_upload)
|
|
67
|
+
|
|
68
|
+
if not redirects and not failed_rows:
|
|
69
|
+
raise BadRequest("No valid redirects found in CSV file")
|
|
70
|
+
|
|
71
|
+
storage = getUtility(IStorageUtility)
|
|
72
|
+
success_count, failed_redirects = add_redirects(storage, redirects)
|
|
73
|
+
failed = failed_rows + failed_redirects
|
|
74
|
+
|
|
75
|
+
if failed:
|
|
76
|
+
self.request.response.setStatus(207) # Multi-Status
|
|
77
|
+
return {
|
|
78
|
+
"type": "partial_success" if success_count > 0 else "error",
|
|
79
|
+
"success_count": success_count,
|
|
80
|
+
"failed_count": len(failed),
|
|
81
|
+
"failed": failed,
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
return {
|
|
85
|
+
"type": "success",
|
|
86
|
+
"success_count": success_count,
|
|
87
|
+
"failed_count": 0,
|
|
88
|
+
"failed": [],
|
|
89
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
0.2
|