├── .env ├── .gitignore ├── .python-version ├── .travis.yml ├── CHANGELOG.rst ├── LICENSE ├── MANIFEST.in ├── Pipfile ├── Pipfile.lock ├── README.rst ├── django_generic_flatblocks ├── __init__.py ├── admin.py ├── contrib │ ├── __init__.py │ └── gblocks │ │ ├── __init__.py │ │ ├── admin.py │ │ ├── migrations │ │ ├── 0001_initial.py │ │ └── __init__.py │ │ ├── models.py │ │ └── templates │ │ └── gblocks │ │ ├── image │ │ └── flatblock.html │ │ ├── text │ │ └── flatblock.html │ │ ├── title │ │ └── flatblock.html │ │ ├── titleandtext │ │ └── flatblock.html │ │ └── titletextandimage │ │ └── flatblock.html ├── locale │ ├── de │ │ └── LC_MESSAGES │ │ │ ├── django.mo │ │ │ └── django.po │ ├── dk │ │ └── LC_MESSAGES │ │ │ ├── django.mo │ │ │ └── django.po │ ├── en │ │ └── LC_MESSAGES │ │ │ ├── django.mo │ │ │ └── django.po │ └── fr │ │ └── LC_MESSAGES │ │ ├── django.mo │ │ └── django.po ├── migrations │ ├── 0001_initial.py │ └── __init__.py ├── models.py ├── templates │ └── admin │ │ └── django_generic_flatblocks │ │ └── change_form_forward.html ├── templatetags │ ├── __init__.py │ └── generic_flatblocks.py └── tests │ ├── __init__.py │ ├── templates │ ├── auth │ │ └── user │ │ │ ├── flatblock.html │ │ │ └── flatblock_firstname.html │ └── test_template.html │ ├── test_flatblocks.py │ └── testapp │ ├── __init__.py │ ├── settings.py │ └── urls.py ├── docs ├── changelog.rst ├── conf.py ├── contributed_nodes.rst ├── creating_nodes.rst ├── index.rst ├── installation.rst ├── quickstart.rst └── usage.rst ├── runtests.py ├── setup.cfg ├── setup.py └── tox.ini /.env: -------------------------------------------------------------------------------- 1 | DJANGO_SETTINGS_MODULE=django_generic_flatblocks.tests.testapp.settings 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.pyc 2 | *.egg-info 3 | docs/_build 4 | build/ 5 | -------------------------------------------------------------------------------- /.python-version: -------------------------------------------------------------------------------- 1 | 3.6.6 2 | 3.5.5 3 | 3.4.8 4 | 2.7.15 5 | 3.7.0 -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: python 2 | 3 | # https://github.com/travis-ci/travis-ci/issues/9815#issuecomment-411073985 4 | matrix: 5 | include: 6 | - python: 2.7 7 | - python: 3.4 8 | - python: 3.5 9 | - python: 3.6 10 | - python: 3.7 11 | dist: xenial 12 | sudo: true 13 | 14 | install: pip install tox-travis coverage codacy-coverage 15 | 16 | script: tox 17 | 18 | after_success: 19 | - coverage xml 20 | - python-codacy-coverage -r coverage.xml 21 | -------------------------------------------------------------------------------- /CHANGELOG.rst: -------------------------------------------------------------------------------- 1 | ========= 2 | Changelog 3 | ========= 4 | 5 | v1.3 (2019-03-16): 6 | ------------------ 7 | 8 | - Django 2.1 compatibility and tests. 9 | - Python 3.7 compatibility and tests. 10 | - Pipenv support. 11 | - General code and package cleanup. 12 | 13 | v1.2.1 (2018-02-18): 14 | -------------------- 15 | 16 | - Python backwards compatbility and coverage improvements. 17 | 18 | v1.2 (2018-02-18): 19 | ------------------ 20 | 21 | - Django 2.0 compatibility and tests. 22 | 23 | v1.1.1 (2017-04-30): 24 | -------------------- 25 | 26 | - Django 1.11 compatibility and tests. 27 | 28 | v1.1 (2017-03-18): 29 | ------------------ 30 | 31 | - Django 1.10 compatibility and tests. 32 | - Python 3.6 compatibility. 33 | - `TEMPLATE_DEBUG` setting is no longer honored to raise individual 34 | errors, in favor of standard `DEBUG`. 35 | 36 | v1.0 (2016-03-23): 37 | ------------------ 38 | 39 | - Code cleanup and update for Django 1.8+. Python3 Support. Better 40 | test integration. Better docs. 41 | 42 | v0.9.1 (2010-03-22): 43 | -------------------- 44 | 45 | - Django 1.2 compatibility! Fixed a bug where tests did not pass 46 | under Django 1.2. Thanks to Brian Rosner for this. 47 | 48 | v0.9 (2010-02-25): 49 | ------------------ 50 | 51 | - Fixed a bug where an integer was not allowed as a part of a slug. 52 | 53 | v0.4 (2009-09-08): 54 | ------------------ 55 | 56 | - Added Danish translation. 57 | - Added better documentation. 58 | - Added unittests. 59 | - If you fetch a not existing "primary key" object the templatetag 60 | will fail silently if settings.TEMPLATE_DEBUG is False. 61 | 62 | v0.3.0 (2009-03-21): 63 | -------------------- 64 | 65 | - Added the *into* argument. You can now display any instance directly 66 | without creating and rendering a template. 67 | 68 | v0.2.1 (2009-03-20): 69 | -------------------- 70 | 71 | - You can now pass a context variable with a integer to fetch a specific 72 | object. 73 | 74 | v0.2.0 (2009-03-20): 75 | -------------------- 76 | 77 | - Added the ability to pass an integer as slug. This will cause that the 78 | templatetag fetches the specific *for* model with the primary key named 79 | in *slug*. 80 | 81 | v0.1.2 (2009-03-20): 82 | -------------------- 83 | 84 | - Switched from distutils to setuptools. Fixed whitespace. 85 | 86 | v0.1.1 (2009-03-15): 87 | -------------------- 88 | 89 | - Fixed wrong upload path of a contributed, generic block 90 | 91 | v0.1 (2009-03-13): 92 | ------------------ 93 | 94 | - Initial release 95 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2009, Martin Mahner 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without modification, 5 | are permitted provided that the following conditions are met: 6 | 7 | * Redistributions of source code must retain the above copyright notice, 8 | this list of conditions and the following disclaimer. 9 | * Redistributions in binary form must reproduce the above copyright notice, 10 | this list of conditions and the following disclaimer in the documentation 11 | and/or other materials provided with the distribution. 12 | * Neither the name django-generic-flatblocks nor the names of its contributors 13 | may be used to endorse or promote products derived from this software without 14 | specific prior written permission. 15 | * If you meet the author(s) some day, and you think this stuff is worth it, 16 | you can buy the author(s) a beer in return. 17 | 18 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 19 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 20 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 21 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 22 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 23 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 24 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 25 | ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 27 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | include *.py 2 | include *.rst 3 | include LICENSE 4 | include tox.ini 5 | recursive-include django_generic_flatblocks *.html 6 | recursive-include django_generic_flatblocks *.po 7 | recursive-include django_generic_flatblocks *.py 8 | recursive-include docs *.py 9 | recursive-include docs *.rst 10 | recursive-include docs Makefile 11 | -------------------------------------------------------------------------------- /Pipfile: -------------------------------------------------------------------------------- 1 | [[source]] 2 | name = "pypi" 3 | url = "https://pypi.org/simple" 4 | verify_ssl = true 5 | 6 | [packages] 7 | django-generic-flatblocks = {editable = true,path = "."} 8 | 9 | [dev-packages] 10 | sphinx = "*" 11 | sphinx-rtd-theme = "*" 12 | sphinx-autobuild = "*" 13 | coverage = "*" 14 | codacy-coverage = "*" 15 | ipdb = "*" 16 | isort = "*" 17 | black = "*" 18 | 19 | [pipenv] 20 | allow_prereleases = true 21 | 22 | [scripts] 23 | test = "./runtests.py" 24 | docs = "sphinx-build -c docs docs docs/_build/html" 25 | cleanup = "sh -c \"isort -rc django_generic_flatblocks && black --skip-string-normalization --line-length=80 --exclude='/(migrations)/' django_generic_flatblocks\"" 26 | -------------------------------------------------------------------------------- /Pipfile.lock: -------------------------------------------------------------------------------- 1 | { 2 | "_meta": { 3 | "hash": { 4 | "sha256": "f53a3c1e0ed9a0fe6e9edbee201eea0e302faef5f96f5111c0cc2e5141d82d99" 5 | }, 6 | "pipfile-spec": 6, 7 | "requires": {}, 8 | "sources": [ 9 | { 10 | "name": "pypi", 11 | "url": "https://pypi.org/simple", 12 | "verify_ssl": true 13 | } 14 | ] 15 | }, 16 | "default": { 17 | "django-generic-flatblocks": { 18 | "editable": true, 19 | "path": "." 20 | }, 21 | "pillow": { 22 | "hashes": [ 23 | "sha256:00fdeb23820f30e43bba78eb9abb00b7a937a655de7760b2e09101d63708b64e", 24 | "sha256:01f948e8220c85eae1aa1a7f8edddcec193918f933fb07aaebe0bfbbcffefbf1", 25 | "sha256:08abf39948d4b5017a137be58f1a52b7101700431f0777bec3d897c3949f74e6", 26 | "sha256:099a61618b145ecb50c6f279666bbc398e189b8bc97544ae32b8fcb49ad6b830", 27 | "sha256:2c1c61546e73de62747e65807d2cc4980c395d4c5600ecb1f47a650c6fa78c79", 28 | "sha256:2ed9c4f694861642401f27dc3cb99772be67cd190e84845c749dae0a06c3bfae", 29 | "sha256:338581b30b908e111be578f0297255f6b57a51358cd16fa0e6f664c9a1f88bff", 30 | "sha256:38c7d48a21cd06fdeee93987147b9b1c55b73b4cfcbf83240568bfbd5adee447", 31 | "sha256:43fd026f613c8e48a25eba1a92f4d2ad7f3903c95d8c33a11611a7717d2ab654", 32 | "sha256:4548236844327a718ce3bb182ab32a16fa2050c61e334e959f554cac052fb0df", 33 | "sha256:5090857876c58885cfa388dc649e5db30aae98a068c26f3fd0ac9d7d9a4d9572", 34 | "sha256:5bbba34f97a26a93f5e8dec469ca4ddd712451418add43da946dbaed7f7a98d2", 35 | "sha256:65a28969a025a0eb4594637b6103201dc4ed2a9508bdab56ac33e43e3081c404", 36 | "sha256:892bb52b70bd5ea9dbbc3ac44f38e84f5a04e9d8b1bff48159d96cb795b81159", 37 | "sha256:8a9becd5cbd5062f973bcd2e7bc79483af310222de112b6541f8af1f93a3cc42", 38 | "sha256:972a7aaeb7c4a2795b52eef52ee991ef040b31009f36deca6207a986607b55f3", 39 | "sha256:97b119c436bfa96a92ac2ca525f7025836d4d4e64b1c9f9eff8dbaf3ff1d86f3", 40 | "sha256:9ba37698e242223f8053cc158f130aee046a96feacbeab65893dbe94f5530118", 41 | "sha256:b1b0e1f626a0f079c0d3696db70132fb1f29aa87c66aecb6501a9b8be64ce9f7", 42 | "sha256:c14c1224fd1a5be2733530d648a316974dbbb3c946913562c6005a76f21ca042", 43 | "sha256:c79a8546c48ae6465189e54e3245a97ddf21161e33ff7eaa42787353417bb2b6", 44 | "sha256:ceb76935ac4ebdf6d7bc845482a4450b284c6ccfb281e34da51d510658ab34d8", 45 | "sha256:e22bffaad04b4d16e1c091baed7f2733fc1ebb91e0c602abf1b6834d17158b1f", 46 | "sha256:ec883b8e44d877bda6f94a36313a1c6063f8b1997aa091628ae2f34c7f97c8d5", 47 | "sha256:f1baa54d50ec031d1a9beb89974108f8f2c0706f49798f4777df879df0e1adb6", 48 | "sha256:f53a5385932cda1e2c862d89460992911a89768c65d176ff8c50cddca4d29bed" 49 | ], 50 | "index": "pypi", 51 | "version": "==6.2.0" 52 | } 53 | }, 54 | "develop": { 55 | "alabaster": { 56 | "hashes": [ 57 | "sha256:446438bdcca0e05bd45ea2de1668c1d9b032e1a9154c2c259092d77031ddd359", 58 | "sha256:a661d72d58e6ea8a57f7a86e37d86716863ee5e92788398526d58b26a4e4dc02" 59 | ], 60 | "version": "==0.7.12" 61 | }, 62 | "appdirs": { 63 | "hashes": [ 64 | "sha256:9e5896d1372858f8dd3344faf4e5014d21849c756c8d5701f78f8a103b372d92", 65 | "sha256:d8b24664561d0d34ddfaec54636d502d7cea6e29c3eaf68f3df6180863e2166e" 66 | ], 67 | "version": "==1.4.3" 68 | }, 69 | "argh": { 70 | "hashes": [ 71 | "sha256:a9b3aaa1904eeb78e32394cd46c6f37ac0fb4af6dc488daa58971bdc7d7fcaf3", 72 | "sha256:e9535b8c84dc9571a48999094fda7f33e63c3f1b74f3e5f3ac0105a58405bb65" 73 | ], 74 | "version": "==0.26.2" 75 | }, 76 | "attrs": { 77 | "hashes": [ 78 | "sha256:08a96c641c3a74e44eb59afb61a24f2cb9f4d7188748e76ba4bb5edfa3cb7d1c", 79 | "sha256:f7b7ce16570fe9965acd6d30101a28f62fb4a7f9e926b3bbc9b61f8b04247e72" 80 | ], 81 | "version": "==19.3.0" 82 | }, 83 | "babel": { 84 | "hashes": [ 85 | "sha256:af92e6106cb7c55286b25b38ad7695f8b4efb36a90ba483d7f7a6628c46158ab", 86 | "sha256:e86135ae101e31e2c8ec20a4e0c5220f4eed12487d5cf3f78be7e98d3a57fc28" 87 | ], 88 | "version": "==2.7.0" 89 | }, 90 | "backcall": { 91 | "hashes": [ 92 | "sha256:38ecd85be2c1e78f77fd91700c76e14667dc21e2713b63876c0eb901196e01e4", 93 | "sha256:bbbf4b1e5cd2bdb08f915895b51081c041bac22394fdfcfdfbe9f14b77c08bf2" 94 | ], 95 | "version": "==0.1.0" 96 | }, 97 | "black": { 98 | "hashes": [ 99 | "sha256:09a9dcb7c46ed496a9850b76e4e825d6049ecd38b611f1224857a79bd985a8cf", 100 | "sha256:68950ffd4d9169716bcb8719a56c07a2f4485354fec061cdd5910aa07369731c" 101 | ], 102 | "index": "pypi", 103 | "version": "==19.3b0" 104 | }, 105 | "certifi": { 106 | "hashes": [ 107 | "sha256:e4f3620cfea4f83eedc95b24abd9cd56f3c4b146dd0177e83a21b4eb49e21e50", 108 | "sha256:fd7c7c74727ddcf00e9acd26bba8da604ffec95bf1c2144e67aff7a8b50e6cef" 109 | ], 110 | "version": "==2019.9.11" 111 | }, 112 | "chardet": { 113 | "hashes": [ 114 | "sha256:84ab92ed1c4d4f16916e05906b6b75a6c0fb5db821cc65e70cbd64a3e2a5eaae", 115 | "sha256:fc323ffcaeaed0e0a02bf4d117757b98aed530d9ed4531e3e15460124c106691" 116 | ], 117 | "version": "==3.0.4" 118 | }, 119 | "click": { 120 | "hashes": [ 121 | "sha256:2335065e6395b9e67ca716de5f7526736bfa6ceead690adf616d925bdc622b13", 122 | "sha256:5b94b49521f6456670fdb30cd82a4eca9412788a93fa6dd6df72c94d5a8ff2d7" 123 | ], 124 | "version": "==7.0" 125 | }, 126 | "codacy-coverage": { 127 | "hashes": [ 128 | "sha256:b94651934745c638a980ad8d67494077e60f71e19e29aad1c275b66e0a070cbc", 129 | "sha256:d8a1ce56b0dd156d6b1de14fa6217d32ec86097902f08a17ff2f95ba27264474" 130 | ], 131 | "index": "pypi", 132 | "version": "==1.3.11" 133 | }, 134 | "coverage": { 135 | "hashes": [ 136 | "sha256:029c69deaeeeae1b15bc6c59f0ffa28aa8473721c614a23f2c2976dec245cd12", 137 | "sha256:02abbbebc6e9d5abe13cd28b5e963dedb6ffb51c146c916d17b18f141acd9947", 138 | "sha256:1bbfe5b82a3921d285e999c6d256c1e16b31c554c29da62d326f86c173d30337", 139 | "sha256:210c02f923df33a8d0e461c86fdcbbb17228ff4f6d92609fc06370a98d283c2d", 140 | "sha256:2d0807ba935f540d20b49d5bf1c0237b90ce81e133402feda906e540003f2f7a", 141 | "sha256:35d7a013874a7c927ce997350d314144ffc5465faf787bb4e46e6c4f381ef562", 142 | "sha256:3636f9d0dcb01aed4180ef2e57a4e34bb4cac3ecd203c2a23db8526d86ab2fb4", 143 | "sha256:42f4be770af2455a75e4640f033a82c62f3fb0d7a074123266e143269d7010ef", 144 | "sha256:48440b25ba6cda72d4c638f3a9efa827b5b87b489c96ab5f4ff597d976413156", 145 | "sha256:4dac8dfd1acf6a3ac657475dfdc66c621f291b1b7422a939cc33c13ac5356473", 146 | "sha256:4e8474771c69c2991d5eab65764289a7dd450bbea050bc0ebb42b678d8222b42", 147 | "sha256:551f10ddfeff56a1325e5a34eff304c5892aa981fd810babb98bfee77ee2fb17", 148 | "sha256:5b104982f1809c1577912519eb249f17d9d7e66304ad026666cb60a5ef73309c", 149 | "sha256:5c62aef73dfc87bfcca32cee149a1a7a602bc74bac72223236b0023543511c88", 150 | "sha256:633151f8d1ad9467b9f7e90854a7f46ed8f2919e8bc7d98d737833e8938fc081", 151 | "sha256:772207b9e2d5bf3f9d283b88915723e4e92d9a62c83f44ec92b9bd0cd685541b", 152 | "sha256:7d5e02f647cd727afc2659ec14d4d1cc0508c47e6cfb07aea33d7aa9ca94d288", 153 | "sha256:a9798a4111abb0f94584000ba2a2c74841f2cfe5f9254709756367aabbae0541", 154 | "sha256:b38ea741ab9e35bfa7015c93c93bbd6a1623428f97a67083fc8ebd366238b91f", 155 | "sha256:b6a5478c904236543c0347db8a05fac6fc0bd574c870e7970faa88e1d9890044", 156 | "sha256:c6248bfc1de36a3844685a2e10ba17c18119ba6252547f921062a323fb31bff1", 157 | "sha256:c705ab445936457359b1424ef25ccc0098b0491b26064677c39f1d14a539f056", 158 | "sha256:d95a363d663ceee647291131dbd213af258df24f41350246842481ec3709bd33", 159 | "sha256:e27265eb80cdc5dab55a40ef6f890e04ecc618649ad3da5265f128b141f93f78", 160 | "sha256:ebc276c9cb5d917bd2ae959f84ffc279acafa9c9b50b0fa436ebb70bbe2166ea", 161 | "sha256:f4d229866d030863d0fe3bf297d6d11e6133ca15bbb41ed2534a8b9a3d6bd061", 162 | "sha256:f95675bd88b51474d4fe5165f3266f419ce754ffadfb97f10323931fa9ac95e5", 163 | "sha256:f95bc54fb6d61b9f9ff09c4ae8ff6a3f5edc937cda3ca36fc937302a7c152bf1", 164 | "sha256:fd0f6be53de40683584e5331c341e65a679dbe5ec489a0697cec7c2ef1a48cda" 165 | ], 166 | "index": "pypi", 167 | "version": "==5.0a4" 168 | }, 169 | "decorator": { 170 | "hashes": [ 171 | "sha256:86156361c50488b84a3f148056ea716ca587df2f0de1d34750d35c21312725de", 172 | "sha256:f069f3a01830ca754ba5258fde2278454a0b5b79e0d7f5c13b3b97e57d4acff6" 173 | ], 174 | "version": "==4.4.0" 175 | }, 176 | "docutils": { 177 | "hashes": [ 178 | "sha256:6c4f696463b79f1fb8ba0c594b63840ebd41f059e92b31957c46b74a4599b6d0", 179 | "sha256:9e4d7ecfc600058e07ba661411a2b7de2fd0fafa17d1a7f7361cd47b1175c827", 180 | "sha256:a2aeea129088da402665e92e0b25b04b073c04b2dce4ab65caaa38b7ce2e1a99" 181 | ], 182 | "version": "==0.15.2" 183 | }, 184 | "idna": { 185 | "hashes": [ 186 | "sha256:c357b3f628cf53ae2c4c05627ecc484553142ca23264e593d327bcde5e9c3407", 187 | "sha256:ea8b7f6188e6fa117537c3df7da9fc686d485087abf6ac197f9c46432f7e4a3c" 188 | ], 189 | "version": "==2.8" 190 | }, 191 | "imagesize": { 192 | "hashes": [ 193 | "sha256:3f349de3eb99145973fefb7dbe38554414e5c30abd0c8e4b970a7c9d09f3a1d8", 194 | "sha256:f3832918bc3c66617f92e35f5d70729187676313caa60c187eb0f28b8fe5e3b5" 195 | ], 196 | "version": "==1.1.0" 197 | }, 198 | "ipdb": { 199 | "hashes": [ 200 | "sha256:7081c65ed7bfe7737f83fa4213ca8afd9617b42ff6b3f1daf9a3419839a2a00a" 201 | ], 202 | "index": "pypi", 203 | "version": "==0.11" 204 | }, 205 | "ipython": { 206 | "hashes": [ 207 | "sha256:c4ab005921641e40a68e405e286e7a1fcc464497e14d81b6914b4fd95e5dee9b", 208 | "sha256:dd76831f065f17bddd7eaa5c781f5ea32de5ef217592cf019e34043b56895aa1" 209 | ], 210 | "version": "==7.8.0" 211 | }, 212 | "ipython-genutils": { 213 | "hashes": [ 214 | "sha256:72dd37233799e619666c9f639a9da83c34013a73e8bbc79a7a6348d93c61fab8", 215 | "sha256:eb2e116e75ecef9d4d228fdc66af54269afa26ab4463042e33785b887c628ba8" 216 | ], 217 | "version": "==0.2.0" 218 | }, 219 | "isort": { 220 | "hashes": [ 221 | "sha256:18c796c2cd35eb1a1d3f012a214a542790a1aed95e29768bdcb9f2197eccbd0b", 222 | "sha256:96151fca2c6e736503981896495d344781b60d18bfda78dc11b290c6125ebdb6" 223 | ], 224 | "index": "pypi", 225 | "version": "==4.3.15" 226 | }, 227 | "jedi": { 228 | "hashes": [ 229 | "sha256:786b6c3d80e2f06fd77162a07fed81b8baa22dde5d62896a790a331d6ac21a27", 230 | "sha256:ba859c74fa3c966a22f2aeebe1b74ee27e2a462f56d3f5f7ca4a59af61bfe42e" 231 | ], 232 | "version": "==0.15.1" 233 | }, 234 | "jinja2": { 235 | "hashes": [ 236 | "sha256:74320bb91f31270f9551d46522e33af46a80c3d619f4a4bf42b3164d30b5911f", 237 | "sha256:9fe95f19286cfefaa917656583d020be14e7859c6b0252588391e47db34527de" 238 | ], 239 | "version": "==2.10.3" 240 | }, 241 | "livereload": { 242 | "hashes": [ 243 | "sha256:78d55f2c268a8823ba499305dcac64e28ddeb9a92571e12d543cd304faf5817b", 244 | "sha256:89254f78d7529d7ea0a3417d224c34287ebfe266b05e67e51facaf82c27f0f66" 245 | ], 246 | "version": "==2.6.1" 247 | }, 248 | "markupsafe": { 249 | "hashes": [ 250 | "sha256:00bc623926325b26bb9605ae9eae8a215691f33cae5df11ca5424f06f2d1f473", 251 | "sha256:09027a7803a62ca78792ad89403b1b7a73a01c8cb65909cd876f7fcebd79b161", 252 | "sha256:09c4b7f37d6c648cb13f9230d847adf22f8171b1ccc4d5682398e77f40309235", 253 | "sha256:1027c282dad077d0bae18be6794e6b6b8c91d58ed8a8d89a89d59693b9131db5", 254 | "sha256:24982cc2533820871eba85ba648cd53d8623687ff11cbb805be4ff7b4c971aff", 255 | "sha256:29872e92839765e546828bb7754a68c418d927cd064fd4708fab9fe9c8bb116b", 256 | "sha256:43a55c2930bbc139570ac2452adf3d70cdbb3cfe5912c71cdce1c2c6bbd9c5d1", 257 | "sha256:46c99d2de99945ec5cb54f23c8cd5689f6d7177305ebff350a58ce5f8de1669e", 258 | "sha256:500d4957e52ddc3351cabf489e79c91c17f6e0899158447047588650b5e69183", 259 | "sha256:535f6fc4d397c1563d08b88e485c3496cf5784e927af890fb3c3aac7f933ec66", 260 | "sha256:62fe6c95e3ec8a7fad637b7f3d372c15ec1caa01ab47926cfdf7a75b40e0eac1", 261 | "sha256:6dd73240d2af64df90aa7c4e7481e23825ea70af4b4922f8ede5b9e35f78a3b1", 262 | "sha256:717ba8fe3ae9cc0006d7c451f0bb265ee07739daf76355d06366154ee68d221e", 263 | "sha256:79855e1c5b8da654cf486b830bd42c06e8780cea587384cf6545b7d9ac013a0b", 264 | "sha256:7c1699dfe0cf8ff607dbdcc1e9b9af1755371f92a68f706051cc8c37d447c905", 265 | "sha256:88e5fcfb52ee7b911e8bb6d6aa2fd21fbecc674eadd44118a9cc3863f938e735", 266 | "sha256:8defac2f2ccd6805ebf65f5eeb132adcf2ab57aa11fdf4c0dd5169a004710e7d", 267 | "sha256:98c7086708b163d425c67c7a91bad6e466bb99d797aa64f965e9d25c12111a5e", 268 | "sha256:9add70b36c5666a2ed02b43b335fe19002ee5235efd4b8a89bfcf9005bebac0d", 269 | "sha256:9bf40443012702a1d2070043cb6291650a0841ece432556f784f004937f0f32c", 270 | "sha256:ade5e387d2ad0d7ebf59146cc00c8044acbd863725f887353a10df825fc8ae21", 271 | "sha256:b00c1de48212e4cc9603895652c5c410df699856a2853135b3967591e4beebc2", 272 | "sha256:b1282f8c00509d99fef04d8ba936b156d419be841854fe901d8ae224c59f0be5", 273 | "sha256:b2051432115498d3562c084a49bba65d97cf251f5a331c64a12ee7e04dacc51b", 274 | "sha256:ba59edeaa2fc6114428f1637ffff42da1e311e29382d81b339c1817d37ec93c6", 275 | "sha256:c8716a48d94b06bb3b2524c2b77e055fb313aeb4ea620c8dd03a105574ba704f", 276 | "sha256:cd5df75523866410809ca100dc9681e301e3c27567cf498077e8551b6d20e42f", 277 | "sha256:e249096428b3ae81b08327a63a485ad0878de3fb939049038579ac0ef61e17e7" 278 | ], 279 | "version": "==1.1.1" 280 | }, 281 | "packaging": { 282 | "hashes": [ 283 | "sha256:28b924174df7a2fa32c1953825ff29c61e2f5e082343165438812f00d3a7fc47", 284 | "sha256:d9551545c6d761f3def1677baf08ab2a3ca17c56879e70fecba2fc4dde4ed108" 285 | ], 286 | "version": "==19.2" 287 | }, 288 | "parso": { 289 | "hashes": [ 290 | "sha256:63854233e1fadb5da97f2744b6b24346d2750b85965e7e399bec1620232797dc", 291 | "sha256:666b0ee4a7a1220f65d367617f2cd3ffddff3e205f3f16a0284df30e774c2a9c" 292 | ], 293 | "version": "==0.5.1" 294 | }, 295 | "pathtools": { 296 | "hashes": [ 297 | "sha256:7c35c5421a39bb82e58018febd90e3b6e5db34c5443aaaf742b3f33d4655f1c0" 298 | ], 299 | "version": "==0.1.2" 300 | }, 301 | "pexpect": { 302 | "hashes": [ 303 | "sha256:2094eefdfcf37a1fdbfb9aa090862c1a4878e5c7e0e7e7088bdb511c558e5cd1", 304 | "sha256:9e2c1fd0e6ee3a49b28f95d4b33bc389c89b20af6a1255906e90ff1262ce62eb" 305 | ], 306 | "markers": "sys_platform != 'win32'", 307 | "version": "==4.7.0" 308 | }, 309 | "pickleshare": { 310 | "hashes": [ 311 | "sha256:87683d47965c1da65cdacaf31c8441d12b8044cdec9aca500cd78fc2c683afca", 312 | "sha256:9649af414d74d4df115d5d718f82acb59c9d418196b7b4290ed47a12ce62df56" 313 | ], 314 | "version": "==0.7.5" 315 | }, 316 | "port-for": { 317 | "hashes": [ 318 | "sha256:b16a84bb29c2954db44c29be38b17c659c9c27e33918dec16b90d375cc596f1c" 319 | ], 320 | "version": "==0.3.1" 321 | }, 322 | "prompt-toolkit": { 323 | "hashes": [ 324 | "sha256:46642344ce457641f28fc9d1c9ca939b63dadf8df128b86f1b9860e59c73a5e4", 325 | "sha256:e7f8af9e3d70f514373bf41aa51bc33af12a6db3f71461ea47fea985defb2c31", 326 | "sha256:f15af68f66e664eaa559d4ac8a928111eebd5feda0c11738b5998045224829db" 327 | ], 328 | "version": "==2.0.10" 329 | }, 330 | "ptyprocess": { 331 | "hashes": [ 332 | "sha256:923f299cc5ad920c68f2bc0bc98b75b9f838b93b599941a6b63ddbc2476394c0", 333 | "sha256:d7cc528d76e76342423ca640335bd3633420dc1366f258cb31d05e865ef5ca1f" 334 | ], 335 | "version": "==0.6.0" 336 | }, 337 | "pygments": { 338 | "hashes": [ 339 | "sha256:71e430bc85c88a430f000ac1d9b331d2407f681d6f6aec95e8bcfbc3df5b0127", 340 | "sha256:881c4c157e45f30af185c1ffe8d549d48ac9127433f2c380c24b84572ad66297" 341 | ], 342 | "version": "==2.4.2" 343 | }, 344 | "pyparsing": { 345 | "hashes": [ 346 | "sha256:6f98a7b9397e206d78cc01df10131398f1c8b8510a2f4d97d9abd82e1aacdd80", 347 | "sha256:d9338df12903bbf5d65a0e4e87c2161968b10d2e489652bb47001d82a9b028b4" 348 | ], 349 | "version": "==2.4.2" 350 | }, 351 | "pytz": { 352 | "hashes": [ 353 | "sha256:1c557d7d0e871de1f5ccd5833f60fb2550652da6be2693c1e02300743d21500d", 354 | "sha256:b02c06db6cf09c12dd25137e563b31700d3b80fcc4ad23abb7a315f2789819be" 355 | ], 356 | "version": "==2019.3" 357 | }, 358 | "pyyaml": { 359 | "hashes": [ 360 | "sha256:0113bc0ec2ad727182326b61326afa3d1d8280ae1122493553fd6f4397f33df9", 361 | "sha256:01adf0b6c6f61bd11af6e10ca52b7d4057dd0be0343eb9283c878cf3af56aee4", 362 | "sha256:5124373960b0b3f4aa7df1707e63e9f109b5263eca5976c66e08b1c552d4eaf8", 363 | "sha256:5ca4f10adbddae56d824b2c09668e91219bb178a1eee1faa56af6f99f11bf696", 364 | "sha256:7907be34ffa3c5a32b60b95f4d95ea25361c951383a894fec31be7252b2b6f34", 365 | "sha256:7ec9b2a4ed5cad025c2278a1e6a19c011c80a3caaac804fd2d329e9cc2c287c9", 366 | "sha256:87ae4c829bb25b9fe99cf71fbb2140c448f534e24c998cc60f39ae4f94396a73", 367 | "sha256:9de9919becc9cc2ff03637872a440195ac4241c80536632fffeb6a1e25a74299", 368 | "sha256:a5a85b10e450c66b49f98846937e8cfca1db3127a9d5d1e31ca45c3d0bef4c5b", 369 | "sha256:b0997827b4f6a7c286c01c5f60384d218dca4ed7d9efa945c3e1aa623d5709ae", 370 | "sha256:b631ef96d3222e62861443cc89d6563ba3eeb816eeb96b2629345ab795e53681", 371 | "sha256:bf47c0607522fdbca6c9e817a6e81b08491de50f3766a7a0e6a5be7905961b41", 372 | "sha256:f81025eddd0327c7d4cfe9b62cf33190e1e736cc6e97502b3ec425f574b3e7a8" 373 | ], 374 | "version": "==5.1.2" 375 | }, 376 | "requests": { 377 | "hashes": [ 378 | "sha256:11e007a8a2aa0323f5a921e9e6a2d7e4e67d9877e85773fba9ba6419025cbeb4", 379 | "sha256:9cf5292fcd0f598c671cfc1e0d7d1a7f13bb8085e9a590f48c010551dc6c4b31" 380 | ], 381 | "version": "==2.22.0" 382 | }, 383 | "six": { 384 | "hashes": [ 385 | "sha256:3350809f0555b11f552448330d0b52d5f24c91a322ea4a15ef22629740f3761c", 386 | "sha256:d16a0141ec1a18405cd4ce8b4613101da75da0e9a7aec5bdd4fa804d0e0eba73" 387 | ], 388 | "version": "==1.12.0" 389 | }, 390 | "snowballstemmer": { 391 | "hashes": [ 392 | "sha256:209f257d7533fdb3cb73bdbd24f436239ca3b2fa67d56f6ff88e86be08cc5ef0", 393 | "sha256:df3bac3df4c2c01363f3dd2cfa78cce2840a79b9f1c2d2de9ce8d31683992f52" 394 | ], 395 | "version": "==2.0.0" 396 | }, 397 | "sphinx": { 398 | "hashes": [ 399 | "sha256:230af939a2f678ab4f2a0a948c3b24a822a0d280821859caaefb750ef7413003", 400 | "sha256:835c701420102a0a71ba2ed54a5bada2da6fd01263bf6dc8c5c80c798e27709c" 401 | ], 402 | "index": "pypi", 403 | "version": "==2.0.0b1" 404 | }, 405 | "sphinx-autobuild": { 406 | "hashes": [ 407 | "sha256:66388f81884666e3821edbe05dd53a0cfb68093873d17320d0610de8db28c74e", 408 | "sha256:e60aea0789cab02fa32ee63c7acae5ef41c06f1434d9fd0a74250a61f5994692" 409 | ], 410 | "index": "pypi", 411 | "version": "==0.7.1" 412 | }, 413 | "sphinx-rtd-theme": { 414 | "hashes": [ 415 | "sha256:00cf895504a7895ee433807c62094cf1e95f065843bf3acd17037c3e9a2becd4", 416 | "sha256:728607e34d60456d736cc7991fd236afb828b21b82f956c5ea75f94c8414040a" 417 | ], 418 | "index": "pypi", 419 | "version": "==0.4.3" 420 | }, 421 | "sphinxcontrib-applehelp": { 422 | "hashes": [ 423 | "sha256:edaa0ab2b2bc74403149cb0209d6775c96de797dfd5b5e2a71981309efab3897", 424 | "sha256:fb8dee85af95e5c30c91f10e7eb3c8967308518e0f7488a2828ef7bc191d0d5d" 425 | ], 426 | "version": "==1.0.1" 427 | }, 428 | "sphinxcontrib-devhelp": { 429 | "hashes": [ 430 | "sha256:6c64b077937330a9128a4da74586e8c2130262f014689b4b89e2d08ee7294a34", 431 | "sha256:9512ecb00a2b0821a146736b39f7aeb90759834b07e81e8cc23a9c70bacb9981" 432 | ], 433 | "version": "==1.0.1" 434 | }, 435 | "sphinxcontrib-htmlhelp": { 436 | "hashes": [ 437 | "sha256:4670f99f8951bd78cd4ad2ab962f798f5618b17675c35c5ac3b2132a14ea8422", 438 | "sha256:d4fd39a65a625c9df86d7fa8a2d9f3cd8299a3a4b15db63b50aac9e161d8eff7" 439 | ], 440 | "version": "==1.0.2" 441 | }, 442 | "sphinxcontrib-jsmath": { 443 | "hashes": [ 444 | "sha256:2ec2eaebfb78f3f2078e73666b1415417a116cc848b72e5172e596c871103178", 445 | "sha256:a9925e4a4587247ed2191a22df5f6970656cb8ca2bd6284309578f2153e0c4b8" 446 | ], 447 | "version": "==1.0.1" 448 | }, 449 | "sphinxcontrib-qthelp": { 450 | "hashes": [ 451 | "sha256:513049b93031beb1f57d4daea74068a4feb77aa5630f856fcff2e50de14e9a20", 452 | "sha256:79465ce11ae5694ff165becda529a600c754f4bc459778778c7017374d4d406f" 453 | ], 454 | "version": "==1.0.2" 455 | }, 456 | "sphinxcontrib-serializinghtml": { 457 | "hashes": [ 458 | "sha256:c0efb33f8052c04fd7a26c0a07f1678e8512e0faec19f4aa8f2473a8b81d5227", 459 | "sha256:db6615af393650bf1151a6cd39120c29abaf93cc60db8c48eb2dddbfdc3a9768" 460 | ], 461 | "version": "==1.1.3" 462 | }, 463 | "toml": { 464 | "hashes": [ 465 | "sha256:229f81c57791a41d65e399fc06bf0848bab550a9dfd5ed66df18ce5f05e73d5c", 466 | "sha256:235682dd292d5899d361a811df37e04a8828a5b1da3115886b73cf81ebc9100e" 467 | ], 468 | "version": "==0.10.0" 469 | }, 470 | "tornado": { 471 | "hashes": [ 472 | "sha256:349884248c36801afa19e342a77cc4458caca694b0eda633f5878e458a44cb2c", 473 | "sha256:398e0d35e086ba38a0427c3b37f4337327231942e731edaa6e9fd1865bbd6f60", 474 | "sha256:4e73ef678b1a859f0cb29e1d895526a20ea64b5ffd510a2307b5998c7df24281", 475 | "sha256:559bce3d31484b665259f50cd94c5c28b961b09315ccd838f284687245f416e5", 476 | "sha256:abbe53a39734ef4aba061fca54e30c6b4639d3e1f59653f0da37a0003de148c7", 477 | "sha256:c845db36ba616912074c5b1ee897f8e0124df269468f25e4fe21fe72f6edd7a9", 478 | "sha256:c9399267c926a4e7c418baa5cbe91c7d1cf362d505a1ef898fde44a07c9dd8a5" 479 | ], 480 | "version": "==6.0.3" 481 | }, 482 | "traitlets": { 483 | "hashes": [ 484 | "sha256:70b4c6a1d9019d7b4f6846832288f86998aa3b9207c6821f3578a6a6a467fe44", 485 | "sha256:d023ee369ddd2763310e4c3eae1ff649689440d4ae59d7485eb4cfbbe3e359f7" 486 | ], 487 | "version": "==4.3.3" 488 | }, 489 | "urllib3": { 490 | "hashes": [ 491 | "sha256:3de946ffbed6e6746608990594d08faac602528ac7015ac28d33cee6a45b7398", 492 | "sha256:9a107b99a5393caf59c7aa3c1249c16e6879447533d0887f4336dde834c7be86" 493 | ], 494 | "version": "==1.25.6" 495 | }, 496 | "watchdog": { 497 | "hashes": [ 498 | "sha256:965f658d0732de3188211932aeb0bb457587f04f63ab4c1e33eab878e9de961d" 499 | ], 500 | "version": "==0.9.0" 501 | }, 502 | "wcwidth": { 503 | "hashes": [ 504 | "sha256:3df37372226d6e63e1b1e1eda15c594bca98a22d33a23832a90998faa96bc65e", 505 | "sha256:f4ebe71925af7b40a864553f761ed559b43544f8f71746c2d756c7fe788ade7c" 506 | ], 507 | "version": "==0.1.7" 508 | } 509 | } 510 | } 511 | -------------------------------------------------------------------------------- /README.rst: -------------------------------------------------------------------------------- 1 | .. image:: https://img.shields.io/pypi/v/django-generic-flatblocks.svg 2 | :target: https://pypi.org/project/django-generic-flatblocks/ 3 | 4 | .. image:: https://travis-ci.org/bartTC/django-generic-flatblocks.svg?branch=master 5 | :target: https://travis-ci.org/bartTC/django-generic-flatblocks 6 | 7 | .. image:: https://api.codacy.com/project/badge/Coverage/606e8ced3f0a48ee8a4b623cd8314b72 8 | :target: https://www.codacy.com/app/bartTC/django-generic-flatblocks 9 | 10 | .. image:: https://api.codacy.com/project/badge/Grade/606e8ced3f0a48ee8a4b623cd8314b72 11 | :target: https://www.codacy.com/app/bartTC/django-generic-flatblocks 12 | 13 | ---- 14 | 15 | 📖 **Full documentation: https://django-generic-flatblocks.readthedocs.io/** 16 | 17 | ========================= 18 | django-generic-flatblocks 19 | ========================= 20 | 21 | If you want to add tiny snippets of text to your site, manageable by the admin 22 | backend, you would use either `django-chunks`_ or `django-flatblocks`_. 23 | However, both of them have one problem: you are limited to a predefined 24 | content field; a "text" field in chunks and a "title" and "text" field in 25 | flatblocks. 26 | 27 | django-generic-flatblocks solves this problem as it knows nothing about the 28 | content itself. You *attach* your hand made content node (a simple model) where 29 | you can define any fields you want. 30 | 31 | .. _`django-flatblocks`: http://github.com/zerok/django-flatblocks/tree/master 32 | .. _`django-chunks`: http://code.google.com/p/django-chunks/ 33 | 34 | 35 | -------------------------------------------------------------------------------- /django_generic_flatblocks/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bartTC/django-generic-flatblocks/a5df4f12dcbcd6c4e34d4bb3b777c7702baaf6bc/django_generic_flatblocks/__init__.py -------------------------------------------------------------------------------- /django_generic_flatblocks/admin.py: -------------------------------------------------------------------------------- 1 | from django.contrib import admin 2 | from django.utils.translation import ugettext_lazy as _ 3 | 4 | from django_generic_flatblocks.models import GenericFlatblock 5 | 6 | 7 | class GenericFlatblockAdmin(admin.ModelAdmin): 8 | 9 | list_display = ('related_object_changelink', 'slug') 10 | 11 | list_display_links = ('slug',) 12 | 13 | def related_object_changelink(self, obj): 14 | return '%s - %s' % ( 15 | self.generate_related_object_admin_link(obj.content_object), 16 | obj.slug, 17 | obj.content_object.__str__(), 18 | ) 19 | 20 | related_object_changelink.allow_tags = True 21 | related_object_changelink.short_description = _('change related object') 22 | 23 | def generate_related_object_admin_link(self, related_object): 24 | return '../../%s/%s/%s/' % ( 25 | related_object._meta.app_label, 26 | related_object._meta.model_name, 27 | related_object.pk, 28 | ) 29 | 30 | def change_view(self, request, object_id, form_url='', extra_context=None): 31 | """ 32 | Haven't figured out how to edit the related object as an inline. 33 | This template adds a link to the change view of the related 34 | object.. 35 | """ 36 | related_object = self.model.objects.get(pk=object_id).content_object 37 | c = { 38 | 'admin_url': self.generate_related_object_admin_link( 39 | related_object 40 | ), 41 | 'related_object': related_object, 42 | 'related_app_label': related_object._meta.app_label, 43 | 'related_model_name': related_object._meta.model_name, 44 | } 45 | c.update(extra_context or {}) 46 | self.change_form_template = ( 47 | 'admin/django_generic_flatblocks/change_form_forward.html' 48 | ) 49 | return super(GenericFlatblockAdmin, self).change_view( 50 | request, object_id, extra_context=c 51 | ) 52 | 53 | 54 | admin.site.register(GenericFlatblock, GenericFlatblockAdmin) 55 | -------------------------------------------------------------------------------- /django_generic_flatblocks/contrib/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bartTC/django-generic-flatblocks/a5df4f12dcbcd6c4e34d4bb3b777c7702baaf6bc/django_generic_flatblocks/contrib/__init__.py -------------------------------------------------------------------------------- /django_generic_flatblocks/contrib/gblocks/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bartTC/django-generic-flatblocks/a5df4f12dcbcd6c4e34d4bb3b777c7702baaf6bc/django_generic_flatblocks/contrib/gblocks/__init__.py -------------------------------------------------------------------------------- /django_generic_flatblocks/contrib/gblocks/admin.py: -------------------------------------------------------------------------------- 1 | from django.contrib import admin 2 | 3 | from django_generic_flatblocks.contrib.gblocks.models import * 4 | 5 | admin.site.register(Title) 6 | admin.site.register(Text) 7 | admin.site.register(Image) 8 | admin.site.register(TitleAndText) 9 | admin.site.register(TitleTextAndImage) 10 | -------------------------------------------------------------------------------- /django_generic_flatblocks/contrib/gblocks/migrations/0001_initial.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.9 on 2016-03-23 12:03 3 | from __future__ import unicode_literals 4 | 5 | from django.db import migrations, models 6 | 7 | 8 | class Migration(migrations.Migration): 9 | 10 | initial = True 11 | 12 | dependencies = [ 13 | ] 14 | 15 | operations = [ 16 | migrations.CreateModel( 17 | name='Image', 18 | fields=[ 19 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), 20 | ('image', models.ImageField(blank=True, upload_to=b'gblocks/', verbose_name='image')), 21 | ], 22 | ), 23 | migrations.CreateModel( 24 | name='Text', 25 | fields=[ 26 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), 27 | ('text', models.TextField(blank=True, verbose_name='text')), 28 | ], 29 | ), 30 | migrations.CreateModel( 31 | name='Title', 32 | fields=[ 33 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), 34 | ('title', models.CharField(blank=True, max_length=255, verbose_name='title')), 35 | ], 36 | ), 37 | migrations.CreateModel( 38 | name='TitleAndText', 39 | fields=[ 40 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), 41 | ('title', models.CharField(blank=True, max_length=255, verbose_name='title')), 42 | ('text', models.TextField(blank=True, verbose_name='text')), 43 | ], 44 | ), 45 | migrations.CreateModel( 46 | name='TitleTextAndImage', 47 | fields=[ 48 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), 49 | ('title', models.CharField(blank=True, max_length=255, verbose_name='title')), 50 | ('text', models.TextField(blank=True, verbose_name='text')), 51 | ('image', models.ImageField(blank=True, upload_to=b'gblocks/', verbose_name='image')), 52 | ], 53 | ), 54 | ] 55 | -------------------------------------------------------------------------------- /django_generic_flatblocks/contrib/gblocks/migrations/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bartTC/django-generic-flatblocks/a5df4f12dcbcd6c4e34d4bb3b777c7702baaf6bc/django_generic_flatblocks/contrib/gblocks/migrations/__init__.py -------------------------------------------------------------------------------- /django_generic_flatblocks/contrib/gblocks/models.py: -------------------------------------------------------------------------------- 1 | from django.db import models 2 | from django.utils.encoding import python_2_unicode_compatible 3 | from django.utils.translation import ugettext_lazy as _ 4 | 5 | 6 | @python_2_unicode_compatible 7 | class Title(models.Model): 8 | title = models.CharField(_('title'), max_length=255, blank=True) 9 | 10 | def __str__(self): 11 | return "(TitleBlock) %s" % self.title 12 | 13 | 14 | @python_2_unicode_compatible 15 | class Text(models.Model): 16 | text = models.TextField(_('text'), blank=True) 17 | 18 | def __str__(self): 19 | return "(TextBlock) %s..." % self.text[:20] 20 | 21 | 22 | @python_2_unicode_compatible 23 | class Image(models.Model): 24 | image = models.ImageField(_('image'), upload_to='gblocks/', blank=True) 25 | 26 | def __str__(self): 27 | return "(ImageBlock) %s" % self.image 28 | 29 | 30 | @python_2_unicode_compatible 31 | class TitleAndText(models.Model): 32 | title = models.CharField(_('title'), max_length=255, blank=True) 33 | text = models.TextField(_('text'), blank=True) 34 | 35 | def __str__(self): 36 | return "(TitleAndTextBlock) %s" % self.title 37 | 38 | 39 | @python_2_unicode_compatible 40 | class TitleTextAndImage(models.Model): 41 | title = models.CharField(_('title'), max_length=255, blank=True) 42 | text = models.TextField(_('text'), blank=True) 43 | image = models.ImageField(_('image'), upload_to='gblocks/', blank=True) 44 | 45 | def __str__(self): 46 | return "(TitleTextAndImageBlock) %s" % self.title 47 | -------------------------------------------------------------------------------- /django_generic_flatblocks/contrib/gblocks/templates/gblocks/image/flatblock.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | {% if admin_url %}edit{% endif %} -------------------------------------------------------------------------------- /django_generic_flatblocks/contrib/gblocks/templates/gblocks/text/flatblock.html: -------------------------------------------------------------------------------- 1 | {{ object.text|linebreaks }} 2 | 3 | {% if admin_url %}edit{% endif %} -------------------------------------------------------------------------------- /django_generic_flatblocks/contrib/gblocks/templates/gblocks/title/flatblock.html: -------------------------------------------------------------------------------- 1 |

{{ object.title }}

2 | 3 | {% if admin_url %}edit{% endif %} -------------------------------------------------------------------------------- /django_generic_flatblocks/contrib/gblocks/templates/gblocks/titleandtext/flatblock.html: -------------------------------------------------------------------------------- 1 |

{{ object.title }}

2 | 3 | {{ object.text|safe }} 4 | 5 | {% if admin_url %}edit{% endif %} -------------------------------------------------------------------------------- /django_generic_flatblocks/contrib/gblocks/templates/gblocks/titletextandimage/flatblock.html: -------------------------------------------------------------------------------- 1 |

{{ object.title }}

2 | 3 |

4 | 5 | {{ object.text|safe }} 6 | 7 | {% if admin_url %}edit{% endif %} -------------------------------------------------------------------------------- /django_generic_flatblocks/locale/de/LC_MESSAGES/django.mo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bartTC/django-generic-flatblocks/a5df4f12dcbcd6c4e34d4bb3b777c7702baaf6bc/django_generic_flatblocks/locale/de/LC_MESSAGES/django.mo -------------------------------------------------------------------------------- /django_generic_flatblocks/locale/de/LC_MESSAGES/django.po: -------------------------------------------------------------------------------- 1 | # SOME DESCRIPTIVE TITLE. 2 | # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER 3 | # This file is distributed under the same license as the PACKAGE package. 4 | # FIRST AUTHOR , YEAR. 5 | # 6 | #, fuzzy 7 | msgid "" 8 | msgstr "" 9 | "Project-Id-Version: PACKAGE VERSION\n" 10 | "Report-Msgid-Bugs-To: \n" 11 | "POT-Creation-Date: 2009-09-08 10:29+0200\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 | 19 | #: admin.py:22 20 | msgid "change related object" 21 | msgstr "Verknüpftes Objekt ändern" 22 | 23 | #: models.py:8 24 | msgid "slug" 25 | msgstr "Slug" 26 | 27 | #: contrib/gblocks/models.py:5 contrib/gblocks/models.py:23 28 | #: contrib/gblocks/models.py:30 29 | msgid "title" 30 | msgstr "Title" 31 | 32 | #: contrib/gblocks/models.py:11 contrib/gblocks/models.py:24 33 | #: contrib/gblocks/models.py:31 34 | msgid "text" 35 | msgstr "Text" 36 | 37 | #: contrib/gblocks/models.py:17 contrib/gblocks/models.py:32 38 | msgid "image" 39 | msgstr "Bild" 40 | 41 | #: templates/admin/django_generic_flatblocks/change_form_forward.html:6 42 | msgid "Notice" 43 | msgstr "Hinweis" 44 | 45 | #: templates/admin/django_generic_flatblocks/change_form_forward.html:7 46 | #, python-format 47 | msgid "" 48 | "This object stores only the path to the related %(related_app_label)s/%" 49 | "(related_model_name)s object with the primary key %(related_pk)s." 51 | msgstr "" 52 | "Dieses Objekt enthält nur den Pfad zum verknüpften %(related_app_label)s/" 53 | "%(related_model_name)s Objekt mit dem Primärschlüssel %(related_pk)" 54 | "s." 55 | 56 | #: templates/admin/django_generic_flatblocks/change_form_forward.html:9 57 | msgid "Click this link to edit the object itself." 58 | msgstr "Klicken Sie diesen Link um das Objekt selbst zu bearbeiten." 59 | -------------------------------------------------------------------------------- /django_generic_flatblocks/locale/dk/LC_MESSAGES/django.mo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bartTC/django-generic-flatblocks/a5df4f12dcbcd6c4e34d4bb3b777c7702baaf6bc/django_generic_flatblocks/locale/dk/LC_MESSAGES/django.mo -------------------------------------------------------------------------------- /django_generic_flatblocks/locale/dk/LC_MESSAGES/django.po: -------------------------------------------------------------------------------- 1 | # django-generic-flatblocks in Danish. 2 | # django-generic-flatblocks på Dansk. 3 | # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER 4 | # This file is distributed under the same license as the PACKAGE package. 5 | # Michael Lind Mortensen , 2009. 6 | # 7 | #, fuzzy 8 | msgid "" 9 | msgstr "" 10 | "Project-Id-Version: PACKAGE VERSION\n" 11 | "Report-Msgid-Bugs-To: \n" 12 | "POT-Creation-Date: 2009-09-04 12:10+0200\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 | 20 | #: admin.py:22 21 | msgid "change related object" 22 | msgstr "ændre relateret objekt" 23 | 24 | #: models.py:8 25 | msgid "slug" 26 | msgstr "slug" 27 | 28 | #: contrib/gblocks/models.py:5 contrib/gblocks/models.py:23 29 | #: contrib/gblocks/models.py:30 30 | msgid "title" 31 | msgstr "titel" 32 | 33 | #: contrib/gblocks/models.py:11 contrib/gblocks/models.py:24 34 | #: contrib/gblocks/models.py:31 35 | msgid "text" 36 | msgstr "tekst" 37 | 38 | #: contrib/gblocks/models.py:17 contrib/gblocks/models.py:32 39 | msgid "image" 40 | msgstr "billede" 41 | 42 | #: templates/admin/django_generic_flatblocks/change_form_forward.html:6 43 | msgid "Notice" 44 | msgstr "Notifikation" 45 | 46 | #: templates/admin/django_generic_flatblocks/change_form_forward.html:7 47 | #, python-format 48 | msgid "" 49 | "This object stores only the path to the related %(related_app_label)s/%" 50 | "(related_model_name)s object with the primary key %(related_pk)s." 52 | msgstr "" 53 | "Dette objekt gemmer udelukkende stien til det relaterede %(related_app_label)s/%" 54 | "(related_model_name)s objekt med den primære nøgle %(related_pk)s." 56 | 57 | #: templates/admin/django_generic_flatblocks/change_form_forward.html:9 58 | msgid "Click this link to edit the object itself." 59 | msgstr "Klik på dette link for at ændre objektet selv." 60 | -------------------------------------------------------------------------------- /django_generic_flatblocks/locale/en/LC_MESSAGES/django.mo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bartTC/django-generic-flatblocks/a5df4f12dcbcd6c4e34d4bb3b777c7702baaf6bc/django_generic_flatblocks/locale/en/LC_MESSAGES/django.mo -------------------------------------------------------------------------------- /django_generic_flatblocks/locale/en/LC_MESSAGES/django.po: -------------------------------------------------------------------------------- 1 | # SOME DESCRIPTIVE TITLE. 2 | # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER 3 | # This file is distributed under the same license as the PACKAGE package. 4 | # FIRST AUTHOR , YEAR. 5 | # 6 | #, fuzzy 7 | msgid "" 8 | msgstr "" 9 | "Project-Id-Version: PACKAGE VERSION\n" 10 | "Report-Msgid-Bugs-To: \n" 11 | "POT-Creation-Date: 2009-09-08 10:29+0200\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 | 19 | #: admin.py:22 20 | msgid "change related object" 21 | msgstr "" 22 | 23 | #: models.py:8 24 | msgid "slug" 25 | msgstr "" 26 | 27 | #: contrib/gblocks/models.py:5 contrib/gblocks/models.py:23 28 | #: contrib/gblocks/models.py:30 29 | msgid "title" 30 | msgstr "" 31 | 32 | #: contrib/gblocks/models.py:11 contrib/gblocks/models.py:24 33 | #: contrib/gblocks/models.py:31 34 | msgid "text" 35 | msgstr "" 36 | 37 | #: contrib/gblocks/models.py:17 contrib/gblocks/models.py:32 38 | msgid "image" 39 | msgstr "" 40 | 41 | #: templates/admin/django_generic_flatblocks/change_form_forward.html:6 42 | msgid "Notice" 43 | msgstr "" 44 | 45 | #: templates/admin/django_generic_flatblocks/change_form_forward.html:7 46 | #, python-format 47 | msgid "" 48 | "This object stores only the path to the related %(related_app_label)s/%" 49 | "(related_model_name)s object with the primary key %(related_pk)s." 51 | msgstr "" 52 | 53 | #: templates/admin/django_generic_flatblocks/change_form_forward.html:9 54 | msgid "Click this link to edit the object itself." 55 | msgstr "" 56 | -------------------------------------------------------------------------------- /django_generic_flatblocks/locale/fr/LC_MESSAGES/django.mo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bartTC/django-generic-flatblocks/a5df4f12dcbcd6c4e34d4bb3b777c7702baaf6bc/django_generic_flatblocks/locale/fr/LC_MESSAGES/django.mo -------------------------------------------------------------------------------- /django_generic_flatblocks/locale/fr/LC_MESSAGES/django.po: -------------------------------------------------------------------------------- 1 | # SOME DESCRIPTIVE TITLE. 2 | # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER 3 | # This file is distributed under the same license as the PACKAGE package. 4 | # FIRST AUTHOR , YEAR. 5 | # 6 | #, fuzzy 7 | msgid "" 8 | msgstr "" 9 | "Project-Id-Version: PACKAGE VERSION\n" 10 | "Report-Msgid-Bugs-To: \n" 11 | "POT-Creation-Date: 2011-08-27 23:52-0400\n" 12 | "PO-Revision-Date: 2011-08-27 11:57+EDT\n" 13 | "Last-Translator: Maxime Haineault \n" 14 | "Language-Team: LANGUAGE \n" 15 | "Language: French\n" 16 | "MIME-Version: 1.0\n" 17 | "Content-Type: text/plain; charset=UTF-8\n" 18 | "Content-Transfer-Encoding: 8bit\n" 19 | "Plural-Forms: nplurals=2; plural=(n > 1)\n" 20 | 21 | #: admin.py:22 22 | msgid "change related object" 23 | msgstr "Modifier l'objet relié" 24 | 25 | #: models.py:7 26 | msgid "slug" 27 | msgstr "slug" 28 | 29 | #: contrib/gblocks/models.py:5 contrib/gblocks/models.py:23 30 | #: contrib/gblocks/models.py:30 31 | msgid "title" 32 | msgstr "titre" 33 | 34 | #: contrib/gblocks/models.py:11 contrib/gblocks/models.py:24 35 | #: contrib/gblocks/models.py:31 36 | msgid "text" 37 | msgstr "text" 38 | 39 | #: contrib/gblocks/models.py:17 contrib/gblocks/models.py:32 40 | msgid "image" 41 | msgstr "image" 42 | 43 | #: templates/admin/django_generic_flatblocks/change_form_forward.html:6 44 | msgid "Notice" 45 | msgstr "Note" 46 | 47 | #: templates/admin/django_generic_flatblocks/change_form_forward.html:7 48 | #, python-format 49 | msgid "" 50 | "This object stores only the path to the related %(related_app_label)s/" 51 | "%(related_model_name)s object with the primary key %(related_pk)s." 53 | msgstr "" 54 | "Cet objet ne sert que de liaison vers l'objet %(related_app_label)s/" 55 | "%(related_model_name)s avec la clé primaire %(related_pk)s." 57 | 58 | 59 | #: templates/admin/django_generic_flatblocks/change_form_forward.html:9 60 | msgid "Click this link to edit the object itself." 61 | msgstr "Cliquer ce lien pour modifier l'objet désigné." 62 | -------------------------------------------------------------------------------- /django_generic_flatblocks/migrations/0001_initial.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.9 on 2016-03-23 12:03 3 | from __future__ import unicode_literals 4 | 5 | from django.db import migrations, models 6 | import django.db.models.deletion 7 | 8 | 9 | class Migration(migrations.Migration): 10 | 11 | initial = True 12 | 13 | dependencies = [ 14 | ('contenttypes', '0002_remove_content_type_name'), 15 | ] 16 | 17 | operations = [ 18 | migrations.CreateModel( 19 | name='GenericFlatblock', 20 | fields=[ 21 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), 22 | ('slug', models.SlugField(max_length=255, unique=True, verbose_name='slug')), 23 | ('object_id', models.PositiveIntegerField()), 24 | ('content_type', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='contenttypes.ContentType')), 25 | ], 26 | ), 27 | ] 28 | -------------------------------------------------------------------------------- /django_generic_flatblocks/migrations/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bartTC/django-generic-flatblocks/a5df4f12dcbcd6c4e34d4bb3b777c7702baaf6bc/django_generic_flatblocks/migrations/__init__.py -------------------------------------------------------------------------------- /django_generic_flatblocks/models.py: -------------------------------------------------------------------------------- 1 | from django.contrib.contenttypes.fields import GenericForeignKey 2 | from django.contrib.contenttypes.models import ContentType 3 | from django.db import models 4 | from django.utils.encoding import python_2_unicode_compatible 5 | from django.utils.translation import ugettext_lazy as _ 6 | 7 | 8 | @python_2_unicode_compatible 9 | class GenericFlatblock(models.Model): 10 | slug = models.SlugField(_('slug'), max_length=255, unique=True) 11 | content_type = models.ForeignKey(ContentType, on_delete=models.CASCADE) 12 | object_id = models.PositiveIntegerField() 13 | content_object = GenericForeignKey('content_type', 'object_id') 14 | 15 | def __str__(self): 16 | return self.slug 17 | -------------------------------------------------------------------------------- /django_generic_flatblocks/templates/admin/django_generic_flatblocks/change_form_forward.html: -------------------------------------------------------------------------------- 1 | {% extends "admin/change_form.html" %} 2 | {% load i18n %} 3 | 4 | {% block content %} 5 |

6 | {% trans "Notice" %}: 7 | {% blocktrans with related_object.pk as related_pk %}This object stores only the path to the related {{related_app_label}}/{{related_model_name}} object with the primary key {{ related_pk }}.{% endblocktrans %}

8 |

{% trans "Click this link to edit the object itself." %} 10 | {{ block.super }} 11 | {% endblock %} 12 | -------------------------------------------------------------------------------- /django_generic_flatblocks/templatetags/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bartTC/django-generic-flatblocks/a5df4f12dcbcd6c4e34d4bb3b777c7702baaf6bc/django_generic_flatblocks/templatetags/__init__.py -------------------------------------------------------------------------------- /django_generic_flatblocks/templatetags/generic_flatblocks.py: -------------------------------------------------------------------------------- 1 | from django import VERSION 2 | from django.apps import apps 3 | from django.conf import settings 4 | from django.template import ( 5 | Library, 6 | Node, 7 | Variable, 8 | ) 9 | from django.template.defaultfilters import slugify 10 | from django.template.loader import select_template 11 | 12 | from django_generic_flatblocks.models import GenericFlatblock 13 | 14 | from logging import getLogger 15 | 16 | logger = getLogger(__file__) 17 | register = Library() 18 | DJANGO_20 = VERSION[0] >= 2 19 | 20 | 21 | class GenericFlatblockNode(Node): 22 | def __init__( 23 | self, 24 | slug, 25 | modelname=None, 26 | template_path=None, 27 | variable_name=None, 28 | store_in_object=None, 29 | ): 30 | self.slug = slug 31 | self.modelname = modelname 32 | self.template_path = template_path 33 | self.variable_name = variable_name 34 | self.store_in_object = store_in_object 35 | 36 | def generate_slug(self, slug, context): 37 | """ 38 | Generates a slug out of a comma-separated string. Automatically resolves 39 | variables in it. Examples:: 40 | 41 | "website","title" -> website_title 42 | "website",LANGUAGE_CODE -> website_en 43 | """ 44 | # If the user passed a integer as slug, use it as a primary key in 45 | # self.get_content_object() 46 | if not ',' in slug and isinstance(self.resolve(slug, context), int): 47 | return self.resolve(slug, context) 48 | return slugify( 49 | '_'.join([str(self.resolve(i, context)) for i in slug.split(',')]) 50 | ) 51 | 52 | def generate_admin_link(self, related_object, context): 53 | """ 54 | Generates a link to contrib.admin change view. In Django 1.1 this 55 | will work automatically using urlresolvers. 56 | """ 57 | app_label = related_object._meta.app_label 58 | model_name = related_object._meta.model_name 59 | 60 | # Check if user has change permissions 61 | if DJANGO_20: 62 | # Django 1.10+ made is_authenticated a property, 2.0 removed 63 | # the fallback. 64 | authenticated = context['request'].user.is_authenticated 65 | else: 66 | authenticated = context['request'].user.is_authenticated() 67 | if authenticated and context['request'].user.has_perm( 68 | '%s.change' % model_name 69 | ): 70 | admin_url_prefix = getattr(settings, 'ADMIN_URL_PREFIX', '/admin/') 71 | return '%s%s/%s/%s/' % ( 72 | admin_url_prefix, 73 | app_label, 74 | model_name, 75 | related_object.pk, 76 | ) 77 | else: 78 | return None 79 | 80 | def get_content_object(self, related_model, slug): 81 | 82 | # If the user passed a Integer as a slug, assume that we should fetch 83 | # this specific object 84 | if isinstance(slug, int): 85 | try: 86 | related_object = related_model._default_manager.get(pk=slug) 87 | return None, related_object 88 | except related_model.DoesNotExist: 89 | if settings.DEBUG: 90 | raise 91 | related_object = related_model() 92 | return None, related_object 93 | 94 | # Otherwise, try to generate a new, related object 95 | try: 96 | generic_object = GenericFlatblock._default_manager.get(slug=slug) 97 | related_object = generic_object.content_object 98 | if related_object is None: 99 | # The related object must have been deleted. Let's start over. 100 | generic_object.delete() 101 | raise GenericFlatblock.DoesNotExist 102 | except GenericFlatblock.DoesNotExist: 103 | related_object = related_model._default_manager.create() 104 | generic_object = GenericFlatblock._default_manager.create( 105 | slug=slug, content_object=related_object 106 | ) 107 | return generic_object, related_object 108 | 109 | def resolve(self, var, context): 110 | """Resolves a variable out of context if it's not in quotes""" 111 | if var[0] in ('"', "'") and var[-1] == var[0]: 112 | return var[1:-1] 113 | else: 114 | return Variable(var).resolve(context) 115 | 116 | def resolve_model_for_label(self, modelname, context): 117 | """resolves a model for a applabel.modelname string""" 118 | applabel, modellabel = self.resolve(modelname, context).split(".") 119 | related_model = apps.get_model(applabel, modellabel) 120 | return related_model 121 | 122 | def render(self, context): 123 | 124 | slug = self.generate_slug(self.slug, context) 125 | related_model = self.resolve_model_for_label(self.modelname, context) 126 | 127 | # Get the generic and related object 128 | generic_object, related_object = self.get_content_object( 129 | related_model, slug 130 | ) 131 | admin_url = self.generate_admin_link(related_object, context) 132 | 133 | # if "into" is provided, store the related object into this variable 134 | if self.store_in_object: 135 | into_var = self.resolve(self.store_in_object, context) 136 | context[into_var] = related_object 137 | context["%s_generic_object" % into_var] = generic_object 138 | context["%s_admin_url" % into_var] = admin_url 139 | return '' 140 | 141 | # Add the model instances to the current context 142 | context['generic_object'] = generic_object 143 | context['object'] = related_object 144 | context['admin_url'] = admin_url 145 | 146 | # Resolve the template(s) 147 | template_paths = [] 148 | if self.template_path: 149 | template_paths.append(self.resolve(self.template_path, context)) 150 | template_paths.append( 151 | '%s/%s/flatblock.html' 152 | % tuple(self.resolve(self.modelname, context).lower().split(".")) 153 | ) 154 | 155 | try: 156 | t = select_template(template_paths) 157 | except Exception as e: 158 | logger.exception(e) 159 | if settings.DEBUG: 160 | raise e 161 | return '' 162 | 163 | content = t.template.render(context) 164 | 165 | # Set content as variable inside context, if variable_name is given 166 | if self.variable_name: 167 | context[self.resolve(self.variable_name, context)] = content 168 | return '' 169 | return content 170 | 171 | 172 | def do_genericflatblock(parser, token): 173 | """ 174 | {% gblock "slug" for "appname.modelname" %} 175 | {% gblock "slug" for "appname.modelname" into "slug_object" %} 176 | {% gblock "slug" for "appname.modelname" with "templatename.html" %} 177 | {% gblock "slug" for "appname.modelname" with "templatename.html" as "variable" %} 178 | """ 179 | 180 | def next_bit_for(bits, key, if_none=None): 181 | try: 182 | return bits[bits.index(key) + 1] 183 | except ValueError: 184 | return if_none 185 | 186 | bits = token.contents.split() 187 | args = { 188 | 'slug': next_bit_for(bits, 'gblock'), 189 | 'modelname': next_bit_for(bits, 'for'), 190 | 'template_path': next_bit_for(bits, 'with'), 191 | 'variable_name': next_bit_for(bits, 'as'), 192 | 'store_in_object': next_bit_for(bits, 'into'), 193 | } 194 | return GenericFlatblockNode(**args) 195 | 196 | 197 | register.tag('gblock', do_genericflatblock) 198 | -------------------------------------------------------------------------------- /django_generic_flatblocks/tests/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bartTC/django-generic-flatblocks/a5df4f12dcbcd6c4e34d4bb3b777c7702baaf6bc/django_generic_flatblocks/tests/__init__.py -------------------------------------------------------------------------------- /django_generic_flatblocks/tests/templates/auth/user/flatblock.html: -------------------------------------------------------------------------------- 1 | {{ object.username }} -------------------------------------------------------------------------------- /django_generic_flatblocks/tests/templates/auth/user/flatblock_firstname.html: -------------------------------------------------------------------------------- 1 | {{ object.first_name }} -------------------------------------------------------------------------------- /django_generic_flatblocks/tests/templates/test_template.html: -------------------------------------------------------------------------------- 1 | {{ object.title }} 2 | {{ admin_url }} -------------------------------------------------------------------------------- /django_generic_flatblocks/tests/test_flatblocks.py: -------------------------------------------------------------------------------- 1 | from django.conf import settings 2 | from django.template import Template, TemplateDoesNotExist, TemplateSyntaxError 3 | from django.template.context import Context 4 | from django.test import TestCase 5 | 6 | 7 | class HttpRequest(object): 8 | """ 9 | Stupid simple HttpRequest class to store a user. May need to be expanded 10 | in the future to act more like the real deal. 11 | """ 12 | 13 | def __init__(self, user): 14 | self.user = user 15 | 16 | 17 | class GenericFlatblocksTestCase(TestCase): 18 | def setUp(self): 19 | # Create a dummy user 20 | from django.contrib.auth.models import User 21 | 22 | dummy_user = User.objects.create_user( 23 | u'johndoe', u'john@example.com', u'foobar' 24 | ) 25 | dummy_user.first_name = u'John' 26 | dummy_user.last_naem = u'Doe' 27 | dummy_user.save() 28 | 29 | self.assertEqual(dummy_user.pk, 1) 30 | self.dummy_user = dummy_user 31 | 32 | self.admin_user = User.objects.create_superuser( 33 | u'admin', u'admin@example.com', u'foobar' 34 | ) 35 | 36 | def tearDown(self): 37 | from django.contrib.auth.models import User 38 | 39 | User.objects.all().delete() 40 | 41 | def parseTemplate(self, template_string, admin_user=False): 42 | user = admin_user and self.admin_user or self.dummy_user 43 | t = Template(template_string) 44 | c = Context( 45 | {'user': user, 'request': HttpRequest(user), 'LANGUAGE_CODE': 'en'} 46 | ) 47 | return t.render(c) 48 | 49 | def testModelBehaviour(self): 50 | """ 51 | Direct flatblock creation. 52 | """ 53 | from django_generic_flatblocks.models import GenericFlatblock 54 | from django_generic_flatblocks.contrib.gblocks.models import Title 55 | 56 | title_obj = Title.objects.create(title=u'Hello World') 57 | generic_obj = GenericFlatblock() 58 | generic_obj.slug = u'hello_world' 59 | generic_obj.content_object = title_obj 60 | generic_obj.save() 61 | 62 | self.assertEqual(generic_obj.slug, u'hello_world') 63 | self.assertEqual(generic_obj.content_object, title_obj) 64 | self.assertEqual(generic_obj.__str__(), u'hello_world') 65 | 66 | def testSlugArgument(self): 67 | from django_generic_flatblocks.models import GenericFlatblock 68 | 69 | template_string = ''' 70 | {% load generic_flatblocks %} 71 | {% gblock "title" for "gblocks.Title" %} 72 | {% gblock "title","foo" for "gblocks.Title" %} 73 | {% gblock "title","foo",LANGUAGE_CODE for "gblocks.Title" %} 74 | {% gblock "user",user.pk for "gblocks.Title" %} 75 | {% gblock "int","slug",1 for "gblocks.Title" %} 76 | ''' 77 | self.parseTemplate(template_string) 78 | self.assertTrue(GenericFlatblock.objects.get(slug='title')) 79 | self.assertTrue(GenericFlatblock.objects.get(slug='title_foo')) 80 | self.assertTrue(GenericFlatblock.objects.get(slug='title_foo_en')) 81 | self.assertTrue(GenericFlatblock.objects.get(slug='user_1')) 82 | self.assertTrue(GenericFlatblock.objects.get(slug='int_slug_1')) 83 | 84 | def testSlugArgumentWithInteger(self): 85 | # Integer slug 86 | template_string = ''' 87 | {% load generic_flatblocks %} 88 | {% gblock 1 for "auth.user" %} 89 | ''' 90 | t = self.parseTemplate(template_string) 91 | self.assertTrue(self.dummy_user.username in t) 92 | 93 | # Integer slug with custom template 94 | template_string = ''' 95 | {% load generic_flatblocks %} 96 | {% gblock 1 for "auth.user" with "auth/user/flatblock_firstname.html" %} 97 | ''' 98 | t = self.parseTemplate(template_string) 99 | self.assertTrue(self.dummy_user.first_name in t) 100 | 101 | # Non exisiting integer, empty template 102 | template_string = ''' 103 | {% load generic_flatblocks %} 104 | {% gblock 5 for "auth.user" %} 105 | ''' 106 | t = self.parseTemplate(template_string) 107 | self.assertFalse(t.strip()) 108 | 109 | def testForArgument(self): 110 | template_string = ''' 111 | {% load generic_flatblocks %} 112 | {% gblock "title" for "foo.Bar" %} 113 | ''' 114 | self.assertRaises(LookupError, self.parseTemplate, template_string) 115 | 116 | # Missing for argument 117 | template_string = ''' 118 | {% load generic_flatblocks %} 119 | {% gblock "title" %} 120 | ''' 121 | self.assertRaises(TypeError, self.parseTemplate, template_string) 122 | 123 | def testWithArgument(self): 124 | # With template string 125 | template_string = ''' 126 | {% load generic_flatblocks %} 127 | {% gblock "title" for "gblocks.Title" with "test_template.html" %} 128 | ''' 129 | t = self.parseTemplate(template_string) 130 | self.assertTrue(u'' in t) 131 | 132 | # Non existing templates should fallback to the default one 133 | template_string = ''' 134 | {% load generic_flatblocks %} 135 | {% gblock "title" for "gblocks.Title" with "test_template_doesnotexist.html" %} 136 | ''' 137 | t = self.parseTemplate(template_string) 138 | self.assertTrue(u'

' in t) 139 | 140 | # Raise exception if the template does not exist if DEBUG is True 141 | settings.DEBUG = True 142 | template_string = ''' 143 | {% load generic_flatblocks %} 144 | {% gblock "title" for "auth.Permission" %} 145 | ''' 146 | self.assertRaises( 147 | (TemplateDoesNotExist, TemplateSyntaxError), 148 | self.parseTemplate, 149 | template_string, 150 | ) 151 | settings.DEBUG = False 152 | 153 | # Fail silently if the template does not exist but DEBUG is False 154 | template_string = ''' 155 | {% load generic_flatblocks %} 156 | {% gblock "title" for "auth.Permission" %} 157 | ''' 158 | self.assertTrue(self.parseTemplate(template_string).strip() == u'') 159 | 160 | def testAsArgument(self): 161 | template_string = ''' 162 | {% load generic_flatblocks %} 163 | {% gblock "title" for "gblocks.Title" as "title_object" %} 164 |
{{ title_object }}
165 | ''' 166 | t = self.parseTemplate(template_string) 167 | t = ''.join(t.splitlines()) # Remove spaces 168 | self.assertTrue(u'

' in t) 169 | 170 | def testIntoArgument(self): 171 | template_string = ''' 172 | {% load generic_flatblocks %} 173 | {% gblock "title" for "gblocks.Title" into "title_object" %} 174 | {{ title_object.title }} 175 | ''' 176 | self.parseTemplate(template_string) 177 | 178 | from django_generic_flatblocks.models import GenericFlatblock 179 | 180 | o = GenericFlatblock.objects.get(slug='title') 181 | o.content_object.title = u'Into Argument' 182 | o.content_object.save() 183 | 184 | t = self.parseTemplate(template_string) 185 | self.assertTrue(u'Into Argument' in t) 186 | 187 | template_string = ''' 188 | {% load generic_flatblocks %} 189 | {% gblock 1 for "auth.User" into "the_user" %} 190 | {{ the_user.username }} 191 | ''' 192 | t = self.parseTemplate(template_string) 193 | self.assertTrue(u'johndoe' in t) 194 | 195 | template_string = ''' 196 | {% load generic_flatblocks %} 197 | {% gblock 5 for "auth.User" into "the_user" %} 198 | {{ the_user.username }} 199 | ''' 200 | t = self.parseTemplate(template_string) 201 | self.assertTrue(u'' in t) 202 | 203 | from django.contrib.auth.models import User 204 | 205 | settings.DEBUG = True 206 | template_string = ''' 207 | {% load generic_flatblocks %} 208 | {% gblock 5 for "auth.User" into "the_user" %} 209 | {{ the_user.username }} 210 | ''' 211 | self.assertRaises( 212 | (User.DoesNotExist, TemplateSyntaxError), 213 | self.parseTemplate, 214 | template_string, 215 | ) 216 | settings.DEBUG = False 217 | 218 | def testRelatedObjectDeletion(self): 219 | template_string = ''' 220 | {% load generic_flatblocks %} 221 | {% gblock "title" for "gblocks.Title" into "title_object" %} 222 | {{ title_object.title }} 223 | ''' 224 | self.parseTemplate(template_string) 225 | 226 | from django_generic_flatblocks.models import GenericFlatblock 227 | 228 | o = GenericFlatblock.objects.get(slug='title') 229 | old_content_object = o.content_object 230 | o.content_object.delete() 231 | 232 | template_string = ''' 233 | {% load generic_flatblocks %} 234 | {% gblock "title" for "gblocks.Title" into "title_object" %} 235 | {{ title_object.title }} 236 | ''' 237 | self.parseTemplate(template_string) 238 | o = GenericFlatblock.objects.get(slug='title') 239 | self.assertNotEqual(old_content_object, o.content_object) 240 | 241 | def testContributedModels(self): 242 | 243 | template_string = ''' 244 | {% load generic_flatblocks %} 245 | {% gblock "title" for "gblocks.Title" %} 246 | {% gblock "text" for "gblocks.Text" %} 247 | {% gblock "image" for "gblocks.Image" %} 248 | {% gblock "title_and_text" for "gblocks.TitleAndText" %} 249 | {% gblock "title_text_and_image" for "gblocks.TitleTextAndImage" %} 250 | ''' 251 | self.parseTemplate(template_string) 252 | 253 | from django_generic_flatblocks.contrib.gblocks import models 254 | from django_generic_flatblocks.models import GenericFlatblock 255 | 256 | self.assertTrue( 257 | isinstance( 258 | GenericFlatblock.objects.get(slug=u'title').content_object, 259 | models.Title, 260 | ) 261 | ) 262 | self.assertTrue( 263 | isinstance( 264 | GenericFlatblock.objects.get(slug=u'text').content_object, 265 | models.Text, 266 | ) 267 | ) 268 | self.assertTrue( 269 | isinstance( 270 | GenericFlatblock.objects.get(slug=u'image').content_object, 271 | models.Image, 272 | ) 273 | ) 274 | self.assertTrue( 275 | isinstance( 276 | GenericFlatblock.objects.get( 277 | slug=u'title_and_text' 278 | ).content_object, 279 | models.TitleAndText, 280 | ) 281 | ) 282 | self.assertTrue( 283 | isinstance( 284 | GenericFlatblock.objects.get( 285 | slug=u'title_text_and_image' 286 | ).content_object, 287 | models.TitleTextAndImage, 288 | ) 289 | ) 290 | 291 | # Test str method of contributed models 292 | self.assertTrue( 293 | GenericFlatblock.objects.get(slug=u'title').content_object.__str__() 294 | ) 295 | self.assertTrue( 296 | GenericFlatblock.objects.get(slug=u'text').content_object.__str__() 297 | ) 298 | self.assertTrue( 299 | GenericFlatblock.objects.get(slug=u'image').content_object.__str__() 300 | ) 301 | self.assertTrue( 302 | GenericFlatblock.objects.get( 303 | slug=u'title_and_text' 304 | ).content_object.__str__() 305 | ) 306 | self.assertTrue( 307 | GenericFlatblock.objects.get( 308 | slug=u'title_text_and_image' 309 | ).content_object.__str__() 310 | ) 311 | 312 | def testAdminLink(self): 313 | template_string = ''' 314 | {% load generic_flatblocks %} 315 | {% gblock "title" for "gblocks.Title" with "test_template.html" %} 316 | ''' 317 | t = self.parseTemplate(template_string, admin_user=True) 318 | self.assertTrue("/admin/gblocks/title/1/" in t) 319 | 320 | # The admin link gets appended to the "into" argument 321 | template_string = ''' 322 | {% load generic_flatblocks %} 323 | {% gblock "title" for "gblocks.Title" into "title_object" %} 324 | {{ title_object_admin_url }} 325 | ''' 326 | t = self.parseTemplate(template_string, admin_user=True) 327 | self.assertTrue("/admin/gblocks/title/1/" in t) 328 | 329 | # You can define the admin prefix using a setting 330 | from django.conf import settings 331 | 332 | settings.ADMIN_URL_PREFIX = '/secret-admin-url/' 333 | 334 | template_string = ''' 335 | {% load generic_flatblocks %} 336 | {% gblock "title" for "gblocks.Title" with "test_template.html" %} 337 | ''' 338 | t = self.parseTemplate(template_string, admin_user=True) 339 | self.assertTrue("/secret-admin-url/gblocks/title/1/" in t) 340 | 341 | template_string = ''' 342 | {% load generic_flatblocks %} 343 | {% gblock "title" for "gblocks.Title" into "title_object" %} 344 | {{ title_object_admin_url }} 345 | ''' 346 | t = self.parseTemplate(template_string, admin_user=True) 347 | self.assertTrue("/secret-admin-url/gblocks/title/1/" in t) 348 | -------------------------------------------------------------------------------- /django_generic_flatblocks/tests/testapp/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bartTC/django-generic-flatblocks/a5df4f12dcbcd6c4e34d4bb3b777c7702baaf6bc/django_generic_flatblocks/tests/testapp/__init__.py -------------------------------------------------------------------------------- /django_generic_flatblocks/tests/testapp/settings.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | DEBUG = True 4 | 5 | TESTAPP_DIR = os.path.abspath(os.path.dirname(__file__)) 6 | 7 | SECRET_KEY = 'testsecretkey' 8 | 9 | ALLOWED_HOSTS = ['*'] 10 | 11 | EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend' 12 | 13 | DATABASES = { 14 | 'default': { 15 | 'ENGINE': 'django.db.backends.sqlite3', 16 | 'NAME': os.path.join(TESTAPP_DIR, 'testdb.sqlite'), 17 | } 18 | } 19 | 20 | STATIC_ROOT = os.path.join(TESTAPP_DIR, '.static') 21 | MEDIA_ROOT = os.path.join(TESTAPP_DIR, '.uploads') 22 | 23 | STATIC_URL = '/static/' 24 | MEDIA_URL = '/uploads/' 25 | 26 | ROOT_URLCONF = 'django_generic_flatblocks.tests.testapp.urls' 27 | 28 | INSTALLED_APPS = [ 29 | 'django_generic_flatblocks', 30 | 'django_generic_flatblocks.tests', 31 | 'django_generic_flatblocks.contrib.gblocks', 32 | 'django.contrib.admin', 33 | 'django.contrib.auth', 34 | 'django.contrib.contenttypes', 35 | 'django.contrib.sessions', 36 | 'django.contrib.messages', 37 | ] 38 | 39 | MIDDLEWARE = ( 40 | 'django.contrib.sessions.middleware.SessionMiddleware', 41 | 'django.contrib.auth.middleware.AuthenticationMiddleware', 42 | 'django.contrib.messages.middleware.MessageMiddleware', 43 | ) 44 | 45 | MIDDLEWARE_CLASSES = MIDDLEWARE 46 | 47 | TEMPLATES = [ 48 | { 49 | 'BACKEND': 'django.template.backends.django.DjangoTemplates', 50 | 'DIRS': [], 51 | 'APP_DIRS': True, 52 | 'OPTIONS': { 53 | 'context_processors': [ 54 | 'django.template.context_processors.debug', 55 | 'django.template.context_processors.request', 56 | 'django.template.context_processors.i18n', 57 | 'django.contrib.auth.context_processors.auth', 58 | 'django.contrib.messages.context_processors.messages', 59 | ] 60 | }, 61 | } 62 | ] 63 | -------------------------------------------------------------------------------- /django_generic_flatblocks/tests/testapp/urls.py: -------------------------------------------------------------------------------- 1 | from django.conf import settings 2 | from django.conf.urls import url 3 | from django.conf.urls.static import static 4 | from django.contrib import admin 5 | 6 | admin.autodiscover() 7 | 8 | urlpatterns = [url(r'^admin/', admin.site.urls)] + static( 9 | settings.STATIC_URL, document_root=settings.STATIC_ROOT 10 | ) 11 | -------------------------------------------------------------------------------- /docs/changelog.rst: -------------------------------------------------------------------------------- 1 | .. _changelog: 2 | 3 | .. include:: ../CHANGELOG.rst 4 | -------------------------------------------------------------------------------- /docs/conf.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # 3 | # django-generic-flatblocks documentation build configuration file, created by 4 | # sphinx-quickstart on Wed Mar 23 12:45:19 2016. 5 | # 6 | # This file is execfile()d with the current directory set to its 7 | # containing dir. 8 | # 9 | # Note that not all possible configuration values are present in this 10 | # autogenerated file. 11 | # 12 | # All configuration values have a default; values that are commented out 13 | # serve to show the default. 14 | 15 | import sys 16 | import os 17 | 18 | # If extensions (or modules to document with autodoc) are in another directory, 19 | # add these directories to sys.path here. If the directory is relative to the 20 | # documentation root, use os.path.abspath to make it absolute, like shown here. 21 | #sys.path.insert(0, os.path.abspath('.')) 22 | 23 | # -- General configuration ------------------------------------------------ 24 | 25 | # If your documentation needs a minimal Sphinx version, state it here. 26 | #needs_sphinx = '1.0' 27 | 28 | # Add any Sphinx extension module names here, as strings. They can be 29 | # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom 30 | # ones. 31 | extensions = [ 32 | 'sphinx.ext.autodoc', 33 | 'sphinx.ext.viewcode', 34 | ] 35 | 36 | # Add any paths that contain templates here, relative to this directory. 37 | templates_path = ['_templates'] 38 | 39 | # The suffix(es) of source filenames. 40 | # You can specify multiple suffix as a list of string: 41 | # source_suffix = ['.rst', '.md'] 42 | source_suffix = '.rst' 43 | 44 | # The encoding of source files. 45 | #source_encoding = 'utf-8-sig' 46 | 47 | # The master toctree document. 48 | master_doc = 'index' 49 | 50 | # General information about the project. 51 | project = u'django-generic-flatblocks' 52 | copyright = u'2016, Martin Mahner' 53 | author = u'Martin Mahner' 54 | 55 | # The version info for the project you're documenting, acts as replacement for 56 | # |version| and |release|, also used in various other places throughout the 57 | # built documents. 58 | # 59 | # The short X.Y version. 60 | #version = u'1.0' 61 | # The full version, including alpha/beta/rc tags. 62 | #release = u'1.0' 63 | 64 | # The language for content autogenerated by Sphinx. Refer to documentation 65 | # for a list of supported languages. 66 | # 67 | # This is also used if you do content translation via gettext catalogs. 68 | # Usually you set "language" from the command line for these cases. 69 | language = None 70 | 71 | # There are two options for replacing |today|: either, you set today to some 72 | # non-false value, then it is used: 73 | #today = '' 74 | # Else, today_fmt is used as the format for a strftime call. 75 | #today_fmt = '%B %d, %Y' 76 | 77 | # List of patterns, relative to source directory, that match files and 78 | # directories to ignore when looking for source files. 79 | exclude_patterns = ['_build'] 80 | 81 | # The reST default role (used for this markup: `text`) to use for all 82 | # documents. 83 | #default_role = None 84 | 85 | # If true, '()' will be appended to :func: etc. cross-reference text. 86 | #add_function_parentheses = True 87 | 88 | # If true, the current module name will be prepended to all description 89 | # unit titles (such as .. function::). 90 | #add_module_names = True 91 | 92 | # If true, sectionauthor and moduleauthor directives will be shown in the 93 | # output. They are ignored by default. 94 | #show_authors = False 95 | 96 | # The name of the Pygments (syntax highlighting) style to use. 97 | pygments_style = 'sphinx' 98 | 99 | # A list of ignored prefixes for module index sorting. 100 | #modindex_common_prefix = [] 101 | 102 | # If true, keep warnings as "system message" paragraphs in the built documents. 103 | #keep_warnings = False 104 | 105 | # If true, `todo` and `todoList` produce output, else they produce nothing. 106 | todo_include_todos = False 107 | 108 | 109 | # -- Options for HTML output ---------------------------------------------- 110 | 111 | import sphinx_rtd_theme 112 | html_theme = "sphinx_rtd_theme" 113 | html_theme_path = [sphinx_rtd_theme.get_html_theme_path()] 114 | 115 | # The theme to use for HTML and HTML Help pages. See the documentation for 116 | # a list of builtin themes. 117 | #html_theme = 'nature' 118 | 119 | # Theme options are theme-specific and customize the look and feel of a theme 120 | # further. For a list of options available for each theme, see the 121 | # documentation. 122 | #html_theme_options = {} 123 | 124 | # Add any paths that contain custom themes here, relative to this directory. 125 | #html_theme_path = [] 126 | 127 | # The name for this set of Sphinx documents. If None, it defaults to 128 | # " v documentation". 129 | #html_title = None 130 | 131 | # A shorter title for the navigation bar. Default is the same as html_title. 132 | #html_short_title = None 133 | 134 | # The name of an image file (relative to this directory) to place at the top 135 | # of the sidebar. 136 | #html_logo = None 137 | 138 | # The name of an image file (relative to this directory) to use as a favicon of 139 | # the docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 140 | # pixels large. 141 | #html_favicon = None 142 | 143 | # Add any paths that contain custom static files (such as style sheets) here, 144 | # relative to this directory. They are copied after the builtin static files, 145 | # so a file named "default.css" will overwrite the builtin "default.css". 146 | #html_static_path = ['_static'] 147 | 148 | # Add any extra paths that contain custom files (such as robots.txt or 149 | # .htaccess) here, relative to this directory. These files are copied 150 | # directly to the root of the documentation. 151 | #html_extra_path = [] 152 | 153 | # If not '', a 'Last updated on:' timestamp is inserted at every page bottom, 154 | # using the given strftime format. 155 | #html_last_updated_fmt = '%b %d, %Y' 156 | 157 | # If true, SmartyPants will be used to convert quotes and dashes to 158 | # typographically correct entities. 159 | #html_use_smartypants = True 160 | 161 | # Custom sidebar templates, maps document names to template names. 162 | #html_sidebars = {} 163 | 164 | # Additional templates that should be rendered to pages, maps page names to 165 | # template names. 166 | #html_additional_pages = {} 167 | 168 | # If false, no module index is generated. 169 | #html_domain_indices = True 170 | 171 | # If false, no index is generated. 172 | #html_use_index = True 173 | 174 | # If true, the index is split into individual pages for each letter. 175 | #html_split_index = False 176 | 177 | # If true, links to the reST sources are added to the pages. 178 | #html_show_sourcelink = True 179 | 180 | # If true, "Created using Sphinx" is shown in the HTML footer. Default is True. 181 | #html_show_sphinx = True 182 | 183 | # If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. 184 | #html_show_copyright = True 185 | 186 | # If true, an OpenSearch description file will be output, and all pages will 187 | # contain a tag referring to it. The value of this option must be the 188 | # base URL from which the finished HTML is served. 189 | #html_use_opensearch = '' 190 | 191 | # This is the file name suffix for HTML files (e.g. ".xhtml"). 192 | #html_file_suffix = None 193 | 194 | # Language to be used for generating the HTML full-text search index. 195 | # Sphinx supports the following languages: 196 | # 'da', 'de', 'en', 'es', 'fi', 'fr', 'hu', 'it', 'ja' 197 | # 'nl', 'no', 'pt', 'ro', 'ru', 'sv', 'tr' 198 | #html_search_language = 'en' 199 | 200 | # A dictionary with options for the search language support, empty by default. 201 | # Now only 'ja' uses this config value 202 | #html_search_options = {'type': 'default'} 203 | 204 | # The name of a javascript file (relative to the configuration directory) that 205 | # implements a search results scorer. If empty, the default will be used. 206 | #html_search_scorer = 'scorer.js' 207 | 208 | # Output file base name for HTML help builder. 209 | htmlhelp_basename = 'django-generic-flatblocksdoc' 210 | 211 | # -- Options for LaTeX output --------------------------------------------- 212 | 213 | latex_elements = { 214 | # The paper size ('letterpaper' or 'a4paper'). 215 | #'papersize': 'letterpaper', 216 | 217 | # The font size ('10pt', '11pt' or '12pt'). 218 | #'pointsize': '10pt', 219 | 220 | # Additional stuff for the LaTeX preamble. 221 | #'preamble': '', 222 | 223 | # Latex figure (float) alignment 224 | #'figure_align': 'htbp', 225 | } 226 | 227 | # Grouping the document tree into LaTeX files. List of tuples 228 | # (source start file, target name, title, 229 | # author, documentclass [howto, manual, or own class]). 230 | latex_documents = [ 231 | (master_doc, 'django-generic-flatblocks.tex', u'django-generic-flatblocks Documentation', 232 | u'Martin Mahner', 'manual'), 233 | ] 234 | 235 | # The name of an image file (relative to this directory) to place at the top of 236 | # the title page. 237 | #latex_logo = None 238 | 239 | # For "manual" documents, if this is true, then toplevel headings are parts, 240 | # not chapters. 241 | #latex_use_parts = False 242 | 243 | # If true, show page references after internal links. 244 | #latex_show_pagerefs = False 245 | 246 | # If true, show URL addresses after external links. 247 | #latex_show_urls = False 248 | 249 | # Documents to append as an appendix to all manuals. 250 | #latex_appendices = [] 251 | 252 | # If false, no module index is generated. 253 | #latex_domain_indices = True 254 | 255 | 256 | # -- Options for manual page output --------------------------------------- 257 | 258 | # One entry per manual page. List of tuples 259 | # (source start file, name, description, authors, manual section). 260 | man_pages = [ 261 | (master_doc, 'django-generic-flatblocks', u'django-generic-flatblocks Documentation', 262 | [author], 1) 263 | ] 264 | 265 | # If true, show URL addresses after external links. 266 | #man_show_urls = False 267 | 268 | 269 | # -- Options for Texinfo output ------------------------------------------- 270 | 271 | # Grouping the document tree into Texinfo files. List of tuples 272 | # (source start file, target name, title, author, 273 | # dir menu entry, description, category) 274 | texinfo_documents = [ 275 | (master_doc, 'django-generic-flatblocks', u'django-generic-flatblocks Documentation', 276 | author, 'django-generic-flatblocks', 'One line description of project.', 277 | 'Miscellaneous'), 278 | ] 279 | 280 | # Documents to append as an appendix to all manuals. 281 | #texinfo_appendices = [] 282 | 283 | # If false, no module index is generated. 284 | #texinfo_domain_indices = True 285 | 286 | # How to display URL addresses: 'footnote', 'no', or 'inline'. 287 | #texinfo_show_urls = 'footnote' 288 | 289 | # If true, do not generate a @detailmenu in the "Top" node's menu. 290 | #texinfo_no_detailmenu = False 291 | -------------------------------------------------------------------------------- /docs/contributed_nodes.rst: -------------------------------------------------------------------------------- 1 | .. _ref-contributed-nodes: 2 | 3 | ========================= 4 | Contributed content nodes 5 | ========================= 6 | 7 | django-generic-flatblocks comes with some very commonly used content-nodes. 8 | They are not installed by default. To do so, insert ``django_generic_flatblocks.contrib.gblocks`` 9 | to your ``INSTALLED_APPS`` in your settings and resync your database: 10 | ``./manage.py syncdb``. 11 | 12 | The contributed content nodes are: 13 | 14 | - **gblocks.Title**: A CharField rendered as a

Tag. 15 | 16 | - **gblocks.Text**: A TextField rendered as html paragraphs. (This is what 17 | django-chunks provides) 18 | 19 | - **gblocks.Image**: A ImageField rendered as Tag. 20 | 21 | - **gblocks.TitleAndText**: A CharField and a TextField. (This is what 22 | django-flatblocks provides) 23 | 24 | - **gblocks.TitleTextAndImage**: A CharField, TextField and ImageField 25 | 26 | So if you want to display a title and textfield, use this templatetag for 27 | example:: 28 | 29 | {% gblock "about_me" for "gblocks.TitleAndText" %} 30 | -------------------------------------------------------------------------------- /docs/creating_nodes.rst: -------------------------------------------------------------------------------- 1 | .. _ref-creating-nodes: 2 | 3 | ============================ 4 | Create your own content node 5 | ============================ 6 | 7 | A content node is a simple django-model. No quirks. If you want to use a title 8 | and a textfield as your content-node, define a new model ``Entry`` in your 9 | application ``myproject``:: 10 | 11 | from django.db import models 12 | from django.contrib import admin 13 | from django.utils.encoding import python_2_unicode_compatible 14 | 15 | @python_2_unicode_compatible 16 | class Entry(models.Model): 17 | title = models.CharField(max_length=255, blank=True) 18 | content = models.TextField(blank=True) 19 | 20 | def __str__(self): 21 | return self.title 22 | 23 | admin.site.register(Entry) 24 | 25 | .. important:: 26 | django-generic-flatblocks creates an empty content-node upon first 27 | request, so make sure each field has either it's default value or 28 | allow ``blank=True``. Don't forget to register your Model in the 29 | admin backend, if you want to edit it there. 30 | 31 | Then create a template ``myproject/entry/flatblock.html`` in your 32 | template directory. This template is the default template to render the 33 | content node, if you do not provide a unique template for it (*with* 34 | argument). 35 | 36 | In this template are all context-variables from the *parent* template 37 | available plus some extra variables. See arguments/with above for details. 38 | 39 | A common template source for the content node would be:: 40 | 41 |

{{ object.title }}

42 | {{ object.content|safe }} 43 | 44 | {% if admin_url %}edit this{% endif %} 45 | 46 | In your templates, create a new content node using the templatetag:: 47 | 48 | {% gblock "about_me" for "myproject.Entry" %} 49 | 50 | For some pre defined nodes see :ref:`ref-contributed-nodes` 51 | -------------------------------------------------------------------------------- /docs/index.rst: -------------------------------------------------------------------------------- 1 | .. index: 2 | 3 | .. include:: ../README.rst 4 | 5 | Contents: 6 | ========= 7 | 8 | .. toctree:: 9 | :glob: 10 | 11 | installation 12 | quickstart 13 | usage 14 | creating_nodes 15 | contributed_nodes 16 | changelog 17 | -------------------------------------------------------------------------------- /docs/installation.rst: -------------------------------------------------------------------------------- 1 | .. _installation: 2 | 3 | ============ 4 | Installation 5 | ============ 6 | 7 | This package is available through the python package index, pypi. You can 8 | install the latest version by:: 9 | 10 | pip install django-generic-flatblocks 11 | 12 | 13 | Add the module to your ``INSTALLED_APPS`` in your settings:: 14 | 15 | INSTALLED_APPS = ( 16 | ... 17 | 'django_generic_flatblocks', 18 | 'django_generic_flatblocks.contrib.gblocks', # Optional sample models 19 | ) 20 | 21 | Make sure that ``django.core.context_processors.request`` was added to your 22 | ``TEMPLATE`` options:: 23 | 24 | TEMPLATES = [{ 25 | 'BACKEND': 'django.template.backends.django.DjangoTemplates', 26 | 'OPTIONS': { 27 | 'context_processors': [ 28 | 'django.template.context_processors.request', 29 | ... 30 | 31 | *(Optional)* Define the url prefix to your contrib.admin installation in the 32 | setting ``ADMIN_URL_PREFIX``. Most commonly this is ``/admin/``. Beware 33 | the trailing slash. 34 | 35 | Migrate the database schemas:: 36 | 37 | ./manage.py migrate 38 | 39 | See :ref:`quickstart` for a quick demonstration or :ref:`ref-usage` for a 40 | detailed integration. 41 | 42 | 43 | Local Development 44 | ================= 45 | 46 | Install the package using pipenv:: 47 | 48 | $ cd django-generic-flatblocks 49 | $ pipenv install --dev 50 | $ pipenv run test 51 | 52 | You can run the testsuite against a variety of Python and Django versions with 53 | tox:: 54 | 55 | $ cd django-generic-flatblocks 56 | $ tox 57 | -------------------------------------------------------------------------------- /docs/quickstart.rst: -------------------------------------------------------------------------------- 1 | .. _quickstart: 2 | 3 | ========== 4 | Quickstart 5 | ========== 6 | 7 | You can join unlimited of slug-strings or context-variables to one slug. Most 8 | commonly you will do this if you need to use the users LANGUAGE_CODE in your 9 | slug, to have different content nodes for every language:: 10 | 11 | {% load generic_flatblocks %} 12 | {% gblock "website","title",LANGUAGE_CODE for "gblocks.Title" %} 13 | 14 | The slug can also be a context variable:: 15 | 16 | {% with "website_teaser" as my_slug %} 17 | {% gblock my_slug for "gblocks.Text" %} 18 | {% endwith %} 19 | 20 | You can render each generic block with a template of your choice:: 21 | 22 | {% gblock "website_urgent_notice" for "gblocks.Text" with "urgent.html" %} 23 | 24 | You can pass an integer as slug. In this case, generic-flatblocks 25 | will fetch the model instance with the primary key you named in slug. 26 | Basically this is a {% include %} tag on model level:: 27 | 28 | {% gblock 1 for "auth.user" with "current_user.html" %} 29 | 30 | You can store the related object directly in the context using 31 | the "into" argument. This is the quickest way to display any 32 | model. The "for" and "as" arguments are ignored:: 33 | 34 | {% gblock 1 for "auth.user" into "the_user_object" %} 35 |

The first user is {{ the_user_object.username }}!

36 | {% if the_user_object_admin_url %}edit{% endif %} 37 | 38 | 39 | Let's create an flatblock with a "as" argument. We publish this 40 | block at the end of this page in a variable called ``FOOTER``:: 41 | 42 | {% gblock "footer" for "gblocks.Text" as "FOOTER" %} 43 | 44 | {{ FOOTER }} 45 | 46 | -------------------------------------------------------------------------------- /docs/usage.rst: -------------------------------------------------------------------------------- 1 | .. _ref-usage: 2 | 3 | ============== 4 | Detailed usage 5 | ============== 6 | 7 | First of all, in every template you want to use generic-flatblocks, load the 8 | templatetags library:: 9 | 10 | {% load generic_flatblocks %} 11 | 12 | Then define a content node using the ``gblock`` templatetag:: 13 | 14 | {% gblock "unique_slug" for "applabel.modelname" with "render/with/template.html" as "variable" %} 15 | 16 | The arguments in detail: 17 | ======================== 18 | 19 | **"unique_slug"** (required): 20 | ----------------------------- 21 | 22 | The slug argument defines under which 23 | *key* the content is stored in your database. You can define as many slugs 24 | as you want, just use a comma as separator. You can use context-variables as 25 | well. Examples:: 26 | 27 | "homepage headline" becomes "homepage_headline" 28 | "homepage","headline" becomes "homepage_headline" 29 | "homepage_title",LANGUAGE_CODE becomes "homepage_title_en" (depends on the users locale code) 30 | "user",user.pk becomes "user_1" (depends on the primary key of the currently logged in user) 31 | 32 | You can pass an *integer* as the slug. This will cause the templatetag to fetch 33 | the model named in *for* with the primary key you named in *slug*. Example:: 34 | 35 | {% gblock 1 for "auth.user" with "path/to/template.html" %} 36 | 37 | This will fetch the auth.User with the primary key 1 and renders this model 38 | object with the template "path/to/template.html". In this case, the 39 | ``generic_object`` in ``None``. Basically this is a ``{% include %}`` tag on 40 | model level. This can also be a context variable. 41 | 42 | *for* **"applabel.modelname"** (required): 43 | ------------------------------------------ 44 | 45 | The *for* argument defines, what content-node (model) will be used to store 46 | and display the content. The format is *appname.modelname*. For some 47 | contributed content-nodes see :ref:`ref-contributed-nodes` below. 48 | This argument can be a context-variable. 49 | 50 | *with* **"template_path"** (optional): 51 | -------------------------------------- 52 | 53 | You can define a template that is used for rendering the content node. If you 54 | do not provide any template, the default template ``//flatblock.html`` 55 | is used. This argument can be a context-variable. 56 | 57 | In this template are all context-variables from the *parent* template 58 | available plus some extra variables: 59 | 60 | - ``object``: This variable is the model-instance for the generic block. 61 | 62 | - ``generic_object``: This variable is the model-instance for the generic 63 | content object itself. Mostly you don't need this. 64 | 65 | - ``admin_url``: A URL to the change view of the current object. This variable 66 | is ``None`` if the current user has no change permissions for the object. 67 | 68 | *as* **"variable name"** (optional): 69 | -------------------------------------- 70 | 71 | If you provide a variable name, the *rendered content node* is stored in it. 72 | Otherwise it's displayed directly. This argument can be a context-variable. 73 | 74 | *into* **"variable_name"** (optional): 75 | -------------------------------------- 76 | 77 | If you provide a variable name, the *related object* is stored in it. No 78 | template rendering is done. The *with* and the *as* arguments are ignored. 79 | This argument can be a context-variable. 80 | 81 | After calling the gblock templatetag, you have the same variables available 82 | as in the *with* template: 83 | 84 | - ``variable_name``: This variable is the model-instance for the generic block. 85 | 86 | - ``variable_name`` + ``"_genric_object"``: This variable is the model-instance for 87 | the generic content object itself. Mostly you don't need this. 88 | 89 | - ``variable_name`` + ``"_admin_url"``: A URL to the change view of the current object. 90 | This variable is ``None`` if the current user has no change permissions for 91 | the object. 92 | 93 | This is the quickest way to display any model instance or content-node 94 | directly without creating a template:: 95 | 96 | {% gblock 1 for "auth.User" into "theuser" %} 97 | The first user is {{ theuser.username }}! (edit) 98 | 99 | would be rendered as:: 100 | 101 | The first user is johndoe! (edit) 102 | 103 | .. note:: 104 | If you have `settings.TEMPLATE_DEBUG` set to `True` and your related object 105 | does not exist, the templatetag will raise a ObjectNotFound exception. It 106 | will fail silently if you set `settings.TEMPLATE_DEBUG` to `False` and 107 | return an (empty, not saved) instance of the related model. 108 | -------------------------------------------------------------------------------- /runtests.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | import sys 4 | 5 | from django import setup 6 | from django.conf import settings 7 | from django.test.runner import DiscoverRunner 8 | 9 | 10 | def runtests(*test_args): 11 | # Setup settings 12 | if not settings.configured: 13 | from django_generic_flatblocks.tests.testapp import settings as TEST_SETTINGS 14 | settings.configure(**TEST_SETTINGS.__dict__) 15 | setup() 16 | test_runner = DiscoverRunner(verbosity=1) 17 | failures = test_runner.run_tests(['django_generic_flatblocks']) 18 | if failures: 19 | sys.exit(failures) 20 | 21 | 22 | if __name__ == '__main__': 23 | runtests(*sys.argv[1:]) 24 | -------------------------------------------------------------------------------- /setup.cfg: -------------------------------------------------------------------------------- 1 | [metadata] 2 | name = django-generic-flatblocks 3 | version = 1.3 4 | description = 5 | A flatpages/flatblock application using generic relations to content models. 6 | long_description = file: README.rst, CHANGELOG.rst 7 | author = Martin Mahner 8 | author_email = martin@mahner.org 9 | url = https://github.com/bartTC/django-generic-flatblocks 10 | keywords = django, snippets, text, models, flatblock, flatpages 11 | license = MIT 12 | classifiers = 13 | Development Status :: 5 - Production/Stable 14 | Environment :: Web Environment 15 | Intended Audience :: Developers 16 | License :: OSI Approved :: MIT License 17 | Operating System :: OS Independent 18 | Programming Language :: Python 19 | Programming Language :: Python :: 2.7 20 | Programming Language :: Python :: 3.4 21 | Programming Language :: Python :: 3.5 22 | Programming Language :: Python :: 3.6 23 | Programming Language :: Python :: 3.7 24 | Framework :: Django 25 | 26 | [options] 27 | packages = find: 28 | include_package_data = True 29 | zip_safe = False 30 | python_requires = '>=2.7' 31 | install_requires = 32 | django>=1.8 33 | pillow 34 | 35 | [isort] 36 | known_first_party = django_generic_flatblocks 37 | default_section = THIRDPARTY 38 | sections = FUTURE,STDLIB,THIRDPARTY,FIRSTPARTY,LOCALFOLDER 39 | multi_line_output = 0 40 | skip = migrations 41 | 42 | [coverage:run] 43 | source = django_generic_flatblocks 44 | branch = True 45 | omit = 46 | django_generic_flatblocks/migrations/* 47 | django_generic_flatblocks/contrib/gblocks/migrations/* 48 | django_generic_flatblocks/tests/* 49 | 50 | [coverage:report] 51 | exclude_lines = 52 | pragma: no cover 53 | def __repr__ 54 | 55 | [coverage:html] 56 | directory = /tmp/coverage_report/django-generic-flatblocks 57 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | from setuptools import setup 3 | setup() 4 | -------------------------------------------------------------------------------- /tox.ini: -------------------------------------------------------------------------------- 1 | [tox] 2 | toxworkdir = /tmp/tox/django-generic-flatblocks 3 | envlist = 4 | readme 5 | begin 6 | py{27}-django-{18,111} 7 | py{34,35,36,37}-django-{111,20} 8 | py{35,36,37}-django-{21} 9 | end 10 | 11 | [testenv] 12 | install_command = 13 | pip install {opts} {packages} 14 | setenv = 15 | DJANGO_SETTINGS_MODULE=django_generic_flatblocks.tests.testapp.settings 16 | commands = 17 | coverage run --append runtests.py 18 | deps= 19 | # Django versions 20 | django-18: django==1.8.* 21 | django-111: django==1.11.* 22 | django-20: django==2.0.* 23 | django-21: django==2.1.* 24 | coverage 25 | 26 | [testenv:coverage_setup] 27 | basepython=python3.6 28 | commands= 29 | coverage erase 30 | 31 | [testenv:coverage_report] 32 | basepython=python3.6 33 | commands= 34 | coverage report 35 | coverage html 36 | 37 | [testenv:readme] 38 | skip_install = True 39 | deps = 40 | docutils 41 | Pygments 42 | commands = 43 | rst2html.py --report=info --halt=warning README.rst /dev/null 44 | rst2html.py --report=info --halt=warning CHANGELOG.rst /dev/null 45 | --------------------------------------------------------------------------------