├── .coveragerc ├── .github └── workflows │ ├── release.yml │ └── test.yml ├── .gitignore ├── .pre-commit-config.yaml ├── AUTHORS.txt ├── CHANGELOG.rst ├── CODE_OF_CONDUCT.md ├── LICENSE.txt ├── MANIFEST.in ├── README.rst ├── flatblocks ├── __init__.py ├── admin.py ├── apps.py ├── forms.py ├── locale │ ├── de │ │ └── LC_MESSAGES │ │ │ ├── django.mo │ │ │ └── django.po │ ├── django.pot │ ├── hu │ │ └── LC_MESSAGES │ │ │ ├── django.mo │ │ │ └── django.po │ ├── no │ │ └── LC_MESSAGES │ │ │ ├── django.mo │ │ │ └── django.po │ └── ru │ │ └── LC_MESSAGES │ │ ├── django.mo │ │ └── django.po ├── management │ ├── __init__.py │ └── commands │ │ ├── __init__.py │ │ ├── createflatblock.py │ │ ├── deleteflatblock.py │ │ └── unassignedflatblocks.py ├── migrations │ ├── 0001_initial.py │ ├── 0002_alter_flatblock_id.py │ └── __init__.py ├── models.py ├── settings.py ├── south_migrations │ ├── 0001_initial.py │ ├── 0002_auto__chg_field_flatblock_content__chg_field_flatblock_header.py │ └── __init__.py ├── templates │ └── flatblocks │ │ ├── edit.html │ │ └── flatblock.html ├── templatetags │ ├── __init__.py │ └── flatblocks.py ├── urls.py └── views.py ├── setup.py ├── tests ├── __init__.py ├── models.py ├── settings.py ├── templates │ └── index.html ├── tests.py └── urls.py └── tox.ini /.coveragerc: -------------------------------------------------------------------------------- 1 | [run] 2 | source = flatblocks 3 | branch = 1 4 | 5 | [report] 6 | omit = *tests*,*migrations*,.tox/*,setup.py,*settings.py 7 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: Release 2 | 3 | on: 4 | push: 5 | tags: 6 | - '*' 7 | 8 | jobs: 9 | build: 10 | if: github.repository == 'jazzband/django-flatblocks' 11 | runs-on: ubuntu-latest 12 | 13 | steps: 14 | - uses: actions/checkout@v2 15 | with: 16 | fetch-depth: 0 17 | 18 | - name: Set up Python 19 | uses: actions/setup-python@v2 20 | with: 21 | python-version: 3.8 22 | 23 | - name: Get pip cache dir 24 | id: pip-cache 25 | run: | 26 | echo "::set-output name=dir::$(pip cache dir)" 27 | 28 | - name: Cache 29 | uses: actions/cache@v2 30 | with: 31 | path: ${{ steps.pip-cache.outputs.dir }} 32 | key: release-${{ hashFiles('**/setup.py') }}-${{ hashFiles('**/tox.ini') }} 33 | restore-keys: | 34 | release- 35 | 36 | - name: Install dependencies 37 | run: | 38 | python -m pip install -U pip 39 | python -m pip install -U setuptools twine wheel 40 | 41 | - name: Build package 42 | run: | 43 | python setup.py --version 44 | python setup.py sdist --format=gztar bdist_wheel 45 | twine check dist/* 46 | 47 | - name: Upload packages to Jazzband 48 | if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags') 49 | uses: pypa/gh-action-pypi-publish@master 50 | with: 51 | user: jazzband 52 | password: ${{ secrets.JAZZBAND_RELEASE_KEY }} 53 | repository_url: https://jazzband.co/projects/django-flatblocks/upload 54 | -------------------------------------------------------------------------------- /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | name: Test 2 | 3 | on: [push, pull_request] 4 | 5 | jobs: 6 | build: 7 | name: build (Python ${{ matrix.python-version }}, Django ${{ matrix.django-version }}) 8 | runs-on: ubuntu-latest 9 | strategy: 10 | fail-fast: false 11 | max-parallel: 5 12 | matrix: 13 | python-version: ['3.7', '3.8', '3.9', '3.10'] 14 | django-version: ['3.2', '4.0', 'main'] 15 | exclude: 16 | - django-version: '4.0' 17 | python-version: '3.7' 18 | - django-version: 'main' 19 | python-version: '3.7' 20 | 21 | steps: 22 | - uses: actions/checkout@v2 23 | 24 | - name: Set up Python ${{ matrix.python-version }} 25 | uses: actions/setup-python@v2 26 | with: 27 | python-version: ${{ matrix.python-version }} 28 | 29 | - name: Get pip cache dir 30 | id: pip-cache 31 | run: | 32 | echo "::set-output name=dir::$(pip cache dir)" 33 | 34 | - name: Cache 35 | uses: actions/cache@v2 36 | with: 37 | path: ${{ steps.pip-cache.outputs.dir }} 38 | key: 39 | ${{ matrix.python-version }}-v1-${{ hashFiles('**/setup.py') }}-${{ hashFiles('**/tox.ini') }} 40 | restore-keys: | 41 | ${{ matrix.python-version }}-v1- 42 | 43 | - name: Install dependencies 44 | run: | 45 | python -m pip install --upgrade pip 46 | python -m pip install --upgrade tox tox-gh-actions 47 | 48 | - name: Tox tests 49 | run: | 50 | tox -v 51 | env: 52 | DJANGO: ${{ matrix.django-version }} 53 | 54 | - name: Upload coverage 55 | uses: codecov/codecov-action@v1 56 | with: 57 | name: Python ${{ matrix.python-version }} 58 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.pyc 2 | *.bak 3 | *.db 4 | .*.swp 5 | ._* 6 | .idea 7 | README.html 8 | local_settings.py 9 | django_flatblocks.egg-info 10 | dist 11 | build 12 | .tox 13 | coverage.xml 14 | .coverage 15 | -------------------------------------------------------------------------------- /.pre-commit-config.yaml: -------------------------------------------------------------------------------- 1 | repos: [] 2 | -------------------------------------------------------------------------------- /AUTHORS.txt: -------------------------------------------------------------------------------- 1 | Original work by Clint Ecker: http://code.google.com/p/django-chunks/ 2 | 3 | Extended by 4 | 5 | * Alexander Artemenko 6 | * Peter Baumgardner 7 | * Kevin Fricovsky 8 | * Horst Gutmann 9 | * Curtis Maloney (FunkyBob) 10 | 11 | Contributions by 12 | 13 | * Michael Greene 14 | * Stephan Jaekel 15 | * James O'Donnell 16 | * Mikhail Korobov 17 | * Henrik Heimbuerger 18 | * Alexander Ovchinnikov 19 | * Craig Anderson 20 | -------------------------------------------------------------------------------- /CHANGELOG.rst: -------------------------------------------------------------------------------- 1 | Unreleased: 2 | * Add support for Django 3.2 and 4.0. 3 | * Add support for Python 3.10. 4 | * Remove support for Django 2.0, 2.2, 3.0, and 3.1. 5 | * Remove support for Python 3.6. 6 | * Change default primary key to use BigAutoField. 7 | 8 | 1.0.0: 9 | * Add support for Django 2.2, 3.0, and 3.1. 10 | * Add support for Python 3.6, 3.7, 3.8 and 3.9. 11 | * Remove support for Django 1.7, 1.8, 1.9, 1.10, and 1.11. 12 | * Remove support for Python 3.4 and 3.5. 13 | * Move CI to GitHub Actions: https://github.com/jazzband/django-flatblocks/actions 14 | 15 | 0.9.4: 16 | * Drop Python 3.3 support. 17 | * Add support for Django 1.11. 18 | 19 | 0.9.3: 20 | * Fixed Django 1.10 compatibility 21 | 22 | 0.9.2: 23 | * Fixed reading of README in setup.py 24 | * Dropped Django 1.4 testing 25 | * Tidied code with flake8 and isort 26 | * Fix support for Django 1.7+ 27 | * Fix packaging to exclude tests module 28 | 29 | 0.9.1: 30 | * Dropped testing of Django 1.5 and 1.6 31 | * Added migrations [Thanks Sergey Fedoseev] 32 | 33 | 0.9: 34 | NOTE: Major tag syntax changes! 35 | 36 | * Modernised to use simple_tag and standard kwarg syntax. 37 | * Removed caching - use {% cache %} tag instead 38 | 39 | 0.8: 40 | * Python 3 & Django 1.6 support 41 | 42 | 0.7: 43 | * Support for evaluated blocks offering access to context variables 44 | 45 | 0.6: 46 | * South support 47 | * Installation and upgrade instructions 48 | 49 | Note: This is primarily a transitional release to get South in here and 50 | open this project up for some database changes in the future. 51 | 52 | 0.5.1 53 | * Removed rendering of the content attribute from the admin list by Michael Fladischer 54 | * PyBabel compatibility by Michael Fladischer 55 | * Fixed caching issue with memcache backend 56 | 57 | 0.5 58 | * Hungarian translation by Török Gábor 59 | * Method added to demo edit form (#5) by Bill Evans 60 | 61 | 0.4 62 | * FlatBlock autocreation by Mikhail Korobov (can be enabled/disabled 63 | with FLATBLOCKS\_AUTOCREATE\_STATIC\_BLOCKS setting) 64 | * Various fixes by Mikhail Korobov 65 | * Fix by Henrik Heimbuerger for the manifest 66 | 67 | 0.3.5 68 | * Russian translation by Mikhail Korobov 69 | 70 | 0.3.4 71 | * Norwegian translation by Eivind Uggedal 72 | 73 | 0.3.3 74 | * FlatBlock.save should also accept optional kwargs. 75 | 76 | 0.3.2 77 | * All settings are now in the flatblocks.settings module 78 | 79 | 0.3.1 80 | * Fixes a bug with FlatBlock.save() failing to reset the cache 81 | * Buildout integration for easier testing 82 | * Example urls.py and flatblocks/edit.html-template 83 | 84 | 0.3 85 | * createflatblock and deleteflatblock commands 86 | * On saving a flatblock its cache will be cleared 87 | * As last argument of the template tag you can now also specify a template 88 | name. 89 | 0.2 90 | * Translatable 91 | * ``flatblocks.views.edit`` view for editing flatblocks 92 | 0.1 93 | Initial release 94 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Code of Conduct 2 | 3 | As contributors and maintainers of the Jazzband projects, and in the interest of 4 | fostering an open and welcoming community, we pledge to respect all people who 5 | contribute through reporting issues, posting feature requests, updating documentation, 6 | submitting pull requests or patches, and other activities. 7 | 8 | We are committed to making participation in the Jazzband a harassment-free experience 9 | for everyone, regardless of the level of experience, gender, gender identity and 10 | expression, sexual orientation, disability, personal appearance, body size, race, 11 | ethnicity, age, religion, or nationality. 12 | 13 | Examples of unacceptable behavior by participants include: 14 | 15 | - The use of sexualized language or imagery 16 | - Personal attacks 17 | - Trolling or insulting/derogatory comments 18 | - Public or private harassment 19 | - Publishing other's private information, such as physical or electronic addresses, 20 | without explicit permission 21 | - Other unethical or unprofessional conduct 22 | 23 | The Jazzband roadies have the right and responsibility to remove, edit, or reject 24 | comments, commits, code, wiki edits, issues, and other contributions that are not 25 | aligned to this Code of Conduct, or to ban temporarily or permanently any contributor 26 | for other behaviors that they deem inappropriate, threatening, offensive, or harmful. 27 | 28 | By adopting this Code of Conduct, the roadies commit themselves to fairly and 29 | consistently applying these principles to every aspect of managing the jazzband 30 | projects. Roadies who do not follow or enforce the Code of Conduct may be permanently 31 | removed from the Jazzband roadies. 32 | 33 | This code of conduct applies both within project spaces and in public spaces when an 34 | individual is representing the project or its community. 35 | 36 | Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by 37 | contacting the roadies at `roadies@jazzband.co`. All complaints will be reviewed and 38 | investigated and will result in a response that is deemed necessary and appropriate to 39 | the circumstances. Roadies are obligated to maintain confidentiality with regard to the 40 | reporter of an incident. 41 | 42 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 43 | 1.3.0, available at [https://contributor-covenant.org/version/1/3/0/][version] 44 | 45 | [homepage]: https://contributor-covenant.org 46 | [version]: https://contributor-covenant.org/version/1/3/0/ 47 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2008, Clint Ecker and Kevin Fricovsky and Peter Baumgartner and Alexander Artemenko 2 | Copyright (c) 2009, Horst Gutmann 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 6 | 7 | * Redistributions of source code must retain the above copyright notice, this 8 | list of conditions and the following disclaimer. 9 | 10 | * Redistributions in binary form must reproduce the above copyright notice, 11 | this list of conditions and the following disclaimer in the documentation 12 | and/or other materials provided with the distribution. 13 | 14 | * Neither the name of the project nor the names of its contributors may 15 | be used to endorse or promote products derived from this software without 16 | specific prior written permission. 17 | 18 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 19 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 21 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE 22 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 24 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 25 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 26 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | include README.rst 2 | recursive-include flatblocks/locale * 3 | recursive-include flatblocks/templates/flatblocks *.html 4 | -------------------------------------------------------------------------------- /README.rst: -------------------------------------------------------------------------------- 1 | django-flatblocks 2 | ================= 3 | 4 | .. image:: https://jazzband.co/static/img/badge.svg 5 | :target: https://jazzband.co/ 6 | :alt: Jazzband 7 | 8 | .. image:: https://github.com/jazzband/django-flatblocks/workflows/Test/badge.svg 9 | :target: https://github.com/jazzband/django-flatblocks/actions 10 | :alt: GitHub Actions 11 | 12 | .. image:: https://codecov.io/gh/jazzband/django-flatblocks/branch/master/graph/badge.svg 13 | :target: https://codecov.io/gh/jazzband/django-flatblocks 14 | 15 | django-flatblocks is a simple application for handling small text-blocks on 16 | websites. Think about it like ``django.contrib.flatpages`` just not for a 17 | whole page but for only parts of it, like an information text describing what 18 | you can do on a site. 19 | 20 | Installation 21 | ------------ 22 | 23 | Probably the easiest way to install this application is to first run 24 | ``pip install django-flatblocks``. Once this step is complete add 25 | ``"flatblocks"`` to your ``INSTALLED_APPS`` setting in your ``settings.py`` 26 | file and run ``python manage.py syncdb`` to update your database. 27 | 28 | 29 | Upgrading 30 | --------- 31 | 32 | django-flatblocks uses `South`_ for handling data and schema migrations 33 | starting with version 0.6.0, so the South-typical update path applies here. 34 | 35 | If you're upgrading from a 0.5.x version or earlier you will have to migrate 36 | in 3 steps: 37 | 38 | 1. Install south. 39 | 40 | 2. Migrate your database to the first version of flatblocks using South: 41 | 42 | .. code-block:: sh 43 | 44 | ./manage.py migrate flatblocks 0001 --fake 45 | 46 | 3. Then migrate your database to the latest version of flatblocks' database 47 | and data structure: 48 | 49 | .. code-block:: sh 50 | 51 | ./manage.py migrate flatblocks 52 | 53 | Usage 54 | ----- 55 | 56 | Once you've created some instances of the ``flatblocks.models.FlatBlock`` 57 | model, you can load it it using the ``flatblocks`` templatetag-library: 58 | 59 | .. code-block:: html+django 60 | 61 | {% load flatblocks %} 62 | 63 | 64 | 65 | 66 | 67 | 68 |
69 |
70 | 71 |
72 | 75 |
76 | 77 | 78 | 79 | This way you can display a text block with the name 'page.info'. If you 80 | have the name of a block in a template variable, leave out the quotes. 81 | 82 | Additionally you can also specify which template should be used to render the 83 | flatblock: 84 | 85 | .. code-block:: django 86 | 87 | {% flatblock "page.info" using="my_template.html" %} 88 | 89 | {% flatblock "page.about" using="my_template.html" %} 90 | 91 | If you want to simply output the value of the ``content`` field of a flatblock 92 | without using any template, you can use either options: 93 | 94 | .. code-block:: django 95 | 96 | {% flatblock "page.info" using=False %} 97 | 98 | or 99 | 100 | .. code-block:: django 101 | 102 | {% plain_flatblock "page.info" %} 103 | 104 | As with the slug of the flatblock also with the template name you have the 105 | choice of using the literal name of the template or pass it to the templatetag 106 | as a variable. 107 | 108 | The content of a flatblock (as well as its header) can also be evaluated as a 109 | full-fledged Django template: 110 | 111 | .. code-block:: django 112 | 113 | {% flatblock "page.info" evaluated=True %} 114 | 115 | This also works with the other parameters like the custom template and with 116 | the ``plain_flatblock`` templatetag: 117 | 118 | .. code-block:: django 119 | 120 | {% flatblock "page.info" evaluated=True using="my_template.html" %} 121 | 122 | {% plain_flatblock "page.about" evaluated=True %} 123 | 124 | 125 | edit-view 126 | --------- 127 | 128 | With ``flatblocks.views.edit`` django-flatblocks offers a simple view to edit 129 | your flatblocks from your frontend. To use it simply include it in your 130 | URLconf and create a ``flatblocks/edit.html`` template. 131 | 132 | By default the view doesn't do any permission checking, so you should decorate 133 | it accordingly in your URLconf: 134 | 135 | .. code-block:: python 136 | 137 | from flatblocks.views import edit 138 | from django.contrib.auth.decorators import login_required 139 | 140 | # ... 141 | 142 | urlpatterns = pattern('', 143 | url(r'^flatblocks/(?P\d+)/edit/$', login_required(edit), 144 | name='flatblocks-edit'), 145 | # ... 146 | ) 147 | 148 | The template can operate on following variables: 149 | 150 | * ``form`` 151 | * ``flatblock`` 152 | * ``origin`` (the URL of the previous page) 153 | 154 | Additionally the view offers some basic customization hooks via these keyword 155 | arguments: 156 | 157 | ``template_name`` 158 | Name of the template to be used for rendering this view. By default 159 | ``flatblocks/edit.html`` is used. 160 | 161 | ``success_url`` 162 | After successfully editing a flatblock the view will redirect the user to 163 | the URL specified here. By default the view will try to determine the last 164 | visited page before entering the edit-view (which is normally a page where 165 | the flatblock is used) and redirect the user back there. 166 | 167 | ``modelform_class`` 168 | If you want to use a customized ModelForm class for flatblocks you can 169 | specify it here. 170 | 171 | ``permission_check`` 172 | This argument lets you specify a callback function to do some 173 | flatblock-specific permission checking. Such a function could look like 174 | this: 175 | 176 | .. code-block:: python 177 | 178 | def my_permcheck(request, flatblock): 179 | if request.user.is_staff or flatblock.slug == 'free_for_all': 180 | return True 181 | return HttpResponseRedirect('/') 182 | 183 | With this permission callback set, a user that is not a staff-user is not 184 | allowed to edit this view unless it's the "free_for_all" block. If these 185 | criteria are not met, the user is redirected to the root URL of the page. 186 | 187 | The contract here is pretty simple. The permission callback should return 188 | ``False``, if the user should receive a 403 message when trying to edit 189 | this link. If the function returns an instance of ``HttpResponse`` the 190 | view will proceed from the assumption that your view already did 191 | everything there is to do and return that response-object. Any other 192 | return value tells the view that the permissions are OK for the current 193 | user and that it should proceed. 194 | 195 | 196 | History 197 | ------- 198 | 199 | Since this application targets use-cases that are basically applicable to 200 | most web-projects out there, there are tons of solutions similar to this one. 201 | In fact, this app is a fork originally from `django-chunks`_ developed by 202 | Clint Ecker. 203 | 204 | In November 2008 Kevin Fricovsky created the `original fork`_ in order to add 205 | an additional "active"-flag to each chunk. That project was later on `forked 206 | by Peter Baumgardner`_ who removed that flag again and added a "header"-field 207 | in order to directly associate and optional title with each text block. 208 | 209 | This fork aims now to add more features like variable chunks and also 210 | integrate some of the features developed by H. Waara and S. Cranford in 211 | the `django-better-chunks`_ fork (``django.contrib.site``- and i18n-support). 212 | 213 | .. _`original fork`: http://github.com/howiworkdaily/django-flatblock/ 214 | .. _`django-chunks`: http://code.google.com/p/django-chunks/ 215 | .. _`django-better-chunks`: http://bitbucket.org/hakanw/django-better-chunks/ 216 | .. _`forked by Peter Baumgardner`: http://github.com/lincolnloop/django-flatblock/ 217 | .. _`south`: http://south.aeracode.org/ 218 | -------------------------------------------------------------------------------- /flatblocks/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jazzband/django-flatblocks/8ecea624a3e38364b773eecb386ea7a92b103249/flatblocks/__init__.py -------------------------------------------------------------------------------- /flatblocks/admin.py: -------------------------------------------------------------------------------- 1 | from django.contrib import admin 2 | from flatblocks.models import FlatBlock 3 | 4 | 5 | class FlatBlockAdmin(admin.ModelAdmin): 6 | ordering = ['slug', ] 7 | list_display = ('slug', 'header') 8 | search_fields = ('slug', 'header', 'content') 9 | 10 | admin.site.register(FlatBlock, FlatBlockAdmin) 11 | -------------------------------------------------------------------------------- /flatblocks/apps.py: -------------------------------------------------------------------------------- 1 | from django.apps import AppConfig 2 | 3 | 4 | class DjangoFlatblocksAppConfig(AppConfig): 5 | default_auto_field = "django.db.models.BigAutoField" 6 | name = "flatblocks" 7 | -------------------------------------------------------------------------------- /flatblocks/forms.py: -------------------------------------------------------------------------------- 1 | from django.forms import ModelForm 2 | from flatblocks.models import FlatBlock 3 | 4 | 5 | class FlatBlockForm(ModelForm): 6 | class Meta: 7 | model = FlatBlock 8 | exclude = ('slug', ) 9 | -------------------------------------------------------------------------------- /flatblocks/locale/de/LC_MESSAGES/django.mo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jazzband/django-flatblocks/8ecea624a3e38364b773eecb386ea7a92b103249/flatblocks/locale/de/LC_MESSAGES/django.mo -------------------------------------------------------------------------------- /flatblocks/locale/de/LC_MESSAGES/django.po: -------------------------------------------------------------------------------- 1 | msgid "" 2 | msgstr "" 3 | "Report-Msgid-Bugs-To: \n" 4 | "POT-Creation-Date: 2013-09-26 08:58+0700\n" 5 | "Last-Translator: Horst Gutmann \n" 6 | "Language-Team: de \n" 7 | "Language: \n" 8 | "MIME-Version: 1.0\n" 9 | "Content-Type: text/plain; charset=UTF-8\n" 10 | "Content-Transfer-Encoding: 8bit\n" 11 | "Plural-Forms: nplurals=2; plural=(n != 1)\n" 12 | 13 | #: models.py:15 14 | msgid "Slug" 15 | msgstr "Slug" 16 | 17 | #: models.py:16 18 | msgid "A unique name used for reference in the templates" 19 | msgstr "" 20 | "Ein eindeutiger Name, über den dieser Block in Templates referenziert werden " 21 | "kann" 22 | 23 | #: models.py:19 24 | msgid "Header" 25 | msgstr "Überschrift" 26 | 27 | #: models.py:20 28 | msgid "An optional header for this content" 29 | msgstr "Eine optionale Überschrift für diesen Block" 30 | 31 | #: models.py:22 32 | msgid "Content" 33 | msgstr "Inhalt" 34 | 35 | #: models.py:44 36 | msgid "Flat block" 37 | msgstr "Flatblock" 38 | 39 | #: models.py:45 40 | msgid "Flat blocks" 41 | msgstr "Flatblocks" 42 | 43 | #: views.py:46 44 | msgid "You are not allowed to edit this flatblock" 45 | msgstr "Du darfst diesen Flatblock nicht editieren" 46 | -------------------------------------------------------------------------------- /flatblocks/locale/django.pot: -------------------------------------------------------------------------------- 1 | # Translations template for PROJECT. 2 | # Copyright (C) 2010 ORGANIZATION 3 | # This file is distributed under the same license as the PROJECT project. 4 | # FIRST AUTHOR , 2010. 5 | # 6 | #, fuzzy 7 | msgid "" 8 | msgstr "" 9 | "Project-Id-Version: PROJECT VERSION\n" 10 | "Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" 11 | "POT-Creation-Date: 2010-11-26 10:27+0100\n" 12 | "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" 13 | "Last-Translator: FULL NAME \n" 14 | "Language-Team: LANGUAGE \n" 15 | "MIME-Version: 1.0\n" 16 | "Content-Type: text/plain; charset=utf-8\n" 17 | "Content-Transfer-Encoding: 8bit\n" 18 | "Generated-By: Babel 0.9.5\n" 19 | 20 | #: models.py:15 21 | msgid "Slug" 22 | msgstr "" 23 | 24 | #: models.py:16 25 | msgid "A unique name used for reference in the templates" 26 | msgstr "" 27 | 28 | #: models.py:18 29 | msgid "Header" 30 | msgstr "" 31 | 32 | #: models.py:19 33 | msgid "An optional header for this content" 34 | msgstr "" 35 | 36 | #: models.py:20 37 | msgid "Content" 38 | msgstr "" 39 | 40 | #: models.py:31 41 | msgid "Flat block" 42 | msgstr "" 43 | 44 | #: models.py:32 45 | msgid "Flat blocks" 46 | msgstr "" 47 | 48 | #: views.py:44 49 | msgid "You are not allowed to edit this flatblock" 50 | msgstr "" 51 | 52 | -------------------------------------------------------------------------------- /flatblocks/locale/hu/LC_MESSAGES/django.mo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jazzband/django-flatblocks/8ecea624a3e38364b773eecb386ea7a92b103249/flatblocks/locale/hu/LC_MESSAGES/django.mo -------------------------------------------------------------------------------- /flatblocks/locale/hu/LC_MESSAGES/django.po: -------------------------------------------------------------------------------- 1 | # Hungarian translations for django-flatblocks. 2 | # This file is distributed under the same license as the django-flatblocks project. 3 | # 4 | #, fuzzy 5 | msgid "" 6 | msgstr "" 7 | "Project-Id-Version: django-flatblocks\n" 8 | "Report-Msgid-Bugs-To: \n" 9 | "POT-Creation-Date: 2013-09-26 08:58+0700\n" 10 | "PO-Revision-Date: 2010-11-26 10:49+0100\n" 11 | "Last-Translator: Török Gábor \n" 12 | "Language-Team: Hungarian \n" 13 | "Language: hu\n" 14 | "MIME-Version: 1.0\n" 15 | "Content-Type: text/plain; charset=UTF-8\n" 16 | "Content-Transfer-Encoding: 8bit\n" 17 | "Plural-Forms: nplurals=2; plural=(n != 1);\n" 18 | "Generated-By: Babel 0.9.5\n" 19 | 20 | #: models.py:15 21 | msgid "Slug" 22 | msgstr "Név" 23 | 24 | #: models.py:16 25 | msgid "A unique name used for reference in the templates" 26 | msgstr "Egyedi név, amivel a sablonokban tud majd a dobozra hivatkozni" 27 | 28 | #: models.py:19 29 | msgid "Header" 30 | msgstr "Cím" 31 | 32 | #: models.py:20 33 | msgid "An optional header for this content" 34 | msgstr "Opcionális cím a tartalomhoz" 35 | 36 | #: models.py:22 37 | msgid "Content" 38 | msgstr "Tartalom" 39 | 40 | #: models.py:44 41 | msgid "Flat block" 42 | msgstr "Egyszerű doboz" 43 | 44 | #: models.py:45 45 | msgid "Flat blocks" 46 | msgstr "Egyszerű dobozok" 47 | 48 | #: views.py:46 49 | msgid "You are not allowed to edit this flatblock" 50 | msgstr "Nincs joga a doboz módosításához" 51 | -------------------------------------------------------------------------------- /flatblocks/locale/no/LC_MESSAGES/django.mo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jazzband/django-flatblocks/8ecea624a3e38364b773eecb386ea7a92b103249/flatblocks/locale/no/LC_MESSAGES/django.mo -------------------------------------------------------------------------------- /flatblocks/locale/no/LC_MESSAGES/django.po: -------------------------------------------------------------------------------- 1 | msgid "" 2 | msgstr "" 3 | "Report-Msgid-Bugs-To: \n" 4 | "POT-Creation-Date: 2013-09-26 08:58+0700\n" 5 | "Last-Translator: Eivind Uggedal \n" 6 | "Language-Team: Norsk \n" 7 | "Language: \n" 8 | "MIME-Version: 1.0\n" 9 | "Content-Type: text/plain; charset=UTF-8\n" 10 | "Content-Transfer-Encoding: 8bit\n" 11 | "Plural-Forms: nplurals=2; plural=(n != 1)\n" 12 | 13 | #: models.py:15 14 | msgid "Slug" 15 | msgstr "Slug" 16 | 17 | #: models.py:16 18 | msgid "A unique name used for reference in the templates" 19 | msgstr "Et unikt navn som kan refereres til fra en mal" 20 | 21 | #: models.py:19 22 | msgid "Header" 23 | msgstr "Overskrift" 24 | 25 | #: models.py:20 26 | msgid "An optional header for this content" 27 | msgstr "En overskrift for dette innholdet (ikke påkrevd)" 28 | 29 | #: models.py:22 30 | msgid "Content" 31 | msgstr "Innhold" 32 | 33 | #: models.py:44 34 | msgid "Flat block" 35 | msgstr "Flatblokk" 36 | 37 | #: models.py:45 38 | msgid "Flat blocks" 39 | msgstr "Flatblokker" 40 | 41 | #: views.py:46 42 | msgid "You are not allowed to edit this flatblock" 43 | msgstr "Du har ikke lov til endre denne flatblokken" 44 | -------------------------------------------------------------------------------- /flatblocks/locale/ru/LC_MESSAGES/django.mo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jazzband/django-flatblocks/8ecea624a3e38364b773eecb386ea7a92b103249/flatblocks/locale/ru/LC_MESSAGES/django.mo -------------------------------------------------------------------------------- /flatblocks/locale/ru/LC_MESSAGES/django.po: -------------------------------------------------------------------------------- 1 | msgid "" 2 | msgstr "" 3 | "Report-Msgid-Bugs-To: \n" 4 | "POT-Creation-Date: 2013-09-26 08:58+0700\n" 5 | "Last-Translator: Mikhail Korobov \n" 6 | "Language-Team: Russian\n" 7 | "Language: \n" 8 | "MIME-Version: 1.0\n" 9 | "Content-Type: text/plain; charset=UTF-8\n" 10 | "Content-Transfer-Encoding: 8bit\n" 11 | "Plural-Forms: nplurals=3; plural=n%10==1 && n%100!=11 ? 0 : n%10>=2 && n" 12 | "%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;\n" 13 | 14 | #: models.py:15 15 | msgid "Slug" 16 | msgstr "Название" 17 | 18 | #: models.py:16 19 | msgid "A unique name used for reference in the templates" 20 | msgstr "Уникальное имя, которое используется в шаблонах" 21 | 22 | #: models.py:19 23 | msgid "Header" 24 | msgstr "Заголовок" 25 | 26 | #: models.py:20 27 | msgid "An optional header for this content" 28 | msgstr "Заголовок (не обязательно)" 29 | 30 | #: models.py:22 31 | msgid "Content" 32 | msgstr "Содержание" 33 | 34 | #: models.py:44 35 | msgid "Flat block" 36 | msgstr "Блок текста" 37 | 38 | #: models.py:45 39 | msgid "Flat blocks" 40 | msgstr "Блоки текста" 41 | 42 | #: views.py:46 43 | msgid "You are not allowed to edit this flatblock" 44 | msgstr "Недостаточно прав для редактирования этого блока текста" 45 | -------------------------------------------------------------------------------- /flatblocks/management/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jazzband/django-flatblocks/8ecea624a3e38364b773eecb386ea7a92b103249/flatblocks/management/__init__.py -------------------------------------------------------------------------------- /flatblocks/management/commands/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jazzband/django-flatblocks/8ecea624a3e38364b773eecb386ea7a92b103249/flatblocks/management/commands/__init__.py -------------------------------------------------------------------------------- /flatblocks/management/commands/createflatblock.py: -------------------------------------------------------------------------------- 1 | from django.core.management import BaseCommand, CommandError 2 | from django.db import IntegrityError 3 | from flatblocks.models import FlatBlock 4 | 5 | 6 | class Command(BaseCommand): 7 | help = "Create a new flatblock with the given slug" 8 | 9 | def handle(self, *args, **options): 10 | if len(args) != 1: 11 | raise CommandError("This command requires the slug of the new " 12 | "flatblock as its first argument") 13 | slug = args[0] 14 | block = FlatBlock(header="[{0}]".format(slug), 15 | content="Empty flatblock", 16 | slug=slug) 17 | try: 18 | block.save() 19 | except IntegrityError: 20 | raise CommandError("A flatblock with this name already exists") 21 | -------------------------------------------------------------------------------- /flatblocks/management/commands/deleteflatblock.py: -------------------------------------------------------------------------------- 1 | from django.core.management import BaseCommand, CommandError 2 | from flatblocks.models import FlatBlock 3 | 4 | 5 | class Command(BaseCommand): 6 | help = "Delete a flatblock with the given slug" 7 | 8 | def handle(self, *args, **options): 9 | if len(args) != 1: 10 | raise CommandError("This command requires the slug of the " 11 | "flatblock as its first argument") 12 | slug = args[0] 13 | try: 14 | FlatBlock.objects.get(slug=slug).delete() 15 | except FlatBlock.DoesNotExist: 16 | raise CommandError("The requested flatblock doesn't exist") 17 | -------------------------------------------------------------------------------- /flatblocks/management/commands/unassignedflatblocks.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | from django.conf import settings 4 | from django.core.management.base import BaseCommand 5 | from django.template.loader import get_template 6 | from flatblocks.models import FlatBlock 7 | from flatblocks.templatetags.flatblock_tags import FlatBlockNode 8 | 9 | 10 | class Command(BaseCommand): 11 | help = "List unassigned flatblocks in the templates" 12 | 13 | def handle(self, *args, **options): 14 | save_nodes = (len(args) and args[0] == 'create') 15 | flatblock_nodes = set() 16 | print_nodes = [] 17 | 18 | # get list of templates 19 | for templ_dir in settings.TEMPLATE_DIRS: 20 | for path, dirlist, fnlist in os.walk(templ_dir): 21 | for fn in fnlist: 22 | try: 23 | t = get_template(os.path.join(path, fn)) 24 | flatblock_nodes.update( 25 | flatblock_nodes.update( 26 | node.slug 27 | for node in t.nodelist.get_nodes_by_type(FlatBlockNode) 28 | ) 29 | ) 30 | except: 31 | # Should log at debug level? 32 | pass 33 | 34 | # check if flatblocks have entry in database 35 | for node in flatblock_nodes: 36 | if not FlatBlock.objects.filter(slug=node).exists(): 37 | # if create argument was supplied, save empty nodes 38 | if save_nodes: 39 | FlatBlock.objects.create(header="[{0}]".format(node), 40 | content="Generated flatblock", 41 | slug=node) 42 | print_nodes.append(node) 43 | 44 | if print_nodes: 45 | if save_nodes: 46 | self.stdout.write("Following nodes were created:") 47 | self.stdout.write("\n".join(print_nodes)) 48 | else: 49 | self.stdout.write("All FlatBlock items are in database") 50 | -------------------------------------------------------------------------------- /flatblocks/migrations/0001_initial.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from __future__ import unicode_literals 3 | 4 | from django.db import migrations, models 5 | 6 | 7 | class Migration(migrations.Migration): 8 | 9 | dependencies = [ 10 | ] 11 | 12 | operations = [ 13 | migrations.CreateModel( 14 | name='FlatBlock', 15 | fields=[ 16 | ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), 17 | ('slug', models.CharField(help_text='A unique name used for reference in the templates', unique=True, max_length=255, verbose_name='Slug')), 18 | ('header', models.CharField(help_text='An optional header for this content', max_length=255, verbose_name='Header', blank=True)), 19 | ('content', models.TextField(verbose_name='Content', blank=True)), 20 | ], 21 | options={ 22 | 'verbose_name': 'Flat block', 23 | 'verbose_name_plural': 'Flat blocks', 24 | }, 25 | bases=(models.Model,), 26 | ), 27 | ] 28 | -------------------------------------------------------------------------------- /flatblocks/migrations/0002_alter_flatblock_id.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.2.11 on 2022-01-08 02:21 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('flatblocks', '0001_initial'), 10 | ] 11 | 12 | operations = [ 13 | migrations.AlterField( 14 | model_name='flatblock', 15 | name='id', 16 | field=models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID'), 17 | ), 18 | ] 19 | -------------------------------------------------------------------------------- /flatblocks/migrations/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jazzband/django-flatblocks/8ecea624a3e38364b773eecb386ea7a92b103249/flatblocks/migrations/__init__.py -------------------------------------------------------------------------------- /flatblocks/models.py: -------------------------------------------------------------------------------- 1 | 2 | from django.db import models 3 | from django.utils.translation import gettext_lazy as _ 4 | 5 | 6 | class FlatBlock(models.Model): 7 | """ 8 | Think of a flatblock as a flatpage but for just part of a site. It's 9 | basically a piece of content with a given name (slug) and an optional 10 | title (header) which you can, for example, use in a sidebar of a website. 11 | """ 12 | slug = models.CharField(_('Slug'), max_length=255, unique=True, 13 | help_text=_("A unique name used for reference in the templates") 14 | ) 15 | header = models.CharField(_('Header'), blank=True, max_length=255, 16 | help_text=_("An optional header for this content") 17 | ) 18 | content = models.TextField(verbose_name=_('Content'), blank=True) 19 | 20 | # Helper attributes used if content should be evaluated in order to 21 | # represent the original content. 22 | raw_content = None 23 | raw_header = None 24 | 25 | def __str__(self): 26 | return self.slug 27 | 28 | class Meta: 29 | verbose_name = _('Flat block') 30 | verbose_name_plural = _('Flat blocks') 31 | -------------------------------------------------------------------------------- /flatblocks/settings.py: -------------------------------------------------------------------------------- 1 | from django.conf import settings 2 | 3 | AUTOCREATE_STATIC_BLOCKS = getattr(settings, 4 | 'FLATBLOCKS_AUTOCREATE_STATIC_BLOCKS', 5 | False) 6 | -------------------------------------------------------------------------------- /flatblocks/south_migrations/0001_initial.py: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | import datetime 3 | 4 | from django.db import models 5 | from south.db import db 6 | from south.v2 import SchemaMigration 7 | 8 | 9 | class Migration(SchemaMigration): 10 | 11 | def forwards(self, orm): 12 | 13 | # Adding model 'FlatBlock' 14 | db.create_table('flatblocks_flatblock', ( 15 | ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)), 16 | ('slug', self.gf('django.db.models.fields.CharField')(unique=True, max_length=255)), 17 | ('header', self.gf('django.db.models.fields.CharField')(max_length=255, null=True, blank=True)), 18 | ('content', self.gf('django.db.models.fields.TextField')(null=True, blank=True)), 19 | )) 20 | db.send_create_signal('flatblocks', ['FlatBlock']) 21 | 22 | def backwards(self, orm): 23 | 24 | # Deleting model 'FlatBlock' 25 | db.delete_table('flatblocks_flatblock') 26 | 27 | models = { 28 | 'flatblocks.flatblock': { 29 | 'Meta': {'object_name': 'FlatBlock'}, 30 | 'content': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), 31 | 'header': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}), 32 | 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), 33 | 'slug': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'}) 34 | } 35 | } 36 | 37 | complete_apps = ['flatblocks'] 38 | -------------------------------------------------------------------------------- /flatblocks/south_migrations/0002_auto__chg_field_flatblock_content__chg_field_flatblock_header.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import datetime 3 | 4 | from django.db import models 5 | from south.db import db 6 | from south.v2 import SchemaMigration 7 | 8 | 9 | class Migration(SchemaMigration): 10 | 11 | def forwards(self, orm): 12 | 13 | # Changing field 'FlatBlock.content' 14 | db.alter_column(u'flatblocks_flatblock', 'content', self.gf('django.db.models.fields.TextField')(default='')) 15 | 16 | # Changing field 'FlatBlock.header' 17 | db.alter_column(u'flatblocks_flatblock', 'header', self.gf('django.db.models.fields.CharField')(default='', max_length=255)) 18 | 19 | def backwards(self, orm): 20 | 21 | # Changing field 'FlatBlock.content' 22 | db.alter_column(u'flatblocks_flatblock', 'content', self.gf('django.db.models.fields.TextField')(null=True)) 23 | 24 | # Changing field 'FlatBlock.header' 25 | db.alter_column(u'flatblocks_flatblock', 'header', self.gf('django.db.models.fields.CharField')(max_length=255, null=True)) 26 | 27 | models = { 28 | u'flatblocks.flatblock': { 29 | 'Meta': {'object_name': 'FlatBlock'}, 30 | 'content': ('django.db.models.fields.TextField', [], {'blank': 'True'}), 31 | 'header': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}), 32 | u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), 33 | 'slug': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'}) 34 | } 35 | } 36 | 37 | complete_apps = ['flatblocks'] 38 | -------------------------------------------------------------------------------- /flatblocks/south_migrations/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jazzband/django-flatblocks/8ecea624a3e38364b773eecb386ea7a92b103249/flatblocks/south_migrations/__init__.py -------------------------------------------------------------------------------- /flatblocks/templates/flatblocks/edit.html: -------------------------------------------------------------------------------- 1 |

Edit "{{ flatblock.slug }}"

2 | 3 |
4 | {{ form.as_p }} 5 | 6 |
7 | -------------------------------------------------------------------------------- /flatblocks/templates/flatblocks/flatblock.html: -------------------------------------------------------------------------------- 1 |
2 | {% if flatblock.header %} 3 |

{{ flatblock.header }}

4 | {% endif %} 5 |
{{ flatblock.content|safe }}
6 |
7 | -------------------------------------------------------------------------------- /flatblocks/templatetags/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jazzband/django-flatblocks/8ecea624a3e38364b773eecb386ea7a92b103249/flatblocks/templatetags/__init__.py -------------------------------------------------------------------------------- /flatblocks/templatetags/flatblocks.py: -------------------------------------------------------------------------------- 1 | """ 2 | This module offers one templatetag called "flatblock" which allows you to 3 | easily embed small text-snippets (like for example the help section of a page) 4 | into a template. 5 | 6 | It requires one argument, and accepts several extra keywords: 7 | 8 | {% flatblock {slug} [evaluated={bool}] [using={template}] %} 9 | 10 | slug:: 11 | The slug/key of the text (for example 'contact_help'). 12 | 13 | evaluated:: 14 | If set to True, the content and header of the FlatBlock will be 15 | rendered as a Template before being passed to the template. 16 | 17 | This allows you to embed template tags into your FlatBlocks. 18 | 19 | The original values for the content and header will be saved to 20 | raw_content and raw_header respectively. 21 | 22 | using:: 23 | The template to render the FlatBlock with. If not supplied, will 24 | default to "flatblocks/flatblock.html". 25 | 26 | If set to False, will not be used, and the output of the tag will be 27 | the ``content`` property of the FlatBlock. 28 | 29 | Example:: 30 | 31 | {% load flatblock_tags %} 32 | 33 | ... 34 | 35 | {% flatblock 'contact_help' %} 36 | {% flatblock name_in_variable %} 37 | 38 | The 'flatblock' template tag acts like an inclusiontag and operates on the 39 | ``flatblock/flatblock.html`` template file, which gets (besides the global 40 | context) also the ``flatblock`` variable passed. 41 | 42 | Compared to the original implementation this includes not only the block's 43 | content but the whole object inclusing title, slug and id. This way you 44 | can easily for example offer administrative operations (like editing) 45 | within that template. 46 | 47 | """ 48 | from __future__ import absolute_import 49 | 50 | import logging 51 | 52 | from django import VERSION, template 53 | from django.template.loader import render_to_string 54 | from flatblocks import settings 55 | 56 | if VERSION >= (1, 7): 57 | from django.apps import apps 58 | get_model = apps.get_model 59 | else: 60 | from django.db import models 61 | get_model = models.get_model 62 | 63 | 64 | register = template.Library() 65 | logger = logging.getLogger(__name__) 66 | 67 | FlatBlock = get_model('flatblocks', 'flatblock') 68 | 69 | 70 | @register.simple_tag(takes_context=True) 71 | def flatblock(context, slug, evaluated=False, 72 | using='flatblocks/flatblock.html'): 73 | 74 | if not settings.AUTOCREATE_STATIC_BLOCKS: 75 | try: 76 | flatblock = FlatBlock.objects.get(slug=slug) 77 | except FlatBlock.DoesNotExist: 78 | return '' 79 | else: 80 | flatblock, _ = FlatBlock.objects.get_or_create(slug=slug, 81 | defaults={'content': slug} 82 | ) 83 | 84 | if evaluated: 85 | flatblock.raw_content = flatblock.content 86 | flatblock.raw_header = flatblock.header 87 | flatblock.content = template.Template(flatblock.content).render(context) 88 | flatblock.header = template.Template(flatblock.header).render(context) 89 | 90 | if using: 91 | ctx = context.flatten() 92 | ctx['flatblock'] = flatblock 93 | result = render_to_string(using, ctx) 94 | else: 95 | result = flatblock.content 96 | 97 | return result 98 | 99 | 100 | @register.simple_tag(takes_context=True) 101 | def plain_flatblock(context, slug, evaluated=False): 102 | return flatblock(context, slug, evaluated=evaluated, using=None) 103 | -------------------------------------------------------------------------------- /flatblocks/urls.py: -------------------------------------------------------------------------------- 1 | from django.contrib.admin.views.decorators import staff_member_required 2 | from django.urls import re_path 3 | from flatblocks.views import edit 4 | 5 | urlpatterns = [ 6 | re_path( 7 | r"^edit/(?P\d+)/$", 8 | staff_member_required(edit), 9 | name="flatblocks-edit", 10 | ), 11 | ] 12 | -------------------------------------------------------------------------------- /flatblocks/views.py: -------------------------------------------------------------------------------- 1 | from django.http import HttpResponse, HttpResponseForbidden 2 | from django.shortcuts import get_object_or_404, redirect, render 3 | from django.utils.translation import gettext as _ 4 | from flatblocks.forms import FlatBlockForm 5 | from flatblocks.models import FlatBlock 6 | 7 | 8 | def edit(request, pk, modelform_class=FlatBlockForm, permission_check=None, 9 | template_name='flatblocks/edit.html', success_url=None): 10 | """ 11 | This view provides a simple editor implementation for flatblocks. 12 | 13 | There are two customization hooks. First of all you can specify your own 14 | ModelForm class by passing it to the view using the ``modelform_class`` 15 | keyword-argument. 16 | 17 | The other entry point helps you check permissions: Pass a simple function 18 | via the ``permission_check`` keyword-argument in order to check 19 | permissions on the flatblock-level:: 20 | 21 | def my_perm_check(request, flatblock): 22 | return request.user.is_staff 23 | 24 | # ... 25 | 26 | urlpatterns('flatblocks.views', 27 | url('flatblocks/(?P\d+)/edit/$', 'edit', 28 | kwargs={'permission_check': my_perm_check}), 29 | ) 30 | 31 | The contract here is pretty simple: If the function returns False, the 32 | view will return HttpResponseForbidden. Otherwise it will pass. So if you 33 | want to do some fancy redirects if the permissions are wrong, return your 34 | own HttpResponse-object/-subclass. 35 | 36 | If everything is alright with the permissions, simply return True. 37 | """ 38 | flatblock = get_object_or_404(FlatBlock, pk=pk) 39 | if permission_check: 40 | permcheck_result = permission_check(request, flatblock) 41 | if permcheck_result is False: 42 | return HttpResponseForbidden( 43 | _('You are not allowed to edit this flatblock')) 44 | if isinstance(permcheck_result, HttpResponse): 45 | return permcheck_result 46 | 47 | session_key = 'flatblock.origin.%d' % (int(pk), ) 48 | if request.method == 'POST': 49 | origin = request.session.get(session_key, 50 | request.META.get('HTTP_REFERER', '/')) 51 | form = modelform_class(request.POST, instance=flatblock) 52 | if form.is_valid(): 53 | instance = form.save(commit=False) 54 | instance.slug = flatblock.slug 55 | instance.save() 56 | del request.session[session_key] 57 | redirect_to = success_url if success_url else origin 58 | return redirect(redirect_to) 59 | else: 60 | origin = request.META.get('HTTP_REFERER', '/') 61 | # Don't set origin to this view's url no matter what 62 | origin = ( 63 | request.session.get(session_key, '/') 64 | if origin == request.get_full_path() 65 | else origin 66 | ) 67 | form = modelform_class(instance=flatblock) 68 | request.session[session_key] = origin 69 | return render(request, template_name, { 70 | 'form': form, 71 | 'origin': origin, 72 | 'flatblock': flatblock, 73 | }) 74 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | import io 2 | 3 | from setuptools import setup, find_packages 4 | 5 | 6 | setup( 7 | name='django-flatblocks', 8 | version='1.0.0', 9 | description='django-flatblocks acts like django.contrib.flatpages but ' 10 | 'for parts of a page; like an editable help box you want ' 11 | 'show alongside the main content.', 12 | long_description=io.open('README.rst', encoding='utf-8').read(), 13 | keywords='django apps', 14 | license='New BSD License', 15 | author='Horst Gutmann, Curtis Maloney', 16 | author_email='curtis@tinbrain.net', 17 | url='http://github.com/funkybob/django-flatblocks/', 18 | classifiers=[ 19 | 'Development Status :: 5 - Production/Stable', 20 | 'Environment :: Plugins', 21 | 'Environment :: Web Environment', 22 | 'Framework :: Django', 23 | 'Framework :: Django :: 3.2', 24 | 'Framework :: Django :: 4.0', 25 | 'Intended Audience :: Developers', 26 | 'License :: OSI Approved :: BSD License', 27 | 'Operating System :: OS Independent', 28 | 'Programming Language :: Python', 29 | 'Programming Language :: Python :: 3', 30 | 'Programming Language :: Python :: 3.7', 31 | 'Programming Language :: Python :: 3.8', 32 | 'Programming Language :: Python :: 3.9', 33 | 'Programming Language :: Python :: 3.10', 34 | 'Programming Language :: Python :: Implementation :: PyPy', 35 | 'Topic :: Software Development :: Libraries :: Python Modules', 36 | 'Topic :: Internet :: WWW/HTTP', 37 | 'Topic :: Internet :: WWW/HTTP :: Dynamic Content', 38 | ], 39 | packages=find_packages(exclude=['tests']), 40 | package_data={ 41 | 'flatblocks': [ 42 | 'templates/flatblocks/*.html', 43 | 'locale/*/*/*.mo', 44 | 'locale/*/*/*.po', 45 | ] 46 | }, 47 | zip_safe=False, 48 | requires = [ 49 | 'Django (>=2.2)', 50 | ], 51 | ) 52 | -------------------------------------------------------------------------------- /tests/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jazzband/django-flatblocks/8ecea624a3e38364b773eecb386ea7a92b103249/tests/__init__.py -------------------------------------------------------------------------------- /tests/models.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jazzband/django-flatblocks/8ecea624a3e38364b773eecb386ea7a92b103249/tests/models.py -------------------------------------------------------------------------------- /tests/settings.py: -------------------------------------------------------------------------------- 1 | DATABASES = { 2 | "default": { 3 | "ENGINE": "django.db.backends.sqlite3", 4 | } 5 | } 6 | 7 | INSTALLED_APPS = ( 8 | "django.contrib.contenttypes", 9 | "django.contrib.sessions", 10 | "django.contrib.auth", 11 | "django.contrib.admin", 12 | "django.contrib.messages", 13 | "flatblocks", 14 | "tests", 15 | ) 16 | 17 | ROOT_URLCONF = "tests.urls" 18 | 19 | MIDDLEWARE = [ 20 | "django.contrib.sessions.middleware.SessionMiddleware", 21 | "django.contrib.auth.middleware.AuthenticationMiddleware", 22 | "django.contrib.messages.middleware.MessageMiddleware", 23 | ] 24 | 25 | SECRET_KEY = "super-secret" 26 | 27 | TEMPLATES = [ 28 | { 29 | "BACKEND": "django.template.backends.django.DjangoTemplates", 30 | "DIRS": [], 31 | "APP_DIRS": True, 32 | "OPTIONS": { 33 | "debug": True, 34 | "context_processors": [ 35 | "django.template.context_processors.request", 36 | "django.contrib.auth.context_processors.auth", 37 | "django.contrib.messages.context_processors.messages", 38 | ], 39 | }, 40 | }, 41 | ] 42 | -------------------------------------------------------------------------------- /tests/templates/index.html: -------------------------------------------------------------------------------- 1 | {% load flatblock_tags %} 2 | 3 | 4 | 5 | Test page for flatblock rendering 6 | 7 | 8 | {% flatblock 'test1' 0 %} 9 | {% flatblock 'test2' 10 %} 10 | {% flatblock 'test3' None %} 11 | 12 | 13 | -------------------------------------------------------------------------------- /tests/tests.py: -------------------------------------------------------------------------------- 1 | from django import template 2 | from django.test import TestCase 3 | from django.contrib.auth.models import User 4 | from django import db 5 | 6 | from flatblocks.models import FlatBlock 7 | from flatblocks import settings 8 | 9 | 10 | class BasicTests(TestCase): 11 | def setUp(self): 12 | self.testblock = FlatBlock.objects.create( 13 | slug="block", header="HEADER", content="CONTENT" 14 | ) 15 | self.admin = User.objects.create_superuser( 16 | "admin", "admin@localhost", "adminpwd" 17 | ) 18 | 19 | def testURLConf(self): 20 | resp = self.client.get("/flatblocks/edit/1/", follow=True) 21 | self.assertTemplateUsed(resp, "admin/login.html") 22 | self.client.login(username="admin", password="adminpwd") 23 | resp = self.client.get("/flatblocks/edit/1/") 24 | self.assertTemplateUsed(resp, "flatblocks/edit.html") 25 | 26 | def testSaveForceUpdate(self): 27 | block = FlatBlock(slug="missing") 28 | with self.assertRaises(ValueError): 29 | block.save(force_update=True) 30 | 31 | def testSaveForceInsert(self): 32 | block = FlatBlock.objects.get(slug="block") 33 | with self.assertRaises(db.IntegrityError): 34 | block.save(force_insert=True) 35 | 36 | 37 | class TagTests(TestCase): 38 | def setUp(self): 39 | self.testblock = FlatBlock.objects.create( 40 | slug="block", header="HEADER", content="CONTENT" 41 | ) 42 | 43 | def testLoadingTaglib(self): 44 | """Tests if the taglib defined in this app can be loaded""" 45 | tpl = template.Template("{% load flatblocks %}") 46 | tpl.render(template.Context({})) 47 | 48 | def testExistingPlain(self): 49 | tpl = template.Template('{% load flatblocks %}{% plain_flatblock "block" %}') 50 | self.assertEqual("CONTENT", tpl.render(template.Context({})).strip()) 51 | 52 | def testExistingTemplate(self): 53 | expected = """
54 | 55 |

HEADER

56 | 57 |
CONTENT
58 |
59 | """ 60 | tpl = template.Template('{% load flatblocks %}{% flatblock "block" %}') 61 | self.assertEqual(expected, tpl.render(template.Context({}))) 62 | 63 | def testUsingMissingTemplate(self): 64 | tpl = template.Template( 65 | "{% load flatblocks %}" 66 | '{% flatblock "block" using="missing_template.html" %}' 67 | ) 68 | exception = template.TemplateDoesNotExist 69 | self.assertRaises(exception, tpl.render, template.Context({})) 70 | 71 | def testBlockAsVariable(self): 72 | tpl = template.Template("{% load flatblocks %}{% flatblock blockvar %}") 73 | tpl.render(template.Context({"blockvar": "block"})) 74 | 75 | def testContentEvaluation(self): 76 | """ 77 | If a block is set in the template to be evaluated the actual content of 78 | the block is treated as a Django template and receives the parent 79 | template's context. 80 | """ 81 | FlatBlock.objects.create( 82 | slug="tmpl_block", header="HEADER", content="{{ variable }}" 83 | ) 84 | tpl = template.Template( 85 | "{% load flatblocks %}" '{% plain_flatblock "tmpl_block" evaluated=True %}' 86 | ) 87 | result = tpl.render(template.Context({"variable": "value"})) 88 | self.assertEqual("value", result) 89 | 90 | def testDisabledEvaluation(self): 91 | """ 92 | If "evaluated" is not passed, no evaluation should take place. 93 | """ 94 | FlatBlock.objects.create( 95 | slug="tmpl_block", header="HEADER", content="{{ variable }}" 96 | ) 97 | tpl = template.Template( 98 | '{% load flatblocks %}{% plain_flatblock "tmpl_block" %}' 99 | ) 100 | result = tpl.render(template.Context({"variable": "value"})) 101 | self.assertEqual("{{ variable }}", result) 102 | 103 | def testHeaderEvaluation(self): 104 | """ 105 | Also the header should receive the context and get evaluated. 106 | """ 107 | FlatBlock.objects.create( 108 | slug="tmpl_block", header="{{ header_variable }}", content="{{ variable }}" 109 | ) 110 | tpl = template.Template( 111 | '{% load flatblocks %}{% flatblock "tmpl_block" evaluated=True %}' 112 | ) 113 | result = tpl.render( 114 | template.Context({"variable": "value", "header_variable": "header-value"}) 115 | ) 116 | self.assertTrue("header-value" in result) 117 | 118 | 119 | class AutoCreationTest(TestCase): 120 | """ Test case for block autcreation """ 121 | 122 | def testMissingStaticBlock(self): 123 | """Tests if a missing block with hardcoded name will be auto-created""" 124 | expected = """
125 | 126 |
foo
127 |
""" 128 | tpl = template.Template('{% load flatblocks %}{% flatblock "foo" %}') 129 | settings.AUTOCREATE_STATIC_BLOCKS = True 130 | self.assertEqual(expected, tpl.render(template.Context({})).strip()) 131 | self.assertEqual(FlatBlock.objects.count(), 1) 132 | self.assertEqual(expected, tpl.render(template.Context({})).strip()) 133 | self.assertEqual(FlatBlock.objects.count(), 1) 134 | 135 | def testNotAutocreatedMissingStaticBlock(self): 136 | """ 137 | Tests if a missing block with hardcoded name won't be auto-created if 138 | feature is disabled""" 139 | expected = "" 140 | settings.AUTOCREATE_STATIC_BLOCKS = False 141 | tpl = template.Template('{% load flatblocks %}{% flatblock "block" %}') 142 | self.assertEqual(expected, tpl.render(template.Context({})).strip()) 143 | self.assertEqual(FlatBlock.objects.filter(slug="block").count(), 0) 144 | 145 | def _testMissingVariableBlock(self): 146 | """ 147 | Tests if a missing block with variable name will simply return an empty 148 | string 149 | """ 150 | settings.AUTOCREATE_STATIC_BLOCKS = True 151 | tpl = template.Template("{% load flatblocks %}{% flatblock name %}") 152 | output = tpl.render(template.Context({"name": "foo"})).strip() 153 | self.assertEqual("", output) 154 | -------------------------------------------------------------------------------- /tests/urls.py: -------------------------------------------------------------------------------- 1 | from django.contrib import admin 2 | from django.shortcuts import render 3 | from django.urls import path, include 4 | 5 | urlpatterns = [ 6 | path('flatblocks/', include("flatblocks.urls")), 7 | path('admin/', admin.site.urls), 8 | path('', render, {'template_name': 'index.html'}), 9 | ] 10 | -------------------------------------------------------------------------------- /tox.ini: -------------------------------------------------------------------------------- 1 | [tox] 2 | envlist = 3 | py{37,38,39,310}-dj{32,40,main} 4 | 5 | [testenv] 6 | deps = 7 | coverage 8 | dj32: Django>=3.2,<3.3 9 | dj40: Django>=4.0,<4.1 10 | djmain: https://github.com/django/django/archive/main.tar.gz 11 | usedevelop = True 12 | ignore_outcome = 13 | djmain: True 14 | commands = 15 | coverage run {envbindir}/django-admin test -v2 16 | coverage report 17 | coverage xml 18 | setenv = 19 | DJANGO_SETTINGS_MODULE=tests.settings 20 | 21 | [gh-actions] 22 | python = 23 | 3.7: py37 24 | 3.8: py38 25 | 3.9: py39 26 | 3.10: py310 27 | 28 | [gh-actions:env] 29 | DJANGO = 30 | 3.2: dj32 31 | 4.0: dj40 32 | main: djmain 33 | --------------------------------------------------------------------------------