├── .github └── workflows │ └── ci.yml ├── .gitignore ├── .readthedocs.yaml ├── CHANGELOG.rst ├── CONTRIBUTING.rst ├── LICENSE ├── MANIFEST.in ├── Makefile ├── README.rst ├── docs ├── Makefile ├── _static │ └── images │ │ ├── favicon.png │ │ ├── helloworld.png │ │ ├── logo.png │ │ ├── webmanager-cn.png │ │ └── webmanager-en.png ├── api.rst ├── changelog.rst ├── conf.py ├── contributing.rst ├── faq.rst ├── index.rst ├── installation.rst ├── locale │ └── zh_CN │ │ └── LC_MESSAGES │ │ ├── api.po │ │ ├── changelog.po │ │ ├── contributing.po │ │ ├── faq.po │ │ ├── index.po │ │ ├── installation.po │ │ ├── quickstart.po │ │ ├── tutorial │ │ ├── afterword.po │ │ ├── bep.po │ │ ├── config.po │ │ ├── core.po │ │ ├── cvep.po │ │ ├── dcp.po │ │ ├── errhandler.po │ │ ├── filter.po │ │ ├── foreword.po │ │ ├── hep.po │ │ ├── index.po │ │ ├── p3.po │ │ ├── static.po │ │ ├── tcp.po │ │ ├── tep.po │ │ ├── third-party-plugin.po │ │ └── vep.po │ │ └── webmanager.po ├── quickstart.rst ├── requirements.txt ├── tutorial │ ├── afterword.rst │ ├── bep.rst │ ├── config.rst │ ├── core.rst │ ├── cvep.rst │ ├── dcp.rst │ ├── errhandler.rst │ ├── filter.rst │ ├── foreword.rst │ ├── hep.rst │ ├── index.rst │ ├── p3.rst │ ├── static.rst │ ├── tcp.rst │ ├── tep.rst │ ├── third-party-plugin.rst │ └── vep.rst └── webmanager.rst ├── examples ├── fulldemo │ ├── README.md │ ├── app.py │ ├── demo.png │ ├── plugins │ │ ├── __init__.py │ │ ├── local_demo │ │ │ ├── __init__.py │ │ │ ├── static │ │ │ │ ├── css │ │ │ │ │ └── style.css │ │ │ │ └── js │ │ │ │ │ └── hello.js │ │ │ └── templates │ │ │ │ └── localdemo │ │ │ │ └── title.html │ │ └── repeat_demo │ │ │ └── __init__.py │ └── requirements.txt └── helloworld │ ├── app.py │ └── plugins │ ├── __init__.py │ └── helloworld │ └── __init__.py ├── flask_pluginkit ├── __init__.py ├── _compat.py ├── _installer.py ├── _web.py ├── exceptions.py ├── pluginkit.py ├── static │ └── translations.ini ├── templates │ └── manager.html └── utils.py ├── setup.py └── tests ├── __init__.py ├── test_installer.py ├── test_pm.py └── test_utils.py /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: ci 2 | 3 | on: [push, pull_request] 4 | 5 | env: 6 | FLASK_PLUGINKIT_TEST_REDISURL: "redis://localhost" 7 | 8 | jobs: 9 | 10 | build: 11 | 12 | runs-on: ubuntu-latest 13 | environment: 14 | name: pypi 15 | url: https://pypi.org/p/Flask-PluginKit 16 | strategy: 17 | matrix: 18 | flask-version: ["3.0.0"] 19 | python-version: ["3.10", "3.12"] 20 | permissions: 21 | id-token: write 22 | 23 | steps: 24 | - uses: actions/checkout@v4 25 | 26 | - name: Set up Python ${{ matrix.python-version }} 27 | uses: actions/setup-python@v5 28 | with: 29 | python-version: ${{ matrix.python-version }} 30 | 31 | - name: Install dependencies 32 | run: | 33 | python -m pip install --upgrade pip 34 | pip install . 35 | pip install codecov redis 36 | pip install Flask==${{ matrix.flask-version }} 37 | pip install -r examples/fulldemo/requirements.txt 38 | 39 | - name: Start Redis 40 | uses: supercharge/redis-github-action@1.7.0 41 | with: 42 | redis-version: 5 43 | 44 | - name: Test & upload report to coverage 45 | run: | 46 | coverage run -m unittest && codecov -t ${{ secrets.CODECOV_TOKEN }} 47 | 48 | - name: Prepare publish packge 49 | if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags') && matrix.flask-version == '3.0.0' && matrix.python-version == '3.12' 50 | run: pip install -U setuptools twine wheel && python setup.py sdist bdist_wheel 51 | 52 | - name: Publish package 53 | if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags') && matrix.flask-version == '3.0.0' && matrix.python-version == '3.12' 54 | uses: pypa/gh-action-pypi-publish@release/v1 55 | with: 56 | user: __token__ 57 | password: ${{ secrets.PYPI_API_TOKEN }} 58 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | env/ 12 | build/ 13 | develop-eggs/ 14 | dist/ 15 | downloads/ 16 | eggs/ 17 | .eggs/ 18 | lib/ 19 | lib64/ 20 | parts/ 21 | sdist/ 22 | var/ 23 | wheels/ 24 | *.egg-info/ 25 | .installed.cfg 26 | *.egg 27 | 28 | # PyInstaller 29 | # Usually these files are written by a python script from a template 30 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 31 | *.manifest 32 | *.spec 33 | 34 | # Installer logs 35 | pip-log.txt 36 | pip-delete-this-directory.txt 37 | 38 | # Unit test / coverage reports 39 | htmlcov/ 40 | .tox/ 41 | .coverage 42 | .coverage.* 43 | .cache 44 | nosetests.xml 45 | coverage.xml 46 | *.cover 47 | .hypothesis/ 48 | 49 | # Translations 50 | *.mo 51 | *.pot 52 | 53 | # Django stuff: 54 | *.log 55 | local_settings.py 56 | 57 | # Flask stuff: 58 | instance/ 59 | .webassets-cache 60 | 61 | # Scrapy stuff: 62 | .scrapy 63 | 64 | # Sphinx documentation 65 | docs/_build/ 66 | docs/html/ 67 | 68 | # PyBuilder 69 | target/ 70 | 71 | # Jupyter Notebook 72 | .ipynb_checkpoints 73 | 74 | # pyenv 75 | .python-version 76 | 77 | # celery beat schedule file 78 | celerybeat-schedule 79 | 80 | # SageMath parsed files 81 | *.sage.py 82 | 83 | # dotenv 84 | .env 85 | 86 | # virtualenv 87 | .venv 88 | venv/ 89 | ENV/ 90 | 91 | # Spyder project settings 92 | .spyderproject 93 | .spyproject 94 | 95 | # Rope project settings 96 | .ropeproject 97 | 98 | # mkdocs documentation 99 | /site 100 | 101 | # mypy 102 | .mypy_cache/ 103 | 104 | # OS generated files # 105 | ###################### 106 | .DS_Store* 107 | ehthumbs.db 108 | Icon? 109 | Thumbs.db 110 | ENABLED 111 | DISABLED 112 | .vscode/ 113 | docs/locale/**.mo -------------------------------------------------------------------------------- /.readthedocs.yaml: -------------------------------------------------------------------------------- 1 | # .readthedocs.yaml 2 | version: 2 3 | 4 | build: 5 | os: 'ubuntu-22.04' 6 | tools: 7 | python: '3.8' # 指定 Python 版本 8 | 9 | # 设置 Python 环境 10 | sphinx: 11 | configuration: docs/conf.py # 指定 Sphinx 配置文件路径 12 | fail_on_warning: false # 可选:警告是否视为构建失败 13 | 14 | # 安装项目依赖(如果需要) 15 | python: 16 | install: 17 | - requirements: docs/requirements.txt # 从文件安装依赖 18 | - method: pip 19 | path: . # 如果根目录有 setup.py/pyproject.toml 可自动安装包 20 | 21 | # 安装系统级依赖(可选) 22 | # build: 23 | # apt_packages: 24 | # - libgeos-dev # 示例:系统库依赖 25 | 26 | # 自定义构建命令(可选) 27 | # build: 28 | # commands: 29 | # - make -C docs clean html # 手动调用构建命令 30 | -------------------------------------------------------------------------------- /CHANGELOG.rst: -------------------------------------------------------------------------------- 1 | .. currentmodule:: flask-pluginkit 2 | 3 | v3.9.0 4 | ------ 5 | 6 | Released in 2025-05-21 7 | 8 | - feat: add :class:`~flask_pluginkit.ExpiredLocalStorage` 9 | - feat: plugin_info return `__proxy__` of plugin object 10 | 11 | v3.8.0 12 | ------ 13 | 14 | Released in 2024-05-23 15 | 16 | Only support python 3.8+ and flask 3.0+ 17 | 18 | 19 | v3.7.2 20 | ------ 21 | 22 | Released in 2023-05-17 23 | 24 | - Fixed semver version 25 | - Drop hep `before_first_request` 26 | 27 | v3.7.1 28 | ------ 29 | 30 | Released in 2021-09-07 31 | 32 | Restore translations.ini 33 | 34 | v3.7.0 35 | ------ 36 | 37 | Released in 2021-09-02 38 | 39 | - feat: add :ref:`p3`, is beta 40 | - chore: the stpl_reverse falls into stpl 41 | - deprecated: will remove the try_compatible and stpl_reverse in :class:`~flask_pluginkit.PluginManager` 42 | - misc: remove web static files(change to CDN) 43 | 44 | v3.6.2 45 | ------ 46 | 47 | Released in 2021-06-22 48 | 49 | - chore: compatible with flask 2.0 nested blueprints, but only supports blueprints of nested plugins 50 | - fix ci(deploy pypi) & update docs 51 | 52 | v3.6.1 53 | ------ 54 | 55 | Released in 2021-06-22 56 | 57 | - ci tests from travis to github action 58 | - chore: web message api with SSE, page with CDN 59 | 60 | v3.6.0 61 | ------ 62 | 63 | Released in 2020-12-31 64 | 65 | - feat: add :ref:`vep-on-blueprint`, is beta 66 | - feat: the :meth:`~flask_pluginkit.PluginManager.emit_assets` add _external 67 | - fix: semver.compare and semver.parse 68 | - update docs & test case, example 69 | - deprecated: MongoStorage 70 | 71 | v3.5.0 72 | ------ 73 | 74 | Released in 2020-04-08 75 | 76 | - feat: add class-based view extension point (beta, use Flask-Classful) 77 | - chore: web plugin manager page setInterval (called as needed) 78 | - update docs & test case, example 79 | 80 | v3.4.1 81 | ------ 82 | 83 | Released in 2020-03-25 84 | 85 | - feat: add :meth:`~flask_pluginkit.LocalStorage.setmany` 86 | - feat: add :meth:`~flask_pluginkit.RedisStorage.setmany` 87 | - fix: update result(original) :meth:`~flask_pluginkit.RedisStorage.get` 88 | 89 | v3.4.0 90 | ------ 91 | 92 | Released in 2019-09-27 93 | 94 | - feat: add before_first_request hook to hep 95 | - feat: errhandler allow exception 96 | - feat: emit_assets add _raw param 97 | - feat: filter allow [f1, f2, (name, f3)] 98 | - feat: web manager add a button, select language 99 | - feat: stpl alow asc or desc, open the template sorting 100 | - fix: check getPluginClass 101 | - fix: check url prefix 102 | - chore: update docs, example and test case 103 | 104 | v3.3.1 105 | ------ 106 | 107 | Released in 2019-09-17 108 | 109 | - feat: automatically compatible 2.x partial registration method 110 | - fix: update shelve set key type 111 | - fix: remove the number of tests `/` 112 | - test: add docs 113 | - chore: update travis.yml 114 | - chore: formatting code and optimizing import 115 | 116 | v3.3.0 117 | ------ 118 | 119 | Released in 2019-07-31 120 | 121 | - feat: update :class:`~flask_pluginkit.PluginInstaller`. 122 | - feat: add a blueprint to manage plugins 123 | - fix: close shelve connection 124 | - test: add test case 125 | - docs: update docs and translation 126 | - chore: update example 127 | - chore: update travis.yml, add deploy 128 | 129 | v3.2.0 130 | ------ 131 | 132 | Released in 2019-07-25 133 | 134 | - feat: dcp, be similar to flask-plugins event system, 135 | add :class:`~flask_pluginkit.utils.DcpManager` 136 | and :func:`~flask_pluginkit.push_dcp` 137 | - feat: add error handler 138 | - feat: add template filter 139 | - feat: add template context processor 140 | - test: add test case 141 | - docs: update docs and translation 142 | - chore: update example 143 | - chore: make some of the code more elegant 144 | 145 | v3.1.0 146 | ------ 147 | 148 | Released in 2019-07-22 149 | 150 | - feat: add :class:`~flask_pluginkit.PluginInstaller` 151 | - feat: add vep :ref:`vep` 152 | - test: add test case 153 | - docs: update docs 154 | - chore: update example 155 | 156 | v3.0.1 157 | ------ 158 | 159 | Released in 2019-07-21 160 | 161 | - The register functin can return empty dict, not necessarily meaningless. 162 | - Update docs, translation and add test. 163 | 164 | v3.0.0 165 | ------ 166 | 167 | Released in 2019-07-20 168 | 169 | - Refactoring everyting 170 | 171 | Previous Versions 172 | ----------------- 173 | 174 | Prior to 3.3.1, the new version of the refactoring was not compatible 175 | with the old version, and the update history is no longer recorded here. 176 | -------------------------------------------------------------------------------- /CONTRIBUTING.rst: -------------------------------------------------------------------------------- 1 | How to contribute to Flask-PluginKit 2 | ==================================== 3 | 4 | Thank you for considering contributing to Flask-PluginKit! 5 | 6 | Feedback 7 | -------- 8 | 9 | * The `issues `_ on GitHub. 10 | 11 | Report a problem 12 | ---------------- 13 | 14 | - Describe what you expect to happen. 15 | 16 | - If possible, include a minimal, complete, verifiable example to 17 | help us identify the problem. This also helps to check if the issue 18 | is not related to your own code. 19 | 20 | - Describe what actually happened. If there is an exception, 21 | include the complete exception stack. 22 | 23 | - List your Python, Flask, Flask-PluginKit versions. 24 | If possible, check if the new version has fixed this issue. 25 | 26 | Submit pull request 27 | ------------------- 28 | 29 | **First is the environment** 30 | 31 | * fork repository `Flask-PluginKit `_ 32 | 33 | * git clone Flask-PluginKit under your account and set your `git config` 34 | 35 | * Install the dependency module of the development environment 36 | with command ``make dev`` 37 | 38 | **Followed by coding** 39 | 40 | * Write code, please try to comply with the PEP8 specification. 41 | 42 | * Write test cases and documentation. 43 | 44 | * Run the test ``make dev && make test`` using the py3.8+ environments respectively. 45 | 46 | * Generate the documentation ``make dev && make html``. 47 | 48 | * If you contribute a translation document, proceed as follows: 49 | 50 | 1. execute the command ``make gettext`` 51 | 52 | Extract translatable messages into pot files. The generated pot file 53 | will be placed in the docs/_build/gettext directory. 54 | 55 | 2. execute the command ``make cn`` 56 | 57 | Generate or update the po file and place it in 58 | ``docs/locale/zh_CN/LC_MESSAGES/`` directory, then translate the po file. 59 | 60 | 3. execute the command ``make html-cn`` 61 | 62 | Constructing Translation Documents, at the same time, it will generate 63 | or update mo file. 64 | 65 | **Last request for merger** 66 | 67 | * Submit your code 68 | 69 | * Initiate ``pull request`` on GitHub 70 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | BSD 3-Clause License 2 | 3 | Copyright (c) 2019, staugur 4 | All rights reserved. 5 | 6 | Redistribution and use in source and binary forms, with or without 7 | modification, are permitted provided that the following conditions are met: 8 | 9 | * Redistributions of source code must retain the above copyright notice, this 10 | list of conditions and the following disclaimer. 11 | 12 | * Redistributions in binary form must reproduce the above copyright notice, 13 | this list of conditions and the following disclaimer in the documentation 14 | and/or other materials provided with the distribution. 15 | 16 | * Neither the name of the copyright holder nor the names of its 17 | contributors may be used to endorse or promote products derived from 18 | this software without specific prior written permission. 19 | 20 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 21 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 23 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 24 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 26 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 27 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 28 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | recursive-include flask_pluginkit/templates * 2 | recursive-include flask_pluginkit/static * -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: clean 2 | 3 | help: 4 | @echo " clean remove unwanted stuff" 5 | @echo " dev make a development package" 6 | @echo " test run the tests" 7 | @echo " html use the sphinx-build based on reST build HTML file" 8 | @echo " gettext make gettext" 9 | @echo " cn sphinx-intl update pot for zh_CN" 10 | @echo " html-cn generate the zh_CN translation document" 11 | @echo " pipe make html with en and cn, then clean" 12 | 13 | clean: 14 | find . -name '*.pyc' -exec rm -f {} + 15 | find . -name '*.pyo' -exec rm -f {} + 16 | find . -name '*~' -exec rm -f {} + 17 | find . -name '.DS_Store' -exec rm -f {} + 18 | find . -name '__pycache__' -exec rm -rf {} + 19 | find . -name '.coverage' -exec rm -rf {} + 20 | rm -rf build dist .eggs *.egg-info + 21 | 22 | dev: 23 | pip install . 24 | $(MAKE) clean 25 | 26 | test: 27 | python -m unittest 28 | $(MAKE) clean 29 | 30 | html: 31 | cd docs && sphinx-build -E -T -b html . _build/html 32 | 33 | gettext: 34 | cd docs && sphinx-build -b gettext . _build/gettext 35 | 36 | cn: 37 | cd docs && sphinx-intl update -p _build/gettext -l zh_CN 38 | 39 | html-cn: 40 | cd docs && sphinx-build -E -T -D language=zh_CN -b html . _build/html_cn 41 | 42 | pipe: 43 | $(MAKE) html 44 | $(MAKE) gettext 45 | $(MAKE) cn 46 | $(MAKE) html-cn 47 | $(MAKE) clean 48 | -------------------------------------------------------------------------------- /README.rst: -------------------------------------------------------------------------------- 1 | Flask-PluginKit 2 | =============== 3 | 4 | Web program plugin development kit based on Flask. 5 | 6 | |Build Status| |Documentation Status| |codecov| |PyPI| 7 | 8 | 9 | Installation 10 | ------------ 11 | 12 | - Production Version 13 | 14 | `$ pip install -U Flask-PluginKit` 15 | 16 | - Development Version 17 | 18 | `$ pip install -U git+https://github.com/staugur/Flask-PluginKit.git@master` 19 | 20 | 21 | Quickstart 22 | ---------- 23 | 24 | - The normal pattern 25 | 26 | .. code:: python 27 | 28 | from flask_pluginkit import PluginManager 29 | pm = PluginManager(app) 30 | 31 | - The factory pattern 32 | 33 | .. code:: python 34 | 35 | from flask_pluginkit import PluginManager 36 | pm = PluginManager() 37 | pm.init_app(app) 38 | 39 | 40 | Documentation 41 | ------------- 42 | 43 | - `简体中文 `__ 44 | 45 | - `English `__ 46 | 47 | 48 | Contributing 49 | ------------ 50 | 51 | For setting up the development environment, 52 | and how to contribute to Flask-PluginKit, 53 | please see `contributing guidelines`_. 54 | 55 | .. _contributing guidelines: https://github.com/staugur/Flask-PluginKit/blob/master/CONTRIBUTING.rst 56 | 57 | 58 | Links 59 | ----- 60 | 61 | - GitHub https://github.com/staugur/Flask-PluginKit 62 | - Author https://www.saintic.com 63 | - Issues https://github.com/staugur/Flask-PluginKit/issues 64 | - Official plugins based on `Flask-PluginKit `_ 65 | 66 | 67 | LICENSE 68 | ------- 69 | 70 | BSD 3-Clause License, more see LICENSE. 71 | 72 | 73 | END 74 | --- 75 | 76 | Welcome to submit pull request and star. 77 | 78 | .. |Build Status| image:: https://github.com/staugur/Flask-PluginKit/actions/workflows/ci.yml/badge.svg 79 | :target: https://github.com/staugur/Flask-PluginKit/actions/workflows/ci.yml 80 | .. |Documentation Status| image:: https://open.saintic.com/rtfd/badge/flask-pluginkit 81 | :target: https://flask-pluginkit.rtfd.vip 82 | .. |codecov| image:: https://codecov.io/gh/staugur/Flask-PluginKit/branch/master/graph/badge.svg 83 | :target: https://codecov.io/gh/staugur/Flask-PluginKit 84 | .. |PyPI| image:: https://img.shields.io/pypi/v/Flask-PluginKit.svg?style=popout 85 | :target: https://pypi.org/project/Flask-PluginKit/ 86 | -------------------------------------------------------------------------------- /docs/Makefile: -------------------------------------------------------------------------------- 1 | # Minimal makefile for Sphinx documentation 2 | # 3 | 4 | # You can set these variables from the command line. 5 | SPHINXOPTS = 6 | SPHINXBUILD = sphinx-build 7 | SOURCEDIR = . 8 | BUILDDIR = _build 9 | 10 | # Put it first so that "make" without argument is like "make help". 11 | help: 12 | @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) 13 | 14 | .PHONY: help Makefile 15 | 16 | # Catch-all target: route all unknown targets to Sphinx using the new 17 | # "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). 18 | %: Makefile 19 | @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) -------------------------------------------------------------------------------- /docs/_static/images/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/staugur/Flask-PluginKit/79316fc1033fde481049d642b2b59dfbaf8d5c34/docs/_static/images/favicon.png -------------------------------------------------------------------------------- /docs/_static/images/helloworld.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/staugur/Flask-PluginKit/79316fc1033fde481049d642b2b59dfbaf8d5c34/docs/_static/images/helloworld.png -------------------------------------------------------------------------------- /docs/_static/images/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/staugur/Flask-PluginKit/79316fc1033fde481049d642b2b59dfbaf8d5c34/docs/_static/images/logo.png -------------------------------------------------------------------------------- /docs/_static/images/webmanager-cn.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/staugur/Flask-PluginKit/79316fc1033fde481049d642b2b59dfbaf8d5c34/docs/_static/images/webmanager-cn.png -------------------------------------------------------------------------------- /docs/_static/images/webmanager-en.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/staugur/Flask-PluginKit/79316fc1033fde481049d642b2b59dfbaf8d5c34/docs/_static/images/webmanager-en.png -------------------------------------------------------------------------------- /docs/api.rst: -------------------------------------------------------------------------------- 1 | .. _api: 2 | 3 | API 4 | === 5 | 6 | .. module:: flask_pluginkit 7 | 8 | This part of the documentation covers all the interfaces of Flask-PluginKit. 9 | 10 | 11 | PluginManager Object 12 | -------------------- 13 | 14 | .. currentmodule:: flask_pluginkit.pluginkit 15 | 16 | .. autoclass:: PluginManager 17 | :members: 18 | 19 | .. automethod:: __init__ 20 | .. automethod:: init_app 21 | .. automethod:: _tep_handler 22 | .. automethod:: _hep_handler 23 | .. automethod:: _bep_handler 24 | .. automethod:: _vep_handler 25 | .. automethod:: _cvep_handler 26 | .. automethod:: _filter_handler 27 | .. automethod:: _error_handler 28 | .. automethod:: _context_processor_handler 29 | .. automethod:: _p3_handler 30 | .. attribute:: _dcp_manager 31 | 32 | the instance of :class:`~flask_pluginkit.utils.DcpManager` 33 | 34 | .. versionadded:: 3.2.0 35 | 36 | .. autofunction:: push_dcp 37 | 38 | .. data:: blueprint 39 | 40 | The :class:`~flask.blueprints.Blueprint` instance for managing plugins. 41 | 42 | .. versionadded:: 3.3.0 43 | 44 | Inherited Application Objects 45 | ----------------------------- 46 | 47 | .. currentmodule:: flask_pluginkit.utils 48 | 49 | .. autoclass:: JsonResponse 50 | :members: 51 | 52 | Storage Objects 53 | ---------------- 54 | 55 | .. currentmodule:: flask_pluginkit.utils 56 | 57 | .. autoclass:: LocalStorage 58 | :members: 59 | 60 | .. attribute:: index 61 | 62 | The default index, as the only key, you can override it. 63 | 64 | .. autoclass:: RedisStorage 65 | :members: 66 | 67 | .. attribute:: index 68 | 69 | The default index, as the only key, you can override it. 70 | 71 | .. autoclass:: ExpiredLocalStorage 72 | :members: 73 | 74 | .. attribute:: index 75 | 76 | The default index, as the only key, you can override it. 77 | 78 | Useful Functions and Classes 79 | ---------------------------- 80 | 81 | .. currentmodule:: flask_pluginkit.utils 82 | 83 | .. autofunction:: isValidSemver 84 | 85 | .. autofunction:: sortedSemver 86 | 87 | .. autoclass:: BaseStorage 88 | :members: 89 | 90 | .. autoclass:: DcpManager 91 | :members: 92 | 93 | .. currentmodule:: flask_pluginkit._installer 94 | 95 | .. autoclass:: PluginInstaller 96 | :members: 97 | 98 | Custom Exceptions 99 | ----------------- 100 | 101 | .. automodule:: flask_pluginkit.exceptions 102 | :members: 103 | :show-inheritance: 104 | -------------------------------------------------------------------------------- /docs/changelog.rst: -------------------------------------------------------------------------------- 1 | Changelog 2 | ========= 3 | 4 | .. include:: ../CHANGELOG.rst 5 | -------------------------------------------------------------------------------- /docs/conf.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # 3 | # Configuration file for the Sphinx documentation builder. 4 | # 5 | # This file does only contain a selection of the most common options. For a 6 | # full list see the documentation: 7 | # http://www.sphinx-doc.org/en/master/config 8 | 9 | # -- Path setup -------------------------------------------------------------- 10 | 11 | # If extensions (or modules to document with autodoc) are in another directory, 12 | # add these directories to sys.path here. If the directory is relative to the 13 | # documentation root, use os.path.abspath to make it absolute, like shown here. 14 | # 15 | # import os 16 | # import sys 17 | # sys.path.insert(0, os.path.abspath('.')) 18 | from pallets_sphinx_themes import get_version, ProjectLink 19 | 20 | # -- Project information ----------------------------------------------------- 21 | 22 | project = u'Flask-PluginKit' 23 | copyright = u'2018, staugur' 24 | author = u'staugur' 25 | 26 | # The short X.Y version 27 | # version = version 28 | # The full version, including alpha/beta/rc tags 29 | release, version = get_version(project) 30 | 31 | # -- General configuration --------------------------------------------------- 32 | 33 | # If your documentation needs a minimal Sphinx version, state it here. 34 | # 35 | # needs_sphinx = '1.0' 36 | 37 | # Add any Sphinx extension module names here, as strings. They can be 38 | # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom 39 | # ones. 40 | extensions = [ 41 | 'sphinx.ext.autodoc', 42 | 'sphinx.ext.viewcode', 43 | "pallets_sphinx_themes", 44 | ] 45 | issues_github_path = "staugur/Flask-PluginKit" 46 | 47 | # Add any paths that contain templates here, relative to this directory. 48 | templates_path = ['_templates'] 49 | 50 | # The suffix(es) of source filenames. 51 | # You can specify multiple suffix as a list of string: 52 | # 53 | source_suffix = '.rst' 54 | 55 | # The master toctree document. 56 | master_doc = 'index' 57 | 58 | # The language for content autogenerated by Sphinx. Refer to documentation 59 | # for a list of supported languages. 60 | # 61 | # This is also used if you do content translation via gettext catalogs. 62 | # Usually you set "language" from the command line for these cases. 63 | # language = "en" 64 | locale_dirs = ['locale/'] 65 | gettext_compact = False 66 | 67 | # List of patterns, relative to source directory, that match files and 68 | # directories to ignore when looking for source files. 69 | # This pattern also affects html_static_path and html_extra_path. 70 | exclude_patterns = [u'_build', 'Thumbs.db', '.DS_Store'] 71 | 72 | # The name of the Pygments (syntax highlighting) style to use. 73 | pygments_style = None 74 | 75 | 76 | # -- Options for HTML output ------------------------------------------------- 77 | 78 | # The theme to use for HTML and HTML Help pages. See the documentation for 79 | # a list of builtin themes. 80 | # 81 | # html_theme = 'sphinx_rtd_theme' 82 | html_theme = 'flask' 83 | # html_theme_path = ["", ] 84 | 85 | # Theme options are theme-specific and customize the look and feel of a theme 86 | # further. For a list of options available for each theme, see the 87 | # documentation. 88 | # 89 | html_theme_options = { 90 | "index_sidebar_logo": True 91 | } 92 | html_context = { 93 | "project_links": [ 94 | ProjectLink("PyPI releases", "https://pypi.org/project/Flask-PluginKit"), 95 | ProjectLink("Source Code", "https://github.com/staugur/Flask-PluginKit"), 96 | ProjectLink("Issue Tracker", "https://github.com/staugur/Flask-PluginKit/issues/"), 97 | ProjectLink("Official Document", "https://flask-pluginkit.rtfd.vip") 98 | ] 99 | } 100 | 101 | # Add any paths that contain custom static files (such as style sheets) here, 102 | # relative to this directory. They are copied after the builtin static files, 103 | # so a file named "default.css" will overwrite the builtin "default.css". 104 | html_static_path = ['_static'] 105 | html_logo = '_static/images/logo.png' 106 | html_favicon = '_static/images/favicon.png' 107 | html_show_sourcelink = False 108 | 109 | # Custom sidebar templates, must be a dictionary that maps document names 110 | # to template names. 111 | # 112 | # The default sidebars (for documents that don't match any pattern) are 113 | # defined by theme itself. Builtin themes are using these templates by 114 | # default: ``['localtoc.html', 'relations.html', 'sourcelink.html', 115 | # 'searchbox.html']``. 116 | # 117 | # html_sidebars = {} 118 | #html_sidebars = { 119 | # '**': ['sourcelink.html', 'searchbox.html'], 120 | #} 121 | html_sidebars = { 122 | "index": ["project.html", "localtoc.html", "searchbox.html"], 123 | "**": ["localtoc.html", "relations.html", "searchbox.html"], 124 | } 125 | singlehtml_sidebars = {"index": ["project.html", "localtoc.html"]} 126 | 127 | # -- Options for HTMLHelp output --------------------------------------------- 128 | 129 | # Output file base name for HTML help builder. 130 | htmlhelp_basename = 'Flask-PluginKitdoc' 131 | 132 | 133 | # -- Options for LaTeX output ------------------------------------------------ 134 | 135 | latex_elements = { 136 | # The paper size ('letterpaper' or 'a4paper'). 137 | # 138 | # 'papersize': 'letterpaper', 139 | 140 | # The font size ('10pt', '11pt' or '12pt'). 141 | # 142 | # 'pointsize': '10pt', 143 | 144 | # Additional stuff for the LaTeX preamble. 145 | # 146 | # 'preamble': '', 147 | 148 | # Latex figure (float) alignment 149 | # 150 | # 'figure_align': 'htbp', 151 | } 152 | 153 | # Grouping the document tree into LaTeX files. List of tuples 154 | # (source start file, target name, title, 155 | # author, documentclass [howto, manual, or own class]). 156 | latex_documents = [ 157 | (master_doc, 'Flask-PluginKit.tex', u'Flask-PluginKit Documentation', 158 | u'staugur', 'manual'), 159 | ] 160 | 161 | 162 | # -- Options for manual page output ------------------------------------------ 163 | 164 | # One entry per manual page. List of tuples 165 | # (source start file, name, description, authors, manual section). 166 | man_pages = [ 167 | (master_doc, 'flask-pluginkit', u'Flask-PluginKit Documentation', 168 | [author], 1) 169 | ] 170 | 171 | 172 | # -- Options for Texinfo output ---------------------------------------------- 173 | 174 | # Grouping the document tree into Texinfo files. List of tuples 175 | # (source start file, target name, title, author, 176 | # dir menu entry, description, category) 177 | texinfo_documents = [ 178 | (master_doc, 'Flask-PluginKit', u'Flask-PluginKit Documentation', 179 | author, 'Flask-PluginKit', 'One line description of project.', 180 | 'Miscellaneous'), 181 | ] 182 | 183 | 184 | # -- Options for Epub output ------------------------------------------------- 185 | 186 | # Bibliographic Dublin Core info. 187 | epub_title = project 188 | 189 | # The unique identifier of the text. This can be a ISBN number 190 | # or the project homepage. 191 | # 192 | # epub_identifier = '' 193 | 194 | # A unique identification for the text. 195 | # 196 | # epub_uid = '' 197 | 198 | # A list of files that should not be packed into the epub file. 199 | epub_exclude_files = ['search.html'] 200 | 201 | 202 | # -- Extension configuration ------------------------------------------------- 203 | -------------------------------------------------------------------------------- /docs/contributing.rst: -------------------------------------------------------------------------------- 1 | .. include:: ../CONTRIBUTING.rst 2 | -------------------------------------------------------------------------------- /docs/faq.rst: -------------------------------------------------------------------------------- 1 | FAQ 2 | === 3 | 4 | .. _faq-cov: 5 | 6 | Nothing. 7 | -------------------------------------------------------------------------------- /docs/index.rst: -------------------------------------------------------------------------------- 1 | =============== 2 | Flask-PluginKit 3 | =============== 4 | 5 | Welcome to Flask-PluginKit's documentation. 6 | 7 | This is a Flask-based plugin development kit that supports multiple 8 | extension types that you can use to create plugins for web applications 9 | with little or no change to the core code. 10 | 11 | Q: Why did you develop this extension? 12 | 13 | A: Play. 14 | 15 | Get started with :ref:`installation` and then get an overview with the 16 | :ref:`quickstart`. There is also a more detailed :ref:`tutorial` 17 | that shows how to create a small but complete plugin with Flask-PluginKit. 18 | The rest of the docs describe each component of Flask-PluginKit in detail, 19 | with a full reference in the :ref:`api` section. 20 | 21 | Flask-PluginKit depends on the `Flask`_. 22 | 23 | The plugin based on Flask-PluginKit can be a local directory or a third-party 24 | package (such as pypi), which is the `official plugin organization`_. 25 | 26 | .. image:: https://github.com/staugur/Flask-PluginKit/actions/workflows/ci.yml/badge.svg 27 | :target: https://github.com/staugur/Flask-PluginKit/actions/workflows/ci.yml 28 | 29 | .. image:: https://codecov.io/gh/staugur/Flask-PluginKit/branch/master/graph/badge.svg 30 | :target: https://codecov.io/gh/staugur/Flask-PluginKit 31 | 32 | .. image:: https://img.shields.io/pypi/v/Flask-PluginKit.svg?style=popout 33 | :target: https://pypi.org/project/Flask-PluginKit/ 34 | 35 | 36 | User Guide 37 | ---------- 38 | 39 | Describe in detail how to develop a plugin using Flask-PluginKit. 40 | 41 | .. toctree:: 42 | :maxdepth: 2 43 | 44 | installation 45 | quickstart 46 | tutorial/index 47 | webmanager 48 | 49 | 50 | API Reference 51 | ------------- 52 | 53 | If you are looking for information on a specific function, class or 54 | method, this part of the documentation is for you. 55 | 56 | .. toctree:: 57 | :maxdepth: 2 58 | 59 | api 60 | 61 | 62 | Additional Notes 63 | ---------------- 64 | 65 | Some additional instructions, styleguide, changelog, contributing, 66 | will be supplemented by others. 67 | 68 | .. toctree:: 69 | :maxdepth: 2 70 | 71 | changelog 72 | contributing 73 | faq 74 | Pocoo Styleguide 75 | 76 | 77 | .. _Flask: https://www.palletsprojects.com/p/flask/ 78 | 79 | .. _official plugin organization: https://github.com/saintic?q=flask-pluginkit 80 | -------------------------------------------------------------------------------- /docs/installation.rst: -------------------------------------------------------------------------------- 1 | .. _installation: 2 | 3 | Installation 4 | ============ 5 | 6 | Python Version 7 | -------------- 8 | 9 | Flask-PluginKit only supports Python 3.8+. 10 | 11 | Dependencies 12 | ------------ 13 | 14 | These distributions will be installed automatically 15 | when installing Flask-PluginKit. 16 | 17 | * `Flask`_ is a Lightweight Web Application Framework Written in Python, 18 | theoretically support 3.0.0+. 19 | 20 | * `semver`_ is a Semantic Version Control Specification. 21 | 22 | .. _Flask: https://www.palletsprojects.com/p/flask/ 23 | .. _semver: https://semver.org 24 | 25 | Virtual environments 26 | -------------------- 27 | 28 | We recommend using a virtual environment to manage the dependencies for 29 | your project, both in development and in production. 30 | 31 | Of course, this requires you to understand it yourself. 32 | 33 | Python 3 comes bundled with the :mod:`venv` module to 34 | create virtual environments. 35 | 36 | Install Flask-PluginKit 37 | ----------------------- 38 | 39 | Within the activated virtual environment or global environment, 40 | use the following command to install Flask-PluginKit: 41 | 42 | .. code-block:: sh 43 | 44 | $ pip install -U Flask-PluginKit 45 | 46 | Flask-PluginKit is now installed. Check out the :doc:`/quickstart` or 47 | go to the :doc:`Documentation Overview `. 48 | 49 | Install development version 50 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~ 51 | 52 | If you want to work with the latest Flask-PluginKit code 53 | before it's released, install or update the code from the master branch: 54 | 55 | .. code-block:: sh 56 | 57 | $ pip install -U git+https://github.com/staugur/Flask-PluginKit.git@master 58 | 59 | .. _virtualenv: https://virtualenv.pypa.io/ 60 | -------------------------------------------------------------------------------- /docs/locale/zh_CN/LC_MESSAGES/changelog.po: -------------------------------------------------------------------------------- 1 | # SOME DESCRIPTIVE TITLE. 2 | # Copyright (C) 2018, staugur 3 | # This file is distributed under the same license as the Flask-PluginKit 4 | # package. 5 | # FIRST AUTHOR , 2019. 6 | # 7 | #, fuzzy 8 | msgid "" 9 | msgstr "" 10 | "Project-Id-Version: Flask-PluginKit 3.0.x\n" 11 | "Report-Msgid-Bugs-To: \n" 12 | "POT-Creation-Date: 2024-05-22 21:39+0800\n" 13 | "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" 14 | "Last-Translator: FULL NAME \n" 15 | "Language-Team: LANGUAGE \n" 16 | "MIME-Version: 1.0\n" 17 | "Content-Type: text/plain; charset=utf-8\n" 18 | "Content-Transfer-Encoding: 8bit\n" 19 | "Generated-By: Babel 2.15.0\n" 20 | 21 | #: ../../changelog.rst:2 22 | msgid "Changelog" 23 | msgstr "更新日志" 24 | 25 | #: ../../../CHANGELOG.rst:4 26 | msgid "v3.8.0" 27 | msgstr "" 28 | 29 | #: ../../../CHANGELOG.rst:6 30 | msgid "Released in 2024-05-22" 31 | msgstr "" 32 | 33 | #: ../../../CHANGELOG.rst:8 34 | msgid "Only support python 3.8+ and flask 3.0+" 35 | msgstr "" 36 | 37 | #: ../../../CHANGELOG.rst:12 38 | msgid "v3.7.2" 39 | msgstr "" 40 | 41 | #: ../../../CHANGELOG.rst:14 42 | msgid "Released in 2023-05-17" 43 | msgstr "" 44 | 45 | #: ../../../CHANGELOG.rst:16 46 | msgid "Fixed semver version" 47 | msgstr "" 48 | 49 | #: ../../../CHANGELOG.rst:17 50 | msgid "Drop hep `before_first_request`" 51 | msgstr "" 52 | 53 | #: ../../../CHANGELOG.rst:20 54 | msgid "v3.7.1" 55 | msgstr "" 56 | 57 | #: ../../../CHANGELOG.rst:22 58 | msgid "Released in 2021-09-07" 59 | msgstr "" 60 | 61 | #: ../../../CHANGELOG.rst:24 62 | msgid "Restore translations.ini" 63 | msgstr "" 64 | 65 | #: ../../../CHANGELOG.rst:27 66 | msgid "v3.7.0" 67 | msgstr "" 68 | 69 | #: ../../../CHANGELOG.rst:29 70 | msgid "Released in 2021-09-02" 71 | msgstr "" 72 | 73 | #: ../../../CHANGELOG.rst:31 74 | msgid "feat: add :ref:`p3`, is beta" 75 | msgstr "" 76 | 77 | #: ../../../CHANGELOG.rst:32 78 | msgid "chore: the stpl_reverse falls into stpl" 79 | msgstr "" 80 | 81 | #: ../../../CHANGELOG.rst:33 82 | msgid "" 83 | "deprecated: will remove the try_compatible and stpl_reverse in " 84 | ":class:`~flask_pluginkit.PluginManager`" 85 | msgstr "" 86 | 87 | #: ../../../CHANGELOG.rst:34 88 | msgid "misc: remove web static files(change to CDN)" 89 | msgstr "" 90 | 91 | #: ../../../CHANGELOG.rst:37 92 | msgid "v3.6.2" 93 | msgstr "" 94 | 95 | #: ../../../CHANGELOG.rst:39 ../../../CHANGELOG.rst:47 96 | msgid "Released in 2021-06-22" 97 | msgstr "" 98 | 99 | #: ../../../CHANGELOG.rst:41 100 | msgid "" 101 | "chore: compatible with flask 2.0 nested blueprints, but only supports " 102 | "blueprints of nested plugins" 103 | msgstr "" 104 | 105 | #: ../../../CHANGELOG.rst:42 106 | msgid "fix ci(deploy pypi) & update docs" 107 | msgstr "" 108 | 109 | #: ../../../CHANGELOG.rst:45 110 | msgid "v3.6.1" 111 | msgstr "" 112 | 113 | #: ../../../CHANGELOG.rst:49 114 | msgid "ci tests from travis to github action" 115 | msgstr "" 116 | 117 | #: ../../../CHANGELOG.rst:50 118 | msgid "chore: web message api with SSE, page with CDN" 119 | msgstr "" 120 | 121 | #: ../../../CHANGELOG.rst:53 122 | msgid "v3.6.0" 123 | msgstr "" 124 | 125 | #: ../../../CHANGELOG.rst:55 126 | msgid "Released in 2020-12-31" 127 | msgstr "" 128 | 129 | #: ../../../CHANGELOG.rst:57 130 | msgid "feat: add :ref:`vep-on-blueprint`, is beta" 131 | msgstr "" 132 | 133 | #: ../../../CHANGELOG.rst:58 134 | msgid "feat: the :meth:`~flask_pluginkit.PluginManager.emit_assets` add _external" 135 | msgstr "" 136 | 137 | #: ../../../CHANGELOG.rst:59 138 | msgid "fix: semver.compare and semver.parse" 139 | msgstr "" 140 | 141 | #: ../../../CHANGELOG.rst:60 ../../../CHANGELOG.rst:70 142 | msgid "update docs & test case, example" 143 | msgstr "" 144 | 145 | #: ../../../CHANGELOG.rst:61 146 | msgid "deprecated: MongoStorage" 147 | msgstr "" 148 | 149 | #: ../../../CHANGELOG.rst:64 150 | msgid "v3.5.0" 151 | msgstr "" 152 | 153 | #: ../../../CHANGELOG.rst:66 154 | msgid "Released in 2020-04-08" 155 | msgstr "" 156 | 157 | #: ../../../CHANGELOG.rst:68 158 | msgid "feat: add class-based view extension point (beta, use Flask-Classful)" 159 | msgstr "" 160 | 161 | #: ../../../CHANGELOG.rst:69 162 | msgid "chore: web plugin manager page setInterval (called as needed)" 163 | msgstr "" 164 | 165 | #: ../../../CHANGELOG.rst:73 166 | msgid "v3.4.1" 167 | msgstr "" 168 | 169 | #: ../../../CHANGELOG.rst:75 170 | msgid "Released in 2020-03-25" 171 | msgstr "" 172 | 173 | #: ../../../CHANGELOG.rst:77 174 | msgid "feat: add :meth:`~flask_pluginkit.LocalStorage.setmany`" 175 | msgstr "" 176 | 177 | #: ../../../CHANGELOG.rst:78 178 | msgid "feat: add :meth:`~flask_pluginkit.RedisStorage.setmany`" 179 | msgstr "" 180 | 181 | #: ../../../CHANGELOG.rst:79 182 | msgid "fix: update result(original) :meth:`~flask_pluginkit.RedisStorage.get`" 183 | msgstr "" 184 | 185 | #: ../../../CHANGELOG.rst:82 186 | msgid "v3.4.0" 187 | msgstr "" 188 | 189 | #: ../../../CHANGELOG.rst:84 190 | msgid "Released in 2019-09-27" 191 | msgstr "" 192 | 193 | #: ../../../CHANGELOG.rst:86 194 | msgid "feat: add before_first_request hook to hep" 195 | msgstr "" 196 | 197 | #: ../../../CHANGELOG.rst:87 198 | msgid "feat: errhandler allow exception" 199 | msgstr "" 200 | 201 | #: ../../../CHANGELOG.rst:88 202 | msgid "feat: emit_assets add _raw param" 203 | msgstr "" 204 | 205 | #: ../../../CHANGELOG.rst:89 206 | msgid "feat: filter allow [f1, f2, (name, f3)]" 207 | msgstr "" 208 | 209 | #: ../../../CHANGELOG.rst:90 210 | msgid "feat: web manager add a button, select language" 211 | msgstr "" 212 | 213 | #: ../../../CHANGELOG.rst:91 214 | msgid "feat: stpl alow asc or desc, open the template sorting" 215 | msgstr "" 216 | 217 | #: ../../../CHANGELOG.rst:92 218 | msgid "fix: check getPluginClass" 219 | msgstr "" 220 | 221 | #: ../../../CHANGELOG.rst:93 222 | msgid "fix: check url prefix" 223 | msgstr "" 224 | 225 | #: ../../../CHANGELOG.rst:94 226 | msgid "chore: update docs, example and test case" 227 | msgstr "" 228 | 229 | #: ../../../CHANGELOG.rst:97 230 | msgid "v3.3.1" 231 | msgstr "" 232 | 233 | #: ../../../CHANGELOG.rst:99 234 | msgid "Released in 2019-09-17" 235 | msgstr "" 236 | 237 | #: ../../../CHANGELOG.rst:101 238 | msgid "feat: automatically compatible 2.x partial registration method" 239 | msgstr "" 240 | 241 | #: ../../../CHANGELOG.rst:102 242 | msgid "fix: update shelve set key type" 243 | msgstr "" 244 | 245 | #: ../../../CHANGELOG.rst:103 246 | msgid "fix: remove the number of tests `/`" 247 | msgstr "" 248 | 249 | #: ../../../CHANGELOG.rst:104 250 | msgid "test: add docs" 251 | msgstr "" 252 | 253 | #: ../../../CHANGELOG.rst:105 254 | msgid "chore: update travis.yml" 255 | msgstr "" 256 | 257 | #: ../../../CHANGELOG.rst:106 258 | msgid "chore: formatting code and optimizing import" 259 | msgstr "" 260 | 261 | #: ../../../CHANGELOG.rst:109 262 | msgid "v3.3.0" 263 | msgstr "" 264 | 265 | #: ../../../CHANGELOG.rst:111 266 | msgid "Released in 2019-07-31" 267 | msgstr "" 268 | 269 | #: ../../../CHANGELOG.rst:113 270 | msgid "feat: update :class:`~flask_pluginkit.PluginInstaller`." 271 | msgstr "" 272 | 273 | #: ../../../CHANGELOG.rst:114 274 | msgid "feat: add a blueprint to manage plugins" 275 | msgstr "" 276 | 277 | #: ../../../CHANGELOG.rst:115 278 | msgid "fix: close shelve connection" 279 | msgstr "" 280 | 281 | #: ../../../CHANGELOG.rst:116 ../../../CHANGELOG.rst:132 282 | #: ../../../CHANGELOG.rst:144 283 | msgid "test: add test case" 284 | msgstr "" 285 | 286 | #: ../../../CHANGELOG.rst:117 ../../../CHANGELOG.rst:133 287 | msgid "docs: update docs and translation" 288 | msgstr "" 289 | 290 | #: ../../../CHANGELOG.rst:118 ../../../CHANGELOG.rst:134 291 | #: ../../../CHANGELOG.rst:146 292 | msgid "chore: update example" 293 | msgstr "" 294 | 295 | #: ../../../CHANGELOG.rst:119 296 | msgid "chore: update travis.yml, add deploy" 297 | msgstr "" 298 | 299 | #: ../../../CHANGELOG.rst:122 300 | msgid "v3.2.0" 301 | msgstr "" 302 | 303 | #: ../../../CHANGELOG.rst:124 304 | msgid "Released in 2019-07-25" 305 | msgstr "" 306 | 307 | #: ../../../CHANGELOG.rst:126 308 | msgid "feat: dcp, be similar to flask-plugins event system," 309 | msgstr "" 310 | 311 | #: ../../../CHANGELOG.rst:127 312 | msgid "" 313 | "add :class:`~flask_pluginkit.utils.DcpManager` and " 314 | ":func:`~flask_pluginkit.push_dcp`" 315 | msgstr "" 316 | 317 | #: ../../../CHANGELOG.rst:129 318 | msgid "feat: add error handler" 319 | msgstr "" 320 | 321 | #: ../../../CHANGELOG.rst:130 322 | msgid "feat: add template filter" 323 | msgstr "" 324 | 325 | #: ../../../CHANGELOG.rst:131 326 | msgid "feat: add template context processor" 327 | msgstr "" 328 | 329 | #: ../../../CHANGELOG.rst:135 330 | msgid "chore: make some of the code more elegant" 331 | msgstr "" 332 | 333 | #: ../../../CHANGELOG.rst:138 334 | msgid "v3.1.0" 335 | msgstr "" 336 | 337 | #: ../../../CHANGELOG.rst:140 338 | msgid "Released in 2019-07-22" 339 | msgstr "" 340 | 341 | #: ../../../CHANGELOG.rst:142 342 | msgid "feat: add :class:`~flask_pluginkit.PluginInstaller`" 343 | msgstr "" 344 | 345 | #: ../../../CHANGELOG.rst:143 346 | msgid "feat: add vep :ref:`vep`" 347 | msgstr "" 348 | 349 | #: ../../../CHANGELOG.rst:145 350 | msgid "docs: update docs" 351 | msgstr "" 352 | 353 | #: ../../../CHANGELOG.rst:149 354 | msgid "v3.0.1" 355 | msgstr "" 356 | 357 | #: ../../../CHANGELOG.rst:151 358 | msgid "Released in 2019-07-21" 359 | msgstr "" 360 | 361 | #: ../../../CHANGELOG.rst:153 362 | msgid "The register functin can return empty dict, not necessarily meaningless." 363 | msgstr "" 364 | 365 | #: ../../../CHANGELOG.rst:154 366 | msgid "Update docs, translation and add test." 367 | msgstr "" 368 | 369 | #: ../../../CHANGELOG.rst:157 370 | msgid "v3.0.0" 371 | msgstr "" 372 | 373 | #: ../../../CHANGELOG.rst:159 374 | msgid "Released in 2019-07-20" 375 | msgstr "" 376 | 377 | #: ../../../CHANGELOG.rst:161 378 | msgid "Refactoring everyting" 379 | msgstr "" 380 | 381 | #: ../../../CHANGELOG.rst:164 382 | msgid "Previous Versions" 383 | msgstr "旧版本" 384 | 385 | #: ../../../CHANGELOG.rst:166 386 | msgid "" 387 | "Prior to 3.3.1, the new version of the refactoring was not compatible " 388 | "with the old version, and the update history is no longer recorded here." 389 | msgstr "3.3.1之前,由于新版本重构,完全与旧版本不兼容,此处不再记录更新历史记录。" 390 | 391 | #~ msgid "register可以返回空dict,并不一定没有意义发布修复版本" 392 | #~ msgstr "" 393 | 394 | #~ msgid "Update docs and translate and add test." 395 | #~ msgstr "" 396 | 397 | #~ msgid "Released in 2019-07-30" 398 | #~ msgstr "" 399 | 400 | #~ msgid "feat: add vep on blueprint(emmm, bvep), is beta" 401 | #~ msgstr "" 402 | 403 | -------------------------------------------------------------------------------- /docs/locale/zh_CN/LC_MESSAGES/contributing.po: -------------------------------------------------------------------------------- 1 | # SOME DESCRIPTIVE TITLE. 2 | # Copyright (C) 2018, staugur 3 | # This file is distributed under the same license as the Flask-PluginKit 4 | # package. 5 | # FIRST AUTHOR , 2019. 6 | # 7 | #, fuzzy 8 | msgid "" 9 | msgstr "" 10 | "Project-Id-Version: Flask-PluginKit 3.0.x\n" 11 | "Report-Msgid-Bugs-To: \n" 12 | "POT-Creation-Date: 2024-05-22 21:39+0800\n" 13 | "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" 14 | "Last-Translator: FULL NAME \n" 15 | "Language-Team: LANGUAGE \n" 16 | "MIME-Version: 1.0\n" 17 | "Content-Type: text/plain; charset=utf-8\n" 18 | "Content-Transfer-Encoding: 8bit\n" 19 | "Generated-By: Babel 2.15.0\n" 20 | 21 | #: ../../../CONTRIBUTING.rst:2 22 | msgid "How to contribute to Flask-PluginKit" 23 | msgstr "如何为Flask-PluginKit做出贡献" 24 | 25 | #: ../../../CONTRIBUTING.rst:4 26 | msgid "Thank you for considering contributing to Flask-PluginKit!" 27 | msgstr "感谢您考虑为Flask-PluginKit做贡献" 28 | 29 | #: ../../../CONTRIBUTING.rst:7 30 | msgid "Feedback" 31 | msgstr "反馈" 32 | 33 | #: ../../../CONTRIBUTING.rst:9 34 | msgid "" 35 | "The `issues `_ on " 36 | "GitHub." 37 | msgstr "" 38 | 39 | #: ../../../CONTRIBUTING.rst:10 40 | msgid "The email for staugur@saintic.com" 41 | msgstr "" 42 | 43 | #: ../../../CONTRIBUTING.rst:13 44 | msgid "Report a problem" 45 | msgstr "报告问题" 46 | 47 | #: ../../../CONTRIBUTING.rst:15 48 | msgid "Describe what you expect to happen." 49 | msgstr "描述您期望发生的事情。" 50 | 51 | #: ../../../CONTRIBUTING.rst:17 52 | msgid "" 53 | "If possible, include a minimal, complete, verifiable example to help us " 54 | "identify the problem. This also helps to check if the issue is not " 55 | "related to your own code." 56 | msgstr "如果可能,请提供一个最小、完整、可验证的示例来帮助我们找出问题所在。这也有助于检查问题是否与你自己的代码有关。" 57 | 58 | #: ../../../CONTRIBUTING.rst:21 59 | msgid "" 60 | "Describe what actually happened. If there is an exception, include the " 61 | "complete exception stack." 62 | msgstr "描述实际发生的事情。如果有可能,请提供完整的异常堆栈。" 63 | 64 | #: ../../../CONTRIBUTING.rst:24 65 | msgid "" 66 | "List your Python, Flask, Flask-PluginKit versions. If possible, check if " 67 | "the new version has fixed this issue." 68 | msgstr "列出你的Python、Flask、Flask-PluginKit版本,如果可能,检查新版本是否解决。" 69 | 70 | #: ../../../CONTRIBUTING.rst:28 71 | msgid "Submit pull request" 72 | msgstr "提交pull request" 73 | 74 | #: ../../../CONTRIBUTING.rst:30 75 | msgid "**First is the environment**" 76 | msgstr "**首先是环境**" 77 | 78 | #: ../../../CONTRIBUTING.rst:32 79 | msgid "" 80 | "fork repository `Flask-PluginKit `_" 82 | msgstr "" 83 | 84 | #: ../../../CONTRIBUTING.rst:34 85 | msgid "git clone Flask-PluginKit under your account and set your `git config`" 86 | msgstr "克隆Flask-PluginKit,并设置好个人的 `git config`" 87 | 88 | #: ../../../CONTRIBUTING.rst:36 89 | msgid "" 90 | "Install the dependency module of the development environment with command" 91 | " ``make dev``" 92 | msgstr "使用命令 ``make dev`` 安装开发环境依赖的模块" 93 | 94 | #: ../../../CONTRIBUTING.rst:39 95 | msgid "**Followed by coding**" 96 | msgstr "**接着是编码**" 97 | 98 | #: ../../../CONTRIBUTING.rst:41 99 | msgid "Write code, please try to comply with the PEP8 specification." 100 | msgstr "编写代码,请尽量遵守PEP8规范。" 101 | 102 | #: ../../../CONTRIBUTING.rst:43 103 | msgid "Write test cases and documentation." 104 | msgstr "编写测试用例和文档。" 105 | 106 | #: ../../../CONTRIBUTING.rst:45 107 | #, fuzzy 108 | msgid "" 109 | "Run the test ``make dev && make test`` using the py3.8+ environments " 110 | "respectively." 111 | msgstr "分别使用pypy、py2.7、py3.6+运行测试 ``make dev && make test``" 112 | 113 | #: ../../../CONTRIBUTING.rst:47 114 | msgid "Generate the documentation ``make dev && make html``." 115 | msgstr "使用 ``make dev && make html`` 生成文档" 116 | 117 | #: ../../../CONTRIBUTING.rst:49 118 | msgid "If you contribute a translation document, proceed as follows:" 119 | msgstr "如果您想翻译文档,步骤如下:" 120 | 121 | #: ../../../CONTRIBUTING.rst:51 122 | msgid "execute the command ``make gettext``" 123 | msgstr "执行命令 ``make gettext``" 124 | 125 | #: ../../../CONTRIBUTING.rst:53 126 | msgid "" 127 | "Extract translatable messages into pot files. The generated pot file will" 128 | " be placed in the docs/_build/gettext directory." 129 | msgstr "这个操作会将可翻译的消息提取到pot文件中,生成的pot文件将放在 docs/_build/gettext 目录中。" 130 | 131 | #: ../../../CONTRIBUTING.rst:56 132 | msgid "execute the command ``make cn``" 133 | msgstr "执行命令 ``make cn``" 134 | 135 | #: ../../../CONTRIBUTING.rst:58 136 | msgid "" 137 | "Generate or update the po file and place it in " 138 | "``docs/locale/zh_CN/LC_MESSAGES/`` directory, then translate the po file." 139 | msgstr "在 ``docs/locale//LC_MESSAGES/`` 目录生成或更新po文件,然后就可以翻译不同语言下的po文件了。" 140 | 141 | #: ../../../CONTRIBUTING.rst:61 142 | msgid "execute the command ``make html-cn``" 143 | msgstr "执行命令 ``make html-cn``" 144 | 145 | #: ../../../CONTRIBUTING.rst:63 146 | msgid "" 147 | "Constructing Translation Documents, at the same time, it will generate or" 148 | " update mo file." 149 | msgstr "构建生成翻译文档" 150 | 151 | #: ../../../CONTRIBUTING.rst:66 152 | msgid "**Last request for merger**" 153 | msgstr "**最后合并请求**" 154 | 155 | #: ../../../CONTRIBUTING.rst:68 156 | msgid "Submit your code" 157 | msgstr "提交代码" 158 | 159 | #: ../../../CONTRIBUTING.rst:70 160 | msgid "Initiate ``pull request`` on GitHub" 161 | msgstr "在GitHub上发起 ``pull request``" 162 | 163 | -------------------------------------------------------------------------------- /docs/locale/zh_CN/LC_MESSAGES/faq.po: -------------------------------------------------------------------------------- 1 | # SOME DESCRIPTIVE TITLE. 2 | # Copyright (C) 2018, staugur 3 | # This file is distributed under the same license as the Flask-PluginKit 4 | # package. 5 | # FIRST AUTHOR , 2019. 6 | # 7 | #, fuzzy 8 | msgid "" 9 | msgstr "" 10 | "Project-Id-Version: Flask-PluginKit 3.3.x\n" 11 | "Report-Msgid-Bugs-To: \n" 12 | "POT-Creation-Date: 2024-05-22 21:39+0800\n" 13 | "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" 14 | "Last-Translator: FULL NAME \n" 15 | "Language-Team: LANGUAGE \n" 16 | "MIME-Version: 1.0\n" 17 | "Content-Type: text/plain; charset=utf-8\n" 18 | "Content-Transfer-Encoding: 8bit\n" 19 | "Generated-By: Babel 2.15.0\n" 20 | 21 | #: ../../faq.rst:2 22 | msgid "FAQ" 23 | msgstr "" 24 | 25 | #: ../../faq.rst:6 26 | msgid "Nothing." 27 | msgstr "" 28 | -------------------------------------------------------------------------------- /docs/locale/zh_CN/LC_MESSAGES/index.po: -------------------------------------------------------------------------------- 1 | # SOME DESCRIPTIVE TITLE. 2 | # Copyright (C) 2018, staugur 3 | # This file is distributed under the same license as the Flask-PluginKit 4 | # package. 5 | # FIRST AUTHOR , 2019. 6 | # 7 | #, fuzzy 8 | msgid "" 9 | msgstr "" 10 | "Project-Id-Version: Flask-PluginKit 3.0.x\n" 11 | "Report-Msgid-Bugs-To: \n" 12 | "POT-Creation-Date: 2024-05-22 21:39+0800\n" 13 | "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" 14 | "Last-Translator: FULL NAME \n" 15 | "Language-Team: LANGUAGE \n" 16 | "MIME-Version: 1.0\n" 17 | "Content-Type: text/plain; charset=utf-8\n" 18 | "Content-Transfer-Encoding: 8bit\n" 19 | "Generated-By: Babel 2.15.0\n" 20 | 21 | #: ../../index.rst:68 22 | msgid "Pocoo Styleguide" 23 | msgstr "" 24 | 25 | #: ../../index.rst:3 26 | msgid "Flask-PluginKit" 27 | msgstr "" 28 | 29 | #: ../../index.rst:5 30 | msgid "Welcome to Flask-PluginKit's documentation." 31 | msgstr "欢迎查阅Flask-PluginKit文档!" 32 | 33 | #: ../../index.rst:7 34 | msgid "" 35 | "This is a Flask-based plugin development kit that supports multiple " 36 | "extension types that you can use to create plugins for web applications " 37 | "with little or no change to the core code." 38 | msgstr "这是一个基于Flask的插件开发工具包,支持多种扩展类型,用以增加、扩展web应用,而很少、甚至是无需更改源代码" 39 | 40 | #: ../../index.rst:11 41 | msgid "Q: Why did you develop this extension?" 42 | msgstr "问:为什么开发这个扩展?" 43 | 44 | #: ../../index.rst:13 45 | msgid "A: Play." 46 | msgstr "答:觉得好玩~~" 47 | 48 | #: ../../index.rst:15 49 | msgid "" 50 | "Get started with :ref:`installation` and then get an overview with the " 51 | ":ref:`quickstart`. There is also a more detailed :ref:`tutorial` that " 52 | "shows how to create a small but complete plugin with Flask-PluginKit. The" 53 | " rest of the docs describe each component of Flask-PluginKit in detail, " 54 | "with a full reference in the :ref:`api` section." 55 | msgstr "" 56 | "首先请 :ref:`installation` , 然后是 :ref:`quickstart` ,还有一个更详细的 :ref:`tutorial`" 57 | " ,展示了如何创建一个完整的插件。其他文档详细描述了Flask-PluginKit的每个组件,参考 :ref:`api` 非常重要。" 58 | 59 | #: ../../index.rst:21 60 | msgid "Flask-PluginKit depends on the `Flask`_." 61 | msgstr "Flask-PluginKit依赖于 `Flask`_ !" 62 | 63 | #: ../../index.rst:23 64 | msgid "" 65 | "The plugin based on Flask-PluginKit can be a local directory or a third-" 66 | "party package (such as pypi), which is the `official plugin " 67 | "organization`_." 68 | msgstr "" 69 | "基于Flask-PluginKit的插件可以是一个本地目录或第三方模块(比如来自pypi),这里是官方插件组: `official plugin " 70 | "organization`_" 71 | 72 | #: ../../index.rst:37 73 | msgid "User Guide" 74 | msgstr "用户指南" 75 | 76 | #: ../../index.rst:39 77 | msgid "Describe in detail how to develop a plugin using Flask-PluginKit." 78 | msgstr "详细描述了如何使用Flask-PluginKit开发一个插件。" 79 | 80 | #: ../../index.rst:51 81 | msgid "API Reference" 82 | msgstr "API参考" 83 | 84 | #: ../../index.rst:53 85 | msgid "" 86 | "If you are looking for information on a specific function, class or " 87 | "method, this part of the documentation is for you." 88 | msgstr "如果你正在寻找特定功能,这部分文档也许有帮助。" 89 | 90 | #: ../../index.rst:63 91 | msgid "Additional Notes" 92 | msgstr "附加说明" 93 | 94 | #: ../../index.rst:65 95 | msgid "" 96 | "Some additional instructions, styleguide, changelog, contributing, will " 97 | "be supplemented by others." 98 | msgstr "一些额外的说明,样式指南、更新日志、如何贡献等。" 99 | 100 | #~ msgid "当前版本v3.0.0+,完全不兼容旧版本!" 101 | #~ msgstr "" 102 | 103 | #~ msgid "" 104 | #~ "The current version v3.x is completely" 105 | #~ " incompatible with the old version!" 106 | #~ msgstr "当前版本v3.x,完全不兼容旧版本!" 107 | 108 | #~ msgid "" 109 | #~ "The current version v3.x is completely" 110 | #~ " incompatible with the old version! " 111 | #~ "But the plugin can be easily " 112 | #~ "compatible with the new version, just" 113 | #~ " return the registered method with " 114 | #~ "``register``." 115 | #~ msgstr "当前版本v3.x,完全不兼容旧版本!但是插件可以很容易兼容新版本,只需要将注册的方法用 ``register`` 重新返回即可。" 116 | 117 | #~ msgid "" 118 | #~ "V3.3.1 or later is compatible with " 119 | #~ "some data in v2.x, refer :ref:`faq-" 120 | #~ "cov`" 121 | #~ msgstr "v3.3.1及后续版本可以自动兼容v2.x中部分数据,参考 :ref:`faq-cov`" 122 | 123 | -------------------------------------------------------------------------------- /docs/locale/zh_CN/LC_MESSAGES/installation.po: -------------------------------------------------------------------------------- 1 | # SOME DESCRIPTIVE TITLE. 2 | # Copyright (C) 2018, staugur 3 | # This file is distributed under the same license as the Flask-PluginKit 4 | # package. 5 | # FIRST AUTHOR , 2019. 6 | # 7 | #, fuzzy 8 | msgid "" 9 | msgstr "" 10 | "Project-Id-Version: Flask-PluginKit 3.0.x\n" 11 | "Report-Msgid-Bugs-To: \n" 12 | "POT-Creation-Date: 2024-05-22 21:39+0800\n" 13 | "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" 14 | "Last-Translator: FULL NAME \n" 15 | "Language-Team: LANGUAGE \n" 16 | "MIME-Version: 1.0\n" 17 | "Content-Type: text/plain; charset=utf-8\n" 18 | "Content-Transfer-Encoding: 8bit\n" 19 | "Generated-By: Babel 2.15.0\n" 20 | 21 | #: ../../installation.rst:4 22 | msgid "Installation" 23 | msgstr "安装" 24 | 25 | #: ../../installation.rst:7 26 | msgid "Python Version" 27 | msgstr "Python版本" 28 | 29 | #: ../../installation.rst:9 30 | msgid "Flask-PluginKit only supports Python 3.8+." 31 | msgstr "Flask-PluginKit 仅支持 Python 3.8+" 32 | 33 | #: ../../installation.rst:12 34 | msgid "Dependencies" 35 | msgstr "依赖" 36 | 37 | #: ../../installation.rst:14 38 | msgid "" 39 | "These distributions will be installed automatically when installing " 40 | "Flask-PluginKit." 41 | msgstr "依赖的模块在安装Flask-PluginKit时会自动安装。" 42 | 43 | #: ../../installation.rst:17 44 | msgid "" 45 | "`Flask`_ is a Lightweight Web Application Framework Written in Python, " 46 | "theoretically support 3.0.0+." 47 | msgstr "`Flask`_ 是一个使用 Python 编写的轻量级 Web 应用框架,理论上支持 3.0.0+" 48 | 49 | #: ../../installation.rst:20 50 | msgid "`semver`_ is a Semantic Version Control Specification." 51 | msgstr "`semver`_ 是语义版本控制规范。" 52 | 53 | #: ../../installation.rst:26 54 | msgid "Virtual environments" 55 | msgstr "虚拟环境" 56 | 57 | #: ../../installation.rst:28 58 | msgid "" 59 | "We recommend using a virtual environment to manage the dependencies for " 60 | "your project, both in development and in production." 61 | msgstr "我们推荐您在项目中使用虚拟环境管理依赖,不论是开发还是正式。" 62 | 63 | #: ../../installation.rst:31 64 | msgid "Of course, this requires you to understand it yourself." 65 | msgstr "当然,这取决于您自己。" 66 | 67 | #: ../../installation.rst:33 68 | msgid "" 69 | "Python 3 comes bundled with the :mod:`venv` module to create virtual " 70 | "environments." 71 | msgstr "Python3使用内置 :mod:`venv` 创建虚拟环境。" 72 | 73 | #: ../../installation.rst:37 74 | msgid "Install Flask-PluginKit" 75 | msgstr "安装Flask-PluginKit" 76 | 77 | #: ../../installation.rst:39 78 | msgid "" 79 | "Within the activated virtual environment or global environment, use the " 80 | "following command to install Flask-PluginKit:" 81 | msgstr "在激活的虚拟环境或全局环境中,使用以下命令安装Flask-PluginKit:" 82 | 83 | #: ../../installation.rst:46 84 | msgid "" 85 | "Flask-PluginKit is now installed. Check out the :doc:`/quickstart` or go " 86 | "to the :doc:`Documentation Overview `." 87 | msgstr "" 88 | "Flask-PluginKit已经安装了。那么可以看看 :doc:`/quickstart` 或返回 :doc:`Documentation " 89 | "Overview `" 90 | 91 | #: ../../installation.rst:50 92 | msgid "Install development version" 93 | msgstr "安装开发版本" 94 | 95 | #: ../../installation.rst:52 96 | msgid "" 97 | "If you want to work with the latest Flask-PluginKit code before it's " 98 | "released, install or update the code from the master branch:" 99 | msgstr "如果您想要尝试最新的Flask-PluginKit代码,请安装master分支中的代码:" 100 | 101 | -------------------------------------------------------------------------------- /docs/locale/zh_CN/LC_MESSAGES/quickstart.po: -------------------------------------------------------------------------------- 1 | # SOME DESCRIPTIVE TITLE. 2 | # Copyright (C) 2018, staugur 3 | # This file is distributed under the same license as the Flask-PluginKit 4 | # package. 5 | # FIRST AUTHOR , 2019. 6 | # 7 | #, fuzzy 8 | msgid "" 9 | msgstr "" 10 | "Project-Id-Version: Flask-PluginKit 3.0.x\n" 11 | "Report-Msgid-Bugs-To: \n" 12 | "POT-Creation-Date: 2019-07-31 17:22+0800\n" 13 | "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" 14 | "Last-Translator: FULL NAME \n" 15 | "Language-Team: LANGUAGE \n" 16 | "MIME-Version: 1.0\n" 17 | "Content-Type: text/plain; charset=utf-8\n" 18 | "Content-Transfer-Encoding: 8bit\n" 19 | "Generated-By: Babel 2.6.0\n" 20 | 21 | #: ../../quickstart.rst:4 22 | msgid "Quickstart" 23 | msgstr "快速开始" 24 | 25 | #: ../../quickstart.rst:6 26 | msgid "" 27 | "Suppose you have installed the Flask-PluginKit. If you do not, head over " 28 | "to the :ref:`installation` section." 29 | msgstr "假设你已经安装了Flask-PluginKit,否则请转到 :ref:`installation` 部分。" 30 | 31 | #: ../../quickstart.rst:9 32 | msgid "The first one is to initalize it directly::" 33 | msgstr "第一种方法是直接初始化::" 34 | 35 | #: ../../quickstart.rst:15 36 | msgid "where as the second one is to use the factory pattern::" 37 | msgstr "第二种方式是使用工厂模式::" 38 | 39 | #: ../../quickstart.rst:23 40 | msgid "Plugin Structure" 41 | msgstr "插件结构" 42 | 43 | #: ../../quickstart.rst:25 44 | msgid "After the first step is done, you can start developing your first plugin." 45 | msgstr "完成第一步后,您可以开始开发第一个插件。" 46 | 47 | #: ../../quickstart.rst:27 48 | msgid "" 49 | "The plugin is a legal python package that needs to define some metadata " 50 | "and use register to return the extension point, more see :ref:`core-" 51 | "plugin-structure`" 52 | msgstr "" 53 | "插件是一个合法的python包,需要定义一些元数据并使用register来返回扩展点,更多请参阅 :ref:`core-plugin-" 54 | "structure`" 55 | 56 | #: ../../quickstart.rst:30 57 | msgid "For example, the structure of small plugin can look like this:" 58 | msgstr "例如,一个简单的插件结构看起来像这样:" 59 | 60 | #: ../../quickstart.rst:37 61 | msgid "the structure of a more complex plugin can also look like this:" 62 | msgstr "一个复杂的插件结构看起来像这样:" 63 | 64 | #: ../../quickstart.rst:51 65 | msgid "Hello World" 66 | msgstr "" 67 | 68 | #: ../../quickstart.rst:53 69 | msgid "" 70 | "For a better understanding you can also have a look at the `example`_, it" 71 | " contains a local plugin and a third party plugin." 72 | msgstr "为了更好地理解你可以查看 `example`_ ,它包含一个本地插件和第三方插件。" 73 | 74 | #: ../../quickstart.rst:56 75 | msgid "" 76 | "Now, let's start with a simple plugin example. The plugin name is " 77 | "helloworld." 78 | msgstr "现在,让我们开始制作一个简单的插件吧,名叫helloworld。" 79 | 80 | #: ../../quickstart.rst:58 81 | msgid "This simple helloworld example can be found `here`_." 82 | msgstr "这个helloworld示例可以在这 `here`_ 找到。" 83 | 84 | #: ../../quickstart.rst:60 85 | msgid "" 86 | "First, the developer wrote a simple plugin web application with only one " 87 | "app.py, an index view function, and the content is:" 88 | msgstr "首先,开发者编写了一个简单的插件Web应用程序,只有一个app.py文件,一个名叫index的视图函数,内容是:" 89 | 90 | #: ../../quickstart.rst:77 91 | msgid "" 92 | "Now, we want to limit when accessing the index view (ie /), if ip is " 93 | "127.0.0.1, then redirect to ``/limit``, proceed as follows:" 94 | msgstr "现在,我们想限制访问index视图(即/),如果ip是127.0.0.1,则重定向到 ``/limit``,步骤如下:" 95 | 96 | #: ../../quickstart.rst:80 97 | msgid "Create helloworld directory" 98 | msgstr "创建helloworld目录" 99 | 100 | #: ../../quickstart.rst:87 101 | msgid "Write ``__init__.py`` for the helloworld plugin, content is:" 102 | msgstr "为helloworld插件编写 ``__init__.py``,内容是:" 103 | 104 | #: ../../quickstart.rst:119 105 | msgid "Run" 106 | msgstr "运行" 107 | 108 | #: ../../quickstart.rst:121 109 | msgid "The current web application structure is as follows:" 110 | msgstr "当前web应用结构如下:" 111 | 112 | #: ../../quickstart.rst:132 113 | msgid "Run app:" 114 | msgstr "启动app:" 115 | 116 | #: ../../quickstart.rst:138 117 | msgid "Testing" 118 | msgstr "测试" 119 | 120 | #: ../../quickstart.rst:142 121 | msgid "For details, see :ref:`tutorial`" 122 | msgstr "更多细节,查阅 :ref:`tutorial`" 123 | 124 | #~ msgid "|helloworld_testimg|" 125 | #~ msgstr "" 126 | 127 | -------------------------------------------------------------------------------- /docs/locale/zh_CN/LC_MESSAGES/tutorial/afterword.po: -------------------------------------------------------------------------------- 1 | # SOME DESCRIPTIVE TITLE. 2 | # Copyright (C) 2018, staugur 3 | # This file is distributed under the same license as the Flask-PluginKit 4 | # package. 5 | # FIRST AUTHOR , 2019. 6 | # 7 | #, fuzzy 8 | msgid "" 9 | msgstr "" 10 | "Project-Id-Version: Flask-PluginKit 3.0.x\n" 11 | "Report-Msgid-Bugs-To: \n" 12 | "POT-Creation-Date: 2020-04-08 13:37+0800\n" 13 | "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" 14 | "Last-Translator: FULL NAME \n" 15 | "Language-Team: LANGUAGE \n" 16 | "MIME-Version: 1.0\n" 17 | "Content-Type: text/plain; charset=utf-8\n" 18 | "Content-Transfer-Encoding: 8bit\n" 19 | "Generated-By: Babel 2.6.0\n" 20 | 21 | #: ../../tutorial/afterword.rst:2 22 | msgid "Afterword" 23 | msgstr "后记" 24 | 25 | #: ../../tutorial/afterword.rst:5 26 | msgid "To the plugin user" 27 | msgstr "与插件用户的话" 28 | 29 | #: ../../tutorial/afterword.rst:7 30 | msgid "" 31 | "For the user, this tutorial can basically guarantee the smooth use of the" 32 | " tutorial. Of course, when I write this document, I can't accurately view" 33 | " it from the perspective of each user. Although I am also one of the " 34 | "users, I am also a developer. Familiar with internal processes." 35 | msgstr "对于用户来说,这个教程看完基本能保证使用畅通,当然,我在写这个文档的时候,无法准确地站在每个用户角度看待,虽然我也是用户之一,但是我又是开发者,比较熟悉内部流程。" 36 | 37 | #: ../../tutorial/afterword.rst:12 38 | msgid "" 39 | "At the beginning, I used Flask to write web applications, wrote a lot, " 40 | "and some things were repeated. Later I wanted to study something that has" 41 | " style. I referenced wordpress, flask-plguins, etc., and wrote the " 42 | "original code that can load plugins. Finally, I used More, so independent" 43 | " into a Flask extension and released to pypi, named Flask-PluginKit." 44 | msgstr "" 45 | "刚开始时,我用Flask写web应用,写过不少,有些东西重复,后来想研究点有格调的东西,参考了wordpress、flask-" 46 | "plguins等,写了原始的可以加载插件的代码,最后用的多了,所以独立成一个Flask扩展并发布到pypi,名叫Flask-PluginKit。" 47 | 48 | #: ../../tutorial/afterword.rst:18 49 | msgid "" 50 | "Later released to v2.4.1, I still have a lot of pain points in use, so I " 51 | "reconstructed all the code, rewritten the document, released v3.0.0!" 52 | msgstr "后来发布到v2.4.1,自己在使用中也还是有不少痛点,于是又重构所有代码,重写文档,发布v3.0.0!" 53 | 54 | #: ../../tutorial/afterword.rst:21 55 | msgid "I hope to give your feedback to me." 56 | msgstr "非常希望将您的意见反馈给我。" 57 | 58 | #: ../../tutorial/afterword.rst:24 59 | msgid "To the plugin developer" 60 | msgstr "与插件开发者的话" 61 | 62 | #: ../../tutorial/afterword.rst:26 63 | msgid "" 64 | "For developers, you not only need to read this tutorial, but also api, " 65 | "loading logic, and so on, of course, it is best to see some source code, " 66 | "although it is very good." 67 | msgstr "对于开发者来说,您不止需要看完这个教程,另外API、加载逻辑等也需要熟知,当然,最好能看些源码,虽然写的很菜。" 68 | 69 | #~ msgid "" 70 | #~ "Now this version of the feature is" 71 | #~ " temporarily unavailable, but will be " 72 | #~ "added later." 73 | #~ msgstr "现在这版功能暂时少了,不过后面会补回来的。" 74 | 75 | -------------------------------------------------------------------------------- /docs/locale/zh_CN/LC_MESSAGES/tutorial/bep.po: -------------------------------------------------------------------------------- 1 | # SOME DESCRIPTIVE TITLE. 2 | # Copyright (C) 2018, staugur 3 | # This file is distributed under the same license as the Flask-PluginKit 4 | # package. 5 | # FIRST AUTHOR , 2019. 6 | # 7 | #, fuzzy 8 | msgid "" 9 | msgstr "" 10 | "Project-Id-Version: Flask-PluginKit 3.0.x\n" 11 | "Report-Msgid-Bugs-To: \n" 12 | "POT-Creation-Date: 2021-06-22 16:32+0800\n" 13 | "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" 14 | "Last-Translator: FULL NAME \n" 15 | "Language-Team: LANGUAGE \n" 16 | "MIME-Version: 1.0\n" 17 | "Content-Type: text/plain; charset=utf-8\n" 18 | "Content-Transfer-Encoding: 8bit\n" 19 | "Generated-By: Babel 2.6.0\n" 20 | 21 | #: ../../tutorial/bep.rst:4 22 | msgid "Blueprint Extension Point" 23 | msgstr "蓝图扩展点" 24 | 25 | #: ../../tutorial/bep.rst:7 26 | msgid "Description" 27 | msgstr "描述" 28 | 29 | #: ../../tutorial/bep.rst:9 30 | msgid "The extension point abbreviation is bep." 31 | msgstr "这个扩展点缩写为bep。" 32 | 33 | #: ../../tutorial/bep.rst:11 34 | msgid "" 35 | "This extension point is very simple, just add a blueprint to the web " 36 | "application, no different from the normal blueprint." 37 | msgstr "这个扩展点非常简单,只需在Web应用程序中添加一个蓝图,与正常的蓝图没什么区别。" 38 | 39 | #: ../../tutorial/bep.rst:14 40 | msgid "" 41 | "The plugin needs to return the bep field via register. The bep data type " 42 | "returned is a dictionary with the format {blueprint: Blueprint Instance, " 43 | "prefix: /your_blueprint_url_prefix, parent: Name}. Only one blueprint is " 44 | "currently supported." 45 | msgstr "" 46 | "插件需要通过register返回bep字段,返回的bep数据类型是一个格式为{blueprint: Blueprint Instance, " 47 | "prefix: /your_blueprint_url_prefix, parent: Name}的字典。目前仅支持一个蓝图。" 48 | 49 | #: ../../tutorial/bep.rst:21 50 | msgid "" 51 | "Support Flask2.0 nested blueprint with the `parent` param(beta). But only" 52 | " blueprints of other plugins can be nested." 53 | msgstr "" 54 | "支持 Flask 2.0 的嵌套蓝图,用 `parent` 参数指定(beta)。" 55 | "但是仅支持嵌套其他插件的蓝图。" 56 | 57 | #: ../../tutorial/bep.rst:24 58 | msgid "" 59 | "The Flask-PluginKit loads bep via " 60 | ":meth:`~flask_pluginkit.PluginManager._bep_handler`, this method will " 61 | "detect bep rules and specific content." 62 | msgstr "" 63 | "Flask-PluginKit通过 :meth:`~flask_pluginkit.PluginManager._bep_handler` " 64 | "加载bep,此方法将检测bep规则和内容。" 65 | 66 | #: ../../tutorial/bep.rst:28 67 | msgid "" 68 | "The blueprint can be mounted under None or under other prefixes. Flask-" 69 | "PluginKit does not detect blueprint routing, as long as the prefix is " 70 | "legal." 71 | msgstr "蓝图可以安装在None或其他前缀下,只要前缀合法,Flask-PluginKit不会检测蓝图路由。" 72 | 73 | #: ../../tutorial/bep.rst:32 74 | msgid "" 75 | "The user only needs to ensure that the plugin is harmless and does not " 76 | "pollute your original application. Others are plugin developers." 77 | msgstr "用户需确保插件无害,并且不会污染原始应用程序,其他则是插件开发人员的事情。" 78 | 79 | #: ../../tutorial/bep.rst:35 80 | msgid "" 81 | "If you just want to add the view to the existing blueprint, you can refer" 82 | " to :ref:`vep-on-blueprint`" 83 | msgstr "如果你只想把视图添加到已有蓝图中,可以参考 :ref:`vep-on-blueprint`" 84 | 85 | #: ../../tutorial/bep.rst:39 86 | msgid "Example" 87 | msgstr "示例" 88 | 89 | #: ../../tutorial/bep.rst:41 90 | msgid "Plugin registration for bep" 91 | msgstr "注册bep" 92 | 93 | -------------------------------------------------------------------------------- /docs/locale/zh_CN/LC_MESSAGES/tutorial/config.po: -------------------------------------------------------------------------------- 1 | # SOME DESCRIPTIVE TITLE. 2 | # Copyright (C) 2018, staugur 3 | # This file is distributed under the same license as the Flask-PluginKit 4 | # package. 5 | # FIRST AUTHOR , 2019. 6 | # 7 | #, fuzzy 8 | msgid "" 9 | msgstr "" 10 | "Project-Id-Version: Flask-PluginKit 3.0.x\n" 11 | "Report-Msgid-Bugs-To: \n" 12 | "POT-Creation-Date: 2019-07-20 17:48+0800\n" 13 | "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" 14 | "Last-Translator: FULL NAME \n" 15 | "Language-Team: LANGUAGE \n" 16 | "MIME-Version: 1.0\n" 17 | "Content-Type: text/plain; charset=utf-8\n" 18 | "Content-Transfer-Encoding: 8bit\n" 19 | "Generated-By: Babel 2.6.0\n" 20 | 21 | #: ../../tutorial/config.rst:2 22 | msgid "Configuration" 23 | msgstr "配置" 24 | 25 | #: ../../tutorial/config.rst:5 26 | msgid "Description" 27 | msgstr "描述" 28 | 29 | #: ../../tutorial/config.rst:7 30 | msgid "" 31 | "The configuration here refers to the configuration information of the " 32 | "plugin. In general, the plugin needs to obtain the configuration of the " 33 | "web application. In the first case, the python module of the plugin needs" 34 | " configuration information, and the second case is that the template code" 35 | " of the plugin needs to obtain configuration information." 36 | msgstr "" 37 | "这里的配置是指插件的配置信息。通常,插件需要获取Web应用程序的配置。" 38 | "在第一种情况下,插件的python模块需要配置信息。" 39 | "第二种情况是插件的模板代码需要获取配置信息。" 40 | 41 | #: ../../tutorial/config.rst:13 42 | msgid "" 43 | "In the first case, the plugin can be imported directly into the " 44 | "configuration file module, or use ``current_app.config``." 45 | msgstr "第一种情况下,插件可以直接导入配置文件模块,或使用 ``current_app.config`` " 46 | 47 | #: ../../tutorial/config.rst:16 48 | msgid "" 49 | "In the second case, the Flask itself already supports the configuration " 50 | "of **current_app.config** directly in the template using config, but the " 51 | "old version (before 0.10) may not support it, so Flask-PluginKit adds a " 52 | "template \"global variable\", " 53 | ":meth:`~flask_pluginkit.PluginManager.emit_config`, the configuration " 54 | "passed to :class:`~flask_pluginkit.PluginManager` with the " 55 | ":attr:`~flask_pluginkit.PluginManager.pluginkit_config` parameter and the" 56 | " configuration of **current_app.config** can be obtained directly." 57 | msgstr "" 58 | "在第二种情况下,Flask本身已经使用config直接在模板中获取current_app.config的配置," 59 | "但是旧版本(在0.10之前)可能不支持它,因此Flask-PluginKit添加了一个模板全局变量" 60 | " :meth:`~flask_pluginkit.PluginManager.emit_config` ,用户初始化" 61 | " :class:`~flask_pluginkit.PluginManager` 时,可以传递" 62 | " :attr:`~flask_pluginkit.PluginManager.pluginkit_config` 参数作为配置信息," 63 | "如果找不到,则会直接查找current_app.config。" 64 | 65 | #: ../../tutorial/config.rst:26 66 | msgid "" 67 | "The **emit_config** will first look in the **pluginkit_config** " 68 | "parameter. If not found, then find **current_app.config**. If still can't" 69 | " find, return None." 70 | msgstr "" 71 | "这个 **emit_config** 方法将首先查看 **pluginkit_config** 参数,如果没有找到," 72 | "那么找 **current_app.config** ,如果仍不能找到,返回None。" 73 | 74 | #: ../../tutorial/config.rst:31 75 | msgid "Example" 76 | msgstr "示例" 77 | 78 | #: ../../tutorial/config.rst:33 79 | msgid "User Definition" 80 | msgstr "用户定义" 81 | 82 | #: ../../tutorial/config.rst:40 83 | msgid "Plugin call" 84 | msgstr "插件调用" 85 | 86 | #: ../../tutorial/config.rst:42 87 | msgid "" 88 | "Suppose the following code is a template file under a plugin, get HELLO " 89 | "and DEBUG configuration:" 90 | msgstr "假设以下代码是插件下的模板文件,要获取HELLO和DEBUG配置:" 91 | 92 | -------------------------------------------------------------------------------- /docs/locale/zh_CN/LC_MESSAGES/tutorial/core.po: -------------------------------------------------------------------------------- 1 | # SOME DESCRIPTIVE TITLE. 2 | # Copyright (C) 2018, staugur 3 | # This file is distributed under the same license as the Flask-PluginKit 4 | # package. 5 | # FIRST AUTHOR , 2019. 6 | # 7 | #, fuzzy 8 | msgid "" 9 | msgstr "" 10 | "Project-Id-Version: Flask-PluginKit 3.0.x\n" 11 | "Report-Msgid-Bugs-To: \n" 12 | "POT-Creation-Date: 2021-09-02 21:08+0800\n" 13 | "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" 14 | "Last-Translator: FULL NAME \n" 15 | "Language-Team: LANGUAGE \n" 16 | "MIME-Version: 1.0\n" 17 | "Content-Type: text/plain; charset=utf-8\n" 18 | "Content-Transfer-Encoding: 8bit\n" 19 | "Generated-By: Babel 2.8.0\n" 20 | 21 | #: ../../tutorial/core.rst:2 22 | msgid "Plugin Core" 23 | msgstr "插件核心" 24 | 25 | #: ../../tutorial/core.rst:7 26 | msgid "Minimal Plugin" 27 | msgstr "最小化的插件" 28 | 29 | #: ../../tutorial/core.rst:20 30 | msgid "" 31 | "However, this plugin doesn't make any sense, it just means a valid plugin" 32 | " content, whether it's a local plugin or a third-party plugin, the core " 33 | "part is the same." 34 | msgstr "然而,这个插件没有任何意义,它只是意味着一个有效的插件内容,无论是本地插件还是第三方插件,核心部分都是一样的。" 35 | 36 | #: ../../tutorial/core.rst:24 37 | msgid "" 38 | "The mini plugin above, which starts and ends with ``__``, is called " 39 | "metadata, which is the most important information for getting plugins." 40 | msgstr "上面最小化的插件,以 ``__`` 双下划线开头和结尾,姑且称之为元数据,这用来定义插件的重要信息。" 41 | 42 | #: ../../tutorial/core.rst:27 43 | msgid "" 44 | "The ``register`` function is used to return the extension point. Let's " 45 | "explain it one by one." 46 | msgstr "还有 ``register`` 函数用来返回扩展点。后面文档会一一讲解。" 47 | 48 | #: ../../tutorial/core.rst:33 49 | msgid "Plugin Structure" 50 | msgstr "插件结构" 51 | 52 | #: ../../tutorial/core.rst:35 53 | msgid "" 54 | "The most minimal plugin needs to have at least it's own directory. The " 55 | "directory must contain the ``__init__.py`` file, otherwise it is not " 56 | "considered a plugin!" 57 | msgstr "一个插件至少需要拥有自己的目录,目录必须包含 ``__init__.py`` 文件,否则它不被认为是一个插件!" 58 | 59 | #: ../../tutorial/core.rst:39 60 | msgid "" 61 | "The core code of the plugin can be written in other modules of the " 62 | "package, then returned in **__init__.py** using the ``register`` " 63 | "function, and this file contains the metadata required to register the " 64 | "plugin." 65 | msgstr "" 66 | "插件的核心代码可以写在插件目录下其他模块中,然后在 **__init__.py** 文件中使用 ``register`` " 67 | "函数返回扩展点,此文件还包含注册所需的元数据。" 68 | 69 | #: ../../tutorial/core.rst:43 70 | msgid "" 71 | "In `__init__.py`, you can write your plugin code all in. Of course, the " 72 | "recommended way is to create a module with another name under the plugin " 73 | "package. Write your functions, class, variable, and so on, then import " 74 | "the module in `__init__.py` and use register to return the extension " 75 | "point." 76 | msgstr "" 77 | "在 `__init__.py` " 78 | "中,你可以编写你的插件代码。当然,推荐的方法是在插件目录下创建其他的模块文件,在其中编写你的函数、类、变量等,然后在 `__init__.py` " 79 | "中导入模块并使用 ``register`` 返回扩展点。" 80 | 81 | #: ../../tutorial/core.rst:49 82 | msgid "" 83 | "The project structure of a complete plugin web application is probably " 84 | "like this:" 85 | msgstr "完整的插件化的Web应用程序的项目结构可能是像这样:" 86 | 87 | #: ../../tutorial/core.rst:83 88 | msgid "metadata" 89 | msgstr "元数据" 90 | 91 | #: ../../tutorial/core.rst:85 92 | msgid "" 93 | "Below are all supported metadata configuration items, please note that " 94 | "the first three are required:" 95 | msgstr "以下是所有支持的元数据配置项,请注意前三个是必需的:" 96 | 97 | #: ../../tutorial/core.rst:88 98 | msgid "``__plugin_name__``" 99 | msgstr "" 100 | 101 | #: ../../tutorial/core.rst:90 102 | msgid "" 103 | "Your plugin name is not strictly required to be consistent with the " 104 | "plugin directory name." 105 | msgstr "你的插件名称,不严格要求和插件目录名称保持一致。" 106 | 107 | #: ../../tutorial/core.rst:93 108 | msgid "``__author__``" 109 | msgstr "" 110 | 111 | #: ../../tutorial/core.rst:95 112 | msgid "Plugin Author" 113 | msgstr "插件作者" 114 | 115 | #: ../../tutorial/core.rst:97 116 | msgid "``__version__``" 117 | msgstr "" 118 | 119 | #: ../../tutorial/core.rst:99 120 | msgid "Plugin Version, compliance with `Semantic Version 2.0`_ Rules." 121 | msgstr "插件版本,符合 `Semantic Version 2.0`_ 规范。" 122 | 123 | #: ../../tutorial/core.rst:101 124 | msgid "``__description__``" 125 | msgstr "" 126 | 127 | #: ../../tutorial/core.rst:103 128 | msgid "What is the use of plugin description information." 129 | msgstr "插件描述信息" 130 | 131 | #: ../../tutorial/core.rst:105 132 | msgid "``__url__``" 133 | msgstr "" 134 | 135 | #: ../../tutorial/core.rst:107 136 | msgid "Plugin Homepage" 137 | msgstr "插件主页" 138 | 139 | #: ../../tutorial/core.rst:109 140 | msgid "``__license__``" 141 | msgstr "" 142 | 143 | #: ../../tutorial/core.rst:111 144 | msgid "Plugin LICENSE" 145 | msgstr "插件许可证" 146 | 147 | #: ../../tutorial/core.rst:113 148 | msgid "``__license_file__``" 149 | msgstr "" 150 | 151 | #: ../../tutorial/core.rst:115 152 | msgid "" 153 | "The plugin LICENSE detail file. Your plugin directory should have a " 154 | "LICENSE file." 155 | msgstr "插件许可证文件,位于你的插件目录下" 156 | 157 | #: ../../tutorial/core.rst:118 158 | msgid "``__readme_file__``" 159 | msgstr "" 160 | 161 | #: ../../tutorial/core.rst:120 162 | msgid "" 163 | "The plugin profile should have a README description file in your plugin " 164 | "directory." 165 | msgstr "插件自述文件,位于你的插件目录下" 166 | 167 | #: ../../tutorial/core.rst:123 168 | msgid "``__state__``" 169 | msgstr "" 170 | 171 | #: ../../tutorial/core.rst:125 172 | msgid "The plugin Status, enabled (default) or disabled." 173 | msgstr "插件状态,enabled (默认) 或 disabled。" 174 | 175 | #: ../../tutorial/core.rst:136 176 | msgid "register" 177 | msgstr "" 178 | 179 | #: ../../tutorial/core.rst:138 180 | msgid "" 181 | "This function is also required, it should be defined or imported in " 182 | "`__init__.py`. Flask-PluginKit will call this function when loading, " 183 | "return data is dict, contains various types of extension points, such as:" 184 | msgstr "" 185 | "这个函数也是必需的,应该在 ``__init__.py`` 被定义或导入。Flask-" 186 | "PluginKit将在加载插件时调用此函数,返回数据要求是dict,包含各种类型的扩展点,例如:" 187 | 188 | #: ../../tutorial/core.rst:155 189 | msgid "For the extension points returned, please see the following sections." 190 | msgstr "对于返回的扩展点,请参阅后续章节。" 191 | 192 | #: ../../tutorial/core.rst:160 193 | msgid "Enabling and Disabling Plugins" 194 | msgstr "启用、禁用插件" 195 | 196 | #: ../../tutorial/core.rst:162 197 | msgid "This extension, uses a different approach for handling plugins." 198 | msgstr "此扩展使用不同的方法来处理插件。" 199 | 200 | #: ../../tutorial/core.rst:164 201 | msgid "" 202 | "Anyway, local plugins (a subdirectory located in the application, such as" 203 | " plugins, is a package) or third-party plugins (which can be pypi or from" 204 | " git, svn, etc.), should be installed in the local environment." 205 | msgstr "无论如何,本地插件(位于应用程序中的子目录,例如plugins,是一个包)或第三方插件(可以是pypi或来自git,svn等等),都应该安装在本地环境中。" 206 | 207 | #: ../../tutorial/core.rst:168 208 | msgid "" 209 | "Plugins are enabled by default, and there are two ways to enable or " 210 | "disable a plugin." 211 | msgstr "插件默认是启用的,有两种方法禁用或启用一个插件。" 212 | 213 | #: ../../tutorial/core.rst:171 214 | msgid "" 215 | "The first method is to set the metadata ``__state__`` value to " 216 | "**enabled** or **disabled**." 217 | msgstr "第一种方法是将元数据 ``__state__`` 的值设置为 **enabled** (启用)或 **disabled** (禁用)。" 218 | 219 | #: ../../tutorial/core.rst:174 220 | msgid "" 221 | "The second method is to add the ``ENABLED`` or ``DISABLED`` file in the " 222 | "plugin's root directory, without changing the source code. This can " 223 | "either be done by hand or with the method provided by " 224 | ":meth:`~flask_pluginkit.PluginManager.disable_plugin` or " 225 | ":meth:`~flask_pluginkit.PluginManager.enable_plugin`." 226 | msgstr "" 227 | "第二种方法是在插件目录下添加一个 ``ENABLED`` 或 ``DISABLED`` 空文件。这个操作可以手动完成,也可以依靠Flask-" 228 | "PluginKit提供的方法 :meth:`~flask_pluginkit.PluginManager.disable_plugin` 或 " 229 | ":meth:`~flask_pluginkit.PluginManager.enable_plugin`" 230 | 231 | #: ../../tutorial/core.rst:182 232 | msgid "" 233 | "The second method has a higher priority than the first one, and the " 234 | "DISABLED file has a higher priority than the ENABLED file." 235 | msgstr "第二种方法比第一种优先级高,DISABLED文件比ENABLED文件优先级高。" 236 | 237 | #: ../../tutorial/core.rst:185 238 | msgid "The directory structure of a disabled plugin is shown below." 239 | msgstr "禁用插件的目录结构如下所示:" 240 | 241 | #: ../../tutorial/core.rst:195 242 | msgid "" 243 | "The server needs to be restarted or reloaded to disable the plugin. This " 244 | "is a limitation of Flask. However, it is possible, to restart the " 245 | "application by sending a HUP signal to the application server." 246 | msgstr "需要重新启动或重新加载WSGI服务器以禁用插件,这是Flask的限制。不过,通过向Web应用程序服务器发送HUP信号,也可以重载应用。" 247 | 248 | #: ../../tutorial/core.rst:199 249 | msgid "" 250 | "The following code snippets, are showing how this can be done with the " 251 | "WSGI server gunicorn. Gunicorn has be to started in daemon (``--daemon``)" 252 | " mode in order for this to work." 253 | msgstr "以下代码片段展示了如何使用WSGI服务器gunicorn完成此操作。Gunicorn以守护进程(``--daemon``)启动,才能通过HUP信号重载应用。" 254 | 255 | #: ../../tutorial/core.rst:203 256 | msgid "You can use the command to manually reload:" 257 | msgstr "您可以使用该命令手动重新加载:" 258 | 259 | #: ../../tutorial/core.rst:209 260 | msgid "or direct restart (kill, then start)." 261 | msgstr "或直接重启(杀死进程再启动)。" 262 | 263 | #: ../../tutorial/core.rst:211 264 | msgid "" 265 | "In web applications, according to previous tests, it should use " 266 | ":func:`os.getppid` instead of :func:`os.getpid` to get the master pid of " 267 | "gunicorn, and send SIGHUP signal to master pid." 268 | msgstr "" 269 | "在Web应用程序中,根据以前的测试,它应该使用 :func:`os.getppid` 代替 :func:`os.getpid` " 270 | "来获取gunicorn的master-pid,并向此pid发送SIGHUP信号。" 271 | 272 | #: ../../tutorial/core.rst:215 273 | msgid "For security, the process name should be verified!" 274 | msgstr "为了安全,应该校验下进程名!" 275 | 276 | #: ../../tutorial/core.rst:223 277 | msgid "" 278 | "This feature is implemented in v3.3.0, reference document " 279 | ":doc:`/webmanager`" 280 | msgstr "此功能在v3.3.0中实现,参考文档 :doc:`/webmanager`" 281 | 282 | #~ msgid "Subsequent versions will add this feature." 283 | #~ msgstr "后续版本将添加此功能。" 284 | 285 | -------------------------------------------------------------------------------- /docs/locale/zh_CN/LC_MESSAGES/tutorial/cvep.po: -------------------------------------------------------------------------------- 1 | # SOME DESCRIPTIVE TITLE. 2 | # Copyright (C) 2018, staugur 3 | # This file is distributed under the same license as the Flask-PluginKit 4 | # package. 5 | # FIRST AUTHOR , 2020. 6 | # 7 | #, fuzzy 8 | msgid "" 9 | msgstr "" 10 | "Project-Id-Version: Flask-PluginKit 3.5.x\n" 11 | "Report-Msgid-Bugs-To: \n" 12 | "POT-Creation-Date: 2020-04-08 13:37+0800\n" 13 | "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" 14 | "Last-Translator: FULL NAME \n" 15 | "Language-Team: LANGUAGE \n" 16 | "MIME-Version: 1.0\n" 17 | "Content-Type: text/plain; charset=utf-8\n" 18 | "Content-Transfer-Encoding: 8bit\n" 19 | "Generated-By: Babel 2.6.0\n" 20 | 21 | #: ../../tutorial/cvep.rst:4 22 | msgid "Class-based View Extension Point" 23 | msgstr "基于类的视图扩展点" 24 | 25 | #: ../../tutorial/cvep.rst:7 26 | msgid "Description" 27 | msgstr "描述" 28 | 29 | #: ../../tutorial/cvep.rst:9 30 | msgid "The extension point abbreviation is cvep." 31 | msgstr "这个扩展缩写为cvep。" 32 | 33 | #: ../../tutorial/cvep.rst:11 34 | msgid "Flask-Classful is an extension that adds class-based views to Flask." 35 | msgstr "Flask-Classful是Flask扩展,用以添加基于类的视图。" 36 | 37 | #: ../../tutorial/cvep.rst:13 38 | msgid "" 39 | "This extension point is flask-classful based to extend routing, so your " 40 | "application needs to install it (document url is: " 41 | "`flask_classful_docs`_):" 42 | msgstr "这个扩展点是基于Flask-Classful来扩展路由的," 43 | "所以你的应用程序需要安装它(文档地址: `flask_classful_docs`_ )。" 44 | 45 | #: ../../tutorial/cvep.rst:20 46 | msgid "" 47 | "The plugin needs to return the cvep field via register. The returned cvep" 48 | " data type can be dict, list or tuple, em, the format is similar to vep." 49 | msgstr "插件需要通过register函数返回cvep字段," 50 | "返回的cvep数据类型可以是字典、列表或元组,额,格式类似于vep。" 51 | 52 | #: ../../tutorial/cvep.rst:23 53 | msgid "" 54 | "The dict format is {view_class:, other register options}, see the " 55 | "`flask_classful_register`_ for more options, a common example is:" 56 | msgstr "字典的话,格式是{view_class:, other register options},更多options" 57 | "参数参考文档: `flask_classful_register`_ ,一个普通例子如下:" 58 | 59 | #: ../../tutorial/cvep.rst:37 60 | msgid "" 61 | "Other types are represented as multiple routes, and multiple dict data of" 62 | " the above type can be nested." 63 | msgstr "其他类型表示为多个路由,嵌套多个上面那种字典数据即可。" 64 | 65 | #: ../../tutorial/cvep.rst:40 66 | msgid "" 67 | "The Flask-PluginKit loads cvep via " 68 | ":meth:`~flask_pluginkit.PluginManager._cvep_handler`, this method will " 69 | "detect cvep rules and specific content." 70 | msgstr "" 71 | "Flask-PluginKit通过 :meth:`~flask_pluginkit.PluginManager._cvep_handler` " 72 | "加载cvep,这个方法会检测cvep规则和内容。" 73 | 74 | #: ../../tutorial/cvep.rst:49 75 | msgid "Example" 76 | msgstr "示例" 77 | 78 | #: ../../tutorial/cvep.rst:51 79 | msgid "Plugin registration for cvep" 80 | msgstr "注册cvep" 81 | 82 | -------------------------------------------------------------------------------- /docs/locale/zh_CN/LC_MESSAGES/tutorial/dcp.po: -------------------------------------------------------------------------------- 1 | # SOME DESCRIPTIVE TITLE. 2 | # Copyright (C) 2018, staugur 3 | # This file is distributed under the same license as the Flask-PluginKit 4 | # package. 5 | # FIRST AUTHOR , 2019. 6 | # 7 | #, fuzzy 8 | msgid "" 9 | msgstr "" 10 | "Project-Id-Version: Flask-PluginKit 3.2.x\n" 11 | "Report-Msgid-Bugs-To: \n" 12 | "POT-Creation-Date: 2019-07-25 15:23+0800\n" 13 | "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" 14 | "Last-Translator: FULL NAME \n" 15 | "Language-Team: LANGUAGE \n" 16 | "MIME-Version: 1.0\n" 17 | "Content-Type: text/plain; charset=utf-8\n" 18 | "Content-Transfer-Encoding: 8bit\n" 19 | "Generated-By: Babel 2.6.0\n" 20 | 21 | #: ../../tutorial/dcp.rst:4 22 | msgid "Dynamic Connection Point" 23 | msgstr "动态连接点" 24 | 25 | #: ../../tutorial/dcp.rst:7 26 | msgid "Description" 27 | msgstr "描述" 28 | 29 | #: ../../tutorial/dcp.rst:9 30 | msgid "The connection point abbreviation is dcp." 31 | msgstr "这个连接点缩写为dcp。" 32 | 33 | #: ../../tutorial/dcp.rst:11 34 | msgid "" 35 | "It doesn't need to be returned with **register**, even you may rarely use" 36 | " it." 37 | msgstr "它不需要用 **register** 返回,甚至于你可能很少用到它。" 38 | 39 | #: ../../tutorial/dcp.rst:13 40 | msgid "" 41 | "Dynamic connection points, dynamic registration and execution functions " 42 | "return the results to the template, which is inspired by `flask-" 43 | "plugins`_. However, it has been simplified to support pushing multiple " 44 | "functions in the application context, and in the template to get the " 45 | "execution results of all functions under the event (safe HTML code " 46 | "returned by :class:`~flask.Markup`)." 47 | msgstr "" 48 | "动态连接点,动态注册并执行函数将结果返回给模板使用,它的灵感来源于 `flask-plugins`_" 49 | " ,不过进行了简化,它支持在应用上下文环境中推送给事件多个函数,并在模板中获取事件" 50 | "下所有函数的执行结果(用 :class:`~flask.Markup` 返回的安全的HTML代码)。" 51 | 52 | #: ../../tutorial/dcp.rst:22 53 | msgid "" 54 | "The public push method is :func:`~flask_pluginkit.push_dcp`, in addition," 55 | " it can be managed using " 56 | ":attr:`~flask_pluginkit.PluginManager._dcp_manager`, it is an instance of" 57 | " :class:`~flask_pluginkit.utils.DcpManager`. Flask-PluginKit will update " 58 | "the template with a global method **emit_dcp** when it loads, the method " 59 | "called is :meth:`~flask_pluginkit.utils.DcpManager.emit`." 60 | msgstr "" 61 | "公开的推送方法是 :func:`~flask_pluginkit.push_dcp` ,另外,可以使用" 62 | " :attr:`~flask_pluginkit.PluginManager._dcp_manager` 管理,它是" 63 | " :class:`~flask_pluginkit.utils.DcpManager` 的实例。而Flask-PluginKit在加载时" 64 | "会给模板更新一个全局方法 **emit_dcp** ,实际调用的方法是" 65 | " :meth:`~flask_pluginkit.utils.DcpManager.emit` 。" 66 | 67 | #: ../../tutorial/dcp.rst:29 68 | msgid "Example" 69 | msgstr "示例" 70 | 71 | #: ../../tutorial/dcp.rst:31 72 | msgid "Push dcp" 73 | msgstr "推送dcp" 74 | 75 | #: ../../tutorial/dcp.rst:43 76 | msgid "Call in template" 77 | msgstr "在模板中调用" 78 | 79 | -------------------------------------------------------------------------------- /docs/locale/zh_CN/LC_MESSAGES/tutorial/errhandler.po: -------------------------------------------------------------------------------- 1 | # SOME DESCRIPTIVE TITLE. 2 | # Copyright (C) 2018, staugur 3 | # This file is distributed under the same license as the Flask-PluginKit 4 | # package. 5 | # FIRST AUTHOR , 2019. 6 | # 7 | #, fuzzy 8 | msgid "" 9 | msgstr "" 10 | "Project-Id-Version: Flask-PluginKit 3.2.x\n" 11 | "Report-Msgid-Bugs-To: \n" 12 | "POT-Creation-Date: 2019-09-27 17:18+0800\n" 13 | "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" 14 | "Last-Translator: FULL NAME \n" 15 | "Language-Team: LANGUAGE \n" 16 | "MIME-Version: 1.0\n" 17 | "Content-Type: text/plain; charset=utf-8\n" 18 | "Content-Transfer-Encoding: 8bit\n" 19 | "Generated-By: Babel 2.6.0\n" 20 | 21 | #: ../../tutorial/errhandler.rst:4 22 | msgid "Error Handler Extension Point" 23 | msgstr "错误处理器扩展点" 24 | 25 | #: ../../tutorial/errhandler.rst:7 26 | msgid "Description" 27 | msgstr "描述" 28 | 29 | #: ../../tutorial/errhandler.rst:9 30 | msgid "The extension point abbreviation is errhandler." 31 | msgstr "这个扩展点缩写为errhandler。" 32 | 33 | #: ../../tutorial/errhandler.rst:11 34 | msgid "" 35 | "Error handler, this extension point is also very simple, return the " 36 | "errhandler field through register, this field requires the format is " 37 | "{http-code: error-view-func, other-code: other-error-view-func}, and the " 38 | "key is a standard HTTP code (such as 404, 403, 500), the value is an " 39 | "error handler view function, and supports multiple error code handling." 40 | msgstr "" 41 | "错误处理器,这个扩展点也很简单,通过register返回errhandler字段,这个字段要求格式是{http-code: error-view-" 42 | "func, other-code: other-error-view-func},键是标准的HTTP代码,值是错误处理函数,支持多个错误代码处理。" 43 | 44 | #: ../../tutorial/errhandler.rst:17 45 | msgid "" 46 | "However, there is now a new format: [{error=exception_class, " 47 | "handler=func}, {error=err_code, handler=func}], this form allows you to " 48 | "use http-code to handle error codes while allowing a custom exception " 49 | "class to be passed in. And the associated processor (capturing the " 50 | "handler for this exception class), please refer to the Flask " 51 | "documentation for this class-based form of code." 52 | msgstr "" 53 | "不过,现在新增一种格式:[{error=exception_class, handler=func}, {error=err_code" 54 | ",handler=func}],这种形式允许你使用http-code处理错误码,同时允许传入一个自定义的异" 55 | "常类和相关的处理器(捕获这个异常类的处理函数),请参考Flask文档编写这种基于类的形式的代码。" 56 | 57 | #: ../../tutorial/errhandler.rst:23 58 | msgid "" 59 | "The Flask-PluginKit loads errhandler via " 60 | ":meth:`~flask_pluginkit.PluginManager._error_handler`, this method will " 61 | "detect errhandler rules and specific content." 62 | msgstr "" 63 | "Flask-PluginKit通过 :meth:`~flask_pluginkit.PluginManager._error_handler` " 64 | "加载errhandler,这个方法会检测errhandler规则及其内容。" 65 | 66 | #: ../../tutorial/errhandler.rst:27 67 | msgid "" 68 | "It should be noted that if multiple duplicate error handling functions " 69 | "are eventually overwritten, only one is valid. The error handling " 70 | "function can be written in the official documentation is `flask-error-" 71 | "handlers`_." 72 | msgstr "" 73 | "需要注意的是,如果多个重复的错误处理函数,最终会被覆盖,仅一个有效。错误处理函数的编写可以参考官方文档是 `flask-error-" 74 | "handlers`_ 。" 75 | 76 | #: ../../tutorial/errhandler.rst:35 77 | msgid "Example" 78 | msgstr "示例" 79 | 80 | #: ../../tutorial/errhandler.rst:37 81 | msgid "Plugin registration for errhandler" 82 | msgstr "注册errhandler" 83 | 84 | -------------------------------------------------------------------------------- /docs/locale/zh_CN/LC_MESSAGES/tutorial/filter.po: -------------------------------------------------------------------------------- 1 | # SOME DESCRIPTIVE TITLE. 2 | # Copyright (C) 2018, staugur 3 | # This file is distributed under the same license as the Flask-PluginKit 4 | # package. 5 | # FIRST AUTHOR , 2019. 6 | # 7 | #, fuzzy 8 | msgid "" 9 | msgstr "" 10 | "Project-Id-Version: Flask-PluginKit 3.2.x\n" 11 | "Report-Msgid-Bugs-To: \n" 12 | "POT-Creation-Date: 2019-09-27 17:18+0800\n" 13 | "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" 14 | "Last-Translator: FULL NAME \n" 15 | "Language-Team: LANGUAGE \n" 16 | "MIME-Version: 1.0\n" 17 | "Content-Type: text/plain; charset=utf-8\n" 18 | "Content-Transfer-Encoding: 8bit\n" 19 | "Generated-By: Babel 2.6.0\n" 20 | 21 | #: ../../tutorial/filter.rst:4 22 | msgid "Template Filter Extension Point" 23 | msgstr "模板过滤器扩展点" 24 | 25 | #: ../../tutorial/filter.rst:7 26 | msgid "Description" 27 | msgstr "描述" 28 | 29 | #: ../../tutorial/filter.rst:9 30 | msgid "The extension point abbreviation is filter." 31 | msgstr "这个扩展点缩写为filter。" 32 | 33 | #: ../../tutorial/filter.rst:11 34 | msgid "" 35 | "The concept of template filters comes from jinja2, which is essentially a" 36 | " regular python function. The filter and variables are separated by a " 37 | "pipe symbol ( | ), and optional parameters can also be passed in " 38 | "parentheses. The left side of the filter is used as the first parameter, " 39 | "and the remaining parameters are passed to the filter as additional " 40 | "parameters or keyword parameters." 41 | msgstr "" 42 | "模板过滤器的概念来自于jinja2,其本质是常规的python函数。过滤器与变量用管道符号( | " 43 | ")分割,并且也可以用圆括号传递可选参数,过滤器左边作为第一个参数,其余的参数作为额外的参数或关键字参数传递到过滤器。" 44 | 45 | #: ../../tutorial/filter.rst:18 46 | msgid "" 47 | "The plugin needs to return the filter field via register. The data type " 48 | "of this field can be dict or list. If it is a dict, the format is " 49 | "{filter_name: func}. If it is a list, the format is [func1, func2], and " 50 | "the function name is used as the filter name, but if an anonymous " 51 | "function is used, the filter name is , which is unfriendly!" 52 | msgstr "" 53 | "插件需要通过register返回filter字段,这个字段数据类型可以是dict或者是list。如果是dict,那么格式是{filter_name:" 54 | " func}。如果是list,那么格式是[func1, " 55 | "func2],此时函数名作为过滤器名,但是如果使用了匿名函数,那么过滤器名称是,不友好!" 56 | 57 | #: ../../tutorial/filter.rst:25 58 | msgid "" 59 | "So, if the format is list, the element type is allowed to be tuple, ie: " 60 | "[func1, (name, func2)], thus setting the name of the filter." 61 | msgstr "所以,如果格式是list,允许元素类型为tuple,即:[func1, (name, func2)],从而设置过滤器的名字。" 62 | 63 | #: ../../tutorial/filter.rst:28 64 | msgid "" 65 | "The Flask-PluginKit loads filter via " 66 | ":meth:`~flask_pluginkit.PluginManager._filter_handler`, this method will " 67 | "detect filter rules and specific content." 68 | msgstr "" 69 | "Flask-PluginKit通过 :meth:`~flask_pluginkit.PluginManager._filter_handler` " 70 | "加载filter,这个方法会检测filter规则及内容。" 71 | 72 | #: ../../tutorial/filter.rst:32 73 | msgid "" 74 | "Once the registration is complete, you can use the filter in the template" 75 | " like Jinja2's built-in filter. For this section, you can refer to the " 76 | "Jinja2 documentation `filters`_." 77 | msgstr "一旦注册完成后,你就可以在模板中像 Jinja2 的内建过滤器一样使用它们了,关于这部分可以参考Jinja2文档 `filters`_ 。" 78 | 79 | #: ../../tutorial/filter.rst:39 80 | msgid "Example" 81 | msgstr "示例" 82 | 83 | #: ../../tutorial/filter.rst:41 84 | msgid "Plugin registration for filter" 85 | msgstr "注册filter" 86 | 87 | #: ../../tutorial/filter.rst:53 88 | msgid "Call in template" 89 | msgstr "在模板中调用" 90 | 91 | -------------------------------------------------------------------------------- /docs/locale/zh_CN/LC_MESSAGES/tutorial/foreword.po: -------------------------------------------------------------------------------- 1 | # SOME DESCRIPTIVE TITLE. 2 | # Copyright (C) 2018, staugur 3 | # This file is distributed under the same license as the Flask-PluginKit 4 | # package. 5 | # FIRST AUTHOR , 2019. 6 | # 7 | #, fuzzy 8 | msgid "" 9 | msgstr "" 10 | "Project-Id-Version: Flask-PluginKit 3.0.x\n" 11 | "Report-Msgid-Bugs-To: \n" 12 | "POT-Creation-Date: 2021-09-02 21:08+0800\n" 13 | "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" 14 | "Last-Translator: FULL NAME \n" 15 | "Language-Team: LANGUAGE \n" 16 | "MIME-Version: 1.0\n" 17 | "Content-Type: text/plain; charset=utf-8\n" 18 | "Content-Transfer-Encoding: 8bit\n" 19 | "Generated-By: Babel 2.8.0\n" 20 | 21 | #: ../../tutorial/foreword.rst:2 22 | msgid "Foreword" 23 | msgstr "前言" 24 | 25 | #: ../../tutorial/foreword.rst:5 26 | msgid "What?" 27 | msgstr "它是什么" 28 | 29 | #: ../../tutorial/foreword.rst:7 30 | msgid "" 31 | "Flask-PluginKit is a Flask extension for extending web application " 32 | "functionality. It provides an easy way to create plugins for your " 33 | "application. It is possible to create extension points which can then be " 34 | "used to extend your application without the need to modify your core " 35 | "code." 36 | msgstr "" 37 | "Flask-" 38 | "PluginKit是一个为了增强web应用功能的Flask扩展。它为你的web应用提供了一个简单的方式以创建插件,增强或扩展应用,甚至是不修改源代码。" 39 | 40 | #: ../../tutorial/foreword.rst:12 41 | msgid "" 42 | "Flask-PluginKit can also be said to be a glue, or a bridge, for " 43 | "connecting web and web plugins. It supports native plugins and third-" 44 | "party plugins from pypi, git, svn, and so on. In addition, the plugin is " 45 | "also very simple to write." 46 | msgstr "" 47 | "Flask-" 48 | "PluginKit也可以说是用于连接web应用和插件的粘合剂或桥梁,它支持原生插件和来自pypi、git、svn等的第三方插件,此外,编写插件也很简单。" 49 | 50 | #: ../../tutorial/foreword.rst:18 51 | msgid "Glossary" 52 | msgstr "术语表" 53 | 54 | #: ../../tutorial/foreword.rst:20 55 | msgid "The following is a Chinese-English comparison:" 56 | msgstr "下面是中英对照:" 57 | 58 | #: ../../tutorial/foreword.rst:23 59 | msgid "Extension Point" 60 | msgstr "" 61 | 62 | #: ../../tutorial/foreword.rst:23 63 | msgid "扩展点" 64 | msgstr "" 65 | 66 | #: ../../tutorial/foreword.rst:26 67 | msgid "TEP / tep / Template Extension Point" 68 | msgstr "" 69 | 70 | #: ../../tutorial/foreword.rst:26 71 | msgid "模板扩展点" 72 | msgstr "" 73 | 74 | #: ../../tutorial/foreword.rst:29 75 | msgid "HEP / hep / Hook Extension Point" 76 | msgstr "" 77 | 78 | #: ../../tutorial/foreword.rst:29 79 | msgid "钩子扩展点" 80 | msgstr "" 81 | 82 | #: ../../tutorial/foreword.rst:32 83 | msgid "BEP / bep / Blueprint Extension Point" 84 | msgstr "" 85 | 86 | #: ../../tutorial/foreword.rst:32 87 | msgid "蓝图扩展点" 88 | msgstr "" 89 | 90 | #: ../../tutorial/foreword.rst:35 91 | msgid "VEP / vep / View function Extension Point" 92 | msgstr "" 93 | 94 | #: ../../tutorial/foreword.rst:35 95 | msgid "视图扩展点" 96 | msgstr "" 97 | 98 | #: ../../tutorial/foreword.rst:38 99 | msgid "DCP / dcp / Dynamic Connection Point" 100 | msgstr "" 101 | 102 | #: ../../tutorial/foreword.rst:38 103 | msgid "动态连接点" 104 | msgstr "" 105 | 106 | #: ../../tutorial/foreword.rst:41 107 | msgid "TCP / tcp / Template Context Processor / Context Processor" 108 | msgstr "" 109 | 110 | #: ../../tutorial/foreword.rst:41 111 | msgid "模板上下文处理器扩展点" 112 | msgstr "" 113 | 114 | #: ../../tutorial/foreword.rst:44 115 | msgid "filter" 116 | msgstr "" 117 | 118 | #: ../../tutorial/foreword.rst:44 119 | msgid "模板过滤器扩展点" 120 | msgstr "" 121 | 122 | #: ../../tutorial/foreword.rst:47 123 | msgid "errhandler" 124 | msgstr "" 125 | 126 | #: ../../tutorial/foreword.rst:47 127 | msgid "错误处理器扩展点" 128 | msgstr "" 129 | 130 | #: ../../tutorial/foreword.rst:50 131 | msgid "p3" 132 | msgstr "" 133 | 134 | #: ../../tutorial/foreword.rst:50 135 | msgid "插件预处理器" 136 | msgstr "" 137 | 138 | #: ../../tutorial/foreword.rst:53 139 | msgid "Developer / developer" 140 | msgstr "" 141 | 142 | #: ../../tutorial/foreword.rst:53 143 | msgid "插件开发者" 144 | msgstr "" 145 | 146 | #: ../../tutorial/foreword.rst:56 147 | msgid "Local Plugin" 148 | msgstr "本地插件" 149 | 150 | #: ../../tutorial/foreword.rst:58 151 | msgid "" 152 | "It is a local directory, it should be located in the plugins directory of" 153 | " the web application (of course, can also be defined as other " 154 | "directories), and is a legal python package, that is: plugins contain " 155 | "``__init__.py`` files, plugiin directories also Contains ``__init__.py``," 156 | " this file identifies the directory as a package, and also identifies the" 157 | " plugin, which is the core code, plugin entry." 158 | msgstr "" 159 | "插件是一个本地目录,它应该位于Web应用程序的plugins目录中(当然,也可以定义为其他目录),并且是一个合法的python包,即:plugins包含" 160 | " ``__init__.py`` 文件,插件目录还包含 " 161 | "``__init__.py``,这个文件将目录标识为包并且还标识了插件,这是核心代码,插件入口。" 162 | 163 | #: ../../tutorial/foreword.rst:66 164 | msgid "Third-party Plugin" 165 | msgstr "第三方插件" 166 | 167 | #: ../../tutorial/foreword.rst:68 168 | msgid "" 169 | "Third-party plugins are non-web application subdirectories, but local " 170 | "modules from installations such as pip or easy_install." 171 | msgstr "第三方插件非Web应用程序子目录,而是来自pip或easy_install等安装的本地模块。" 172 | 173 | #: ../../tutorial/foreword.rst:71 174 | msgid "" 175 | "The third-party plugins are free to use. The web application does not " 176 | "need to put the plugin code into a subdirectory. It only needs to be " 177 | "installed to the local machine using `pip install` or `easy_install`, and" 178 | " then pass the :attr:`~flask_pluginkit.PluginManager.plugin_packages` " 179 | "parameter when the :class:`~flask_pluginkit.PluginManager` is " 180 | "initialized." 181 | msgstr "" 182 | "第三方插件解放使用,Web应用程序不需要将插件代码放入子目录,只需要使 `pip install` 或 `easy_install` " 183 | "等安装到本地机器上,然后在初始化 :class:`~flask_pluginkit.PluginManager` 时,传入 " 184 | ":attr:`~flask_pluginkit.PluginManager.plugin_packages` 参数。" 185 | 186 | #: ../../tutorial/foreword.rst:77 187 | msgid "" 188 | "This means that anyone can write a package and publish it to pypi; the " 189 | "user writes `requirements.txt` and installs the dependencies, which are " 190 | "called in the initialization, in one go, and almost no need to worry " 191 | "about subsequent third-party plugin upgrades." 192 | msgstr "" 193 | "这意味着任何人都可以编写一个包并将其发布到pypi。用户编写 ``requirements.txt`` " 194 | "并安装依赖项,这些依赖项在初始化中被调用,而几乎不用担心后续第三方插件升级。" 195 | 196 | #: ../../tutorial/foreword.rst:82 197 | msgid "" 198 | "For instructions on how to write third-party plugins, see :doc:`/tutorial" 199 | "/third-party-plugin`" 200 | msgstr "如何编写第三方插件,请参阅 :doc:`/tutorial/third-party-plugin`" 201 | 202 | #: ../../tutorial/foreword.rst:85 203 | msgid "For official plugin's github group: https://github.com/saintic" 204 | msgstr "官方插件组:https://github.com/saintic" 205 | 206 | #: ../../tutorial/foreword.rst:88 207 | msgid "Loading logic" 208 | msgstr "加载逻辑" 209 | 210 | #: ../../tutorial/foreword.rst:90 211 | msgid "" 212 | "Developers who are not Flask-PluginKit or its plugins can ignore this " 213 | "part." 214 | msgstr "非Flask-PluginKit或其插件的开发人员可以忽略这一部分。" 215 | 216 | #: ../../tutorial/foreword.rst:92 217 | msgid "" 218 | "The plugin load starts when the program starts. The load class is " 219 | ":class:`~flask_pluginkit.PluginManager`, its destructor allows you to " 220 | "pass :attr:`~flask_pluginkit.PluginManager.plugins_base` (the default " 221 | "program directory), :attr:`~flask_pluginkit.PluginManager.plugins_folder`" 222 | " (the directory where the plugin is located), set the plugin absolute " 223 | "path directory, and also support factory mode, see the API documentation " 224 | "for more parameters." 225 | msgstr "" 226 | "Web应用程序启动时加载插件,通过类 :class:`~flask_pluginkit.PluginManager` ,它的析构函数允许你传递 " 227 | ":attr:`~flask_pluginkit.PluginManager.plugins_base` 和 " 228 | ":attr:`~flask_pluginkit.PluginManager.plugins_folder` " 229 | "参数,两者决定了插件所在的目录(相对于Web应用)。加载类的更多参数请参阅API文档。" 230 | 231 | #: ../../tutorial/foreword.rst:102 232 | msgid "The loading process is as follows:" 233 | msgstr "加载流程如下:" 234 | 235 | #: ../../tutorial/foreword.rst:104 236 | msgid "" 237 | "Call :class:`~flask_pluginkit.PluginManager` normal mode or factory mode " 238 | "to initialize the extension." 239 | msgstr "调用 :class:`~flask_pluginkit.PluginManager` 使用普通方式或工厂方式初始化扩展" 240 | 241 | #: ../../tutorial/foreword.rst:107 242 | msgid "" 243 | "Scan the `plugins_folder` plugin directory and the packages that match " 244 | "the plugin rules will be dynamically loaded." 245 | msgstr "扫描 `plugins_folder` 目录,符合规则的插件会动态加载。" 246 | 247 | #: ../../tutorial/foreword.rst:110 248 | msgid "" 249 | "Load third-party plugins in " 250 | ":attr:`~flask_pluginkit.PluginManager.plugin_packages`." 251 | msgstr "扫描并加载 :attr:`~flask_pluginkit.PluginManager.plugin_packages` 参数中定义的第三方插件。" 252 | 253 | #: ../../tutorial/foreword.rst:113 254 | msgid "Add template global variable." 255 | msgstr "添加模板全局变量。" 256 | 257 | #: ../../tutorial/foreword.rst:115 258 | msgid "Support for multiple template directories." 259 | msgstr "支持多个模板目录。" 260 | 261 | #: ../../tutorial/foreword.rst:117 262 | msgid "" 263 | "Add a view function that supports access to plugin directory static " 264 | "resources." 265 | msgstr "添加一个视图函数以访问插件下静态目录里文件。" 266 | 267 | #: ../../tutorial/foreword.rst:120 268 | msgid "Register hep, bep." 269 | msgstr "注册hep、bep等。" 270 | 271 | #: ../../tutorial/foreword.rst:122 272 | msgid "Append the instance to **app.extensions**." 273 | msgstr "将实例化的pluginkit添加到 **app.extensions**" 274 | 275 | -------------------------------------------------------------------------------- /docs/locale/zh_CN/LC_MESSAGES/tutorial/hep.po: -------------------------------------------------------------------------------- 1 | # SOME DESCRIPTIVE TITLE. 2 | # Copyright (C) 2018, staugur 3 | # This file is distributed under the same license as the Flask-PluginKit 4 | # package. 5 | # FIRST AUTHOR , 2019. 6 | # 7 | #, fuzzy 8 | msgid "" 9 | msgstr "" 10 | "Project-Id-Version: Flask-PluginKit 3.0.x\n" 11 | "Report-Msgid-Bugs-To: \n" 12 | "POT-Creation-Date: 2019-09-27 17:18+0800\n" 13 | "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" 14 | "Last-Translator: FULL NAME \n" 15 | "Language-Team: LANGUAGE \n" 16 | "MIME-Version: 1.0\n" 17 | "Content-Type: text/plain; charset=utf-8\n" 18 | "Content-Transfer-Encoding: 8bit\n" 19 | "Generated-By: Babel 2.6.0\n" 20 | 21 | #: ../../tutorial/hep.rst:4 22 | msgid "Hook Extension Point" 23 | msgstr "钩子扩展点" 24 | 25 | #: ../../tutorial/hep.rst:7 26 | msgid "Description" 27 | msgstr "描述" 28 | 29 | #: ../../tutorial/hep.rst:9 30 | msgid "The extension point abbreviation is hep." 31 | msgstr "这个扩展点缩写为hep。" 32 | 33 | #: ../../tutorial/hep.rst:11 34 | msgid "" 35 | "This extension point works in the flask hook function and currently " 36 | "supports three hooks with the following names:" 37 | msgstr "这个扩展点工作在Flask钩子函数中,当前支持三种钩子:" 38 | 39 | #: ../../tutorial/hep.rst:14 ../../tutorial/hep.rst:50 40 | msgid "before_request" 41 | msgstr "" 42 | 43 | #: ../../tutorial/hep.rst:16 44 | msgid "Before request (intercept requests are allowed)" 45 | msgstr "发生在请求之前(允许拦截请求)" 46 | 47 | #: ../../tutorial/hep.rst:18 ../../tutorial/hep.rst:59 48 | msgid "after_request" 49 | msgstr "" 50 | 51 | #: ../../tutorial/hep.rst:20 52 | msgid "After request (no exception before return)" 53 | msgstr "发生在请求后(无异常时)" 54 | 55 | #: ../../tutorial/hep.rst:22 ../../tutorial/hep.rst:65 56 | msgid "teardown_request" 57 | msgstr "" 58 | 59 | #: ../../tutorial/hep.rst:24 60 | msgid "After request (before return, with or without exception)" 61 | msgstr "发生在请求后(即使遇到了异常)" 62 | 63 | #: ../../tutorial/hep.rst:26 ../../tutorial/hep.rst:71 64 | msgid "before_first_request" 65 | msgstr "" 66 | 67 | #: ../../tutorial/hep.rst:28 68 | msgid "" 69 | "Registers a function to be run before the first request to this instance " 70 | "of the application." 71 | msgstr "注册一个函数,在应用程序实例的第一个请求之前运行。" 72 | 73 | #: ../../tutorial/hep.rst:32 74 | msgid "" 75 | "Other hooks will be supported later, and a exception " 76 | ":class:`~flask_pluginkit.exceptions.PEPError` will be triggered if a hook" 77 | " name that is not supported by the current version is used." 78 | msgstr "" 79 | "后续会支持其他钩子,如果使用了当前版本不支持的钩子,将会触发异常 " 80 | ":class:`~flask_pluginkit.exceptions.PEPError`" 81 | 82 | #: ../../tutorial/hep.rst:36 83 | msgid "" 84 | "The plugin needs to return the hep field via register. The hep data type " 85 | "returned is a dictionary with the format {hook_name: callable}, which can" 86 | " have multiple hep_names." 87 | msgstr "" 88 | "插件需要通过register返回hep字段,返回的hep数据类型是一个格式为{hook_name: " 89 | "callable}的字典,它可以有多个hep_name" 90 | 91 | #: ../../tutorial/hep.rst:40 92 | msgid "" 93 | "The Flask-PluginKit loads hep via " 94 | ":meth:`~flask_pluginkit.PluginManager._hep_handler`, this method will " 95 | "detect hep rules and specific content." 96 | msgstr "" 97 | "Flask-PluginKit通过 :meth:`~flask_pluginkit.PluginManager._hep_handler` " 98 | "加载hep,这个方法会检测hep规则和内容。" 99 | 100 | #: ../../tutorial/hep.rst:44 101 | msgid "" 102 | "Similar to tep, each hook_name has many values for the entire web " 103 | "application." 104 | msgstr "与tep类似,在整个Web应用程序中,每个hook_name都有许多值。" 105 | 106 | #: ../../tutorial/hep.rst:46 107 | msgid "" 108 | "When in use, the user does not care how to call, because Flask-PluginKit " 109 | "has registered the hook handler at initialization time, but it should be " 110 | "noted that there are different requirements for different hook type " 111 | "handlers:" 112 | msgstr "在使用时,用户不用关心如何调用,因为Flask-PluginKit在初始化时已经注册了钩子处理程序,但应该注意不同的钩子类型处理程序有不同的要求:" 113 | 114 | #: ../../tutorial/hep.rst:52 115 | msgid "" 116 | "This is simple, any callback function, class method, and so on can be " 117 | "used, there is no parameter, but the hook function here has a special " 118 | "place. As we all know, for Flask, if the before_request return value is " 119 | "not None, the request will be terminated. So Flask-PluginKit will try to " 120 | "detect the return value of the hook function, if it is not None, it will " 121 | "return, and the request is terminated at this moment." 122 | msgstr "" 123 | "这很简单,任何回调函数、类方法等都可以使用,没有参数,但这里的钩子函数有一个特殊的地方。 " 124 | "众所周知,对于Flask,如果before_request返回值不是None,则请求将被终止。 因此Flask-" 125 | "PluginKit将尝试检测钩子函数的返回值,如果它不是None,将返回,此时请求终止。" 126 | 127 | #: ../../tutorial/hep.rst:61 128 | msgid "" 129 | "Will pass a **response** parameter, this is the web application response " 130 | "data, is an instance of :class:`~flask.Response`, the function " 131 | "corresponding to this hook can get response, you can not return response." 132 | msgstr "" 133 | "将传递 **response** 参数,这是Web应用程序的响应数据,是 :class:`~flask.Response` " 134 | "的一个实例,对应这个钩子的函数可以得到response,不过不能返回response,当然也可以返回,不过Flask-" 135 | "PluginKit目前不会处理你的返回。" 136 | 137 | #: ../../tutorial/hep.rst:67 138 | msgid "" 139 | "Similar to after_request, this hook is triggered by the flask when the " 140 | "web exception occurs. Flask-PluginKit will pass the **exception** " 141 | "parameter to the corresponding hook function without returning." 142 | msgstr "" 143 | "与after_request类似,当Web异常发生时,此钩子由Flask触发。Flask-PluginKit将 **exception** " 144 | "传递给相应的钩子函数。" 145 | 146 | #: ../../tutorial/hep.rst:73 147 | msgid "Refer before_request, run only once after the app starts, no return value." 148 | msgstr "参考before_request,仅在应用启动后运行一次,没有返回值。" 149 | 150 | #: ../../tutorial/hep.rst:76 151 | msgid "Example" 152 | msgstr "示例" 153 | 154 | #: ../../tutorial/hep.rst:78 155 | msgid "Plugin registration for hep" 156 | msgstr "注册hep" 157 | 158 | #: ../../tutorial/hep.rst:108 159 | msgid "" 160 | "As above, after your program is running, the `set_login_state` function " 161 | "will be executed before each request, and the `record_access_log` " 162 | "function will be executed before the request returns." 163 | msgstr "" 164 | "如上所述,在程序运行之后,将在每个请求之前执行 `set_login_state` 函数,并且在请求返回之前将执行 " 165 | "`record_access_log` 函数。" 166 | 167 | -------------------------------------------------------------------------------- /docs/locale/zh_CN/LC_MESSAGES/tutorial/index.po: -------------------------------------------------------------------------------- 1 | # SOME DESCRIPTIVE TITLE. 2 | # Copyright (C) 2018, staugur 3 | # This file is distributed under the same license as the Flask-PluginKit 4 | # package. 5 | # FIRST AUTHOR , 2019. 6 | # 7 | #, fuzzy 8 | msgid "" 9 | msgstr "" 10 | "Project-Id-Version: Flask-PluginKit 3.0.x\n" 11 | "Report-Msgid-Bugs-To: \n" 12 | "POT-Creation-Date: 2019-07-19 22:32+0800\n" 13 | "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" 14 | "Last-Translator: FULL NAME \n" 15 | "Language-Team: LANGUAGE \n" 16 | "MIME-Version: 1.0\n" 17 | "Content-Type: text/plain; charset=utf-8\n" 18 | "Content-Transfer-Encoding: 8bit\n" 19 | "Generated-By: Babel 2.6.0\n" 20 | 21 | #: ../../tutorial/index.rst:4 22 | msgid "Tutorial" 23 | msgstr "教程" 24 | 25 | #: ../../tutorial/index.rst:6 26 | msgid "" 27 | "This tutorial will detail the Flask extension named Flask-PluginKit and " 28 | "will walk you through creating a local plugin and a third-party plugin, " 29 | "and cover as much as possible of all the features of Flask-PluginKit." 30 | msgstr "" 31 | "这个教程将详细介绍Flask-PluginKit扩展,并且会指导您如何创建一个本地或第三方插件。" 32 | 33 | #: ../../tutorial/index.rst:10 34 | msgid "Contents:" 35 | msgstr "内容:" 36 | 37 | -------------------------------------------------------------------------------- /docs/locale/zh_CN/LC_MESSAGES/tutorial/p3.po: -------------------------------------------------------------------------------- 1 | # SOME DESCRIPTIVE TITLE. 2 | # Copyright (C) 2018, staugur 3 | # This file is distributed under the same license as the Flask-PluginKit 4 | # package. 5 | # FIRST AUTHOR , 2021. 6 | # 7 | #, fuzzy 8 | msgid "" 9 | msgstr "" 10 | "Project-Id-Version: Flask-PluginKit 3.7.x\n" 11 | "Report-Msgid-Bugs-To: \n" 12 | "POT-Creation-Date: 2021-09-02 21:08+0800\n" 13 | "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" 14 | "Last-Translator: FULL NAME \n" 15 | "Language-Team: LANGUAGE \n" 16 | "MIME-Version: 1.0\n" 17 | "Content-Type: text/plain; charset=utf-8\n" 18 | "Content-Transfer-Encoding: 8bit\n" 19 | "Generated-By: Babel 2.8.0\n" 20 | 21 | #: ../../tutorial/p3.rst:4 22 | msgid "Plugin PreProcessor Extension Point" 23 | msgstr "插件预处理器扩展点" 24 | 25 | #: ../../tutorial/p3.rst:7 26 | msgid "Description" 27 | msgstr "描述" 28 | 29 | #: ../../tutorial/p3.rst:9 30 | msgid "The extension point abbreviation is p3." 31 | msgstr "这个扩展点缩写为p3。" 32 | 33 | #: ../../tutorial/p3.rst:11 34 | msgid "" 35 | "Its function is to take the lead in processing one round of plugin data " 36 | "after loading the plugin." 37 | msgstr "它的作用是在加载完插件之后,率先对插件数据进行一轮处理。" 38 | 39 | #: ../../tutorial/p3.rst:14 40 | msgid "" 41 | "The plugin needs to return the p3 field via register. The data type " 42 | "required for this field is dict, and the format is " 43 | "{plugin_name:{pet:func}}." 44 | msgstr "" 45 | "插件需要通过register返回p3字段,这个字段要求的数据类型是dict,格式是" 46 | "{plugin_name:{pet:func}}。" 47 | 48 | #: ../../tutorial/p3.rst:17 49 | msgid "The `pet` is plugin extension point, e.g: bep, tep, tcp, but without p3." 50 | msgstr "上述格式 `pet` 就是各种扩展点缩写,比如:bep、tep、tcp,不过p3除外。" 51 | 52 | #: ../../tutorial/p3.rst:19 53 | msgid "" 54 | "Need to pay attention to `func`, it receives a parameter, the type varies" 55 | " with pet, it is either a list or a dict, and the return type is required" 56 | " to be consistent with the received type." 57 | msgstr "" 58 | "另外,需要注意 `func` ,它接收一个参数,类型随着pet的不同而不同,不是list就是dict," 59 | "要求返回类型与接收类型一致。" 60 | 61 | #: ../../tutorial/p3.rst:23 62 | msgid "" 63 | "The Flask-PluginKit loads tcp via " 64 | ":meth:`~flask_pluginkit.PluginManager._p3_handler`, this method will " 65 | "detect p3 rules and specific content." 66 | msgstr "" 67 | "Flask-PluginKit通过 :meth:`~flask_pluginkit.PluginManager._p3_handler` 加载p3," 68 | "这个方法会检测p3规则及其内容。" 69 | 70 | #: ../../tutorial/p3.rst:28 71 | msgid "Example" 72 | msgstr "示例" 73 | 74 | #: ../../tutorial/p3.rst:30 75 | msgid "Plugin registration for p3" 76 | msgstr "注册p3" 77 | 78 | -------------------------------------------------------------------------------- /docs/locale/zh_CN/LC_MESSAGES/tutorial/static.po: -------------------------------------------------------------------------------- 1 | # SOME DESCRIPTIVE TITLE. 2 | # Copyright (C) 2018, staugur 3 | # This file is distributed under the same license as the Flask-PluginKit 4 | # package. 5 | # FIRST AUTHOR , 2019. 6 | # 7 | #, fuzzy 8 | msgid "" 9 | msgstr "" 10 | "Project-Id-Version: Flask-PluginKit 3.0.x\n" 11 | "Report-Msgid-Bugs-To: \n" 12 | "POT-Creation-Date: 2019-09-27 17:18+0800\n" 13 | "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" 14 | "Last-Translator: FULL NAME \n" 15 | "Language-Team: LANGUAGE \n" 16 | "MIME-Version: 1.0\n" 17 | "Content-Type: text/plain; charset=utf-8\n" 18 | "Content-Transfer-Encoding: 8bit\n" 19 | "Generated-By: Babel 2.6.0\n" 20 | 21 | #: ../../tutorial/static.rst:4 22 | msgid "Static Resource" 23 | msgstr "静态资源" 24 | 25 | #: ../../tutorial/static.rst:7 26 | msgid "Description" 27 | msgstr "描述" 28 | 29 | #: ../../tutorial/static.rst:9 30 | msgid "" 31 | "Generally speaking, when accessing static files during development, you " 32 | "can use Flask, but in the production environment, it is definitely a " 33 | "combination of WSGI servers and web servers such as nginx and apache. " 34 | "Static resources can be processed through nginx." 35 | msgstr "一般来说,开发的时候访问静态文件可以通过Flask,但是正式环境中,肯定是WSGI服务器和诸如nginx、apache等web服务器结合使用的,静态资源通过nginx处理才能发挥效果。" 36 | 37 | #: ../../tutorial/static.rst:14 38 | msgid "" 39 | "However, for the plugin of Flask-PluginKit, the static resources are " 40 | "located in the static directory of the plugin. If there are many plugins " 41 | "in a web application, then there are several static directories, so it is" 42 | " inconvenient to configure through nginx." 43 | msgstr "" 44 | "不过对Flask-" 45 | "PluginKit的插件来说,静态资源位于插件的static目录中,假如一个web应用很多插件,那么就有数个static目录,那么通过nginx肯定是不方便配置的。" 46 | 47 | #: ../../tutorial/static.rst:19 48 | msgid "" 49 | "So Flask-PluginKit customizes a view with an endpoint to access the " 50 | "static directory under the plugin, and also provides a method " 51 | ":meth:`~flask_pluginkit.PluginManager.emit_assets` to easily get the " 52 | "static files under the plugin in the template. Of course, this method " 53 | "uses :func:`~flask.url_for` internally, so you can also use url_for to " 54 | "build the url." 55 | msgstr "" 56 | "所以Flask-PluginKit自定义了一个端点为assets的视图以访问插件下static目录,同时又提供了一个方法 " 57 | ":meth:`~flask_pluginkit.PluginManager.emit_assets` " 58 | "可以在模板中方便地获取插件下的静态文件。当然,这个方法内部使用的是 :func:`~flask.url_for` " 59 | ",所以你也可以使用url_for构建url。" 60 | 61 | #: ../../tutorial/static.rst:26 62 | msgid "" 63 | "The assets view above can customize its endpoints, pass " 64 | ":attr:`~flask_pluginkit.PluginManager.static_endpoint` when initializing " 65 | ":class:`~flask_pluginkit.PluginManager`, and pass " 66 | ":attr:`~flask_pluginkit.PluginManager.static_url_path` to define view " 67 | "route prefix. When using **emit_assets**, it will call the view function," 68 | " try to find the plugin static directory, and return 404 error if it is " 69 | "not found." 70 | msgstr "" 71 | "上面说的assets视图可以自定义其端点,在初始化 :class:`~flask_pluginkit.PluginManager` 时传入 " 72 | ":attr:`~flask_pluginkit.PluginManager.static_endpoint` ,同时,传入 " 73 | ":attr:`~flask_pluginkit.PluginManager.static_url_path` 可以定义视图路由前缀,使用 " 74 | "**emit_assets** 时会调用视图函数,尝试查找插件static目录,未发现时返回404错误。" 75 | 76 | #: ../../tutorial/static.rst:33 77 | msgid "" 78 | "In addition, it is worth mentioning that the static file suffix is " 79 | "currently processed." 80 | msgstr "另外,值得一提的是,目前会对静态文件后缀做处理。" 81 | 82 | #: ../../tutorial/static.rst:36 83 | msgid "The suffix is **.css**" 84 | msgstr "后缀是 **.css**" 85 | 86 | #: ../../tutorial/static.rst:38 87 | msgid "Will generate the html code for `link css`, for example:" 88 | msgstr "会生成 `link css` 的html代码,例如:" 89 | 90 | #: ../../tutorial/static.rst:44 91 | msgid "The suffix is **.js**" 92 | msgstr "后缀是 **.js**" 93 | 94 | #: ../../tutorial/static.rst:46 95 | msgid "Will generate the html code of `script`, for example:" 96 | msgstr "会生成 `script` 的html代码,例如:" 97 | 98 | #: ../../tutorial/static.rst:52 99 | msgid "Other suffixes" 100 | msgstr "其他后缀" 101 | 102 | #: ../../tutorial/static.rst:54 103 | msgid "Only the url path portion of the static file will be generated." 104 | msgstr "只会生成静态文件的url路径部分。" 105 | 106 | #: ../../tutorial/static.rst:58 107 | msgid "" 108 | "In emit_assets, you can add ``_raw=True`` to let Flask-PluginKit not add code based on " 109 | "the suffix, but instead return the resource path directly." 110 | msgstr "在emit_assets中,你可以添加 ``_raw=True`` 让Flask-PluginKit不根据后缀添加代码,而是直接返回资源路径。" 111 | 112 | #: ../../tutorial/static.rst:62 113 | msgid "Example" 114 | msgstr "示例" 115 | 116 | #: ../../tutorial/static.rst:64 117 | msgid "Static files" 118 | msgstr "静态文件" 119 | 120 | #: ../../tutorial/static.rst:66 121 | msgid "" 122 | "Suppose a plugin called plugin_demo has a static directory. The file " 123 | "structure looks like this:" 124 | msgstr "假设一个名叫plugin_demo的插件有一个static目录,文件结构是这样的:" 125 | 126 | #: ../../tutorial/static.rst:80 127 | msgid "Access static files" 128 | msgstr "访问静态文件" 129 | 130 | #: ../../tutorial/static.rst:82 131 | msgid "" 132 | "In the template, the url of the static file can be built by " 133 | "**emit_assets**." 134 | msgstr "在模板中,通过 **emit_assets** 可以构建静态文件的url。" 135 | 136 | #: ../../tutorial/static.rst:104 137 | msgid "The actual source code for this page is this:" 138 | msgstr "此页面的实际源代码如下:" 139 | 140 | -------------------------------------------------------------------------------- /docs/locale/zh_CN/LC_MESSAGES/tutorial/tcp.po: -------------------------------------------------------------------------------- 1 | # SOME DESCRIPTIVE TITLE. 2 | # Copyright (C) 2018, staugur 3 | # This file is distributed under the same license as the Flask-PluginKit 4 | # package. 5 | # FIRST AUTHOR , 2019. 6 | # 7 | #, fuzzy 8 | msgid "" 9 | msgstr "" 10 | "Project-Id-Version: Flask-PluginKit 3.2.x\n" 11 | "Report-Msgid-Bugs-To: \n" 12 | "POT-Creation-Date: 2019-07-25 15:23+0800\n" 13 | "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" 14 | "Last-Translator: FULL NAME \n" 15 | "Language-Team: LANGUAGE \n" 16 | "MIME-Version: 1.0\n" 17 | "Content-Type: text/plain; charset=utf-8\n" 18 | "Content-Transfer-Encoding: 8bit\n" 19 | "Generated-By: Babel 2.6.0\n" 20 | 21 | #: ../../tutorial/tcp.rst:4 22 | msgid "Template Context Processors Extension Point" 23 | msgstr "模板上下文处理器扩展点" 24 | 25 | #: ../../tutorial/tcp.rst:7 26 | msgid "Description" 27 | msgstr "描述" 28 | 29 | #: ../../tutorial/tcp.rst:9 30 | msgid "The extension point abbreviation is tcp." 31 | msgstr "这个扩展点缩写为tcp。" 32 | 33 | #: ../../tutorial/tcp.rst:11 34 | msgid "" 35 | "Its role is to automatically introduce variables or functions you define " 36 | "into the template environment for use in templates (like g, request, " 37 | "url_for)." 38 | msgstr "" 39 | "它的作用是把你定义的变量或函数等自动引入到模板环境中," 40 | "以便在模板中使用(就像g、request、url_for一样)。" 41 | 42 | #: ../../tutorial/tcp.rst:14 43 | msgid "" 44 | "The plugin needs to return the tcp field via register. The data type " 45 | "required for this field is dict, and the format is {var_name=var, " 46 | "func_name=func}." 47 | msgstr "" 48 | "插件需要通过register返回tcp字段,这个字段要求的数据类型是dict," 49 | "格式是{var_name=var, func_name=func}。" 50 | 51 | #: ../../tutorial/tcp.rst:17 52 | msgid "" 53 | "The Flask-PluginKit loads tcp via " 54 | ":meth:`~flask_pluginkit.PluginManager._context_processor_handler`, this " 55 | "method will detect tcp rules and specific content." 56 | msgstr "" 57 | "Flask-PluginKit通过 :meth:`~flask_pluginkit.PluginManager._context_processor_handler`" 58 | " 加载tcp,这个方法会检测tcp规则及其内容。" 59 | 60 | #: ../../tutorial/tcp.rst:22 61 | msgid "Example" 62 | msgstr "示例" 63 | 64 | #: ../../tutorial/tcp.rst:24 65 | msgid "Plugin registration for tcp" 66 | msgstr "注册tcp" 67 | 68 | #: ../../tutorial/tcp.rst:35 69 | msgid "Call in template" 70 | msgstr "在模板中调用" 71 | 72 | -------------------------------------------------------------------------------- /docs/locale/zh_CN/LC_MESSAGES/tutorial/tep.po: -------------------------------------------------------------------------------- 1 | # SOME DESCRIPTIVE TITLE. 2 | # Copyright (C) 2018, staugur 3 | # This file is distributed under the same license as the Flask-PluginKit 4 | # package. 5 | # FIRST AUTHOR , 2019. 6 | # 7 | #, fuzzy 8 | msgid "" 9 | msgstr "" 10 | "Project-Id-Version: Flask-PluginKit 3.0.x\n" 11 | "Report-Msgid-Bugs-To: \n" 12 | "POT-Creation-Date: 2019-07-19 22:32+0800\n" 13 | "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" 14 | "Last-Translator: FULL NAME \n" 15 | "Language-Team: LANGUAGE \n" 16 | "MIME-Version: 1.0\n" 17 | "Content-Type: text/plain; charset=utf-8\n" 18 | "Content-Transfer-Encoding: 8bit\n" 19 | "Generated-By: Babel 2.6.0\n" 20 | 21 | #: ../../tutorial/tep.rst:4 22 | msgid "Template Extension Point" 23 | msgstr "模板扩展点" 24 | 25 | #: ../../tutorial/tep.rst:7 26 | msgid "Description" 27 | msgstr "描述" 28 | 29 | #: ../../tutorial/tep.rst:9 30 | msgid "The extension point abbreviation is tep." 31 | msgstr "这个扩展点缩写为tep。" 32 | 33 | #: ../../tutorial/tep.rst:11 34 | msgid "" 35 | "As the name implies, tep only works in the template environment. It is " 36 | "used to enhance or extend existing templates. It can be simple html code " 37 | "or complex html files. It needs to be manually called by the user in the " 38 | "existing template." 39 | msgstr "" 40 | "顾名思义,tep仅适用于模板环境,用于增强或扩展现有模板。" 41 | "它可以是简单的html代码或复杂的html文件,需要由用户在现有模板中手动调用。" 42 | 43 | #: ../../tutorial/tep.rst:16 44 | msgid "" 45 | "The plugin needs to return the tep field via register. The tep data type " 46 | "returned is a dictionary with the format {tep_name: html_code_or_file}, " 47 | "which can have multiple tep_names." 48 | msgstr "" 49 | "插件需要通过register返回tep字段,返回的tep数据类型是一个格式为" 50 | "{tep_name: html_code_or_file}的字典,且可以有多个tep_name。" 51 | 52 | #: ../../tutorial/tep.rst:20 53 | msgid "" 54 | "The Flask-PluginKit loads tep via " 55 | ":meth:`~flask_pluginkit.PluginManager._tep_handler`, this method will " 56 | "detect tep rules and specific content. The value corresponding to " 57 | "tep_name is recognized as a template file if it ends with **.html**, " 58 | "**.htm**, **.xhtml**, otherwise it is a simple html code." 59 | msgstr "" 60 | "Flask-PluginKit通过 :meth:`~flask_pluginkit.PluginManager._tep_handler` " 61 | "加载tep,此方法将检测tep规则和内容。如果tep_name对应的值以 **.html** 、" 62 | " **.htm** 、 **.xhtml** 结尾,则会认定为模板文件,否则便是html代码。" 63 | 64 | #: ../../tutorial/tep.rst:26 65 | msgid "" 66 | "Although the tep_name in the tep returned by each plugin is unique, since" 67 | " a web application can have multiple plugins, the tep_name can contain " 68 | "not only the html code but also the template file for the entire web " 69 | "application." 70 | msgstr "" 71 | "虽然每个插件返回的tep中的tep_name是唯一的,但由于Web应用程序可以有多个插件," 72 | "因此对整个Web应用程序来说,tep_name不仅会包含html代码,还包含模板文件。" 73 | 74 | #: ../../tutorial/tep.rst:30 75 | msgid "" 76 | "In use, in the existing template, all contents corresponding to tep_name " 77 | "are called by :meth:`~flask_pluginkit.PluginManager.emit_tep`, the " 78 | "template file is rendered by :func:`flask.render_template`, and the html " 79 | "code is rendered by :func:`flask.render_template_string`, which means " 80 | "that the html code can also be supported by jinja2 like a template file, " 81 | "jinja2 syntax, functions, macros, etc. In addition, use emit_tep to pass " 82 | "in the typ parameter settings to render only html code or files, and also" 83 | " pass in other keyword parameters as context data for rendering." 84 | msgstr "" 85 | "使用时,在现有模板中,tep_name对应的所有内容都通过 86 | " :meth:`~flask_pluginkit.PluginManager.emit_tep` 调用,其中,模板文件通过" 87 | " :func:`flask.render_template` 渲染,html代码通过" 88 | " :func:`flask.render_template_string` 渲染。" 89 | "这意味着,html代码也可以像模板文件一样支持jinja2语法、函数、宏等,此外," 90 | "使用emit_tep可以传递typ参数设置仅渲染HTML代码或模板文件,而且可以传递额外的" 91 | "关键字参数给tep_name,作为渲染的上下文数据。" 92 | 93 | #: ../../tutorial/tep.rst:41 94 | msgid "" 95 | "The template file supports sorting. You need to pass " 96 | ":attr:`~flask_pluginkit.PluginManager.stpl` is True when initializing the" 97 | " :class:`~flask_pluginkit.PluginManager`. The usage is \"sort-field" 98 | "\\@template-file\"." 99 | msgstr "" 100 | "模板文件支持排序。你需要在初始化 :class:`~flask_pluginkit.PluginManager` 时" 101 | "设置 :attr:`~flask_pluginkit.PluginManager.stpl` = True。" 102 | "用法是 \"排序字段\\@模板文件\"。" 103 | 104 | #: ../../tutorial/tep.rst:48 105 | msgid "" 106 | "It is recommended that you create a new directory to store html files " 107 | "under the plugin templates. Because the Flask-PluginKit only loads the " 108 | "templates directory under the plugin, and does not guarantee template " 109 | "conflicts, the new directory can avoid conflicts with other plugin " 110 | "template files, which can not be referenced properly." 111 | msgstr "" 112 | "如果有模板文件,建议您在插件目录的templates下创建一个子目录以存储html文件," 113 | "因为Flask-PluginKit只加载插件下的templates目录,并且不保证模板冲突," 114 | "所以在templates下创建子目录可以尽可能地避免与其他插件模板文件冲突," 115 | "以免文件无法正确引用。" 116 | 117 | #: ../../tutorial/tep.rst:55 118 | msgid "Example" 119 | msgstr "示例" 120 | 121 | #: ../../tutorial/tep.rst:57 122 | msgid "Plugin registration for tep" 123 | msgstr "注册tep" 124 | 125 | #: ../../tutorial/tep.rst:69 126 | msgid "" 127 | "As above, you need to create a new \"templates/example\" directory in the" 128 | " plugin directory, and put header.html into the directory. If it does not" 129 | " exist, the exception " 130 | ":class:`~flask_pluginkit.exceptions.TemplateNotFound` will be thrown." 131 | msgstr "" 132 | "如上所述,您需要在插件中创建一个新的\"templates/example\"目录,并将" 133 | "header.html放入目录中,如果它不存在,那么会抛出异常:" 134 | " :class:`~flask_pluginkit.exceptions.TemplateNotFound`" 135 | 136 | #: ../../tutorial/tep.rst:74 137 | msgid "Call in template" 138 | msgstr "在模板中调用" 139 | 140 | #: ../../tutorial/tep.rst:76 141 | msgid "" 142 | "In the existing template, assume that the following file named base.html " 143 | "is the base template, user need to manually call " 144 | ":meth:`~flask_pluginkit.PluginManager.emit_tep`, can pass additional " 145 | "data:" 146 | msgstr "" 147 | "在现有模板中,假设以下文件名为base.html是基础模板,用户需要手动调用" 148 | " :meth:`~flask_pluginkit.PluginManager.emit_tep` ,可以传递额外的数据:" 149 | -------------------------------------------------------------------------------- /docs/locale/zh_CN/LC_MESSAGES/tutorial/third-party-plugin.po: -------------------------------------------------------------------------------- 1 | # SOME DESCRIPTIVE TITLE. 2 | # Copyright (C) 2018, staugur 3 | # This file is distributed under the same license as the Flask-PluginKit 4 | # package. 5 | # FIRST AUTHOR , 2019. 6 | # 7 | #, fuzzy 8 | msgid "" 9 | msgstr "" 10 | "Project-Id-Version: Flask-PluginKit 3.0.x\n" 11 | "Report-Msgid-Bugs-To: \n" 12 | "POT-Creation-Date: 2019-07-20 17:26+0800\n" 13 | "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" 14 | "Last-Translator: FULL NAME \n" 15 | "Language-Team: LANGUAGE \n" 16 | "MIME-Version: 1.0\n" 17 | "Content-Type: text/plain; charset=utf-8\n" 18 | "Content-Transfer-Encoding: 8bit\n" 19 | "Generated-By: Babel 2.6.0\n" 20 | 21 | #: ../../tutorial/third-party-plugin.rst:2 22 | msgid "Third party plugin" 23 | msgstr "第三方插件" 24 | 25 | #: ../../tutorial/third-party-plugin.rst:5 26 | msgid "Description" 27 | msgstr "描述" 28 | 29 | #: ../../tutorial/third-party-plugin.rst:7 30 | msgid "" 31 | "Third-party plugins are non-program subdirectories, local modules from " 32 | "installations such as ``pip`` or ``easy_install``." 33 | msgstr "第三方插件不是应用程序子目录,而是使用诸如 ``pip install`` 或 ``easy_install`` 等安装到本地的模块。" 34 | 35 | #: ../../tutorial/third-party-plugin.rst:10 36 | msgid "" 37 | "The third-party plugin is easy to use, the program can not put the plugin" 38 | " code into the subdirectory, just use `pip install` to install to the " 39 | "local machine, and then pass the " 40 | ":attr:`~flask_pluginkit.PluginManager.plugin_packages` parameter when the" 41 | " :class:`~flask_pluginkit.PluginManager` is initialized." 42 | msgstr "" 43 | "第三方插件解放使用,Web应用程序不需要将插件代码放入子目录,只需要使 `pip install` 或 `easy_install` " 44 | "等安装到本地机器上,然后在初始化 :class:`~flask_pluginkit.PluginManager` 时,传入 " 45 | ":attr:`~flask_pluginkit.PluginManager.plugin_packages` 参数。" 46 | 47 | #: ../../tutorial/third-party-plugin.rst:16 48 | msgid "" 49 | "This means that anyone can write a package and publish it to pypi, and " 50 | "the user writes **requirements.txt** and installs the dependent plugin, " 51 | "which is called in the initialization, and almost no need to worry about " 52 | "subsequent third-party plugin upgrades." 53 | msgstr "" 54 | "这意味着任何人都可以编写一个包并将其发布到pypi。用户编写 ``requirements.txt`` " 55 | "并安装依赖项,这些依赖项在初始化中被调用,而几乎不用担心后续第三方插件升级。" 56 | 57 | #: ../../tutorial/third-party-plugin.rst:22 58 | msgid "Local Plugin" 59 | msgstr "本地插件" 60 | 61 | #: ../../tutorial/third-party-plugin.rst:24 62 | msgid "" 63 | "It's a package under the web application, which is part of the web " 64 | "application, and the plugin developer is the user, more see :ref:`core-" 65 | "plugin-structure`." 66 | msgstr "" 67 | "它是Web应用程序下的一个包,是Web的一部分,对于本地插件来说,插件开发人员就是用户本人,要了解更多参考 :ref:`core-plugin-" 68 | "structure`" 69 | 70 | #: ../../tutorial/third-party-plugin.rst:28 71 | msgid "How to develop plugins?" 72 | msgstr "如何开发插件?" 73 | 74 | #: ../../tutorial/third-party-plugin.rst:30 75 | msgid "" 76 | "The local plugin only needs the first step, and the third party plugin " 77 | "needs to write ``setup.py``, which requires the next few steps." 78 | msgstr "本地插件只需要第一步,第三方插件需要编写 ``setup.py`` ,需要后面几步。" 79 | 80 | #: ../../tutorial/third-party-plugin.rst:33 81 | msgid "" 82 | "First create a package, the metadata and register functions should be " 83 | "written in ``__init__.py``, the core code can also be written in this " 84 | "file, of course, the recommended approach is to separate the module." 85 | msgstr "" 86 | "首先创建一个包,应该在包的 ``__init__.py`` " 87 | "中编写元数据和register函数返回扩展点,其他核心代码也可以写在这个文件中,当然,推荐做法是独立模块。" 88 | 89 | #: ../../tutorial/third-party-plugin.rst:37 90 | msgid "" 91 | "The first step is actually the process of writing local plugins. In this " 92 | "step, you need to write `setup.py`, so that local plugins can be " 93 | "(published in pypi, optionally) used by others through pip:" 94 | msgstr "第一步实际上是编写本地插件的过程。这一步,你需要编写 `setup.py` ,以便本地插件可以(发布到pypi,可选)让其他人通过pip使用:" 95 | 96 | #: ../../tutorial/third-party-plugin.rst:51 97 | msgid "" 98 | "If your plugin contains template directory or static directory, you need " 99 | "to write an additional manifest file ``MANIFEST.in``:" 100 | msgstr "如果你的插件包含模板文件或静态文件,还需要编写 ``MANIFEST.in`` 清单文件。" 101 | 102 | #: ../../tutorial/third-party-plugin.rst:59 103 | msgid "Testing, Release" 104 | msgstr "测试,发布" 105 | 106 | #: ../../tutorial/third-party-plugin.rst:61 107 | msgid "The modules required by the following commands can be installed like this:" 108 | msgstr "以下命令所需的模块可以这样安装:" 109 | 110 | #: ../../tutorial/third-party-plugin.rst:67 111 | msgid "" 112 | "4.1 Use ``pip install .`` to install to the local environment and test " 113 | "the plugin functionality." 114 | msgstr "4.1 使用 ``pip install .`` 安装到本地虚拟环境或全局,并测试插件功能。" 115 | 116 | #: ../../tutorial/third-party-plugin.rst:70 117 | msgid "" 118 | "4.2 If the plugin is as expected, it can be packaged and the command is: " 119 | "``python setup.py sdist bdist_wheel``, more parameters to adjust " 120 | "themselves." 121 | msgstr "" 122 | "4.2 如果插件功能符合预期,可以使用命令打包:" 123 | " ``python setup.py sdist bdist_wheel`` ,更多参数自行调整。" 124 | 125 | #: ../../tutorial/third-party-plugin.rst:73 126 | msgid "" 127 | "4.3 Before the official release, you can post to test.pypi.org, which is " 128 | "the official pypi test site. The package inside will not be used easily. " 129 | "The command is: ``twine upload --repository-url " 130 | "https://test.pypi.org/legacy/ dist/*``" 131 | msgstr "" 132 | "4.3 在发布到pypi.org前,你可以发布到test.pypi.org,这是官方的pypi测试网站," 133 | "里面的包不会轻易使用,命令是:" 134 | " ``twine upload --repository-url https://test.pypi.org/legacy/ dist/*`` " 135 | 136 | #: ../../tutorial/third-party-plugin.rst:78 137 | msgid "" 138 | "4.4 The test station can look at the interface description and so on " 139 | "whether it meets the requirements of the heart, and publish it to the " 140 | "official station without problems, pypi.org, the command is: ``twine " 141 | "upload dist/*``" 142 | msgstr "" 143 | "4.4 你可以在这个测试站查看描述等有没有问题,无误后发布到正式站pypi.org,命令是:" 144 | " ``twine upload dist/*`` " 145 | 146 | #: ../../tutorial/third-party-plugin.rst:83 147 | msgid "`Third-party example `_" 148 | msgstr "`第三方插件示例 `_" 149 | -------------------------------------------------------------------------------- /docs/locale/zh_CN/LC_MESSAGES/tutorial/vep.po: -------------------------------------------------------------------------------- 1 | # SOME DESCRIPTIVE TITLE. 2 | # Copyright (C) 2018, staugur 3 | # This file is distributed under the same license as the Flask-PluginKit 4 | # package. 5 | # FIRST AUTHOR , 2019. 6 | # 7 | #, fuzzy 8 | msgid "" 9 | msgstr "" 10 | "Project-Id-Version: Flask-PluginKit 3.1.x\n" 11 | "Report-Msgid-Bugs-To: \n" 12 | "POT-Creation-Date: 2020-12-31 17:47+0800\n" 13 | "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" 14 | "Last-Translator: FULL NAME \n" 15 | "Language-Team: LANGUAGE \n" 16 | "MIME-Version: 1.0\n" 17 | "Content-Type: text/plain; charset=utf-8\n" 18 | "Content-Transfer-Encoding: 8bit\n" 19 | "Generated-By: Babel 2.6.0\n" 20 | 21 | #: ../../tutorial/vep.rst:4 22 | msgid "View function Extension Point" 23 | msgstr "视图扩展点" 24 | 25 | #: ../../tutorial/vep.rst:7 26 | msgid "Description" 27 | msgstr "描述" 28 | 29 | #: ../../tutorial/vep.rst:9 30 | msgid "The extension point abbreviation is vep." 31 | msgstr "这个扩展缩写为vep。" 32 | 33 | #: ../../tutorial/vep.rst:11 34 | msgid "" 35 | "Vep is a simple way for plugins to directly add routing functions, " 36 | "because in previous versions, routing was only allowed through " 37 | "blueprints." 38 | msgstr "视图扩展点是插件直接增加路由函数的简单方式,因为之前的版本中,要增加路由只能通过蓝图。" 39 | 40 | #: ../../tutorial/vep.rst:14 41 | msgid "" 42 | "The plugin needs to return the vep field via register. The returned vep " 43 | "data type can be dict, list, tuple." 44 | msgstr "插件需要通过register函数返回vep字段,返回的vep数据类型可以是字典、列表或元组。" 45 | 46 | #: ../../tutorial/vep.rst:17 47 | msgid "" 48 | "The dict format is {rule:, view_func:}, which means that a single route " 49 | "is added. The dict content is the parameter of " 50 | ":meth:`~flask.Flask.add_url_rule`, reference document `add_url_rule`_, a " 51 | "common example is:" 52 | msgstr "" 53 | "字典的话,格式是{rule:, view_func:},表示增加单个路由,这个数据内容即 " 54 | ":meth:`~flask.Flask.add_url_rule` 的参数,参考文档 `add_url_rule`_ ,一个常用的示例是:" 55 | 56 | #: ../../tutorial/vep.rst:29 57 | msgid "" 58 | "Other types are represented as multiple routes, and multiple dict data of" 59 | " the above type can be nested." 60 | msgstr "其他类型表示为多个路由,嵌套多个上面那种字典数据即可。" 61 | 62 | #: ../../tutorial/vep.rst:32 63 | msgid "" 64 | "The Flask-PluginKit loads vep via " 65 | ":meth:`~flask_pluginkit.PluginManager._vep_handler`, this method will " 66 | "detect vep rules and specific content." 67 | msgstr "" 68 | "Flask-PluginKit通过 :meth:`~flask_pluginkit.PluginManager._vep_handler` " 69 | "加载vep,这个方法会检测vep规则和内容。" 70 | 71 | #: ../../tutorial/vep.rst:41 72 | msgid "vep on blueprint" 73 | msgstr "" 74 | 75 | #: ../../tutorial/vep.rst:43 76 | msgid "" 77 | "The vep allows to be set on the blueprint, format: {_blueprint: " 78 | "blueprint-name, rule:/path, view_func:xx}." 79 | msgstr "vep允许设置到蓝图上,格式是:{_blueprint: blueprint-name, rule:/path, view_func:xx}。" 80 | 81 | #: ../../tutorial/vep.rst:45 82 | msgid "" 83 | "Require blueprint to exist, otherwise raise " 84 | ":class:`~flask_pluginkit.exceptions.PEPError`" 85 | msgstr "要求蓝图真实存在,否则引发 :class:`~flask_pluginkit.exceptions.PEPError`" 86 | 87 | #: ../../tutorial/vep.rst:50 88 | msgid "Example" 89 | msgstr "示例" 90 | 91 | #: ../../tutorial/vep.rst:52 92 | msgid "Plugin registration for vep" 93 | msgstr "注册vep" 94 | 95 | #: ../../tutorial/vep.rst:96 96 | msgid "User Access" 97 | msgstr "用户访问" 98 | 99 | #: ../../tutorial/vep.rst:98 100 | msgid "" 101 | "Access /upload display form, access the uploaded file via " 102 | "/uploads/filename." 103 | msgstr "访问/upload显示表单,访问上传的文件通过/uploads/filename" 104 | 105 | -------------------------------------------------------------------------------- /docs/quickstart.rst: -------------------------------------------------------------------------------- 1 | .. _quickstart: 2 | 3 | Quickstart 4 | ========== 5 | 6 | Suppose you have installed the Flask-PluginKit. If you do not, 7 | head over to the :ref:`installation` section. 8 | 9 | The first one is to initalize it directly:: 10 | 11 | from flask_pluginkit import PluginManager, Flask 12 | app = Flask(__name__) 13 | pm = PluginManager(app) 14 | 15 | where as the second one is to use the factory pattern:: 16 | 17 | from flask_pluginkit import PluginManager, Flask 18 | app = Flask(__name__) 19 | pm = PluginManager() 20 | pm.init_app(app) 21 | 22 | Plugin Structure 23 | ---------------- 24 | 25 | After the first step is done, you can start developing your first plugin. 26 | 27 | The plugin is a legal python package that needs to define some metadata and use 28 | register to return the extension point, more see :ref:`core-plugin-structure` 29 | 30 | For example, the structure of small plugin can look like this: 31 | 32 | .. sourcecode:: text 33 | 34 | my_plugin 35 | |-- __init__.py 36 | 37 | the structure of a more complex plugin can also look like this: 38 | 39 | .. sourcecode:: text 40 | 41 | my_plugin 42 | ├── __init__.py 43 | ├── static 44 | │   └── example 45 | │   └── demo.css 46 | └── templates 47 | └── example 48 | └── demo.html 49 | 50 | Hello World 51 | ----------- 52 | 53 | For a better understanding you can also have a look at the `example`_, 54 | it contains a local plugin and a third party plugin. 55 | 56 | Now, let's start with a simple plugin example. The plugin name is helloworld. 57 | 58 | This simple helloworld example can be found `here`_. 59 | 60 | First, the developer wrote a simple plugin web application with only 61 | one app.py, an index view function, and the content is: 62 | 63 | .. code-block:: python 64 | 65 | # -*- coding: utf-8 -*- 66 | 67 | from flask import Flask 68 | from flask_pluginkit import PluginManager 69 | 70 | app = Flask(__name__) 71 | pm = PluginManager(app) 72 | 73 | @app.route('/') 74 | def index(): 75 | return 'hello world' 76 | 77 | Now, we want to limit when accessing the index view (ie /), 78 | if ip is 127.0.0.1, then redirect to ``/limit``, proceed as follows: 79 | 80 | 1. Create helloworld directory 81 | 82 | .. code-block:: bash 83 | 84 | $ mkdir -p plugins/helloworld 85 | $ touch plugins/__init__.py plugins/helloworld/__init__.py 86 | 87 | 2. Write ``__init__.py`` for the helloworld plugin, content is: 88 | 89 | .. code-block:: python 90 | 91 | # -*- coding: utf-8 -*- 92 | 93 | from flask import Blueprint, jsonify, request, redirect, url_for, make_response 94 | 95 | __plugin_name__ = "helloworld" 96 | __version__ = "0.1.0" 97 | __author__ = "staugur" 98 | 99 | bp = Blueprint("helloworld", "helloworld") 100 | 101 | @bp.route("/limit") 102 | def limit(): 103 | return jsonify(dict(status=0, message="Access Denial")) 104 | 105 | def limit_handler(): 106 | """I am running in before_request""" 107 | ip = request.headers.get('X-Real-Ip', request.remote_addr) 108 | if request.endpoint == "index" and ip == "127.0.0.1": 109 | resp = make_response(redirect(url_for("helloworld.limit"))) 110 | resp.is_return = True 111 | return resp 112 | 113 | def register(): 114 | return { 115 | "bep": dict(blueprint=bp, prefix=None), 116 | "hep": dict(before_request=limit_handler) 117 | } 118 | 119 | 3. Run 120 | 121 | The current web application structure is as follows: 122 | 123 | .. code-block:: text 124 | 125 | demo 126 | ├── app.py 127 | └── plugins 128 | ├── helloworld 129 | │   └── __init__.py # Plugin core code file 130 | └── __init__.py # Only an empty file 131 | 132 | Run app: 133 | 134 | .. code-block:: bash 135 | 136 | $ FLASK_ENV=development FLASK_APP=app.py flask run --no-reload 137 | 138 | 4. Testing 139 | 140 | .. image:: ./_static/images/helloworld.png 141 | 142 | For details, see :ref:`tutorial` 143 | 144 | .. _example: https://github.com/staugur/Flask-PluginKit/tree/master/examples/fulldemo 145 | 146 | .. _here: https://github.com/staugur/Flask-PluginKit/tree/master/examples/helloworld 147 | 148 | -------------------------------------------------------------------------------- /docs/requirements.txt: -------------------------------------------------------------------------------- 1 | sphinx 2 | sphinx-intl 3 | Pallets-Sphinx-Themes -------------------------------------------------------------------------------- /docs/tutorial/afterword.rst: -------------------------------------------------------------------------------- 1 | Afterword 2 | ========= 3 | 4 | To the plugin user 5 | ------------------- 6 | 7 | For the user, this tutorial can basically guarantee the smooth use of the 8 | tutorial. Of course, when I write this document, I can't accurately view it 9 | from the perspective of each user. Although I am also one of the users, 10 | I am also a developer. Familiar with internal processes. 11 | 12 | At the beginning, I used Flask to write web applications, wrote a lot, and 13 | some things were repeated. Later I wanted to study something that has style. 14 | I referenced wordpress, flask-plguins, etc., and wrote the original code 15 | that can load plugins. Finally, I used More, so independent into a Flask 16 | extension and released to pypi, named Flask-PluginKit. 17 | 18 | Later released to v2.4.1, I still have a lot of pain points in use, so 19 | I reconstructed all the code, rewritten the document, released v3.0.0! 20 | 21 | I hope to give your feedback to me. 22 | 23 | To the plugin developer 24 | ----------------------- 25 | 26 | For developers, you not only need to read this tutorial, but also 27 | api, loading logic, and so on, of course, it is best to see some source code, 28 | although it is very good. 29 | -------------------------------------------------------------------------------- /docs/tutorial/bep.rst: -------------------------------------------------------------------------------- 1 | .. _bep: 2 | 3 | Blueprint Extension Point 4 | ========================= 5 | 6 | Description 7 | ----------- 8 | 9 | The extension point abbreviation is bep. 10 | 11 | This extension point is very simple, just add a blueprint to the web 12 | application, no different from the normal blueprint. 13 | 14 | The plugin needs to return the bep field via register. The bep data type 15 | returned is a dictionary with the format 16 | {blueprint: Blueprint Instance, prefix: /your_blueprint_url_prefix, parent: Name}. 17 | Only one blueprint is currently supported. 18 | 19 | .. versionchanged:: 3.6.2 20 | 21 | Support Flask2.0 nested blueprint with the `parent` param(beta). 22 | But only blueprints of other plugins can be nested. 23 | 24 | The Flask-PluginKit loads bep via 25 | :meth:`~flask_pluginkit.PluginManager._bep_handler`, this method will 26 | detect bep rules and specific content. 27 | 28 | The blueprint can be mounted under None or under other prefixes. 29 | Flask-PluginKit does not detect blueprint routing, 30 | as long as the prefix is legal. 31 | 32 | The user only needs to ensure that the plugin is harmless and does not 33 | pollute your original application. Others are plugin developers. 34 | 35 | If you just want to add the view to the existing blueprint, 36 | you can refer to :ref:`vep-on-blueprint` 37 | 38 | Example 39 | ------- 40 | 41 | - Plugin registration for bep 42 | 43 | .. code-block:: python 44 | 45 | from os.path import dirname, abspath 46 | from flask import Blueprint 47 | 48 | bp = Blueprint('test', 'test', root_path=dirname(abspath(__file__))) 49 | 50 | @bp.route('/') 51 | def your_route(): 52 | pass 53 | 54 | def register(): 55 | return dict( 56 | bep=dict( 57 | blueprint=bp, 58 | prefix='/test' 59 | ) 60 | ) 61 | -------------------------------------------------------------------------------- /docs/tutorial/config.rst: -------------------------------------------------------------------------------- 1 | .. _config: 2 | 3 | Configuration 4 | ============= 5 | 6 | Description 7 | ----------- 8 | 9 | The configuration here refers to the configuration information of the plugin. 10 | In general, the plugin needs to obtain the configuration of the 11 | web application. In the first case, the python module of the plugin needs 12 | configuration information, and the second case is that the template code of 13 | the plugin needs to obtain configuration information. 14 | 15 | In the first case, the plugin can be imported directly into the configuration 16 | file module, or use ``current_app.config``. 17 | 18 | In the second case, the Flask itself already supports the configuration of 19 | **current_app.config** directly in the template using config, but the 20 | old version (before 0.10) may not support it, so Flask-PluginKit adds a 21 | template "global variable", :meth:`~flask_pluginkit.PluginManager.emit_config`, 22 | the configuration passed to :class:`~flask_pluginkit.PluginManager` with 23 | the :attr:`~flask_pluginkit.PluginManager.pluginkit_config` parameter and 24 | the configuration of **current_app.config** can be obtained directly. 25 | 26 | .. note:: 27 | 28 | The **emit_config** will first look in the **pluginkit_config** parameter. 29 | If not found, then find **current_app.config**. 30 | If still can't find, return None. 31 | 32 | Example 33 | ------- 34 | 35 | - User Definition 36 | 37 | .. code-block:: python 38 | 39 | from flask_pluginkit import PluginManager 40 | PluginManager(app, pluginkit_config=dict(HELLO="WORLD")) 41 | 42 | - Plugin call 43 | 44 | Suppose the following code is a template file under a plugin, 45 | get HELLO and DEBUG configuration: 46 | 47 | .. code-block:: html 48 | 49 |
50 | Hello: {{ emit_config("HELLO") }} 51 |
52 |
53 | App is debug: {{ emit_config("DEBUG") }} 54 |
55 | -------------------------------------------------------------------------------- /docs/tutorial/core.rst: -------------------------------------------------------------------------------- 1 | Plugin Core 2 | =========== 3 | 4 | .. _core-minimal-plugin: 5 | 6 | Minimal Plugin 7 | -------------- 8 | 9 | .. code-block:: python 10 | 11 | # -*- coding: utf-8 -*- 12 | 13 | __plugin_name__ = "Demo" 14 | __author__ = "Mr.tao " 15 | __version__ = "0.1.1" 16 | 17 | def register(): 18 | return {} 19 | 20 | However, this plugin doesn't make any sense, it just means 21 | a valid plugin content, whether it's a local plugin or a third-party plugin, 22 | the core part is the same. 23 | 24 | The mini plugin above, which starts and ends with ``__``, is called metadata, 25 | which is the most important information for getting plugins. 26 | 27 | The ``register`` function is used to return the extension point. 28 | Let's explain it one by one. 29 | 30 | .. _core-plugin-structure: 31 | 32 | Plugin Structure 33 | ---------------- 34 | 35 | The most minimal plugin needs to have at least it's own directory. 36 | The directory must contain the ``__init__.py`` file, otherwise 37 | it is not considered a plugin! 38 | 39 | The core code of the plugin can be written in other modules of the package, 40 | then returned in **__init__.py** using the ``register`` function, and 41 | this file contains the metadata required to register the plugin. 42 | 43 | In `__init__.py`, you can write your plugin code all in. Of course, 44 | the recommended way is to create a module with another name under 45 | the plugin package. Write your functions, class, variable, and so on, 46 | then import the module in `__init__.py` and 47 | use register to return the extension point. 48 | 49 | The project structure of a complete plugin web application is 50 | probably like this: 51 | 52 | .. code-block:: text 53 | 54 | your_project/ 55 | ├── app.py 56 | ├── config.py 57 | ├── libs 58 | │   └── __init__.py 59 | ├── LICENSE 60 | ├── plugins 61 | │   ├── __init__.py 62 | │   └── local_plugin_demo # A local plugin 63 | │   ├── _core.py 64 | │   ├── __init__.py 65 | │   ├── license.txt 66 | │   ├── readme.txt 67 | │   ├── static 68 | │   │   └── demo.css 69 | │   ├── template 70 | │   │   └── demo 71 | │   │   └── demo.html 72 | │   └── _util.py 73 | ├── README.md 74 | ├── requirements.txt 75 | ├── utils 76 | │   └── __init__.py 77 | └── views 78 | └── __init__.py 79 | 80 | .. _core-metadata: 81 | 82 | metadata 83 | --------- 84 | 85 | Below are all supported metadata configuration items, please note that 86 | the first three are required: 87 | 88 | - ``__plugin_name__`` 89 | 90 | Your plugin name is not strictly required to be consistent 91 | with the plugin directory name. 92 | 93 | - ``__author__`` 94 | 95 | Plugin Author 96 | 97 | - ``__version__`` 98 | 99 | Plugin Version, compliance with `Semantic Version 2.0`_ Rules. 100 | 101 | - ``__description__`` 102 | 103 | What is the use of plugin description information. 104 | 105 | - ``__url__`` 106 | 107 | Plugin Homepage 108 | 109 | - ``__license__`` 110 | 111 | Plugin LICENSE 112 | 113 | - ``__license_file__`` 114 | 115 | The plugin LICENSE detail file. Your plugin directory should have 116 | a LICENSE file. 117 | 118 | - ``__readme_file__`` 119 | 120 | The plugin profile should have a README description file 121 | in your plugin directory. 122 | 123 | - ``__state__`` 124 | 125 | The plugin Status, enabled (default) or disabled. 126 | 127 | .. _Semantic Version 2.0: https://semver.org 128 | 129 | .. _core-register: 130 | 131 | register 132 | -------- 133 | 134 | This function is also required, it should be defined or imported 135 | in `__init__.py`. Flask-PluginKit will call this function when loading, 136 | return data is dict, contains various types of extension points, 137 | such as: 138 | 139 | .. code-block:: python 140 | 141 | def register(): 142 | return dict( 143 | bep=dict(), 144 | hep=dict(), 145 | tep=dict(), 146 | errhandler=dict(), 147 | filter=dict(), 148 | tcp=dict(), 149 | ) 150 | 151 | For the extension points returned, please see the following sections. 152 | 153 | .. _core-enabling-and-disabling-plugins: 154 | 155 | Enabling and Disabling Plugins 156 | ------------------------------ 157 | 158 | This extension, uses a different approach for handling plugins. 159 | 160 | Anyway, local plugins (a subdirectory located in the application, 161 | such as plugins, is a package) or third-party plugins (which can be pypi 162 | or from git, svn, etc.), should be installed in the local environment. 163 | 164 | Plugins are enabled by default, and there are two ways to 165 | enable or disable a plugin. 166 | 167 | The first method is to set the metadata ``__state__`` value to **enabled** 168 | or **disabled**. 169 | 170 | The second method is to add the ``ENABLED`` or ``DISABLED`` file in the 171 | plugin's root directory, without changing the source code. 172 | This can either be done by hand or with the method provided 173 | by :meth:`~flask_pluginkit.PluginManager.disable_plugin` or 174 | :meth:`~flask_pluginkit.PluginManager.enable_plugin`. 175 | 176 | .. note:: 177 | 178 | The second method has a higher priority than the first one, and 179 | the DISABLED file has a higher priority than the ENABLED file. 180 | 181 | The directory structure of a disabled plugin is shown below. 182 | 183 | .. sourcecode:: text 184 | 185 | my_plugin 186 | |-- DISABLED # Just add a empty file named "DISABLED" 187 | |-- __init__.py 188 | 189 | .. warning:: 190 | 191 | The server needs to be restarted or reloaded to disable the plugin. 192 | This is a limitation of Flask. However, it is possible, to restart 193 | the application by sending a HUP signal to the application server. 194 | 195 | The following code snippets, are showing how this can be done with 196 | the WSGI server gunicorn. Gunicorn has be to started in 197 | daemon (``--daemon``) mode in order for this to work. 198 | 199 | You can use the command to manually reload: 200 | 201 | .. sourcecode:: bash 202 | 203 | $ kill -HUP Your_APP_Gunicorn_master_pid 204 | 205 | or direct restart (kill, then start). 206 | 207 | In web applications, according to previous tests, it should 208 | use :func:`os.getppid` instead of :func:`os.getpid` 209 | to get the master pid of gunicorn, and send SIGHUP signal to master pid. 210 | 211 | For security, the process name should be verified! 212 | 213 | .. sourcecode:: python 214 | 215 | @app.route('/reload') 216 | def reload(): 217 | os.kill(os.getppid(), signal.SIGHUP) 218 | 219 | This feature is implemented in v3.3.0, reference document :doc:`/webmanager` 220 | -------------------------------------------------------------------------------- /docs/tutorial/cvep.rst: -------------------------------------------------------------------------------- 1 | .. _cvep: 2 | 3 | Class-based View Extension Point 4 | ================================ 5 | 6 | Description 7 | ----------- 8 | 9 | The extension point abbreviation is cvep. 10 | 11 | Flask-Classful is an extension that adds class-based views to Flask. 12 | 13 | This extension point is flask-classful based to extend routing, so your 14 | application needs to install it (document url is: `flask_classful_docs`_): 15 | 16 | .. code-block:: bash 17 | 18 | $ pip install -U Flask-Classful 19 | 20 | The plugin needs to return the cvep field via register. The returned cvep data 21 | type can be dict, list or tuple, em, the format is similar to vep. 22 | 23 | The dict format is {view_class:, other register options}, see the 24 | `flask_classful_register`_ for more options, a common example is: 25 | 26 | .. code-block:: python 27 | 28 | from flask_classful import FlaskView 29 | 30 | class TestView(FlaskView): 31 | 32 | def index(self): 33 | return "test" 34 | 35 | dict(view_class=TestView, route_base="/classful") 36 | 37 | Other types are represented as multiple routes, 38 | and multiple dict data of the above type can be nested. 39 | 40 | The Flask-PluginKit loads cvep via 41 | :meth:`~flask_pluginkit.PluginManager._cvep_handler`, this method will 42 | detect cvep rules and specific content. 43 | 44 | .. _flask_classful_docs: http://flask-classful.teracy.org/ 45 | .. _flask_classful_register: http://flask-classful.teracy.org/#flask_classful.FlaskView.register 46 | .. _add_url_rule: http://flask.palletsprojects.com/api/#flask.Flask.add_url_rule 47 | 48 | Example 49 | ------- 50 | 51 | - Plugin registration for cvep 52 | 53 | .. code-block:: python 54 | 55 | from flask_classful import FlaskView 56 | 57 | quotes = [ 58 | "A noble spirit embiggens the smallest man! ~ Jebediah Springfield", 59 | "If there is a way to do it better... find it. ~ Thomas Edison", 60 | "No one knows what he can do till he tries. ~ Publilius Syrus" 61 | ] 62 | 63 | class QuotesView(FlaskView): 64 | 65 | def index(self): 66 | """Visit: http://localhost:5000/quotes/""" 67 | return "
".join(quotes) 68 | 69 | def get(self, id): 70 | """Visit: http://localhost:5000/quotes/1/""" 71 | id = int(id) 72 | if id < len(quotes) - 1: 73 | return quotes[id] 74 | else: 75 | return "Not Found", 404 76 | 77 | def register(): 78 | return dict( 79 | cvep = [ 80 | dict(view_class=QuotesView) 81 | ] 82 | ) 83 | -------------------------------------------------------------------------------- /docs/tutorial/dcp.rst: -------------------------------------------------------------------------------- 1 | .. _dcp: 2 | 3 | Dynamic Connection Point 4 | ======================== 5 | 6 | Description 7 | ----------- 8 | 9 | The connection point abbreviation is dcp. 10 | 11 | It doesn't need to be returned with **register**, even you may rarely use it. 12 | 13 | Dynamic connection points, dynamic registration and execution functions 14 | return the results to the template, which is inspired by `flask-plugins`_. 15 | However, it has been simplified to support pushing multiple functions 16 | in the application context, and in the template to get the execution results 17 | of all functions under the event (safe HTML code returned by 18 | :class:`~flask.Markup`). 19 | 20 | .. _flask-plugins: https://flask-plugins.readthedocs.io/#events 21 | 22 | The public push method is :func:`~flask_pluginkit.push_dcp`, in addition, 23 | it can be managed using :attr:`~flask_pluginkit.PluginManager._dcp_manager`, 24 | it is an instance of :class:`~flask_pluginkit.utils.DcpManager`. 25 | Flask-PluginKit will update the template with a global method **emit_dcp** when 26 | it loads, the method called is :meth:`~flask_pluginkit.utils.DcpManager.emit`. 27 | 28 | Example 29 | ------- 30 | 31 | - Push dcp 32 | 33 | .. code-block:: python 34 | 35 | from flask_pluginkit import push_dcp 36 | 37 | def test(*args, **kwargs): 38 | return 'hello dcp' 39 | 40 | with app.app_context(): 41 | push_dcp('test', test) 42 | 43 | - Call in template 44 | 45 | .. code-block:: html 46 | 47 |
48 | {{ emit_dcp('test', 1, 2, 3, a='a', b='b', c='c') }} 49 |
50 | 51 | -------------------------------------------------------------------------------- /docs/tutorial/errhandler.rst: -------------------------------------------------------------------------------- 1 | .. _errhandler: 2 | 3 | Error Handler Extension Point 4 | ============================= 5 | 6 | Description 7 | ----------- 8 | 9 | The extension point abbreviation is errhandler. 10 | 11 | Error handler, this extension point is also very simple, return the 12 | errhandler field through register, this field requires the format is 13 | {http-code: error-view-func, other-code: other-error-view-func}, and 14 | the key is a standard HTTP code (such as 404, 403, 500), the value is an 15 | error handler view function, and supports multiple error code handling. 16 | 17 | However, there is now a new format: [{error=exception_class, handler=func}, 18 | {error=err_code, handler=func}], this form allows you to use http-code to 19 | handle error codes while allowing a custom exception class to be passed in. 20 | And the associated processor (capturing the handler for this exception class), 21 | please refer to the Flask documentation for this class-based form of code. 22 | 23 | The Flask-PluginKit loads errhandler via 24 | :meth:`~flask_pluginkit.PluginManager._error_handler`, this method will 25 | detect errhandler rules and specific content. 26 | 27 | It should be noted that if multiple duplicate error handling functions 28 | are eventually overwritten, only one is valid. The error handling function 29 | can be written in the official documentation is `flask-error-handlers`_. 30 | 31 | .. _flask-error-handlers: 32 | https://flask.palletsprojects.com/errorhandling/#error-handlers 33 | 34 | Example 35 | ------- 36 | 37 | - Plugin registration for errhandler 38 | 39 | .. code-block:: python 40 | 41 | from flask import jsonify 42 | 43 | def permission_deny(error): 44 | return jsonify(dict(status=403, msg="permission deny")),403 45 | 46 | class ApiError(Exception): 47 | 48 | def __init__(self, code, message, status_code=200): 49 | super(ApiError, self).__init__() 50 | self.code = code 51 | self.message = message 52 | self.status_code = status_code 53 | 54 | def to_dict(self): 55 | return dict(code=self.code, msg=self.message) 56 | 57 | def handle_api_error(e): 58 | #: e is an instance of an exception class 59 | response = jsonify(e.to_dict()) 60 | response.status_code = e.status_code 61 | return response 62 | 63 | def raise_api_error_view(): 64 | #: Actively triggering ApiError in the view will be intercepted by 65 | #: handle_api_error and return json response. 66 | raise ApiError(10000, "err_message") 67 | 68 | def register(): 69 | return { 70 | 'vep': dict(rule='/api_error', view_func=raise_api_error_view), 71 | 'errhandler': [ 72 | dict(error=403, handler=permission_deny), 73 | dict(error=ApiError, handler=handle_api_error) 74 | ] 75 | } 76 | -------------------------------------------------------------------------------- /docs/tutorial/filter.rst: -------------------------------------------------------------------------------- 1 | .. _filter: 2 | 3 | Template Filter Extension Point 4 | =============================== 5 | 6 | Description 7 | ----------- 8 | 9 | The extension point abbreviation is filter. 10 | 11 | The concept of template filters comes from jinja2, which is essentially a 12 | regular python function. The filter and variables are separated by a 13 | pipe symbol ( | ), and optional parameters can also be passed in parentheses. 14 | The left side of the filter is used as the first parameter, 15 | and the remaining parameters are passed to the filter as 16 | additional parameters or keyword parameters. 17 | 18 | The plugin needs to return the filter field via register. The data type of 19 | this field can be dict or list. 20 | If it is a dict, the format is {filter_name: func}. 21 | If it is a list, the format is [func1, func2], and the function name is 22 | used as the filter name, but if an anonymous function is used, 23 | the filter name is , which is unfriendly! 24 | 25 | So, if the format is list, the element type is allowed to be tuple, ie: 26 | [func1, (name, func2)], thus setting the name of the filter. 27 | 28 | The Flask-PluginKit loads filter via 29 | :meth:`~flask_pluginkit.PluginManager._filter_handler`, this method will 30 | detect filter rules and specific content. 31 | 32 | Once the registration is complete, you can use the filter in the template 33 | like Jinja2's built-in filter. For this section, you can refer to the 34 | Jinja2 documentation `filters`_. 35 | 36 | .. _filters: http://jinja.pocoo.org/docs/templates/#filters 37 | 38 | Example 39 | ------- 40 | 41 | - Plugin registration for filter 42 | 43 | .. code-block:: python 44 | 45 | def reverse_filter(s): 46 | return s[::-1] 47 | 48 | def register(): 49 | return dict( 50 | filter=dict(reverse=reverse_filter) 51 | ) 52 | 53 | - Call in template 54 | 55 | .. code-block:: html 56 | 57 |
58 | {% for x in ['m','y', 'l', 'i', 's', 't'] | reverse %} 59 | {{ x }} 60 | {% endfor %} 61 |
62 | 63 | -------------------------------------------------------------------------------- /docs/tutorial/foreword.rst: -------------------------------------------------------------------------------- 1 | Foreword 2 | ======== 3 | 4 | What? 5 | ----- 6 | 7 | Flask-PluginKit is a Flask extension for extending web application 8 | functionality. It provides an easy way to create plugins for your application. 9 | It is possible to create extension points which can then be used to 10 | extend your application without the need to modify your core code. 11 | 12 | Flask-PluginKit can also be said to be a glue, or a bridge, for 13 | connecting web and web plugins. It supports native plugins and 14 | third-party plugins from pypi, git, svn, and so on. In addition, the plugin 15 | is also very simple to write. 16 | 17 | Glossary 18 | -------- 19 | 20 | The following is a Chinese-English comparison: 21 | 22 | Extension Point 23 | 扩展点 24 | 25 | TEP / tep / Template Extension Point 26 | 模板扩展点 27 | 28 | HEP / hep / Hook Extension Point 29 | 钩子扩展点 30 | 31 | BEP / bep / Blueprint Extension Point 32 | 蓝图扩展点 33 | 34 | VEP / vep / View function Extension Point 35 | 视图扩展点 36 | 37 | DCP / dcp / Dynamic Connection Point 38 | 动态连接点 39 | 40 | TCP / tcp / Template Context Processor / Context Processor 41 | 模板上下文处理器扩展点 42 | 43 | filter 44 | 模板过滤器扩展点 45 | 46 | errhandler 47 | 错误处理器扩展点 48 | 49 | p3 50 | 插件预处理器 51 | 52 | Developer / developer 53 | 插件开发者 54 | 55 | Local Plugin 56 | ------------ 57 | 58 | It is a local directory, it should be located in the plugins directory of 59 | the web application (of course, can also be defined as other directories), 60 | and is a legal python package, that is: plugins contain ``__init__.py`` files, 61 | plugiin directories also Contains ``__init__.py``, this file identifies 62 | the directory as a package, and also identifies the plugin, 63 | which is the core code, plugin entry. 64 | 65 | Third-party Plugin 66 | ------------------ 67 | 68 | Third-party plugins are non-web application subdirectories, but 69 | local modules from installations such as pip or easy_install. 70 | 71 | The third-party plugins are free to use. The web application does not need 72 | to put the plugin code into a subdirectory. It only needs to be installed 73 | to the local machine using `pip install` or `easy_install`, and then pass 74 | the :attr:`~flask_pluginkit.PluginManager.plugin_packages` parameter when 75 | the :class:`~flask_pluginkit.PluginManager` is initialized. 76 | 77 | This means that anyone can write a package and publish it to pypi; 78 | the user writes `requirements.txt` and installs the dependencies, 79 | which are called in the initialization, in one go, and almost no need 80 | to worry about subsequent third-party plugin upgrades. 81 | 82 | For instructions on how to write third-party plugins, 83 | see :doc:`/tutorial/third-party-plugin` 84 | 85 | For official plugin's github group: https://github.com/saintic 86 | 87 | Loading logic 88 | ------------- 89 | 90 | Developers who are not Flask-PluginKit or its plugins can ignore this part. 91 | 92 | The plugin load starts when the program starts. 93 | The load class is :class:`~flask_pluginkit.PluginManager`, 94 | its destructor allows you to pass 95 | :attr:`~flask_pluginkit.PluginManager.plugins_base` 96 | (the default program directory), 97 | :attr:`~flask_pluginkit.PluginManager.plugins_folder` 98 | (the directory where the plugin is located), 99 | set the plugin absolute path directory, and also support factory mode, 100 | see the API documentation for more parameters. 101 | 102 | The loading process is as follows: 103 | 104 | 1. Call :class:`~flask_pluginkit.PluginManager` normal mode or factory mode 105 | to initialize the extension. 106 | 107 | 2. Scan the `plugins_folder` plugin directory and the packages that 108 | match the plugin rules will be dynamically loaded. 109 | 110 | 3. Load third-party plugins in 111 | :attr:`~flask_pluginkit.PluginManager.plugin_packages`. 112 | 113 | 4. Add template global variable. 114 | 115 | 5. Support for multiple template directories. 116 | 117 | 6. Add a view function that supports access to 118 | plugin directory static resources. 119 | 120 | 7. Register hep, bep. 121 | 122 | 8. Append the instance to **app.extensions**. 123 | -------------------------------------------------------------------------------- /docs/tutorial/hep.rst: -------------------------------------------------------------------------------- 1 | .. _hep: 2 | 3 | Hook Extension Point 4 | ==================== 5 | 6 | Description 7 | ----------- 8 | 9 | The extension point abbreviation is hep. 10 | 11 | This extension point works in the flask hook function and currently supports 12 | three hooks with the following names: 13 | 14 | - before_request 15 | 16 | Before request (intercept requests are allowed) 17 | 18 | - after_request 19 | 20 | After request (no exception before return) 21 | 22 | - teardown_request 23 | 24 | After request (before return, with or without exception) 25 | 26 | - before_first_request 27 | 28 | Registers a function to be run before the first request to this instance of 29 | the application. 30 | 31 | 32 | Other hooks will be supported later, and a exception 33 | :class:`~flask_pluginkit.exceptions.PEPError` will be triggered if a hook name 34 | that is not supported by the current version is used. 35 | 36 | The plugin needs to return the hep field via register. The hep data type 37 | returned is a dictionary with the format {hook_name: callable}, 38 | which can have multiple hep_names. 39 | 40 | The Flask-PluginKit loads hep via 41 | :meth:`~flask_pluginkit.PluginManager._hep_handler`, this method will 42 | detect hep rules and specific content. 43 | 44 | Similar to tep, each hook_name has many values for the entire web application. 45 | 46 | When in use, the user does not care how to call, because Flask-PluginKit has 47 | registered the hook handler at initialization time, but it should be noted 48 | that there are different requirements for different hook type handlers: 49 | 50 | - before_request 51 | 52 | This is simple, any callback function, class method, and so on can be used, 53 | there is no parameter, but the hook function here has a special place. 54 | As we all know, for Flask, if the before_request return value is not None, 55 | the request will be terminated. So Flask-PluginKit will try to detect the 56 | return value of the hook function, if it is not None, it will return, 57 | and the request is terminated at this moment. 58 | 59 | - after_request 60 | 61 | Will pass a **response** parameter, this is the web application response 62 | data, is an instance of :class:`~flask.Response`, the function 63 | corresponding to this hook can get response, you can not return response. 64 | 65 | - teardown_request 66 | 67 | Similar to after_request, this hook is triggered by the flask when 68 | the web exception occurs. Flask-PluginKit will pass the **exception** 69 | parameter to the corresponding hook function without returning. 70 | 71 | - before_first_request 72 | 73 | Refer before_request, run only once after the app starts, no return value. 74 | 75 | Example 76 | ------- 77 | 78 | - Plugin registration for hep 79 | 80 | .. code-block:: python 81 | 82 | from flask import request, g, current_app 83 | 84 | def set_login_state(): 85 | g.login_in = request.args.get("username") == "admin" and \ 86 | request.args.get("password") == "admin 87 | 88 | def record_access_log(response): 89 | log = { 90 | "status_code": response.status_code, 91 | "method": request.method, 92 | "ip": request.headers.get('X-Real-Ip', request.remote_addr), 93 | "url": request.url, 94 | "referer": request.headers.get('Referer'), 95 | "agent": request.headers.get("User-Agent"), 96 | "login_in": g.login_in 97 | } 98 | current_app.logger.info(log) 99 | 100 | def register(): 101 | return dict( 102 | hep=dict( 103 | before_request=set_login_state, 104 | after_request=record_access_log 105 | ) 106 | ) 107 | 108 | As above, after your program is running, the `set_login_state` function will 109 | be executed before each request, and the `record_access_log` function will 110 | be executed before the request returns. 111 | 112 | -------------------------------------------------------------------------------- /docs/tutorial/index.rst: -------------------------------------------------------------------------------- 1 | .. _tutorial: 2 | 3 | Tutorial 4 | ======== 5 | 6 | This tutorial will detail the Flask extension named Flask-PluginKit and 7 | will walk you through creating a local plugin and a third-party plugin, 8 | and cover as much as possible of all the features of Flask-PluginKit. 9 | 10 | .. toctree:: 11 | :caption: Contents: 12 | :maxdepth: 2 13 | 14 | foreword 15 | core 16 | tep 17 | hep 18 | bep 19 | vep 20 | cvep 21 | config 22 | static 23 | errhandler 24 | filter 25 | tcp 26 | dcp 27 | p3 28 | third-party-plugin 29 | afterword 30 | 31 | -------------------------------------------------------------------------------- /docs/tutorial/p3.rst: -------------------------------------------------------------------------------- 1 | .. _p3: 2 | 3 | Plugin PreProcessor Extension Point 4 | ==================================== 5 | 6 | Description 7 | ----------- 8 | 9 | The extension point abbreviation is p3. 10 | 11 | Its function is to take the lead in processing one round of plugin data 12 | after loading the plugin. 13 | 14 | The plugin needs to return the p3 field via register. The data type required 15 | for this field is dict, and the format is {plugin_name:{pet:func}}. 16 | 17 | The `pet` is plugin extension point, e.g: bep, tep, tcp, but without p3. 18 | 19 | Need to pay attention to `func`, it receives a parameter, 20 | the type varies with pet, it is either a list or a dict, 21 | and the return type is required to be consistent with the received type. 22 | 23 | The Flask-PluginKit loads tcp via 24 | :meth:`~flask_pluginkit.PluginManager._p3_handler`, 25 | this method will detect p3 rules and specific content. 26 | 27 | Example 28 | ------- 29 | 30 | - Plugin registration for p3 31 | 32 | .. code-block:: python 33 | 34 | def update_tcp(param): 35 | # your code: operate `param` and the return type must be consistent. 36 | print(type(param), param) 37 | return param 38 | 39 | def register(): 40 | # p3 format: {"Target plugin_name": {"pet": "processing_function"}, } 41 | return dict(p3=dict(a_plugin_name=dict(tcp=update_tcp))) 42 | -------------------------------------------------------------------------------- /docs/tutorial/static.rst: -------------------------------------------------------------------------------- 1 | .. _static: 2 | 3 | Static Resource 4 | =============== 5 | 6 | Description 7 | ----------- 8 | 9 | Generally speaking, when accessing static files during development, you can 10 | use Flask, but in the production environment, it is definitely a combination 11 | of WSGI servers and web servers such as nginx and apache. 12 | Static resources can be processed through nginx. 13 | 14 | However, for the plugin of Flask-PluginKit, the static resources are located 15 | in the static directory of the plugin. If there are many plugins in a web 16 | application, then there are several static directories, so it is inconvenient 17 | to configure through nginx. 18 | 19 | So Flask-PluginKit customizes a view with an endpoint to access the static 20 | directory under the plugin, and also provides a method 21 | :meth:`~flask_pluginkit.PluginManager.emit_assets` to easily get the static 22 | files under the plugin in the template. Of course, this method uses 23 | :func:`~flask.url_for` internally, 24 | so you can also use url_for to build the url. 25 | 26 | The assets view above can customize its endpoints, pass 27 | :attr:`~flask_pluginkit.PluginManager.static_endpoint` when initializing 28 | :class:`~flask_pluginkit.PluginManager`, and pass 29 | :attr:`~flask_pluginkit.PluginManager.static_url_path` to define view route 30 | prefix. When using **emit_assets**, it will call the view function, try to 31 | find the plugin static directory, and return 404 error if it is not found. 32 | 33 | In addition, it is worth mentioning that the static file suffix is 34 | currently processed. 35 | 36 | - The suffix is **.css** 37 | 38 | Will generate the html code for `link css`, for example: 39 | 40 | .. code-block:: html 41 | 42 | 43 | 44 | - The suffix is **.js** 45 | 46 | Will generate the html code of `script`, for example: 47 | 48 | .. code-block:: html 49 | 50 | 51 | 52 | - Other suffixes 53 | 54 | Only the url path portion of the static file will be generated. 55 | 56 | .. note:: 57 | 58 | In emit_assets, you can add ``_raw=True`` to let Flask-PluginKit not add 59 | code based on the suffix, but instead return the resource path directly. 60 | 61 | Example 62 | ------- 63 | 64 | - Static files 65 | 66 | Suppose a plugin called plugin_demo has a static directory. 67 | The file structure looks like this: 68 | 69 | .. code-block:: text 70 | 71 | plugin_demo/ 72 | ├── __init__.py 73 | └── static 74 | ├── css 75 | │   └── style.css 76 | ├── hello.png 77 | └── js 78 | └── demo.js 79 | 80 | - Access static files 81 | 82 | In the template, the url of the static file can be built by **emit_assets**. 83 | 84 | .. code-block:: html 85 | 86 | 87 | 88 | 89 | {{ emit_assets('plugin_demo','css/style.css') }} 90 | 91 | 92 |
93 | 94 |
95 | 96 |
97 | {{ emit_assets('plugin_demo', 'js/demo.js', _raw=True) }} 98 |
99 | 100 | {{ emit_assets("plugin_demo", filename="js/demo.js") }} 101 | 102 | 103 | 104 | The actual source code for this page is this: 105 | 106 | .. code-block:: html 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 |
115 | 116 |
117 | 118 |
119 | /assets/plugin_demo/js/demo.js 120 |
121 | 122 | 123 | 124 | 125 | -------------------------------------------------------------------------------- /docs/tutorial/tcp.rst: -------------------------------------------------------------------------------- 1 | .. _tcp: 2 | 3 | Template Context Processors Extension Point 4 | =========================================== 5 | 6 | Description 7 | ----------- 8 | 9 | The extension point abbreviation is tcp. 10 | 11 | Its role is to automatically introduce variables or functions you define into 12 | the template environment for use in templates (like g, request, url_for). 13 | 14 | The plugin needs to return the tcp field via register. The data type required 15 | for this field is dict, and the format is {var_name=var, func_name=func}. 16 | 17 | The Flask-PluginKit loads tcp via 18 | :meth:`~flask_pluginkit.PluginManager._context_processor_handler`, 19 | this method will detect tcp rules and specific content. 20 | 21 | Example 22 | ------- 23 | 24 | - Plugin registration for tcp 25 | 26 | .. code-block:: python 27 | 28 | whoami = 'tcp' 29 | 30 | def register(): 31 | return dict( 32 | tcp=dict(whoami=whoami, get_whoami=lambda :whoami) 33 | ) 34 | 35 | - Call in template 36 | 37 | .. code-block:: html 38 | 39 |
40 | Who are you? 41 | - {{ whoami }} 42 | - {{ get_whoami() }} 43 |
44 | -------------------------------------------------------------------------------- /docs/tutorial/tep.rst: -------------------------------------------------------------------------------- 1 | .. _tep: 2 | 3 | Template Extension Point 4 | ======================== 5 | 6 | Description 7 | ----------- 8 | 9 | The extension point abbreviation is tep. 10 | 11 | As the name implies, tep only works in the template environment. 12 | It is used to enhance or extend existing templates. 13 | It can be simple html code or complex html files. 14 | It needs to be manually called by the user in the existing template. 15 | 16 | The plugin needs to return the tep field via register. The tep data type 17 | returned is a dictionary with the format {tep_name: html_code_or_file}, 18 | which can have multiple tep_names. 19 | 20 | The Flask-PluginKit loads tep via 21 | :meth:`~flask_pluginkit.PluginManager._tep_handler`, this method will 22 | detect tep rules and specific content. The value corresponding to tep_name is 23 | recognized as a template file if it ends with **.html**, **.htm**, **.xhtml**, 24 | otherwise it is a simple html code. 25 | 26 | Although the tep_name in the tep returned by each plugin is unique, since a 27 | web application can have multiple plugins, the tep_name can contain not only 28 | the html code but also the template file for the entire web application. 29 | 30 | In use, in the existing template, all contents corresponding to tep_name are 31 | called by :meth:`~flask_pluginkit.PluginManager.emit_tep`, the template file 32 | is rendered by :func:`flask.render_template`, and the html code is rendered by 33 | :func:`flask.render_template_string`, which means that the html code can also 34 | be supported by jinja2 like a template file, jinja2 syntax, functions, macros, 35 | etc. In addition, use emit_tep to pass in the typ parameter settings to 36 | render only html code or files, and also pass in other keyword parameters 37 | as context data for rendering. 38 | 39 | .. tip:: 40 | 41 | The template file supports sorting. You need to pass 42 | :attr:`~flask_pluginkit.PluginManager.stpl` is True when 43 | initializing the :class:`~flask_pluginkit.PluginManager`. 44 | The usage is "sort-field\@template-file". 45 | 46 | .. note:: 47 | 48 | It is recommended that you create a new directory to store html files 49 | under the plugin templates. Because the Flask-PluginKit only loads the 50 | templates directory under the plugin, and does not guarantee template 51 | conflicts, the new directory can avoid conflicts with other plugin 52 | template files, which can not be referenced properly. 53 | 54 | Example 55 | ------- 56 | 57 | - Plugin registration for tep 58 | 59 | .. code-block:: python 60 | 61 | def register(): 62 | return dict( 63 | tep=dict( 64 | base_header="example/header.html", 65 | base_footer="Copyright 2019." 66 | ) 67 | ) 68 | 69 | As above, you need to create a new "templates/example" directory in the plugin 70 | directory, and put header.html into the directory. If it does not exist, the 71 | exception :class:`~flask_pluginkit.exceptions.TemplateNotFound` 72 | will be thrown. 73 | 74 | - Call in template 75 | 76 | In the existing template, assume that the following file named base.html is 77 | the base template, user need to manually call 78 | :meth:`~flask_pluginkit.PluginManager.emit_tep`, can pass additional data: 79 | 80 | .. code-block:: html 81 | 82 | 83 | 84 | {{ emit_tep("base_header", extra=dict(a=1, b=True, c=[])) }} 85 | 86 | 87 | {{ emit_tep("base_footer") }} 88 | 89 | 90 | 91 | -------------------------------------------------------------------------------- /docs/tutorial/third-party-plugin.rst: -------------------------------------------------------------------------------- 1 | Third party plugin 2 | ================== 3 | 4 | Description 5 | ----------- 6 | 7 | Third-party plugins are non-program subdirectories, local modules from 8 | installations such as ``pip`` or ``easy_install``. 9 | 10 | The third-party plugin is easy to use, the program can not put the plugin 11 | code into the subdirectory, just use `pip install` to install to the local 12 | machine, and then pass the 13 | :attr:`~flask_pluginkit.PluginManager.plugin_packages` parameter when the 14 | :class:`~flask_pluginkit.PluginManager` is initialized. 15 | 16 | This means that anyone can write a package and publish it to pypi, and 17 | the user writes **requirements.txt** and installs the dependent plugin, 18 | which is called in the initialization, and almost no need to worry about 19 | subsequent third-party plugin upgrades. 20 | 21 | Local Plugin 22 | ------------ 23 | 24 | It's a package under the web application, which is part of the web application, 25 | and the plugin developer is the user, more see :ref:`core-plugin-structure`. 26 | 27 | How to develop plugins? 28 | ----------------------- 29 | 30 | The local plugin only needs the first step, and the third party plugin needs 31 | to write ``setup.py``, which requires the next few steps. 32 | 33 | 1. First create a package, the metadata and register functions should be 34 | written in ``__init__.py``, the core code can also be written in this 35 | file, of course, the recommended approach is to separate the module. 36 | 37 | 2. The first step is actually the process of writing local plugins. 38 | In this step, you need to write `setup.py`, so that local plugins 39 | can be (published in pypi, optionally) used by others through pip: 40 | 41 | .. code-block:: python 42 | 43 | from setuptools import setup 44 | setup( 45 | name='flask_pluginkit_demo', 46 | packages=['flask_pluginkit_demo',], 47 | include_package_data=True, 48 | .. 49 | ) 50 | 51 | 3. If your plugin contains template directory or static directory, you need 52 | to write an additional manifest file ``MANIFEST.in``: 53 | 54 | .. code-block:: text 55 | 56 | recursive-include flask_pluginkit_demo/templates * 57 | recursive-include flask_pluginkit_demo/static * 58 | 59 | 4. Testing, Release 60 | 61 | The modules required by the following commands can be installed like this: 62 | 63 | .. code-block:: bash 64 | 65 | $ pip install -U pip twine wheel setuptools 66 | 67 | 4.1 Use ``pip install .`` to install to the local environment and 68 | test the plugin functionality. 69 | 70 | 4.2 If the plugin is as expected, it can be packaged and the command is: 71 | ``python setup.py sdist bdist_wheel``, more parameters to adjust themselves. 72 | 73 | 4.3 Before the official release, you can post to test.pypi.org, which is 74 | the official pypi test site. The package inside will not be used easily. 75 | The command is: 76 | ``twine upload --repository-url https://test.pypi.org/legacy/ dist/*`` 77 | 78 | 4.4 The test station can look at the interface description and so on 79 | whether it meets the requirements of the heart, and publish it to the 80 | official station without problems, pypi.org, the command is: 81 | ``twine upload dist/*`` 82 | 83 | 5. `Third-party example `_ 84 | 85 | -------------------------------------------------------------------------------- /docs/tutorial/vep.rst: -------------------------------------------------------------------------------- 1 | .. _vep: 2 | 3 | View function Extension Point 4 | ============================= 5 | 6 | Description 7 | ----------- 8 | 9 | The extension point abbreviation is vep. 10 | 11 | Vep is a simple way for plugins to directly add routing functions, 12 | because in previous versions, routing was only allowed through blueprints. 13 | 14 | The plugin needs to return the vep field via register. The returned vep 15 | data type can be dict, list, tuple. 16 | 17 | The dict format is {rule:, view_func:}, which means that a single route 18 | is added. The dict content is the parameter of 19 | :meth:`~flask.Flask.add_url_rule`, reference document `add_url_rule`_, 20 | a common example is: 21 | 22 | .. code-block:: python 23 | 24 | def vep(): 25 | return "vep" 26 | 27 | dict(rule="/vep", view_func=vep, endpoint="vep", methods=["GET", "POST"]) 28 | 29 | Other types are represented as multiple routes, 30 | and multiple dict data of the above type can be nested. 31 | 32 | The Flask-PluginKit loads vep via 33 | :meth:`~flask_pluginkit.PluginManager._vep_handler`, this method will 34 | detect vep rules and specific content. 35 | 36 | .. _add_url_rule: http://flask.palletsprojects.com/api/#flask.Flask.add_url_rule 37 | 38 | .. _vep-on-blueprint: 39 | 40 | vep on blueprint 41 | ---------------- 42 | 43 | The vep allows to be set on the blueprint, format: {_blueprint: blueprint-name, rule:/path, view_func:xx}. 44 | 45 | Require blueprint to exist, otherwise raise :class:`~flask_pluginkit.exceptions.PEPError` 46 | 47 | .. versionadded:: 3.6.0 48 | 49 | Example 50 | ------- 51 | 52 | - Plugin registration for vep 53 | 54 | .. code-block:: python 55 | 56 | def uploaded_file(filename): 57 | return send_from_directory(app.config['UPLOAD_FOLDER'], filename) 58 | 59 | def upload_file(): 60 | if request.method == 'POST': 61 | # save post file 62 | return redirect(url_for('uploaded_file', filename=filename)) 63 | else: 64 | return ''' 65 |
66 | 67 | 68 |
69 | ''' 70 | 71 | def bvep(): 72 | return "vep on blueprint" 73 | 74 | def register(): 75 | return dict( 76 | vep = [ 77 | dict( 78 | rule="/uploads/", 79 | view_func=uploaded_file 80 | ), 81 | dict( 82 | rule="/upload", 83 | view_func=upload_file, 84 | methods=["GET", "POST"] 85 | ), 86 | # Suppose a blueprint named test, 87 | # and the endpoint will be test.bvep(endpoint set it) 88 | dict( 89 | _blueprint="test", 90 | rule="/vep-on-blueprint", 91 | view_func=bvep, 92 | ) 93 | ] 94 | ) 95 | 96 | - User Access 97 | 98 | Access /upload display form, access the uploaded file via /uploads/filename. 99 | -------------------------------------------------------------------------------- /examples/fulldemo/README.md: -------------------------------------------------------------------------------- 1 | ### Example 2 | 3 | The demo plugin in this example (located in plugins/local_demo) basically contains all the feature points currently provided by Flask-PluginKit. 4 | 5 | #### Installation 6 | 7 | 1. Installation dependency package 8 | 9 | ```bash 10 | $ pip install -r requirements.txt 11 | ``` 12 | 13 | 2. Run 14 | 15 | ```bash 16 | FLASK_ENV=development FLASK_APP=app.py flask run --no-reload 17 | ``` 18 | 19 | 3. The page is probably like this: 20 | 21 | ![](demo.png) 22 | -------------------------------------------------------------------------------- /examples/fulldemo/app.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | from flask import Flask, render_template_string 4 | from flask_pluginkit import PluginManager, blueprint 5 | 6 | app = Flask(__name__) 7 | app.config.update( 8 | PLUGINKIT_AUTH_AID_METHOD="IP", PLUGINKIT_AUTH_IP_WHITELIST=["127.0.0.1"] 9 | ) 10 | pm = PluginManager( 11 | app, 12 | plugin_packages=["flask_pluginkit_demo"], 13 | pluginkit_config=dict(whoami="localdemo_config"), 14 | ) 15 | app.register_blueprint(blueprint, url_prefix="/pluginmanager") 16 | 17 | 18 | @app.route("/") 19 | def index(): 20 | return render_template_string( 21 | """\ 22 | 23 | 24 | {{ emit_assets('localdemo','css/style.css') }} 25 | 26 | 27 | 28 | {{ emit_tep('code') }} 29 | {{ emit_tep('html') }} 30 | 31 |
32 | 33 | {{ emit_assets('localdemo','js/hello.js') }} 34 | {{ emit_config('whoami') }} 35 | 36 | 37 | """ 38 | ) 39 | 40 | 41 | if __name__ == "__main__": 42 | app.run(debug=True, use_reloader=False) 43 | -------------------------------------------------------------------------------- /examples/fulldemo/demo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/staugur/Flask-PluginKit/79316fc1033fde481049d642b2b59dfbaf8d5c34/examples/fulldemo/demo.png -------------------------------------------------------------------------------- /examples/fulldemo/plugins/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/staugur/Flask-PluginKit/79316fc1033fde481049d642b2b59dfbaf8d5c34/examples/fulldemo/plugins/__init__.py -------------------------------------------------------------------------------- /examples/fulldemo/plugins/local_demo/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | local_demo 4 | ~~~~~~~~~~ 5 | 6 | This is a local plugin demo. 7 | 8 | :copyright: (c) 2019 by staugur. 9 | :license: BSD, see LICENSE for more details. 10 | """ 11 | 12 | from __future__ import absolute_import 13 | import time 14 | from flask import Blueprint, jsonify, request 15 | from flask_pluginkit import LocalStorage 16 | 17 | __plugin_name__ = "localdemo" 18 | __author__ = "Mr.tao " 19 | __version__ = "0.1.0" 20 | 21 | bp = Blueprint(__plugin_name__, __plugin_name__) 22 | 23 | 24 | @bp.route("/") 25 | def index(): 26 | return jsonify(dict(hello="localdemo")) 27 | 28 | 29 | def br(): 30 | local = LocalStorage() 31 | local.set( 32 | "nowtime", 33 | time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(time.time())), 34 | ) 35 | 36 | 37 | def view_limit(path): 38 | if request.method == "GET": 39 | return jsonify(dict(method="GET", status=200, path=path)) 40 | else: 41 | return jsonify(dict(method=request.method, status=403, path=path)) 42 | 43 | 44 | def page_not_found(error): 45 | return jsonify(dict(status=404, msg="Not Found Page")), 404 46 | 47 | 48 | def update_tcp(repeat_tcp): 49 | repeat_tcp["im"] = __plugin_name__ 50 | return repeat_tcp 51 | 52 | 53 | def register(): 54 | return { 55 | "tep": dict( 56 | code=u"

hello local-demo(from html code)

", 57 | html="localdemo/title.html", 58 | ), 59 | "hep": dict(before_request=br), 60 | "bep": dict(blueprint=bp, prefix="/localdemo"), 61 | "vep": dict( 62 | rule="/limit/", view_func=view_limit, methods=["GET", "POST"] 63 | ), 64 | "filter": [("demo_filter2", lambda x: "test-filter")], 65 | "errhandler": {404: page_not_found}, 66 | "tcp": dict(timestamp=int(time.time())), 67 | "p3": dict(repeatdemo=dict(tcp=update_tcp)), 68 | } 69 | -------------------------------------------------------------------------------- /examples/fulldemo/plugins/local_demo/static/css/style.css: -------------------------------------------------------------------------------- 1 | h5 {color:red} -------------------------------------------------------------------------------- /examples/fulldemo/plugins/local_demo/static/js/hello.js: -------------------------------------------------------------------------------- 1 | document.getElementById('demo').innerHTML = 'hello, I am from the js file of the plugin localdemo'; -------------------------------------------------------------------------------- /examples/fulldemo/plugins/local_demo/templates/localdemo/title.html: -------------------------------------------------------------------------------- 1 |

hello local-demo(from html file)

2 | -------------------------------------------------------------------------------- /examples/fulldemo/plugins/repeat_demo/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | repeat_demo 4 | ~~~~~~~~~~~ 5 | 6 | This is a repetitive demonstration of local_demo. 7 | 8 | :copyright: (c) 2019 by staugur. 9 | :license: BSD, see LICENSE for more details. 10 | """ 11 | 12 | from flask import abort, jsonify, request 13 | from flask_classful import FlaskView 14 | 15 | __plugin_name__ = "repeatdemo" 16 | __author__ = "Mr.tao " 17 | __version__ = "0.3.0" 18 | 19 | 20 | def view_abort_403(): 21 | return abort(403) 22 | 23 | 24 | def permission_deny(error): 25 | return jsonify(dict(status=403, msg="permission deny")), 403 26 | 27 | 28 | class ApiError(Exception): 29 | def __init__(self, code, message, status_code=200): 30 | super(ApiError, self).__init__() 31 | self.code = code 32 | self.message = message 33 | self.status_code = status_code 34 | 35 | def to_dict(self): 36 | rv = dict(code=self.code, msg=self.message) 37 | return rv 38 | 39 | 40 | def handle_api_error(e): 41 | response = jsonify(e.to_dict()) 42 | response.status_code = e.status_code 43 | return response 44 | 45 | 46 | def raise_api_error_view(): 47 | raise ApiError(10000, "test_err_class_handler") 48 | 49 | 50 | class ClassfulView(FlaskView): 51 | def index(self): 52 | return "test" 53 | 54 | 55 | def bvep_view(): 56 | return request.endpoint 57 | 58 | 59 | def register(): 60 | return { 61 | "cvep": dict(view_class=ClassfulView), 62 | "vep": [ 63 | dict(rule="/403", view_func=view_abort_403), 64 | dict(rule="/api_error", view_func=raise_api_error_view), 65 | dict(rule="/bvep", view_func=bvep_view, _blueprint="localdemo"), 66 | ], 67 | "filter": dict(repeat_filter=lambda x: "test-filter-repeat"), 68 | "errhandler": [ 69 | dict(error=403, handler=permission_deny), 70 | dict(error=ApiError, handler=handle_api_error), 71 | ], 72 | "tcp": dict(change_to_str=str), 73 | } 74 | -------------------------------------------------------------------------------- /examples/fulldemo/requirements.txt: -------------------------------------------------------------------------------- 1 | Flask>=3.0.0 2 | Flask-PluginKit>=3.6.0 3 | Flask-Classful>=0.16.0 4 | git+https://github.com/saintic/flask-pluginkit-demo@master -------------------------------------------------------------------------------- /examples/helloworld/app.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | from flask import Flask 4 | from flask_pluginkit import PluginManager 5 | 6 | app = Flask(__name__) 7 | pm = PluginManager(app) 8 | 9 | 10 | @app.route("/") 11 | def index(): 12 | return "hello world" 13 | -------------------------------------------------------------------------------- /examples/helloworld/plugins/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/staugur/Flask-PluginKit/79316fc1033fde481049d642b2b59dfbaf8d5c34/examples/helloworld/plugins/__init__.py -------------------------------------------------------------------------------- /examples/helloworld/plugins/helloworld/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | from flask import Blueprint, jsonify, request, redirect, url_for, make_response 4 | 5 | __plugin_name__ = "helloworld" 6 | __version__ = "0.1.0" 7 | __author__ = "staugur" 8 | 9 | 10 | bp = Blueprint("helloworld", "helloworld") 11 | 12 | 13 | @bp.route("/limit") 14 | def limit(): 15 | return jsonify(dict(status=0, message="Access Denial")) 16 | 17 | 18 | def limit_handler(): 19 | """I am running in before_request""" 20 | ip = request.headers.get("X-Real-Ip", request.remote_addr) 21 | if request.endpoint == "index" and ip == "127.0.0.1": 22 | resp = make_response(redirect(url_for("helloworld.limit"))) 23 | resp.is_return = True 24 | return resp 25 | 26 | 27 | def register(): 28 | return { 29 | "bep": dict(blueprint=bp, prefix=None), 30 | "hep": dict(before_request=limit_handler), 31 | } 32 | -------------------------------------------------------------------------------- /flask_pluginkit/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | flask_pluginkit 4 | ~~~~~~~~~~~~~~~ 5 | 6 | Load and run plugins for your Flask application 7 | 8 | :copyright: (c) 2019 by staugur. 9 | :license: BSD 3-Clause, see LICENSE for more details. 10 | """ 11 | 12 | from .pluginkit import PluginManager, push_dcp 13 | from .utils import ( 14 | LocalStorage, 15 | RedisStorage, 16 | JsonResponse, 17 | ExpiredLocalStorage, 18 | ) 19 | from ._installer import PluginInstaller 20 | from ._web import blueprint 21 | 22 | __author__ = "Hiroshi.tao " 23 | 24 | __version__ = "3.9.0" 25 | 26 | __all__ = [ 27 | "PluginManager", 28 | "LocalStorage", 29 | "RedisStorage", 30 | "JsonResponse", 31 | "PluginInstaller", 32 | "push_dcp", 33 | "blueprint", 34 | "ExpiredLocalStorage", 35 | ] 36 | -------------------------------------------------------------------------------- /flask_pluginkit/_compat.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | flask_pluginkit._compat 4 | ~~~~~~~~~~~~~~~~~~~~~~~ 5 | 6 | A module providing tools for cross-version compatibility. 7 | 8 | :copyright: (c) 2019 by staugur. 9 | :license: BSD 3-Clause, see LICENSE for more details. 10 | """ 11 | 12 | import urllib.request as urllib2 13 | from urllib.parse import urlsplit, parse_qs 14 | 15 | 16 | def iteritems(d: dict): 17 | return iter(d.items()) 18 | 19 | 20 | def itervalues(d: dict): 21 | return iter(d.values()) 22 | 23 | 24 | text_type = str 25 | string_types = (str,) 26 | integer_types = (int,) 27 | -------------------------------------------------------------------------------- /flask_pluginkit/exceptions.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | flask_pluginkit.exceptions 4 | ~~~~~~~~~~~~~~~~~~~~~~~~~~ 5 | 6 | Exception Classes 7 | 8 | :copyright: (c) 2019 by staugur. 9 | :license: BSD 3-Clause, see LICENSE for more details. 10 | """ 11 | 12 | 13 | class PluginError(Exception): 14 | pass 15 | 16 | 17 | class ParamError(Exception): 18 | pass 19 | 20 | 21 | class VersionError(PluginError): 22 | pass 23 | 24 | 25 | class PEPError(PluginError): 26 | pass 27 | 28 | 29 | class TemplateNotFound(PluginError): 30 | pass 31 | 32 | 33 | class TarError(PluginError): 34 | pass 35 | 36 | 37 | class ZipError(PluginError): 38 | pass 39 | 40 | 41 | class InstallError(PluginError): 42 | pass 43 | 44 | 45 | class NotCallableError(PluginError): 46 | pass 47 | -------------------------------------------------------------------------------- /flask_pluginkit/static/translations.ini: -------------------------------------------------------------------------------- 1 | [en] 2 | reload = Reload Application 3 | reload.title = No interaction directly overloading your web app! 4 | download = Download Remote Plugin 5 | upload = Upload Local Plugin 6 | version = Version 7 | plugins_count = Plugins Count 8 | packageName = Name 9 | packageName.title = Suspension display package name 10 | submit = Submit 11 | Description = Description 12 | Author = Author 13 | URL = URL 14 | LICENSE = LICENSE 15 | State = State 16 | Action = Action 17 | install = Install Package 18 | 19 | [zh-cn] 20 | reload = 重载应用 21 | reload.title = 没有交互直接重载您的Web应用程序! 22 | download = 下载远程插件 23 | upload = 上传本地插件 24 | version = 版本 25 | plugins_count = 插件数 26 | packageName = 插件名 27 | packageName.title = 悬浮显示包名 28 | submit = 提交 29 | Description = 描述 30 | Author = 作者 31 | URL = 主页 32 | LICENSE = 许可证 33 | State = 状态 34 | Action = 操作 35 | install = 安装模块 -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | from re import compile 4 | from io import open as iopen 5 | from ast import literal_eval 6 | from setuptools import setup 7 | 8 | 9 | def _get_version(): 10 | version_re = compile(r"__version__\s+=\s+(.*)") 11 | 12 | with open("flask_pluginkit/__init__.py", "rb") as fh: 13 | version = literal_eval(version_re.search(fh.read().decode("utf-8")).group(1)) 14 | 15 | return str(version) 16 | 17 | 18 | def _get_author(): 19 | author_re = compile(r"__author__\s+=\s+(.*)") 20 | mail_re = compile(r"(.*)\s<(.*)>") 21 | 22 | with open("flask_pluginkit/__init__.py", "rb") as fh: 23 | author = literal_eval(author_re.search(fh.read().decode("utf-8")).group(1)) 24 | 25 | return (mail_re.search(author).group(1), mail_re.search(author).group(2)) 26 | 27 | 28 | def _get_readme(): 29 | with iopen("README.rst", "rt", encoding="utf8") as f: 30 | return f.read() 31 | 32 | 33 | version = _get_version() 34 | (author, email) = _get_author() 35 | gh = "https://github.com/staugur/Flask-PluginKit" 36 | setup( 37 | name="Flask-PluginKit", 38 | version=version, 39 | url=gh, 40 | download_url="{gh}/releases/tag/v{v}".format(gh=gh, v=version), 41 | project_urls={ 42 | "Code": gh, 43 | "Issue tracker": "{gh}/issues".format(gh=gh), 44 | "Documentation": "https://flask-pluginkit.rtfd.vip", 45 | }, 46 | license="BSD 3-Clause", 47 | author=author, 48 | author_email=email, 49 | keywords="flask plugin", 50 | description="Load and run plugins for your Flask application", 51 | long_description=_get_readme(), 52 | packages=["flask_pluginkit"], 53 | include_package_data=True, 54 | zip_safe=False, 55 | python_requires=">=3.8", 56 | install_requires=["Flask>=3.0.0", "semver>=3.0.0"], 57 | extras_require={"redis": ["redis>=5.0.0"]}, 58 | classifiers=[ 59 | "Development Status :: 4 - Beta", 60 | "Framework :: Flask", 61 | "Environment :: Web Environment", 62 | "Intended Audience :: Developers", 63 | "License :: OSI Approved :: BSD License", 64 | "Operating System :: OS Independent", 65 | "Programming Language :: Python :: 3", 66 | "Programming Language :: Python :: 3.8", 67 | "Programming Language :: Python :: 3.9", 68 | "Programming Language :: Python :: 3.10", 69 | "Programming Language :: Python :: 3.11", 70 | "Programming Language :: Python :: Implementation :: CPython", 71 | "Topic :: Internet :: WWW/HTTP :: Dynamic Content", 72 | "Topic :: Software Development :: Libraries :: Python Modules", 73 | ], 74 | ) 75 | -------------------------------------------------------------------------------- /tests/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf8 -*- 2 | # test examples -------------------------------------------------------------------------------- /tests/test_installer.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | import os 4 | import unittest 5 | from flask_pluginkit import PluginInstaller 6 | from flask_pluginkit.utils import check_url 7 | 8 | 9 | class PITest(unittest.TestCase): 10 | def setUp(self): 11 | self.pi = PluginInstaller(".") 12 | 13 | def test_addPlugin(self): 14 | urls = [ 15 | "https://static.saintic.com/download/thirdApp/JQueryAvatarPreviewCut.zip", 16 | "https://static.saintic.com/download/thirdApp/syncthing-linux-amd64-v0.14.45.tar.gz", 17 | "https://codeload.github.com/saintic/jwt/zip/master", 18 | "https://codeload.github.com/staugur/Flask-PluginKit/tar.gz/v3.0.0", 19 | ] 20 | pkg = os.path.join(self.pi.plugin_abspath, "jwt-master") 21 | # local 22 | res = self.pi.addPlugin( 23 | method="local", filepath="/tmp/1.tar", remove=True 24 | ) 25 | self.assertEqual(res["code"], 1) 26 | res = self.pi.addPlugin( 27 | method="local", filepath="/tmp/1.tbz", remove=True 28 | ) 29 | self.assertEqual(res["code"], 1) 30 | res = self.pi.addPlugin( 31 | method="local", filepath="/tmp/1.tgz", remove=True 32 | ) 33 | self.assertEqual(res["code"], 1) 34 | res = self.pi.addPlugin( 35 | method="local", filepath="/tmp/1.zip", remove=True 36 | ) 37 | self.assertEqual(res["code"], 1) 38 | # remote 39 | res = self.pi.addPlugin(url=urls[2]) 40 | self.assertIsInstance(res, dict) 41 | self.assertEqual(res["code"], 0) 42 | self.assertTrue(os.path.isdir(pkg)) 43 | self.assertTrue(check_url(urls[0])) 44 | self.assertTrue(check_url("http://192.168.1.1/j.zip")) 45 | self.assertFalse(check_url("j.zip")) 46 | self.assertTrue(self.pi._PluginInstaller__isValidFilename("j.zip")) 47 | self.assertTrue(self.pi._PluginInstaller__isValidFilename("j.tar.gz")) 48 | self.assertFalse(self.pi._PluginInstaller__isValidFilename("j.gz")) 49 | self.assertFalse(self.pi._PluginInstaller__isValidFilename("j.rar")) 50 | res = self.pi.removePlugin(pkg) 51 | self.assertIsInstance(res, dict) 52 | self.assertEqual(res["code"], 0) 53 | self.assertFalse(os.path.isdir(pkg)) 54 | # remote 55 | pkg = os.path.join(self.pi.plugin_abspath, "Flask-PluginKit-3.0.0") 56 | res = self.pi.addPlugin(url=urls[3]) 57 | self.assertEqual(res["code"], 0) 58 | res = self.pi.removePlugin(pkg) 59 | self.assertEqual(res["code"], 0) 60 | self.assertFalse(os.path.isdir(pkg)) 61 | 62 | 63 | if __name__ == "__main__" and not os.getenv("TRAVIS"): 64 | unittest.main() 65 | -------------------------------------------------------------------------------- /tests/test_pm.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | import os 4 | import sys 5 | import time 6 | import json 7 | import unittest 8 | from flask import Flask, request, g 9 | from markupsafe import Markup 10 | from flask_pluginkit import ( 11 | PluginManager, 12 | LocalStorage, 13 | push_dcp, 14 | blueprint, 15 | ) 16 | from flask_pluginkit.exceptions import PluginError, NotCallableError 17 | from flask_pluginkit._compat import iteritems 18 | from jinja2 import ChoiceLoader 19 | 20 | EXAMPLE_DIR = os.path.join( 21 | os.path.dirname(os.path.dirname(os.path.abspath(__file__))), 22 | "examples/fulldemo", 23 | ) 24 | sys.path.append(EXAMPLE_DIR) 25 | 26 | 27 | #: app1 with flask-pluginkit 28 | app1 = Flask("app1") 29 | app1.config.update( 30 | TESTING=True, 31 | PLUGINKIT_TEST=True, 32 | PLUGINKIT_AUTH_METHOD="FUNC", 33 | PLUGINKIT_AUTH_FUNC=lambda: True, 34 | ) 35 | PluginManager(app1) 36 | app1.register_blueprint(blueprint) 37 | 38 | #: app2 without flask-pluginkit 39 | app2 = Flask("app2") 40 | app2.config["TESTING"] = True 41 | app2.register_blueprint(blueprint) 42 | 43 | #: app4 with flask-pluginkit full example 44 | from app import app as app4 45 | 46 | app4.testing = True 47 | 48 | 49 | class PMTest(unittest.TestCase): 50 | def setUp(self): 51 | self.app1_pm = app1.extensions.get("pluginkit") 52 | self.app4_pm = app4.extensions.get("pluginkit") 53 | 54 | def test_extself(self): 55 | self.assertIsInstance(self.app1_pm, PluginManager) 56 | self.assertIsInstance(self.app4_pm, PluginManager) 57 | self.assertIsInstance(app1.jinja_loader, ChoiceLoader) 58 | 59 | def test_extself_params(self): 60 | self.assertFalse(os.path.isdir(self.app1_pm.plugins_abspath)) 61 | self.assertTrue(os.path.isdir(self.app4_pm.plugins_abspath)) 62 | self.assertIsInstance(self.app1_pm.plugin_packages, (tuple, list)) 63 | self.assertIsInstance(self.app4_pm.pluginkit_config, dict) 64 | 65 | def test_extself_raise(self): 66 | with self.assertRaises(PluginError) as cm: 67 | PluginManager(Flask(__name__), plugin_packages=123) 68 | PluginManager(Flask(__name__), plugin_packages=["non_ppk"]) 69 | 70 | def test_extself_tpl_global(self): 71 | self.assertIn("emit_tep", app1.jinja_env.globals) 72 | self.assertIn("emit_config", app4.jinja_env.globals) 73 | 74 | def test_config(self): 75 | self.assertTrue("emit_config" in app1.jinja_env.globals) 76 | with app1.test_request_context(): 77 | self.assertTrue(self.app1_pm.emit_config("PLUGINKIT_TEST")) 78 | 79 | def test_hook(self): 80 | with app4.test_request_context("/"): 81 | app4.preprocess_request() 82 | local = LocalStorage() 83 | self.assertTrue("nowtime" in local.list) 84 | nowhour = time.strftime("%Y-%m-%d %H:", time.localtime(time.time())) 85 | self.assertIn(nowhour, local.get("nowtime")) 86 | del local["nowtime"] 87 | 88 | def test_example(self): 89 | with app4.test_client() as c: 90 | rv = c.get("/") 91 | data = rv.data.decode("utf-8") 92 | self.assertTrue("css/style.css" in data) 93 | self.assertTrue("js/hello.js" in data) 94 | self.assertTrue(self.app4_pm.pluginkit_config["whoami"] in data) 95 | self.assertEqual(len(app4.blueprints), 3) 96 | self.assertEqual(len(self.app4_pm.get_all_plugins), 3) 97 | self.assertEqual(len(self.app4_pm.get_enabled_beps), 2) 98 | self.assertIn(self.app4_pm.static_endpoint, app4.view_functions) 99 | with app4.test_request_context(): 100 | link = self.app4_pm.emit_assets("localdemo", "css/style.css") 101 | self.assertTrue("stylesheet" in link) 102 | self.assertTrue("/css/style.css" in link) 103 | 104 | def test_vep(self): 105 | self.assertIn("localdemo.bvep_view", app4.view_functions) 106 | self.assertIn("view_limit", app4.view_functions) 107 | view_limit_data = self.app4_pm.get_enabled_veps[0] 108 | self.assertEqual(5, len(view_limit_data)) 109 | with app4.test_request_context("/limit/test"): 110 | self.assertEqual("view_limit", request.endpoint) 111 | 112 | def test_cvep(self): 113 | self.assertIn("ClassfulView:index", app4.view_functions) 114 | self.assertEqual(2, len(self.app4_pm.get_enabled_cveps[0])) 115 | with app4.test_request_context("/classful/"): 116 | self.assertEqual("ClassfulView:index", request.endpoint) 117 | 118 | def test_filter(self): 119 | self.assertIn("repeat_filter", app4.jinja_env.filters) 120 | self.assertIn("demo_filter2", app4.jinja_env.filters) 121 | self.assertEqual( 122 | "test-filter-repeat", app4.jinja_env.filters["repeat_filter"]("x") 123 | ) 124 | self.assertEqual("test-filter", app4.jinja_env.filters["demo_filter2"]("x")) 125 | 126 | def test_errhandler(self): 127 | ehs = app4.error_handler_spec[None] 128 | self.assertIsInstance(ehs, dict) 129 | self.assertIn(403, ehs) 130 | self.assertIn(404, ehs) 131 | with app4.test_client() as c: 132 | resp404 = c.get("/404") 133 | self.assertEqual(404, resp404.status_code) 134 | self.assertIn(b"Not Found Page", resp404.data) 135 | resp403 = c.get("/403") 136 | self.assertEqual(403, resp403.status_code) 137 | self.assertIn(b"permission deny", resp403.data) 138 | #: raise apierror 139 | respapierror = c.get("/api_error") 140 | if isinstance(respapierror.data, bytes): 141 | data = json.loads(respapierror.data.decode("utf-8")) 142 | else: 143 | data = json.loads(respapierror.data) 144 | self.assertIsInstance(data, dict) 145 | self.assertIn("msg", data) 146 | self.assertEqual("test_err_class_handler", data["msg"]) 147 | self.assertEqual(10000, data["code"]) 148 | 149 | def test_tcp(self): 150 | context = { 151 | k: v 152 | for i in app4.template_context_processors[None] 153 | for k, v in iteritems(i()) 154 | } 155 | self.assertIsInstance(context, dict) 156 | self.assertIn("timestamp", context) 157 | self.assertIn("change_to_str", context) 158 | self.assertTrue(context["change_to_str"], str) 159 | # test p3 160 | self.assertIn("im", context) 161 | 162 | """ 163 | def test_dcp(self): 164 | def callback(): 165 | return "test" 166 | 167 | self.assertRaises(AttributeError, push_dcp, "raise", callback) 168 | with app4.app_context(): 169 | push_dcp("test", callback) 170 | result = [f() for f in self.app4_pm._dcp_manager.list["test"]] 171 | ft = self.app4_pm._dcp_manager.emit("test") 172 | self.assertRaises(PluginError, push_dcp, ["raise"], callback) 173 | self.assertRaises(NotCallableError, push_dcp, "raise", "abc") 174 | self.assertIsInstance(ft, Markup) 175 | self.assertEqual(ft, Markup("test")) 176 | self.assertEqual(result, ["test"]) 177 | """ 178 | 179 | def test_web(self): 180 | with app1.test_client() as c1: 181 | data = c1.post("/api").data 182 | data = data.decode("utf-8") 183 | res = json.loads(data) 184 | self.assertIsInstance(res, dict) 185 | self.assertEqual(res["code"], 1) 186 | with app2.test_client() as c2: 187 | res = c2.post("/api").data 188 | self.assertIn(b"Authentication failed", res) 189 | 190 | with app1.test_request_context("/"): 191 | app1.preprocess_request() 192 | self.assertTrue(hasattr(g, "pluginkit")) 193 | self.assertIsInstance(g.pluginkit.get_all_plugins, list) 194 | with app2.test_request_context("/"): 195 | app2.preprocess_request() 196 | self.assertFalse(hasattr(g, "pluginkit")) 197 | 198 | 199 | if __name__ == "__main__" and not os.getenv("TRAVIS"): 200 | unittest.main() 201 | -------------------------------------------------------------------------------- /tests/test_utils.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | import unittest 4 | from os import getenv 5 | from flask_pluginkit.utils import ( 6 | isValidSemver, 7 | sortedSemver, 8 | isValidPrefix, 9 | LocalStorage, 10 | RedisStorage, 11 | BaseStorage, 12 | allowed_uploaded_plugin_suffix, 13 | Attribution, 14 | check_url, 15 | DcpManager, 16 | ) 17 | from markupsafe import Markup 18 | 19 | 20 | class UtilsTest(unittest.TestCase): 21 | def test_isVer(self): 22 | self.assertTrue(isValidSemver("0.0.1")) 23 | self.assertTrue(isValidSemver("1.1.1-beta")) 24 | self.assertTrue(isValidSemver("1.1.1-beta+compile10")) 25 | self.assertFalse(isValidSemver("1.0.1.0")) 26 | self.assertFalse(isValidSemver("v2.19.10")) 27 | 28 | def test_sortVer(self): 29 | self.assertEqual( 30 | sortedSemver(["0.0.3", "0.1.1", "0.0.2"]), 31 | ["0.0.2", "0.0.3", "0.1.1"], 32 | ) 33 | self.assertRaises(TypeError, sortedSemver, "raise") 34 | self.assertRaises(ValueError, sortedSemver, ["0.0.1", "v0.0.2"]) 35 | 36 | def test_prefix(self): 37 | self.assertTrue(isValidPrefix("/abc")) 38 | self.assertTrue(isValidPrefix("/1")) 39 | self.assertTrue(isValidPrefix("/-")) 40 | self.assertTrue(isValidPrefix("/!@")) 41 | self.assertTrue(isValidPrefix("/=")) 42 | self.assertTrue(isValidPrefix("/#")) 43 | self.assertTrue(isValidPrefix(None, allow_none=True)) 44 | self.assertFalse(isValidPrefix(None)) 45 | self.assertFalse(isValidPrefix("None")) 46 | self.assertFalse(isValidPrefix("abc")) 47 | self.assertFalse(isValidPrefix("1")) 48 | self.assertFalse(isValidPrefix("-")) 49 | self.assertFalse(isValidPrefix("/")) 50 | self.assertFalse(isValidPrefix("//")) 51 | self.assertFalse(isValidPrefix("//abc")) 52 | self.assertFalse(isValidPrefix("/1/")) 53 | self.assertFalse(isValidPrefix("/ ")) 54 | self.assertFalse(isValidPrefix(" ")) 55 | 56 | def test_localstorage(self): 57 | storage = LocalStorage() 58 | self.assertIsInstance(storage, BaseStorage) 59 | data = dict(a=1, b=2) 60 | storage.set("test", data) 61 | newData = storage.get("test") 62 | self.assertIsInstance(newData, dict) 63 | self.assertEqual(newData, data) 64 | self.assertEqual(len(storage), len(storage.list)) 65 | # test setitem getitem 66 | storage["test"] = "hello" 67 | self.assertEqual("hello", storage["test"]) 68 | # Invalid, LocalStorage did not implement this method 69 | del storage["test"] 70 | self.assertIsNone(storage.get("test")) 71 | self.assertIsNone(storage["_non_existent_key_"]) 72 | self.assertEqual(1, storage.get("_non_existent_key_", 1)) 73 | self.assertEqual(0, len(storage)) 74 | 75 | def test_redisstorage(self): 76 | """Run this test when it detects that the environment variable 77 | FLASK_PLUGINKIT_TEST_REDISURL is valid 78 | """ 79 | redis_url = getenv("FLASK_PLUGINKIT_TEST_REDISURL") 80 | if redis_url: 81 | storage = RedisStorage(redis_url=redis_url) 82 | self.assertIsInstance(storage, BaseStorage) 83 | storage["test"] = 1 84 | self.assertEqual(storage["test"], 1) 85 | self.assertEqual(len(storage), len(storage.list)) 86 | self.assertEqual(len(storage), storage._db.hlen(storage.index)) 87 | self.assertIsNone(storage["_non_existent_key_"]) 88 | self.assertEqual(1, storage.get("_non_existent_key_", 1)) 89 | # RedisStorage allow remove 90 | del storage["test"] 91 | self.assertIsNone(storage["test"]) 92 | self.assertEqual(0, len(storage)) 93 | 94 | def test_basestorage(self): 95 | class MyStorage(BaseStorage): 96 | pass 97 | 98 | ms = MyStorage() 99 | with self.assertRaises(AttributeError): 100 | ms.get("test") 101 | 102 | def test_checkurl(self): 103 | self.assertTrue(check_url("http://127.0.0.1")) 104 | self.assertTrue(check_url("http://localhost:5000")) 105 | self.assertTrue(check_url("https://abc.com")) 106 | self.assertTrue(check_url("https://abc.com:8443")) 107 | self.assertFalse(check_url("ftp://192.168.1.2")) 108 | self.assertFalse(check_url("rsync://192.168.1.2")) 109 | self.assertFalse(check_url("192.168.1.2")) 110 | self.assertFalse(check_url("example.com")) 111 | self.assertFalse(check_url("localhost")) 112 | self.assertFalse(check_url("127.0.0.1:8000")) 113 | self.assertFalse(check_url("://127.0.0.1/hello-world")) 114 | 115 | def test_uploadsuffix(self): 116 | z = "https://codeload.github.com/saintic/flask-pluginkit-demo/zip/master" 117 | self.assertTrue(allowed_uploaded_plugin_suffix("1.tar.gz")) 118 | self.assertTrue(allowed_uploaded_plugin_suffix("abc.tgz")) 119 | self.assertTrue(allowed_uploaded_plugin_suffix("demo-1.0.0.zip")) 120 | self.assertFalse(allowed_uploaded_plugin_suffix(z)) 121 | self.assertFalse(allowed_uploaded_plugin_suffix("hello.tar.bz2")) 122 | self.assertFalse(allowed_uploaded_plugin_suffix("test.png")) 123 | 124 | def test_attrclass(self): 125 | d = Attribution(dict(a=1, b=2, c=3)) 126 | with self.assertRaises(AttributeError): 127 | d.d 128 | self.assertEqual(d.a, 1) 129 | self.assertIsInstance(d, dict) 130 | 131 | def test_dcp(self): 132 | dcp = DcpManager() 133 | self.assertIsInstance(dcp.list, dict) 134 | 135 | f = lambda: "test" 136 | 137 | dcp.push("f", f) 138 | self.assertEqual(len(dcp.list), 1) 139 | self.assertEqual(dcp.emit("f"), Markup("test")) 140 | self.assertTrue(dcp.remove("f", f)) 141 | self.assertEqual(len(dcp.list), 1) 142 | 143 | 144 | if __name__ == "__main__": 145 | unittest.main() 146 | --------------------------------------------------------------------------------