├── .github └── FUNDING.yml ├── .gitignore ├── LICENSE ├── README.md ├── example ├── app.py ├── requirements.txt └── templates │ └── index.html ├── flask_toastr.py ├── setup.py └── tests └── test_flask_toastr.py /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2] 4 | patreon: # Replace with a single Patreon username 5 | open_collective: # Replace with a single Open Collective username 6 | ko_fi: # Replace with a single Ko-fi username 7 | tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel 8 | community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry 9 | liberapay: # Replace with a single Liberapay username 10 | issuehunt: # Replace with a single IssueHunt username 11 | otechie: # Replace with a single Otechie username 12 | custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2'] 13 | custom: ['https://www.paypal.com/donate/?cmd=_donations&business=5QJ62BNMRC75W¤cy_code=USD&source=url'] 14 | -------------------------------------------------------------------------------- /.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 | build/ 12 | develop-eggs/ 13 | dist/ 14 | downloads/ 15 | eggs/ 16 | .eggs/ 17 | lib/ 18 | lib64/ 19 | parts/ 20 | sdist/ 21 | var/ 22 | wheels/ 23 | *.egg-info/ 24 | .installed.cfg 25 | *.egg 26 | MANIFEST 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 | .pytest_cache/ 49 | 50 | # Translations 51 | *.mo 52 | *.pot 53 | 54 | # Django stuff: 55 | *.log 56 | local_settings.py 57 | db.sqlite3 58 | 59 | # Flask stuff: 60 | instance/ 61 | .webassets-cache 62 | 63 | # Scrapy stuff: 64 | .scrapy 65 | 66 | # Sphinx documentation 67 | docs/_build/ 68 | 69 | # PyBuilder 70 | target/ 71 | 72 | # Jupyter Notebook 73 | .ipynb_checkpoints 74 | 75 | # pyenv 76 | .python-version 77 | 78 | # celery beat schedule file 79 | celerybeat-schedule 80 | 81 | # SageMath parsed files 82 | *.sage.py 83 | 84 | # Environments 85 | .env 86 | .venv 87 | env/ 88 | venv/ 89 | ENV/ 90 | env.bak/ 91 | venv.bak/ 92 | .vscode 93 | 94 | # Spyder project settings 95 | .spyderproject 96 | .spyproject 97 | 98 | # Rope project settings 99 | .ropeproject 100 | 101 | # mkdocs documentation 102 | /site 103 | 104 | # mypy 105 | .mypy_cache/ 106 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Wilton Rodrigues 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Flask-Toastr 2 | ============ 3 | 4 | [![](https://img.shields.io/badge/python-3.4+-blue.svg)](https://www.python.org/download/releases/3.4.0/) [![](https://img.shields.io/badge/python-2.7+-blue.svg)](https://www.python.org/download/releases/2.7.2/) [![](https://img.shields.io/github/license/ResidentMario/missingno.svg)](https://github.com/wiltonsr/Flask-Toastr/blob/master/README.md) 5 | 6 | Showing Flask's flash non-blocking notifications in templates using [toastr](https://github.com/CodeSeven/toastr). 7 | 8 | Donate to help keep this project maintained. 9 | 10 | Donate with PayPal button 11 | 12 | Quick Start 13 | ----------- 14 | 15 | Step 1: Initialize the extension: 16 | 17 | from flask_toastr import Toastr 18 | toastr = Toastr(app) 19 | 20 | Step 2: In your `` and bottom of ``sections of your base template add the following code: 21 | 22 | ```html 23 | 24 | 25 | {{ toastr.include_jquery() }} 26 | {{ toastr.include_toastr_css() }} 27 | {{ toastr.message() }} 28 | 29 | 30 | {{ toastr.include_toastr_js() }} 31 | 32 | 33 | ``` 34 | 35 | This extension also supports the [Flask application factory pattern](http://flask.pocoo.org/docs/latest/patterns/appfactories/) by allowing you to create a Toastr object and then separately initialize it for an app: 36 | 37 | ```python 38 | toastr = Toastr() 39 | 40 | def create_app(config): 41 | app = Flask(__name__) 42 | app.config.from_object(config) 43 | # initialize toastr on the app within create_app() 44 | toastr.init_app(app) 45 | 46 | app = create_app(prod_config) 47 | ``` 48 | 49 | Note that jQuery is required. If you are already including it on your own then you can remove the `include_jquery()` line. Secure HTTP is used if the request under which these are executed is secure. 50 | 51 | The `include_jquery()`, `include_toastr_js()` and `include_toastr_css()` methods take some optional arguments. If you pass a `version` argument to any of these calls, then the requested version will be loaded from the default CDN. In addition, it is also possible to pass `js_filename` and `css_filename` to `include_toastr_js()` and `include_toastr_css()`, respectively. 52 | 53 | Step 3: Use the `flash()` method with or without category in your views. For example: 54 | 55 | ```python 56 | @app.route('/') 57 | def index(): 58 | flash("All OK") 59 | flash("All OK", 'success') 60 | flash("All Normal", 'info') 61 | flash("Not So OK", 'error') 62 | flash("So So", 'warning') 63 | return render_template('index.html') 64 | ``` 65 | 66 | Or you could also use `flash()` method with custom titles, see [#14](https://github.com/wiltonsr/Flask-Toastr/issues/14) for details: 67 | 68 | ```python 69 | @app.route('/') 70 | def index(): 71 | flash("Message", 'Custom Title') 72 | flash({'title': "Custom Title", 'message': "Error Message"}, 'error') 73 | return render_template('index.html') 74 | ``` 75 | 76 | Step 4: Enjoy 77 | 78 | Parameters 79 | -------- 80 | 81 | Config | Default | Possible Values | Description | Reference | 82 | ------------------------- | ----------------- | ----------------------- | ------------- | --------------------| 83 | TOASTR_VERSION | '2.1.4' | [Releases from here](https://github.com/CodeSeven/toastr/releases) | Version of Toastr | https://github.com/CodeSeven/toastr/tags | 84 | TOASTR_JQUERY_VERSION | '2.1.0' | [Releases from here](https://github.com/jquery/jquery/releases) | Version of JQuery | https://releases.jquery.com/jquery/ | 85 | TOASTR_CSS_FILENAME | 'toastr.min.css' | CDN file name | CSS Filename used in CDN Toastr's link | https://github.com/CodeSeven/toastr#cdns | 86 | TOASTR_JS_FILENAME | 'toastr.min.js' | CDN file name | JS Filename used in CDN Toastr's link | https://github.com/CodeSeven/toastr#cdns | 87 | TOASTR_CLOSE_BUTTON | 'true' | 'true' or 'false' | Enable or Disable close button | https://github.com/CodeSeven/toastr#close-button | 88 | TOASTR_SHOW_EASING | 'swing' | 'swing' or 'linear' | Override the animation easing to show the toasts | https://github.com/CodeSeven/toastr#animation-options | 89 | TOASTR_HIDE_EASING | 'linear' | 'swing' or 'linear' | Override the animation easing to hide the toasts | https://github.com/CodeSeven/toastr#animation-options | 90 | TOASTR_CLOSE_EASING | 'linear' | 'swing' or 'linear' | Override the animation easing to close the toasts | https://github.com/CodeSeven/toastr#animation-options | 91 | TOASTR_SHOW_METHOD | 'fadeIn' | 'fadeIn', 'fadeOut', 'slideDown', 'slideUp, 'show', 'hide' | Override the animation method to show the toasts | https://github.com/CodeSeven/toastr#animation-options | 92 | TOASTR_HIDE_METHOD | 'fadeOut' | 'fadeIn', 'fadeOut', 'slideDown', 'slideUp, 'show', 'hide' | Override the animation method to hide the toasts | https://github.com/CodeSeven/toastr#animation-options | 93 | TOASTR_CLOSE_METHOD | 'fadeOut' | 'fadeIn', 'fadeOut', 'slideDown', 'slideUp, 'show', 'hide' | Override the animation method to close the toasts | https://github.com/CodeSeven/toastr#animation-options | 94 | TOASTR_TIMEOUT | 15000 | any `int` value (in ms) | Time to notification close | https://github.com/CodeSeven/toastr#timeouts | 95 | TOASTR_EXTENDED_TIMEOUT | 1000 | any `int` value (in ms) | Time to notification close after hover mouse or click | https://github.com/CodeSeven/toastr#timeouts | 96 | TOASTR_POSITION_CLASS | 'toast-top-right' | 'toast-top-right', 'toast-bottom-right', 'toast-bottom-left', 'toast-top-left', 'toast-top-full-width', 'toast-bottom-full-width', 'toast-top-center', 'toast-bottom-center' | Notification Positon | https://codeseven.github.io/toastr/demo.html | 97 | TOASTR_PREVENT_DUPLICATES | 'false' | 'true' or 'false' | Doesn't show same notification | https://github.com/CodeSeven/toastr#prevent-duplicates | 98 | TOASTR_NEWS_ON_TOP | 'false' | 'true' or 'false' | Notification's aparition order | https://github.com/CodeSeven/toastr#display-sequence | 99 | TOASTR_PROGRESS_BAR | 'true' | 'true' or 'false' | Enable or Disable progress bar | https://github.com/CodeSeven/toastr#progress-bar | 100 | TOASTR_OPACITY | True | True or False | Enable or Disable notification's opacity | https://stackoverflow.com/a/17640150/7041939 | 101 | 102 | Note: The values `true` and `false` are passed to JS, that is Case Sensitive. 103 | Therefore they must be passed in lowercase. 104 | 105 | Examples 106 | -------- 107 | 108 | ``` 109 | To run the example in your local environment: 110 | 111 | 1. Clone the repository: 112 | 113 | git clone https://github.com/wiltonsr/Flask-Toastr.git 114 | cd Flask-Toastr 115 | 116 | 2. Create and activate a virtual environment: 117 | 118 | virtualenv env 119 | source env/bin/activate 120 | 121 | 3. Install requirements: 122 | 123 | pip install -r 'example/requirements.txt' 124 | 125 | 4. Run the application: 126 | 127 | python example/app.py 128 | ``` 129 | 130 | Function Reference 131 | ------------------ 132 | 133 | Consult the [toastr documentation](https://github.com/CodeSeven/toastr#toastr) for more details. 134 | 135 | Development 136 | ----------- 137 | 138 | This extension is just a project to improve my python and flask skills. Any suggestions or tips are welcome. 139 | -------------------------------------------------------------------------------- /example/app.py: -------------------------------------------------------------------------------- 1 | from flask import Flask, flash, render_template 2 | from flask_toastr import Toastr 3 | 4 | app = Flask(__name__) 5 | toastr = Toastr(app) 6 | app.config['SECRET_KEY'] = 'you-will-never-guess' 7 | 8 | @app.route('/') 9 | def index(): 10 | # Default Title 11 | flash("Info Category and Default Title") 12 | flash("Info Category and Default Title", 'info') 13 | flash("Success Category and Default Title", 'success') 14 | flash("Error Category and Default Title", 'error') 15 | flash("Warning Category and Default Title", 'warning') 16 | # Custom Title 17 | flash("Info Category", 'Custom Title') 18 | flash({'title': "Custom Title", 'message': "Info Category"}) 19 | flash({'title': "Custom Title", 'message': "Error Category"}, 'error') 20 | return render_template('index.html') 21 | 22 | 23 | if __name__ == '__main__': 24 | app.run(debug=True) 25 | -------------------------------------------------------------------------------- /example/requirements.txt: -------------------------------------------------------------------------------- 1 | Flask 2 | Flask-Toastr 3 | -------------------------------------------------------------------------------- /example/templates/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | Flask-Toastr Example App 4 | {{ toastr.include_jquery() }} 5 | {{ toastr.include_toastr_css() }} 6 | {{ toastr.message() }} 7 | 8 | 9 |
10 |

Flask-Toastr Example App - notification popup box using toastr JS Plugin

11 |
12 | 13 | {{ toastr.include_toastr_js() }} 14 | 15 | 16 | -------------------------------------------------------------------------------- /flask_toastr.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | try: 3 | from jinja2 import Markup 4 | except ImportError: 5 | from markupsafe import Markup 6 | from jinja2 import Template 7 | from flask import current_app, render_template, get_flashed_messages 8 | 9 | 10 | class _toastr(object): 11 | @staticmethod 12 | def include_toastr_js(version=None, js_filename=None): 13 | if version is None: 14 | version = current_app.config.get('TOASTR_VERSION') 15 | if js_filename is None: 16 | js_filename = current_app.config.get('TOASTR_JS_FILENAME') 17 | js = '\n'.format(version, js_filename) 19 | return Markup(js) 20 | 21 | @staticmethod 22 | def include_toastr_css(version=None, css_filename=None): 23 | if version is None: 24 | version = current_app.config.get('TOASTR_VERSION') 25 | if css_filename is None: 26 | css_filename = current_app.config.get('TOASTR_CSS_FILENAME') 27 | css = '\n'.format( 29 | version, 30 | css_filename 31 | ) 32 | if current_app.config.get('TOASTR_OPACITY'): 33 | return Markup(css) 34 | else: 35 | return Markup(''' 36 | {0}'''.format(css)) 41 | 42 | @staticmethod 43 | def include_jquery(version=None): 44 | if version is None: 45 | version = current_app.config.get('TOASTR_JQUERY_VERSION') 46 | js = (''.format(version)) 48 | return Markup(js) 49 | 50 | @staticmethod 51 | def message(): 52 | toastr_options = 'toastr.options.closeButton = %s; \ 53 | toastr.options.showEasing = \"%s\"; \ 54 | toastr.options.hideEasing = \"%s\"; \ 55 | toastr.options.closeEasing = \"%s\"; \ 56 | toastr.options.showMethod = \"%s\"; \ 57 | toastr.options.hideMethod = \"%s\"; \ 58 | toastr.options.closeMethod = \"%s\"; \ 59 | toastr.options.timeOut = %s; \ 60 | toastr.options.extendedTimeOut = %s; \ 61 | toastr.options.positionClass = \"%s\"; \ 62 | toastr.options.preventDuplicates = %s; \ 63 | toastr.options.newestOnTop = %s; \ 64 | toastr.options.progressBar = %s; ' % ( 65 | current_app.config.get('TOASTR_CLOSE_BUTTON'), 66 | current_app.config.get('TOASTR_SHOW_EASING'), 67 | current_app.config.get('TOASTR_HIDE_EASING'), 68 | current_app.config.get('TOASTR_CLOSE_EASING'), 69 | current_app.config.get('TOASTR_SHOW_METHOD'), 70 | current_app.config.get('TOASTR_HIDE_METHOD'), 71 | current_app.config.get('TOASTR_CLOSE_METHOD'), 72 | current_app.config.get('TOASTR_TIMEOUT'), 73 | current_app.config.get('TOASTR_EXTENDED_TIMEOUT'), 74 | current_app.config.get('TOASTR_POSITION_CLASS'), 75 | current_app.config.get('TOASTR_PREVENT_DUPLICATES'), 76 | current_app.config.get('TOASTR_NEWS_ON_TOP'), 77 | current_app.config.get('TOASTR_PROGRESS_BAR')) 78 | message = Template(''' 79 | {% with messages = get_flashed_messages(with_categories=true) %} 80 | {% if messages %} 81 | 100 | {% endif %} 101 | {% endwith %} 102 | ''') 103 | return Markup(render_template( 104 | message, 105 | get_flashed_messages=get_flashed_messages, 106 | toastr_options=toastr_options) 107 | ) 108 | 109 | 110 | class Toastr(object): 111 | def __init__(self, app=None): 112 | if app is not None: 113 | self.init_app(app) 114 | 115 | def init_app(self, app): 116 | if not hasattr(app, 'extensions'): 117 | app.extensions = {} 118 | app.extensions['toastr'] = _toastr 119 | app.context_processor(self.context_processor) 120 | 121 | app.config.setdefault('TOASTR_VERSION', '2.1.4') 122 | app.config.setdefault('TOASTR_JQUERY_VERSION', '2.1.0') 123 | app.config.setdefault('TOASTR_CSS_FILENAME', 'toastr.min.css') 124 | app.config.setdefault('TOASTR_JS_FILENAME', 'toastr.min.js') 125 | 126 | app.config.setdefault('TOASTR_CLOSE_BUTTON', 'true') 127 | app.config.setdefault('TOASTR_SHOW_EASING', 'swing') 128 | app.config.setdefault('TOASTR_HIDE_EASING', 'linear') 129 | app.config.setdefault('TOASTR_CLOSE_EASING', 'linear') 130 | app.config.setdefault('TOASTR_SHOW_METHOD', 'fadeIn') 131 | app.config.setdefault('TOASTR_HIDE_METHOD', 'fadeOut') 132 | app.config.setdefault('TOASTR_CLOSE_METHOD', 'fadeOut') 133 | app.config.setdefault('TOASTR_TIMEOUT', 15000) 134 | app.config.setdefault('TOASTR_EXTENDED_TIMEOUT', 1000) 135 | app.config.setdefault('TOASTR_POSITION_CLASS', 'toast-top-right') 136 | app.config.setdefault('TOASTR_PREVENT_DUPLICATES', 'false') 137 | app.config.setdefault('TOASTR_NEWS_ON_TOP', 'false') 138 | app.config.setdefault('TOASTR_PROGRESS_BAR', 'true') 139 | app.config.setdefault('TOASTR_OPACITY', True) 140 | 141 | @staticmethod 142 | def context_processor(): 143 | return {'toastr': current_app.extensions['toastr']} 144 | 145 | def create(self, timestamp=None): 146 | return current_app.extensions['toastr'](timestamp) 147 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | """ 2 | Flask-Toastr 3 | ------------- 4 | 5 | Showing non-blocking notifications in Flask templates using toastr. 6 | """ 7 | import io 8 | from setuptools import setup 9 | 10 | with io.open('README.md', 'rt', encoding='utf8') as f: 11 | readme = f.read() 12 | 13 | setup( 14 | name='Flask-Toastr', 15 | version='0.5.8', 16 | url='https://github.com/wiltonsr/Flask-Toastr/', 17 | license='MIT', 18 | author='Wilton Rodrigues', 19 | author_email='wiltonsr94@gmail.com', 20 | description='Showing non-blocking notifications in Flask templates using toastr.', 21 | long_description=readme, 22 | long_description_content_type='text/markdown', 23 | py_modules=['flask_toastr'], 24 | zip_safe=False, 25 | include_package_data=True, 26 | platforms='any', 27 | install_requires=[ 28 | 'Flask', 29 | 'Jinja2', 30 | ], 31 | classifiers=[ 32 | 'Environment :: Web Environment', 33 | 'Intended Audience :: Developers', 34 | 'License :: OSI Approved :: MIT License', 35 | 'Operating System :: OS Independent', 36 | 'Programming Language :: Python', 37 | 'Topic :: Internet :: WWW/HTTP :: Dynamic Content', 38 | 'Topic :: Software Development :: Libraries :: Python Modules' 39 | ] 40 | ) 41 | -------------------------------------------------------------------------------- /tests/test_flask_toastr.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wiltonsr/Flask-Toastr/e79851665913cbd053c8bf7b997654e0014cc200/tests/test_flask_toastr.py --------------------------------------------------------------------------------