From eb1b28175c45479b668eaf925e438cb17f087bf8 Mon Sep 17 00:00:00 2001
From: Thomas Uher
Date: Wed, 14 Dec 2022 17:17:35 +0100
Subject: [PATCH] TemplateContent
---
MANIFEST.in | 4 +-
docker/localcosmos_private/settings.py | 2 +-
localcosmos_server/__init__.py | 2 +-
localcosmos_server/app_admin/middleware.py | 2 +-
.../app_admin/templates/app_admin/base.html | 2 +-
.../migrations/0002_auto_20221111_1535.py | 46 -
localcosmos_server/datasets/models.py | 10 +-
localcosmos_server/forms.py | 87 +-
localcosmos_server/global_urls.py | 10 +-
...002_servercontentimage_serverimagestore.py | 54 +
localcosmos_server/models.py | 443 +++--
.../online_content/CMSObjects.py | 249 ---
localcosmos_server/online_content/api/urls.py | 12 -
.../online_content/api/views.py | 155 --
localcosmos_server/online_content/apps.py | 5 -
localcosmos_server/online_content/fields.py | 74 -
localcosmos_server/online_content/forms.py | 207 --
.../online_content/migrations/0001_initial.py | 197 --
localcosmos_server/online_content/mixins.py | 40 -
localcosmos_server/online_content/models.py | 1170 ------------
localcosmos_server/online_content/parser.py | 100 -
.../ajax/image_microcontent_form.html | 43 -
.../ajax/reloaded_file_fields.html | 3 -
.../ajax/unpublish_template_content.html | 33 -
.../ajax/upload_custom_template.html | 41 -
.../ajax_filecontent_field.html | 21 -
.../online_content/content_managing_js.html | 7 -
.../online_content/delete_microcontent.html | 45 -
.../online_content/filecontent_field.html | 25 -
.../filecontent_field_form.html | 11 -
.../manage_template_content.html | 201 --
.../online_content/online_content_base.html | 69 -
.../translate_template_content.html | 106 --
.../templatetags/online_content_tags.py | 258 ---
.../online_content/tests/test_forms.py | 189 --
.../online_content/tests/test_models.py | 1596 ----------------
.../online_content/tests/test_views.py | 1678 -----------------
localcosmos_server/online_content/urls.py | 46 -
localcosmos_server/online_content/utils.py | 4 -
localcosmos_server/online_content/views.py | 881 ---------
localcosmos_server/online_content/widgets.py | 117 --
.../templates/server_control_panel/base.html | 3 -
.../template_content/Templates.py | 216 +++
.../__init__.py | 0
.../admin.py | 0
.../api/__init__.py | 0
.../template_content/api/serializers.py | 134 ++
.../template_content/api/urls.py | 12 +
.../template_content/api/views.py | 33 +
localcosmos_server/template_content/apps.py | 5 +
localcosmos_server/template_content/forms.py | 278 +++
.../migrations/0001_initial.py | 61 +
.../migrations/__init__.py | 0
localcosmos_server/template_content/models.py | 425 +++++
.../ckeditor5-build-classic/LICENSE.md | 0
.../ckeditor5-build-classic/README.md | 0
.../ckeditor5-build-classic/ckeditor.js | 0
.../ckeditor5-build-classic/ckeditor.js.map | 0
.../ckeditor5-build-classic/index.html | 0
.../ckeditor5-build-classic/old/LICENSE.md | 0
.../ckeditor5-build-classic/old/README.md | 0
.../ckeditor5-build-classic/old/ckeditor.js | 0
.../old/ckeditor.js.map | 0
.../old/translations/ar.js | 0
.../old/translations/ast.js | 0
.../old/translations/bg.js | 0
.../old/translations/cs.js | 0
.../old/translations/da.js | 0
.../old/translations/de.js | 0
.../old/translations/el.js | 0
.../old/translations/en-au.js | 0
.../old/translations/eo.js | 0
.../old/translations/es.js | 0
.../old/translations/et.js | 0
.../old/translations/eu.js | 0
.../old/translations/fi.js | 0
.../old/translations/fr.js | 0
.../old/translations/gl.js | 0
.../old/translations/gu.js | 0
.../old/translations/hr.js | 0
.../old/translations/hu.js | 0
.../old/translations/it.js | 0
.../old/translations/ja.js | 0
.../old/translations/km.js | 0
.../old/translations/kn.js | 0
.../old/translations/ko.js | 0
.../old/translations/ku.js | 0
.../old/translations/nb.js | 0
.../old/translations/ne.js | 0
.../old/translations/nl.js | 0
.../old/translations/oc.js | 0
.../old/translations/pl.js | 0
.../old/translations/pt-br.js | 0
.../old/translations/pt.js | 0
.../old/translations/ro.js | 0
.../old/translations/ru.js | 0
.../old/translations/si.js | 0
.../old/translations/sk.js | 0
.../old/translations/sq.js | 0
.../old/translations/sv.js | 0
.../old/translations/tr.js | 0
.../old/translations/tt.js | 0
.../old/translations/ug.js | 0
.../old/translations/uk.js | 0
.../old/translations/zh-cn.js | 0
.../old/translations/zh.js | 0
.../sample/css/sample.css | 0
.../ckeditor5-build-classic/sample/img/bg.png | Bin
.../sample/img/github.svg | 0
.../sample/img/logo.svg | 0
.../sample/img/umbrellas.jpg | Bin
.../translations/af.js | 0
.../translations/ar.js | 0
.../translations/ast.js | 0
.../translations/az.js | 0
.../translations/bg.js | 0
.../translations/ca.js | 0
.../translations/cs.js | 0
.../translations/da.js | 0
.../translations/de-ch.js | 0
.../translations/de.js | 0
.../translations/el.js | 0
.../translations/en-au.js | 0
.../translations/en-gb.js | 0
.../translations/eo.js | 0
.../translations/es.js | 0
.../translations/et.js | 0
.../translations/eu.js | 0
.../translations/fa.js | 0
.../translations/fi.js | 0
.../translations/fr.js | 0
.../translations/gl.js | 0
.../translations/gu.js | 0
.../translations/he.js | 0
.../translations/hr.js | 0
.../translations/hu.js | 0
.../translations/id.js | 0
.../translations/it.js | 0
.../translations/ja.js | 0
.../translations/km.js | 0
.../translations/kn.js | 0
.../translations/ko.js | 0
.../translations/ku.js | 0
.../translations/lt.js | 0
.../translations/lv.js | 0
.../translations/ms.js | 0
.../translations/nb.js | 0
.../translations/ne.js | 0
.../translations/nl.js | 0
.../translations/no.js | 0
.../translations/oc.js | 0
.../translations/pl.js | 0
.../translations/pt-br.js | 0
.../translations/pt.js | 0
.../translations/ro.js | 0
.../translations/ru.js | 0
.../translations/si.js | 0
.../translations/sk.js | 0
.../translations/sl.js | 0
.../translations/sq.js | 0
.../translations/sr-latn.js | 0
.../translations/sr.js | 0
.../translations/sv.js | 0
.../translations/th.js | 0
.../translations/tr.js | 0
.../translations/tt.js | 0
.../translations/ug.js | 0
.../translations/uk.js | 0
.../translations/vi.js | 0
.../translations/zh-cn.js | 0
.../translations/zh.js | 0
.../template_content/template_content.css} | 0
.../template_content/template_content.js} | 0
.../ajax/delete_template_content_image.html | 17 +
...manage_template_content_extra_scripts.html | 8 +-
.../ajax/manage_template_content_image.html | 31 +
.../ajax/reload_template_content_images.html | 21 +
.../ajax/reloaded_file_fields.html | 3 +
.../ckeditor/layout-complex.js | 0
.../ckeditor/layout-simple.js | 0
.../create_template_content.html | 9 +-
.../manage_localized_template_content.html | 132 ++
.../template_content_base.html | 52 +
.../template_content_list_entry.html | 30 +-
.../widgets/filecontent_field.html | 30 +
.../widgets}/textcontent_field.html | 0
.../templatetags/__init__.py | 0
.../templatetags/template_content_tags.py | 8 +
localcosmos_server/template_content/tests.py | 3 +
localcosmos_server/template_content/urls.py | 29 +
localcosmos_server/template_content/views.py | 284 +++
.../ajax/delete_server_content_image.html | 21 +
.../ajax/server_content_image_form.html | 68 +
.../snippets/ckeditor_js_snippet.html | 37 +
.../taxonomy/taxonomic_restrictions.html | 7 +
.../widgets/crop_image_input.html | 38 +-
.../templatetags/localcosmos_tags.py | 39 +-
localcosmos_server/urls.py | 4 +-
localcosmos_server/view_mixins.py | 178 ++
localcosmos_server/views.py | 52 +-
setup.py | 2 +-
201 files changed, 2686 insertions(+), 7829 deletions(-)
delete mode 100644 localcosmos_server/datasets/migrations/0002_auto_20221111_1535.py
create mode 100644 localcosmos_server/migrations/0002_servercontentimage_serverimagestore.py
delete mode 100644 localcosmos_server/online_content/CMSObjects.py
delete mode 100644 localcosmos_server/online_content/api/urls.py
delete mode 100644 localcosmos_server/online_content/api/views.py
delete mode 100644 localcosmos_server/online_content/apps.py
delete mode 100644 localcosmos_server/online_content/fields.py
delete mode 100644 localcosmos_server/online_content/forms.py
delete mode 100644 localcosmos_server/online_content/migrations/0001_initial.py
delete mode 100644 localcosmos_server/online_content/mixins.py
delete mode 100644 localcosmos_server/online_content/models.py
delete mode 100644 localcosmos_server/online_content/parser.py
delete mode 100644 localcosmos_server/online_content/templates/online_content/ajax/image_microcontent_form.html
delete mode 100644 localcosmos_server/online_content/templates/online_content/ajax/reloaded_file_fields.html
delete mode 100644 localcosmos_server/online_content/templates/online_content/ajax/unpublish_template_content.html
delete mode 100644 localcosmos_server/online_content/templates/online_content/ajax/upload_custom_template.html
delete mode 100644 localcosmos_server/online_content/templates/online_content/ajax_filecontent_field.html
delete mode 100644 localcosmos_server/online_content/templates/online_content/content_managing_js.html
delete mode 100644 localcosmos_server/online_content/templates/online_content/delete_microcontent.html
delete mode 100644 localcosmos_server/online_content/templates/online_content/filecontent_field.html
delete mode 100644 localcosmos_server/online_content/templates/online_content/filecontent_field_form.html
delete mode 100644 localcosmos_server/online_content/templates/online_content/manage_template_content.html
delete mode 100644 localcosmos_server/online_content/templates/online_content/online_content_base.html
delete mode 100644 localcosmos_server/online_content/templates/online_content/translate_template_content.html
delete mode 100644 localcosmos_server/online_content/templatetags/online_content_tags.py
delete mode 100644 localcosmos_server/online_content/tests/test_forms.py
delete mode 100644 localcosmos_server/online_content/tests/test_models.py
delete mode 100644 localcosmos_server/online_content/tests/test_views.py
delete mode 100644 localcosmos_server/online_content/urls.py
delete mode 100644 localcosmos_server/online_content/utils.py
delete mode 100644 localcosmos_server/online_content/views.py
delete mode 100644 localcosmos_server/online_content/widgets.py
create mode 100644 localcosmos_server/template_content/Templates.py
rename localcosmos_server/{online_content => template_content}/__init__.py (100%)
rename localcosmos_server/{online_content => template_content}/admin.py (100%)
rename localcosmos_server/{online_content => template_content}/api/__init__.py (100%)
create mode 100644 localcosmos_server/template_content/api/serializers.py
create mode 100644 localcosmos_server/template_content/api/urls.py
create mode 100644 localcosmos_server/template_content/api/views.py
create mode 100644 localcosmos_server/template_content/apps.py
create mode 100644 localcosmos_server/template_content/forms.py
create mode 100644 localcosmos_server/template_content/migrations/0001_initial.py
rename localcosmos_server/{online_content => template_content}/migrations/__init__.py (100%)
create mode 100644 localcosmos_server/template_content/models.py
rename localcosmos_server/{online_content/static/online_content => template_content/static/template_content}/ckeditor5-build-classic/LICENSE.md (100%)
rename localcosmos_server/{online_content/static/online_content => template_content/static/template_content}/ckeditor5-build-classic/README.md (100%)
rename localcosmos_server/{online_content/static/online_content => template_content/static/template_content}/ckeditor5-build-classic/ckeditor.js (100%)
rename localcosmos_server/{online_content/static/online_content => template_content/static/template_content}/ckeditor5-build-classic/ckeditor.js.map (100%)
rename localcosmos_server/{online_content/static/online_content => template_content/static/template_content}/ckeditor5-build-classic/index.html (100%)
rename localcosmos_server/{online_content/static/online_content => template_content/static/template_content}/ckeditor5-build-classic/old/LICENSE.md (100%)
rename localcosmos_server/{online_content/static/online_content => template_content/static/template_content}/ckeditor5-build-classic/old/README.md (100%)
rename localcosmos_server/{online_content/static/online_content => template_content/static/template_content}/ckeditor5-build-classic/old/ckeditor.js (100%)
rename localcosmos_server/{online_content/static/online_content => template_content/static/template_content}/ckeditor5-build-classic/old/ckeditor.js.map (100%)
rename localcosmos_server/{online_content/static/online_content => template_content/static/template_content}/ckeditor5-build-classic/old/translations/ar.js (100%)
rename localcosmos_server/{online_content/static/online_content => template_content/static/template_content}/ckeditor5-build-classic/old/translations/ast.js (100%)
rename localcosmos_server/{online_content/static/online_content => template_content/static/template_content}/ckeditor5-build-classic/old/translations/bg.js (100%)
rename localcosmos_server/{online_content/static/online_content => template_content/static/template_content}/ckeditor5-build-classic/old/translations/cs.js (100%)
rename localcosmos_server/{online_content/static/online_content => template_content/static/template_content}/ckeditor5-build-classic/old/translations/da.js (100%)
rename localcosmos_server/{online_content/static/online_content => template_content/static/template_content}/ckeditor5-build-classic/old/translations/de.js (100%)
rename localcosmos_server/{online_content/static/online_content => template_content/static/template_content}/ckeditor5-build-classic/old/translations/el.js (100%)
rename localcosmos_server/{online_content/static/online_content => template_content/static/template_content}/ckeditor5-build-classic/old/translations/en-au.js (100%)
rename localcosmos_server/{online_content/static/online_content => template_content/static/template_content}/ckeditor5-build-classic/old/translations/eo.js (100%)
rename localcosmos_server/{online_content/static/online_content => template_content/static/template_content}/ckeditor5-build-classic/old/translations/es.js (100%)
rename localcosmos_server/{online_content/static/online_content => template_content/static/template_content}/ckeditor5-build-classic/old/translations/et.js (100%)
rename localcosmos_server/{online_content/static/online_content => template_content/static/template_content}/ckeditor5-build-classic/old/translations/eu.js (100%)
rename localcosmos_server/{online_content/static/online_content => template_content/static/template_content}/ckeditor5-build-classic/old/translations/fi.js (100%)
rename localcosmos_server/{online_content/static/online_content => template_content/static/template_content}/ckeditor5-build-classic/old/translations/fr.js (100%)
rename localcosmos_server/{online_content/static/online_content => template_content/static/template_content}/ckeditor5-build-classic/old/translations/gl.js (100%)
rename localcosmos_server/{online_content/static/online_content => template_content/static/template_content}/ckeditor5-build-classic/old/translations/gu.js (100%)
rename localcosmos_server/{online_content/static/online_content => template_content/static/template_content}/ckeditor5-build-classic/old/translations/hr.js (100%)
rename localcosmos_server/{online_content/static/online_content => template_content/static/template_content}/ckeditor5-build-classic/old/translations/hu.js (100%)
rename localcosmos_server/{online_content/static/online_content => template_content/static/template_content}/ckeditor5-build-classic/old/translations/it.js (100%)
rename localcosmos_server/{online_content/static/online_content => template_content/static/template_content}/ckeditor5-build-classic/old/translations/ja.js (100%)
rename localcosmos_server/{online_content/static/online_content => template_content/static/template_content}/ckeditor5-build-classic/old/translations/km.js (100%)
rename localcosmos_server/{online_content/static/online_content => template_content/static/template_content}/ckeditor5-build-classic/old/translations/kn.js (100%)
rename localcosmos_server/{online_content/static/online_content => template_content/static/template_content}/ckeditor5-build-classic/old/translations/ko.js (100%)
rename localcosmos_server/{online_content/static/online_content => template_content/static/template_content}/ckeditor5-build-classic/old/translations/ku.js (100%)
rename localcosmos_server/{online_content/static/online_content => template_content/static/template_content}/ckeditor5-build-classic/old/translations/nb.js (100%)
rename localcosmos_server/{online_content/static/online_content => template_content/static/template_content}/ckeditor5-build-classic/old/translations/ne.js (100%)
rename localcosmos_server/{online_content/static/online_content => template_content/static/template_content}/ckeditor5-build-classic/old/translations/nl.js (100%)
rename localcosmos_server/{online_content/static/online_content => template_content/static/template_content}/ckeditor5-build-classic/old/translations/oc.js (100%)
rename localcosmos_server/{online_content/static/online_content => template_content/static/template_content}/ckeditor5-build-classic/old/translations/pl.js (100%)
rename localcosmos_server/{online_content/static/online_content => template_content/static/template_content}/ckeditor5-build-classic/old/translations/pt-br.js (100%)
rename localcosmos_server/{online_content/static/online_content => template_content/static/template_content}/ckeditor5-build-classic/old/translations/pt.js (100%)
rename localcosmos_server/{online_content/static/online_content => template_content/static/template_content}/ckeditor5-build-classic/old/translations/ro.js (100%)
rename localcosmos_server/{online_content/static/online_content => template_content/static/template_content}/ckeditor5-build-classic/old/translations/ru.js (100%)
rename localcosmos_server/{online_content/static/online_content => template_content/static/template_content}/ckeditor5-build-classic/old/translations/si.js (100%)
rename localcosmos_server/{online_content/static/online_content => template_content/static/template_content}/ckeditor5-build-classic/old/translations/sk.js (100%)
rename localcosmos_server/{online_content/static/online_content => template_content/static/template_content}/ckeditor5-build-classic/old/translations/sq.js (100%)
rename localcosmos_server/{online_content/static/online_content => template_content/static/template_content}/ckeditor5-build-classic/old/translations/sv.js (100%)
rename localcosmos_server/{online_content/static/online_content => template_content/static/template_content}/ckeditor5-build-classic/old/translations/tr.js (100%)
rename localcosmos_server/{online_content/static/online_content => template_content/static/template_content}/ckeditor5-build-classic/old/translations/tt.js (100%)
rename localcosmos_server/{online_content/static/online_content => template_content/static/template_content}/ckeditor5-build-classic/old/translations/ug.js (100%)
rename localcosmos_server/{online_content/static/online_content => template_content/static/template_content}/ckeditor5-build-classic/old/translations/uk.js (100%)
rename localcosmos_server/{online_content/static/online_content => template_content/static/template_content}/ckeditor5-build-classic/old/translations/zh-cn.js (100%)
rename localcosmos_server/{online_content/static/online_content => template_content/static/template_content}/ckeditor5-build-classic/old/translations/zh.js (100%)
rename localcosmos_server/{online_content/static/online_content => template_content/static/template_content}/ckeditor5-build-classic/sample/css/sample.css (100%)
rename localcosmos_server/{online_content/static/online_content => template_content/static/template_content}/ckeditor5-build-classic/sample/img/bg.png (100%)
rename localcosmos_server/{online_content/static/online_content => template_content/static/template_content}/ckeditor5-build-classic/sample/img/github.svg (100%)
rename localcosmos_server/{online_content/static/online_content => template_content/static/template_content}/ckeditor5-build-classic/sample/img/logo.svg (100%)
rename localcosmos_server/{online_content/static/online_content => template_content/static/template_content}/ckeditor5-build-classic/sample/img/umbrellas.jpg (100%)
rename localcosmos_server/{online_content/static/online_content => template_content/static/template_content}/ckeditor5-build-classic/translations/af.js (100%)
rename localcosmos_server/{online_content/static/online_content => template_content/static/template_content}/ckeditor5-build-classic/translations/ar.js (100%)
rename localcosmos_server/{online_content/static/online_content => template_content/static/template_content}/ckeditor5-build-classic/translations/ast.js (100%)
rename localcosmos_server/{online_content/static/online_content => template_content/static/template_content}/ckeditor5-build-classic/translations/az.js (100%)
rename localcosmos_server/{online_content/static/online_content => template_content/static/template_content}/ckeditor5-build-classic/translations/bg.js (100%)
rename localcosmos_server/{online_content/static/online_content => template_content/static/template_content}/ckeditor5-build-classic/translations/ca.js (100%)
rename localcosmos_server/{online_content/static/online_content => template_content/static/template_content}/ckeditor5-build-classic/translations/cs.js (100%)
rename localcosmos_server/{online_content/static/online_content => template_content/static/template_content}/ckeditor5-build-classic/translations/da.js (100%)
rename localcosmos_server/{online_content/static/online_content => template_content/static/template_content}/ckeditor5-build-classic/translations/de-ch.js (100%)
rename localcosmos_server/{online_content/static/online_content => template_content/static/template_content}/ckeditor5-build-classic/translations/de.js (100%)
rename localcosmos_server/{online_content/static/online_content => template_content/static/template_content}/ckeditor5-build-classic/translations/el.js (100%)
rename localcosmos_server/{online_content/static/online_content => template_content/static/template_content}/ckeditor5-build-classic/translations/en-au.js (100%)
rename localcosmos_server/{online_content/static/online_content => template_content/static/template_content}/ckeditor5-build-classic/translations/en-gb.js (100%)
rename localcosmos_server/{online_content/static/online_content => template_content/static/template_content}/ckeditor5-build-classic/translations/eo.js (100%)
rename localcosmos_server/{online_content/static/online_content => template_content/static/template_content}/ckeditor5-build-classic/translations/es.js (100%)
rename localcosmos_server/{online_content/static/online_content => template_content/static/template_content}/ckeditor5-build-classic/translations/et.js (100%)
rename localcosmos_server/{online_content/static/online_content => template_content/static/template_content}/ckeditor5-build-classic/translations/eu.js (100%)
rename localcosmos_server/{online_content/static/online_content => template_content/static/template_content}/ckeditor5-build-classic/translations/fa.js (100%)
rename localcosmos_server/{online_content/static/online_content => template_content/static/template_content}/ckeditor5-build-classic/translations/fi.js (100%)
rename localcosmos_server/{online_content/static/online_content => template_content/static/template_content}/ckeditor5-build-classic/translations/fr.js (100%)
rename localcosmos_server/{online_content/static/online_content => template_content/static/template_content}/ckeditor5-build-classic/translations/gl.js (100%)
rename localcosmos_server/{online_content/static/online_content => template_content/static/template_content}/ckeditor5-build-classic/translations/gu.js (100%)
rename localcosmos_server/{online_content/static/online_content => template_content/static/template_content}/ckeditor5-build-classic/translations/he.js (100%)
rename localcosmos_server/{online_content/static/online_content => template_content/static/template_content}/ckeditor5-build-classic/translations/hr.js (100%)
rename localcosmos_server/{online_content/static/online_content => template_content/static/template_content}/ckeditor5-build-classic/translations/hu.js (100%)
rename localcosmos_server/{online_content/static/online_content => template_content/static/template_content}/ckeditor5-build-classic/translations/id.js (100%)
rename localcosmos_server/{online_content/static/online_content => template_content/static/template_content}/ckeditor5-build-classic/translations/it.js (100%)
rename localcosmos_server/{online_content/static/online_content => template_content/static/template_content}/ckeditor5-build-classic/translations/ja.js (100%)
rename localcosmos_server/{online_content/static/online_content => template_content/static/template_content}/ckeditor5-build-classic/translations/km.js (100%)
rename localcosmos_server/{online_content/static/online_content => template_content/static/template_content}/ckeditor5-build-classic/translations/kn.js (100%)
rename localcosmos_server/{online_content/static/online_content => template_content/static/template_content}/ckeditor5-build-classic/translations/ko.js (100%)
rename localcosmos_server/{online_content/static/online_content => template_content/static/template_content}/ckeditor5-build-classic/translations/ku.js (100%)
rename localcosmos_server/{online_content/static/online_content => template_content/static/template_content}/ckeditor5-build-classic/translations/lt.js (100%)
rename localcosmos_server/{online_content/static/online_content => template_content/static/template_content}/ckeditor5-build-classic/translations/lv.js (100%)
rename localcosmos_server/{online_content/static/online_content => template_content/static/template_content}/ckeditor5-build-classic/translations/ms.js (100%)
rename localcosmos_server/{online_content/static/online_content => template_content/static/template_content}/ckeditor5-build-classic/translations/nb.js (100%)
rename localcosmos_server/{online_content/static/online_content => template_content/static/template_content}/ckeditor5-build-classic/translations/ne.js (100%)
rename localcosmos_server/{online_content/static/online_content => template_content/static/template_content}/ckeditor5-build-classic/translations/nl.js (100%)
rename localcosmos_server/{online_content/static/online_content => template_content/static/template_content}/ckeditor5-build-classic/translations/no.js (100%)
rename localcosmos_server/{online_content/static/online_content => template_content/static/template_content}/ckeditor5-build-classic/translations/oc.js (100%)
rename localcosmos_server/{online_content/static/online_content => template_content/static/template_content}/ckeditor5-build-classic/translations/pl.js (100%)
rename localcosmos_server/{online_content/static/online_content => template_content/static/template_content}/ckeditor5-build-classic/translations/pt-br.js (100%)
rename localcosmos_server/{online_content/static/online_content => template_content/static/template_content}/ckeditor5-build-classic/translations/pt.js (100%)
rename localcosmos_server/{online_content/static/online_content => template_content/static/template_content}/ckeditor5-build-classic/translations/ro.js (100%)
rename localcosmos_server/{online_content/static/online_content => template_content/static/template_content}/ckeditor5-build-classic/translations/ru.js (100%)
rename localcosmos_server/{online_content/static/online_content => template_content/static/template_content}/ckeditor5-build-classic/translations/si.js (100%)
rename localcosmos_server/{online_content/static/online_content => template_content/static/template_content}/ckeditor5-build-classic/translations/sk.js (100%)
rename localcosmos_server/{online_content/static/online_content => template_content/static/template_content}/ckeditor5-build-classic/translations/sl.js (100%)
rename localcosmos_server/{online_content/static/online_content => template_content/static/template_content}/ckeditor5-build-classic/translations/sq.js (100%)
rename localcosmos_server/{online_content/static/online_content => template_content/static/template_content}/ckeditor5-build-classic/translations/sr-latn.js (100%)
rename localcosmos_server/{online_content/static/online_content => template_content/static/template_content}/ckeditor5-build-classic/translations/sr.js (100%)
rename localcosmos_server/{online_content/static/online_content => template_content/static/template_content}/ckeditor5-build-classic/translations/sv.js (100%)
rename localcosmos_server/{online_content/static/online_content => template_content/static/template_content}/ckeditor5-build-classic/translations/th.js (100%)
rename localcosmos_server/{online_content/static/online_content => template_content/static/template_content}/ckeditor5-build-classic/translations/tr.js (100%)
rename localcosmos_server/{online_content/static/online_content => template_content/static/template_content}/ckeditor5-build-classic/translations/tt.js (100%)
rename localcosmos_server/{online_content/static/online_content => template_content/static/template_content}/ckeditor5-build-classic/translations/ug.js (100%)
rename localcosmos_server/{online_content/static/online_content => template_content/static/template_content}/ckeditor5-build-classic/translations/uk.js (100%)
rename localcosmos_server/{online_content/static/online_content => template_content/static/template_content}/ckeditor5-build-classic/translations/vi.js (100%)
rename localcosmos_server/{online_content/static/online_content => template_content/static/template_content}/ckeditor5-build-classic/translations/zh-cn.js (100%)
rename localcosmos_server/{online_content/static/online_content => template_content/static/template_content}/ckeditor5-build-classic/translations/zh.js (100%)
rename localcosmos_server/{online_content/static/online_content/online_content.css => template_content/static/template_content/template_content.css} (100%)
rename localcosmos_server/{online_content/static/online_content/online_content.js => template_content/static/template_content/template_content.js} (100%)
create mode 100644 localcosmos_server/template_content/templates/template_content/ajax/delete_template_content_image.html
rename localcosmos_server/{online_content/templates/online_content => template_content/templates/template_content/ajax}/manage_template_content_extra_scripts.html (60%)
create mode 100644 localcosmos_server/template_content/templates/template_content/ajax/manage_template_content_image.html
create mode 100644 localcosmos_server/template_content/templates/template_content/ajax/reload_template_content_images.html
create mode 100644 localcosmos_server/template_content/templates/template_content/ajax/reloaded_file_fields.html
rename localcosmos_server/{online_content/templates/online_content => template_content/templates/template_content}/ckeditor/layout-complex.js (100%)
rename localcosmos_server/{online_content/templates/online_content => template_content/templates/template_content}/ckeditor/layout-simple.js (100%)
rename localcosmos_server/{online_content/templates/online_content => template_content/templates/template_content}/create_template_content.html (78%)
create mode 100644 localcosmos_server/template_content/templates/template_content/manage_localized_template_content.html
create mode 100644 localcosmos_server/template_content/templates/template_content/template_content_base.html
rename localcosmos_server/{online_content/templates/online_content => template_content/templates/template_content}/template_content_list_entry.html (62%)
create mode 100644 localcosmos_server/template_content/templates/template_content/widgets/filecontent_field.html
rename localcosmos_server/{online_content/templates/online_content => template_content/templates/template_content/widgets}/textcontent_field.html (100%)
rename localcosmos_server/{online_content => template_content}/templatetags/__init__.py (100%)
create mode 100644 localcosmos_server/template_content/templatetags/template_content_tags.py
create mode 100644 localcosmos_server/template_content/tests.py
create mode 100644 localcosmos_server/template_content/urls.py
create mode 100644 localcosmos_server/template_content/views.py
create mode 100644 localcosmos_server/templates/localcosmos_server/ajax/delete_server_content_image.html
create mode 100644 localcosmos_server/templates/localcosmos_server/ajax/server_content_image_form.html
create mode 100644 localcosmos_server/templates/localcosmos_server/snippets/ckeditor_js_snippet.html
create mode 100644 localcosmos_server/templates/localcosmos_server/taxonomy/taxonomic_restrictions.html
diff --git a/MANIFEST.in b/MANIFEST.in
index e64644a..10ba452 100644
--- a/MANIFEST.in
+++ b/MANIFEST.in
@@ -1,8 +1,8 @@
recursive-include localcosmos_server/app_admin/static *
recursive-include localcosmos_server/app_admin/templates *
recursive-include localcosmos_server/datasets/templates *
-recursive-include localcosmos_server/online_content/static *
-recursive-include localcosmos_server/online_content/templates *
+recursive-include localcosmos_server/template__content/static *
+recursive-include localcosmos_server/template_content/templates *
recursive-include localcosmos_server/server_control_panel/templates *
recursive-include localcosmos_server/specifications *
recursive-include localcosmos_server/static *
diff --git a/docker/localcosmos_private/settings.py b/docker/localcosmos_private/settings.py
index 8f3d65f..59ac30d 100644
--- a/docker/localcosmos_private/settings.py
+++ b/docker/localcosmos_private/settings.py
@@ -48,7 +48,7 @@
'localcosmos_server.app_admin',
'localcosmos_server.server_control_panel',
'localcosmos_server.datasets',
- 'localcosmos_server.online_content',
+ 'localcosmos_server.template_content',
'django_road',
'anycluster',
diff --git a/localcosmos_server/__init__.py b/localcosmos_server/__init__.py
index dde181c..4de7ac2 100644
--- a/localcosmos_server/__init__.py
+++ b/localcosmos_server/__init__.py
@@ -1,2 +1,2 @@
name = 'localcosmos_server'
-__version__ = '0.10.0'
+__version__ = '0.12.0'
diff --git a/localcosmos_server/app_admin/middleware.py b/localcosmos_server/app_admin/middleware.py
index 47c713c..6f38168 100644
--- a/localcosmos_server/app_admin/middleware.py
+++ b/localcosmos_server/app_admin/middleware.py
@@ -16,7 +16,7 @@ def process_view(self, request, view_func, view_args, view_kwargs):
request.is_appadmin = False
# the admin has to use localcosmos_server.urls to not conflict with the commercial installation
- # online_content needs the correct urlconf
+ # template_content needs the correct urlconf
if '/app-admin/' in request.path:
request.is_appadmin = True
diff --git a/localcosmos_server/app_admin/templates/app_admin/base.html b/localcosmos_server/app_admin/templates/app_admin/base.html
index 74d23a1..bf147be 100644
--- a/localcosmos_server/app_admin/templates/app_admin/base.html
+++ b/localcosmos_server/app_admin/templates/app_admin/base.html
@@ -56,7 +56,7 @@
{% endif %}
- {% trans 'Online content' %}
+ {% trans 'Template content' %}
{% trans 'Observations' %}
diff --git a/localcosmos_server/datasets/migrations/0002_auto_20221111_1535.py b/localcosmos_server/datasets/migrations/0002_auto_20221111_1535.py
deleted file mode 100644
index 1272a80..0000000
--- a/localcosmos_server/datasets/migrations/0002_auto_20221111_1535.py
+++ /dev/null
@@ -1,46 +0,0 @@
-# Generated by Django 3.1.14 on 2022-11-11 15:35
-
-from django.db import migrations, models
-import django.db.models.deletion
-import uuid
-
-
-class Migration(migrations.Migration):
-
- dependencies = [
- ('datasets', '0001_initial'),
- ]
-
- operations = [
- migrations.CreateModel(
- name='ObservationForm',
- fields=[
- ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
- ('uuid', models.UUIDField(default=uuid.uuid4, editable=False, unique=True)),
- ('version', models.TextField()),
- ('definition', models.JSONField()),
- ],
- options={
- 'unique_together': {('uuid', 'version')},
- },
- ),
- migrations.CreateModel(
- name='MetaData',
- fields=[
- ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
- ('data', models.JSONField()),
- ('observation_form', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='datasets.observationform')),
- ],
- ),
- migrations.AddField(
- model_name='dataset',
- name='meta_data',
- field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.PROTECT, to='datasets.metadata'),
- ),
- migrations.AddField(
- model_name='dataset',
- name='observation_form',
- field=models.ForeignKey(default=1, on_delete=django.db.models.deletion.PROTECT, to='datasets.observationform'),
- preserve_default=False,
- ),
- ]
diff --git a/localcosmos_server/datasets/models.py b/localcosmos_server/datasets/models.py
index 3ac03f5..68cdc37 100644
--- a/localcosmos_server/datasets/models.py
+++ b/localcosmos_server/datasets/models.py
@@ -60,6 +60,7 @@ def import_module(module):
'''
ObservationForm
'''
+'''
class ObservationForm(models.Model):
uuid = models.UUIDField(default=uuid.uuid4, unique=True, editable=False)
@@ -69,15 +70,16 @@ class ObservationForm(models.Model):
class Meta:
unique_together=('uuid', 'version')
-
+'''
'''
MetaData
'''
+'''
class MetaData(models.Model):
observation_form = models.ForeignKey(ObservationForm, on_delete=models.PROTECT)
data = models.JSONField()
-
+'''
'''
Dataset
@@ -93,9 +95,9 @@ class Dataset(ModelWithTaxon):
# all data except the taxononmic inherited from ModelWithTaxon is stored in the json data column without schema
# for quicker queries, some fields have their own (redundant) db columns below
data = models.JSONField()
- observation_form = models.ForeignKey(ObservationForm, on_delete=models.PROTECT)
+ #observation_form = models.ForeignKey(ObservationForm, on_delete=models.PROTECT)
- meta_data = models.ForeignKey(MetaData, null=True, on_delete=models.PROTECT)
+ #meta_data = models.ForeignKey(MetaData, null=True, on_delete=models.PROTECT)
### redundant fields for quick DB queries
# geographic reference, useful for anycluster and quick GIS queries
diff --git a/localcosmos_server/forms.py b/localcosmos_server/forms.py
index 2568e95..35381bf 100644
--- a/localcosmos_server/forms.py
+++ b/localcosmos_server/forms.py
@@ -79,7 +79,7 @@ def clean(self):
'''
ManageContentImageForm
- - used by online_content and app_kit
+ - used by template_content and app_kit
- expects LicencingFormMixin in Form
- add an image to any content of the app, or an app directly
- images have a type, default is 'image', possible types are eg 'background' or 'logo'
@@ -262,3 +262,88 @@ def clean(self):
return cleaned_data
+
+from content_licencing.mixins import LicencingFormMixin, OptionalLicencingFormMixin
+from localcosmos_server.widgets import HiddenJSONInput
+class ManageContentImageForm(ManageContentImageFormCommon, LicencingFormMixin):
+
+ # cropping
+ crop_parameters = forms.CharField(widget=forms.HiddenInput)
+
+ # features like arrows
+ features = forms.CharField(widget=HiddenJSONInput, required=False)
+
+ # image_type
+ image_type = forms.CharField(widget=forms.HiddenInput, required=False)
+
+ # md5
+ md5 = forms.CharField(widget=forms.HiddenInput, required=False)
+
+ requires_translation = forms.BooleanField(required=False)
+
+
+ def clean_source_image(self):
+
+ source_image = self.cleaned_data.get('source_image')
+ if not source_image and not self.current_image:
+ raise forms.ValidationError('An image file is required.')
+
+ return source_image
+
+
+class ManageContentImageWithTextForm(FormLocalizationMixin, ManageContentImageForm):
+
+ input_language = forms.CharField(widget=forms.HiddenInput)
+
+ text = forms.CharField(max_length=355, required=False, widget=forms.Textarea,
+ help_text=_('Text that will be shown together with this image.'))
+
+ localizeable_fields = ['text']
+ layoutable_simple_fields = ['text']
+
+
+class ManageLocalizedContentImageForm(ManageContentImageForm):
+
+ def __init__(self, *args, **kwargs):
+ super().__init__(*args, **kwargs)
+ del self.fields['requires_translation']
+
+
+'''
+ A form with an optional ContentImage. If the user uploads an image, creator_name and licence have to be present
+'''
+class OptionalContentImageForm(ManageContentImageFormCommon, OptionalLicencingFormMixin):
+
+ # cropping
+ crop_parameters = forms.CharField(widget=forms.HiddenInput, required=False)
+
+ # features like arrows
+ features = forms.CharField(widget=HiddenJSONInput, required=False)
+
+ # image_type
+ image_type = forms.CharField(widget=forms.HiddenInput, required=False)
+
+ # md5
+ md5 = forms.CharField(widget=forms.HiddenInput, required=False)
+
+ # if suggested images are provided, the user may click on the suggested image
+ referred_content_image_id = forms.IntegerField(widget=forms.HiddenInput, required=False)
+
+ def fields_required(self, fields):
+ """Used for conditionally marking fields as required."""
+ for field in fields:
+ if not self.cleaned_data.get(field, None):
+ msg = forms.ValidationError(_('This field is required.'))
+ self.add_error(field, msg)
+
+
+ # if an image is present, at least crop_parameters, licence and creator_name have to be present
+ def clean(self):
+ cleaned_data = super().clean()
+ file_ = cleaned_data.get('source_image', None)
+
+ if file_ is not None:
+ self.fields_required(['creator_name', 'licence'])
+
+
+ return cleaned_data
\ No newline at end of file
diff --git a/localcosmos_server/global_urls.py b/localcosmos_server/global_urls.py
index 490a8ca..907b1fd 100644
--- a/localcosmos_server/global_urls.py
+++ b/localcosmos_server/global_urls.py
@@ -31,7 +31,15 @@
extra_context={'base_template': 'base.html'},
template_name='localcosmos_server/registration/password_reset_complete.html'),
name='password_reset_complete'),
-
+ # server content images
+ path('manage-server-content-image///',
+ views.ManageServerContentImage.as_view(), name='manage_server_content_image'),
+ path('manage-server-content-image////',
+ views.ManageServerContentImage.as_view(), name='manage_server_content_image'),
+ path('manage-server-content-image//',
+ views.ManageServerContentImage.as_view(), name='manage_server_content_image'),
+ path('delete-server-content-image//',
+ views.DeleteServerContentImage.as_view(), name='delete_server_content_image'),
# SETUP GUI
path('', include('localcosmos_server.setup_urls')),
diff --git a/localcosmos_server/migrations/0002_servercontentimage_serverimagestore.py b/localcosmos_server/migrations/0002_servercontentimage_serverimagestore.py
new file mode 100644
index 0000000..09ba4ef
--- /dev/null
+++ b/localcosmos_server/migrations/0002_servercontentimage_serverimagestore.py
@@ -0,0 +1,54 @@
+# Generated by Django 3.1.14 on 2022-12-12 14:39
+
+from django.conf import settings
+from django.db import migrations, models
+import django.db.models.deletion
+import localcosmos_server.models
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('contenttypes', '0002_remove_content_type_name'),
+ ('localcosmos_server', '0001_initial'),
+ ]
+
+ operations = [
+ migrations.CreateModel(
+ name='ServerImageStore',
+ fields=[
+ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+ ('taxon_latname', models.CharField(max_length=255, null=True)),
+ ('taxon_author', models.CharField(max_length=255, null=True)),
+ ('taxon_source', models.CharField(max_length=255, null=True)),
+ ('taxon_include_descendants', models.BooleanField(default=False)),
+ ('taxon_nuid', models.CharField(max_length=255, null=True)),
+ ('name_uuid', models.UUIDField(null=True)),
+ ('md5', models.CharField(max_length=255)),
+ ('source_image', models.ImageField(upload_to=localcosmos_server.models.get_image_store_path)),
+ ('uploaded_by', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, to=settings.AUTH_USER_MODEL)),
+ ],
+ options={
+ 'abstract': False,
+ },
+ ),
+ migrations.CreateModel(
+ name='ServerContentImage',
+ fields=[
+ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+ ('crop_parameters', models.TextField(null=True)),
+ ('features', models.JSONField(null=True)),
+ ('object_id', models.IntegerField()),
+ ('image_type', models.CharField(default='image', max_length=100)),
+ ('position', models.IntegerField(default=0)),
+ ('is_primary', models.BooleanField(default=False)),
+ ('text', models.CharField(max_length=355, null=True)),
+ ('requires_translation', models.BooleanField(default=False)),
+ ('content_type', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='contenttypes.contenttype')),
+ ('image_store', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='localcosmos_server.serverimagestore')),
+ ],
+ options={
+ 'abstract': False,
+ },
+ ),
+ ]
diff --git a/localcosmos_server/models.py b/localcosmos_server/models.py
index d95039f..f7158a9 100644
--- a/localcosmos_server/models.py
+++ b/localcosmos_server/models.py
@@ -5,11 +5,13 @@
from django.urls import reverse
from django.db import transaction
+from django.templatetags.static import static
-from .taxonomy.generic import ModelWithRequiredTaxon
+
+from .taxonomy.generic import ModelWithTaxon, ModelWithRequiredTaxon
from .taxonomy.lazy import LazyAppTaxon
from django.contrib.contenttypes.models import ContentType
-from django.contrib.contenttypes.fields import GenericForeignKey
+from django.contrib.contenttypes.fields import GenericRelation, GenericForeignKey
# online ocntent
@@ -18,7 +20,7 @@
from localcosmos_server.slugifier import create_unique_slug
-from localcosmos_server.online_content.utils import verbosify_template_name
+from content_licencing.models import ContentLicenceRegistry
import uuid, os, json, shutil
@@ -256,6 +258,7 @@ def get_preview_url(self):
Domain = get_tenant_domain_model()
domain = Domain.objects.filter(tenant__schema_name='public').first()
+
return '{0}.preview.{1}/'.format(self.uid, domain.domain)
@@ -285,7 +288,7 @@ def get_installed_app_path(self, app_state):
return root
- # read app settings from disk, online_content
+ # read app settings from disk, template_content
# located at /www/settings.json, createb by AppPreviewBuilder or AppReleaseBuilder
# app_state=='preview' or app_state=='review' are for commercial installation only
def get_settings(self, app_state='preview'):
@@ -328,133 +331,6 @@ def languages(self):
def secondary_languages(self):
return SecondaryAppLanguages.objects.filter(app=self).values_list('language_code', flat=True)
-
- ###############################################################################################################
- # online content specific
- # on LOCALCOSMOS_PRIVATE==True app_state is always 'published'
- # on LOCALCOSMOS_PRIVATE==False app_state is always 'preview'
-
- def get_online_content_app_state(self):
-
- if settings.LOCALCOSMOS_PRIVATE == True:
- return 'published'
-
- return 'preview'
-
- # preview or published, depending on LOCALCOSMOS_PRIVATE
- def get_online_content_templates_path(self):
-
- app_state = self.get_online_content_app_state()
-
- return os.path.join(self.get_installed_app_path(app_state), 'online_content', 'templates')
-
-
- def get_user_uploaded_online_content_templates_path(self):
- return os.path.join(self.media_base_path, 'online_content', 'templates')
-
-
- # return the online_content specific app settings
- # called from online_content.api.views and online_content.models.TemplateContentFlagsManager
- def get_online_content_settings(self):
-
- app_state = self.get_online_content_app_state()
-
- app_settings = self.get_settings(app_state=app_state)
-
- oc_settings = app_settings.get('online_content', {})
- return oc_settings
-
- # return a list of available templates, always use the preview version,
- # used by eg online_content.forms.CreateTemplateContentForm
- # eg displays a selection in the AppAdmin
- def get_online_content_templates(self, template_type):
-
- templates_path = os.path.join(self.get_online_content_templates_path(), template_type)
- oc_settings = self.get_online_content_settings()
- language = self.primary_language
-
- templates = []
-
- # the frontend might not supply online content templates
- if os.path.isdir(templates_path):
-
- # iterate over templates shipped with the frontend
- for filename in os.listdir(templates_path):
-
- template_path = '{0}/{1}'.format(template_type, filename)
- verbose_name = template_path
-
- if template_path in oc_settings['verbose_template_names'] and language in oc_settings['verbose_template_names'][template_path]:
- verbose_name = oc_settings['verbose_template_names'][template_path][language]
-
- templates.append((template_path, verbose_name))
-
-
- # iterate over user uploaded templates
- user_uploaded_templates_path = os.path.join(self.get_user_uploaded_online_content_templates_path(),
- template_type)
-
- if os.path.isdir(user_uploaded_templates_path):
- for filename in os.listdir(user_uploaded_templates_path):
- template_path = '{0}/{1}'.format(template_type, filename)
- verbose_name = verbosify_template_name(template_path)
- templates.append((template_path, verbose_name))
-
- return templates
-
-
- def get_online_content_template(self, template_name):
- # return an instance of Template
- # Template(contents, origin, origin.template_name, self.engine,)
- # contents is the content of the .html file
- # origin is an Origin instance
- # engine is a template engine
- # Template can be instantiated directly, only with contents
-
- # templates shipped with frontend
- templates_base_dir = self.get_online_content_templates_path()
-
- # templates uploaded by user
- user_uploaded_templates_base_dir = self.get_user_uploaded_online_content_templates_path()
-
- # check if template is shipped with the frontend
- template_path = os.path.join(templates_base_dir, template_name)
-
- if not os.path.isfile(template_path):
-
- # if not check if the template was uploaded by the user
- template_path = os.path.join(user_uploaded_templates_base_dir, template_name)
-
- if not os.path.isfile(template_path):
- msg = 'Online Content Template %s does not exist. Tried: %s' % (template_name, template_path)
- raise TemplateDoesNotExist(msg)
-
-
- params = {
- 'NAME' : 'OnlineContentEngine',
- #'BACKEND': 'django.template.backends.django.DjangoTemplates',
- 'DIRS': [templates_base_dir, user_uploaded_templates_base_dir],
- 'APP_DIRS': False,
- 'OPTIONS': {
- 'context_processors': [
- 'django.template.context_processors.debug',
- 'django.template.context_processors.request',
- 'django.contrib.auth.context_processors.auth',
- 'django.contrib.messages.context_processors.messages',
- ],
- 'loaders' : [
- 'django.template.loaders.filesystem.Loader',
- ]
- },
- }
- engine = DjangoTemplates(params)
-
- with open(template_path, encoding=engine.engine.file_charset) as fp:
- contents = fp.read()
-
- # use the above engine with dirs
- template = Template(contents, engine=engine.engine)
- return template
# only published app
def get_locale(self, key, language):
@@ -534,3 +410,308 @@ class Meta:
class TaxonomicRestriction(TaxonomicRestrictionBase):
pass
+
+
+
+
+'''
+ Generic Content Images
+'''
+class ServerContentImageMixin:
+
+ def get_model(self):
+ return ServerContentImage
+
+ def _content_images(self, image_type='image'):
+
+ content_type = ContentType.objects.get_for_model(self.__class__)
+ ContentImageModel = self.get_model()
+
+ self.content_images = ContentImageModel.objects.filter(content_type=content_type, object_id=self.pk,
+ image_type=image_type)
+
+ return self.content_images
+
+ def all_images(self):
+
+ content_type = ContentType.objects.get_for_model(self.__class__)
+ ContentImageModel = self.get_model()
+
+ self.content_images = ContentImageModel.objects.filter(
+ content_type=content_type, object_id=self.pk)
+
+ return self.content_images
+
+ def images(self, image_type='image'):
+ return self._content_images(image_type=image_type)
+
+ def image(self, image_type='image'):
+ content_image = self._content_images(image_type=image_type).first()
+
+ if content_image:
+ return content_image
+
+ return None
+
+ def image_url(self, size=400):
+
+ content_image = self.image()
+
+ if content_image:
+ return content_image.image_url(size)
+
+ return static('noimage.png')
+
+ # this also deletes ImageStore entries and images on disk
+
+ def delete_images(self):
+
+ content_type = ContentType.objects.get_for_model(self.__class__)
+ ContentImageModel = self.get_model()
+
+ content_images = ContentImageModel.objects.filter(
+ content_type=content_type, object_id=self.pk)
+
+ for image in content_images:
+ # delete model db entries
+ image_store = image.image_store
+ image.delete()
+
+ image_is_used = ContentImageModel.objects.filter(
+ image_store=image_store).exists()
+
+ if not image_is_used:
+ image_store.delete()
+
+ def get_content_images_primary_localization(self):
+
+ locale = {}
+
+ content_images = self.images()
+
+ for content_image in content_images:
+
+ if content_image.text and len(content_image.text) > 0:
+ locale[content_image.text] = content_image.text
+
+ return locale
+
+
+
+
+
+def get_image_store_path(instance, filename):
+ blankname, ext = os.path.splitext(filename)
+
+ new_filename = '{0}{1}'.format(instance.md5, ext)
+ path = '/'.join(['localcosmos-server', 'imagestore', '{0}'.format(instance.uploaded_by.pk),
+ new_filename])
+ return path
+
+
+
+class ImageStoreAbstract(ModelWithTaxon):
+
+ LazyTaxonClass = LazyAppTaxon
+
+ # null Foreignkey means the user does not exist anymore
+ uploaded_by = models.ForeignKey(
+ settings.AUTH_USER_MODEL, null=True, on_delete=models.SET_NULL)
+
+
+ md5 = models.CharField(max_length=255)
+
+ # enables on delete cascade
+ licences = GenericRelation(ContentLicenceRegistry)
+
+ class Meta:
+ abstract=True
+
+
+class ServerImageStore(ImageStoreAbstract):
+
+ source_image = models.ImageField(upload_to=get_image_store_path)
+
+
+
+class ContentImageAbstract(models.Model):
+
+ crop_parameters = models.TextField(null=True)
+
+ # for things like arrows/vectors on the image
+ # arrows are stored as [{"type" : "arrow" , "initialPoint": {x:1, y:1}, "terminalPoint": {x:2,y:2}, color: string}]
+ features = models.JSONField(null=True)
+
+ content_type = models.ForeignKey(ContentType, on_delete=models.CASCADE)
+ object_id = models.IntegerField()
+ content = GenericForeignKey('content_type', 'object_id')
+
+ # a content can have different images
+ # eg an image of type 'background' and an image of type 'logo'
+ image_type = models.CharField(max_length=100, default='image')
+
+ position = models.IntegerField(default=0)
+ is_primary = models.BooleanField(default=False)
+
+ # only primary language
+ text = models.CharField(max_length=355, null=True)
+
+ # flag if a translation is needed
+ requires_translation = models.BooleanField(
+ default=False) # not all images require a translation
+
+
+ class Meta:
+ abstract=True
+
+
+import hashlib
+from PIL import Image
+class ContentImageProcessing:
+
+ def get_thumb_filename(self, size=400):
+
+ if self.image_store.source_image:
+ filename = os.path.basename(self.image_store.source_image.path)
+ blankname, ext = os.path.splitext(filename)
+
+ suffix = 'uncropped'
+ if self.crop_parameters:
+ suffix = hashlib.md5(
+ self.crop_parameters.encode('utf-8')).hexdigest()
+
+ feature_suffix = 'nofeatures'
+ if self.features:
+ features_str = json.dumps(self.features)
+ feature_suffix = hashlib.md5(
+ features_str.encode('utf-8')).hexdigest()
+
+ thumbname = '{0}-{1}-{2}-{3}{4}'.format(
+ blankname, suffix, feature_suffix, size, ext)
+ return thumbname
+
+ else:
+ return 'noimage.png'
+
+
+ def plot_features(self, pil_image):
+ raise NotImplementedError('Plotting Features not supported by LC Server')
+
+ # apply features and cropping, return pil image
+ # original_image has to be Pil.Image instance
+ # CASE 1: crop parameters given.
+ # - make a canvas according to crop_parameters.width and crop_parameters.height
+ #
+ # CASE 2: no crop parameters given
+ # 1. apply features
+ # 2. thumbnail
+ def get_in_memory_processed_image(self, original_image, size):
+
+ # scale the image to match size
+ original_width, original_height = original_image.size
+
+ larger_original_side = max(original_width, original_height)
+ if larger_original_side < size:
+ size = larger_original_side
+
+ # fill color for the background, if the selection expands the original image
+ fill_color = (255, 255, 255, 255)
+
+ # offset of the image on the canvas
+ offset_x = 0
+ offset_y = 0
+
+ if self.crop_parameters:
+
+ square_size = max(original_width, original_height)
+ offset_x = int((square_size - original_width) / 2)
+ offset_y = int((square_size - original_height) / 2)
+ width = size
+ height = size
+
+ canvas = Image.new('RGBA', (square_size, square_size), fill_color)
+ canvas.paste(original_image, (offset_x, offset_y))
+
+ else:
+
+ # define width and height
+ width = size
+ scaling_factor = original_width / size
+ height = original_height * scaling_factor
+
+ canvas = Image.new(
+ 'RGBA', (original_width, original_height), fill_color)
+ canvas.paste(original_image, (offset_x, offset_y))
+
+ # plot features and creator name
+ # matplotlib is awfully slow - only use it if absolutely necessary
+ if self.features:
+ image_source = self.plot_features(canvas)
+ canvas_with_features = Image.open(image_source)
+ else:
+ canvas_with_features = canvas
+
+ # ATTENTION: crop_parameters are relative to the top-left corner of the original image
+ # -> make them relative to the top left corner of square
+ if self.crop_parameters:
+ # {"x":253,"y":24,"width":454,"height":454,"rotate":0,"scaleX":1,"scaleY":1}
+ crop_parameters = json.loads(self.crop_parameters)
+
+ # first crop, then resize
+ # box: (left, top, right, bottom)
+ box = (
+ crop_parameters['x'] + offset_x,
+ crop_parameters['y'] + offset_y,
+ crop_parameters['x'] + offset_x + crop_parameters['width'],
+ crop_parameters['y'] + offset_y + crop_parameters['height'],
+ )
+
+ cropped_canvas = canvas_with_features.crop(box)
+
+ else:
+ cropped_canvas = canvas_with_features
+
+ cropped_canvas.thumbnail([width, height], Image.ANTIALIAS)
+
+ if original_image.format != 'PNG':
+ cropped_canvas = cropped_canvas.convert('RGB')
+
+ return cropped_canvas
+
+
+ def image_url(self, size=400, force=False):
+
+ if self.image_store.source_image.path.endswith('.svg'):
+ thumburl = self.image_store.source_image.url
+
+ else:
+
+ image_path = self.image_store.source_image.path
+ folder_path = os.path.dirname(image_path)
+
+ thumbname = self.get_thumb_filename(size)
+
+ thumbfolder = os.path.join(folder_path, 'thumbnails')
+ if not os.path.isdir(thumbfolder):
+ os.makedirs(thumbfolder)
+
+ thumbpath = os.path.join(thumbfolder, thumbname)
+
+ if not os.path.isfile(thumbpath) or force == True:
+
+ original_image = Image.open(self.image_store.source_image.path)
+
+ processed_image = self.get_in_memory_processed_image(
+ original_image, size)
+
+ processed_image.save(thumbpath, original_image.format)
+
+ thumburl = os.path.join(os.path.dirname(
+ self.image_store.source_image.url), 'thumbnails', thumbname)
+
+ return thumburl
+
+
+class ServerContentImage(ContentImageProcessing, ContentImageAbstract):
+
+ image_store = models.ForeignKey(ServerImageStore, on_delete=models.CASCADE)
\ No newline at end of file
diff --git a/localcosmos_server/online_content/CMSObjects.py b/localcosmos_server/online_content/CMSObjects.py
deleted file mode 100644
index 9a47f01..0000000
--- a/localcosmos_server/online_content/CMSObjects.py
+++ /dev/null
@@ -1,249 +0,0 @@
-from django import forms
-from django.conf import settings
-
-from django.utils.translation import gettext as _
-
-from .models import microcontent_category_model_map
-
-import os, json
-
-"""
- CMSTagObject is an object created from a template tag
- - currently only reads draft models
- - the templatetag can allow multiple instances of CMSObjects
-"""
-class CMSTag:
-
- def __init__(self, app, template_content, microcontent_category, microcontent_type, *args, **kwargs):
-
- self.app = app
- self.template_content = template_content
-
- self.microcontent_category = microcontent_category
- self.microcontent_type = microcontent_type
- self.args = list(args)
-
- # unlocalized model
- self.Model = self._get_model(microcontent_category)
-
- self.multi = False
- self.min_num = kwargs.get('min', 0)
- self.max_num = kwargs.get('max', None)
-
- self.is_translatable = True
-
- # translatable images have their own microcontent_category
- if self.microcontent_category in ['image', 'images']:
- self.is_translatable = False
-
- if 'multi' in args:
- self.multi = True
-
- elif self.microcontent_category in ['images', 'microcontents']:
- self.multi = True
- self.args.append('multi')
-
-
- def _get_model(self, microcontent_category):
- Model = microcontent_category_model_map[microcontent_category]['draft']
- return Model
-
- """
- return a form field instance with an cms object attached to it
- """
-
- def _get_widget_attrs(self):
- widget_attrs = {
- 'data-microcontentcategory' : self.microcontent_category,
- 'data-microcontenttype' : self.microcontent_type,
- 'data-type' : '{0}-{1}'.format(self.microcontent_category, self.microcontent_type),
- }
-
- return widget_attrs
-
-
- '''
- return an usaved instance of self.Model. self.Model is e.g. DraftTextMicroContent or DraftImageMicrocontent and
- not the Localized Model
- '''
- def get_empty_localized_instance(self):
- LocaleModel = self.Model().get_locale_model()
- instance = LocaleModel()
- return instance
-
- '''
- return the form fields
- a) for the primary language
- b) for the translation language
- - the localized fields do not exist at first, but the unlocalized instance is needed to fetch the content
- in the primary language
- '''
- def form_fields(self, language, template_content=None, **kwargs):
-
- for_translation = kwargs.get('for_translation', False)
-
- # images are currently not translatable -> self.is_translatable == False for images
- if for_translation == True and self.is_translatable == False:
- return []
-
- form_fields = []
-
- widget_attrs = self._get_widget_attrs()
-
- if self.multi:
-
- # the first of multiple fields
- is_first = True
- is_last = False
-
- field_count = 0
-
- # first, fetch all language independant instances
- meta_instances = self.Model.objects.filter(template_content=template_content,
- microcontent_type=self.microcontent_type).order_by('position', 'pk')
-
- # there can be a meta instance without a matching locale, in the case of translation
- for meta_instance in meta_instances:
-
- localized_instance = meta_instance.get_localized(language)
- if not localized_instance:
- localized_instance = self.get_empty_localized_instance()
-
- field_count += 1
-
- # add an empty field if max_num not reached yet
- if self.max_num is None or self.max_num <= field_count:
-
- # check if this is the last field
- if self.max_num is not None and field_count == self.max_num:
- is_last = True
-
- # this field_name is used if no instance with pk is given
- field_name = '{0}-{1}'.format(self.microcontent_type, field_count)
- field = self._create_field(language, meta_instance, localized_instance, widget_attrs,
- is_first=is_first, is_last=is_last, field_name=field_name)
- form_fields.append(field)
-
- is_first = False
-
-
- # add multi-value field for content / multi-image-field for images
- # there is always only one blank field - except if for_translation == True
- if (self.max_num is None or field_count < self.max_num) and for_translation == False:
- # is_last is False
- is_last = True
-
- # append attrs to the widget
- widget_attrs.update({
- 'multi' : True,
- })
-
-
- empty_localized_instance = self.get_empty_localized_instance()
- empty_meta_instance = self.Model()
-
- field = self._create_field(language, empty_meta_instance, empty_localized_instance, widget_attrs,
- is_first=is_first, is_last=is_last)
-
- form_fields.append(field)
- is_first = False
-
- # non-multi fields
- else:
- # check if the cms object already exists, if so, use initial for the field
- meta_instance = self.Model.objects.filter(template_content=template_content,
- microcontent_type=self.microcontent_type).first()
-
- if meta_instance:
- localized_instance = meta_instance.get_localized(language)
- if not localized_instance:
- localized_instance = self.get_empty_localized_instance()
- else:
- localized_instance = self.get_empty_localized_instance()
- meta_instance = self.Model()
-
- field = self._create_field(language, meta_instance, localized_instance, widget_attrs)
- form_fields.append(field)
-
- return form_fields
-
-
- def create_empty_field(self, language):
- localized_instance = self.get_empty_localized_instance()
- meta_instance = self.Model()
- widget_attrs = self._get_widget_attrs()
-
- return self._create_field(language, meta_instance, localized_instance, widget_attrs)
-
- # multi fields also pass is_first/is_last in kwargs
- def _create_field(self, language, meta_instance, localized_instance, widget_attrs={}, **kwargs):
-
- widget_attrs = widget_attrs.copy()
-
- if localized_instance.pk:
- field_name = 'pk-{0}-{1}'.format(localized_instance.microcontent.pk, self.microcontent_type)
- widget_attrs['data-localized-pk'] = localized_instance.pk
- widget_attrs['data-meta-pk'] = localized_instance.microcontent.pk
- else:
- # if is-multi is set, especially for translations, a field_name with a counter suffix like 'type-1'
- # is provided
- field_name = kwargs.get('field_name', self.microcontent_type)
-
- field_kwargs = self._get_field_kwargs(language, localized_instance=localized_instance)
- field_kwargs['label'] = _(self.microcontent_type)
-
- # required for image uploads
- widget_attrs['language'] = language
-
- form_field = meta_instance.get_form_field(self.app, self.template_content, widget_attrs, *self.args,
- **field_kwargs)
-
- form_field.meta_instance = meta_instance
- form_field.localized_instance = localized_instance
-
- form_field.cms_object = CMSObject(self.Model, self.microcontent_category, self.microcontent_type,
- self.multi, self.min_num, self.max_num, meta_instance, localized_instance, *self.args, **kwargs)
-
- field = {
- 'field' : form_field,
- 'name' : field_name,
- }
-
- return field
-
-
- # for translations, language is necessary
- def _get_field_kwargs(self, language, localized_instance=None):
- kwargs = {
- 'required' : False, # fields are only required when publishing
- }
-
- if localized_instance is not None and localized_instance.pk:
- kwargs['initial'] = localized_instance.get_content()
-
- return kwargs
-
-
-
-"""
- content_categories are template_content(TemplateContent), text_microcontent(MicroContent), image_microcontent(ContentImage)
-"""
-
-class CMSObject:
-
- def __init__(self, Model, microcontent_category, microcontent_type, multi, min_num, max_num, meta_instance,
- localized_instance, *args, **kwargs):
-
- self.microcontent_category = microcontent_category
- self.microcontent_type = microcontent_type
- self.args = args
- self.Model = microcontent_category_model_map[microcontent_category]['draft']
- self.multi = multi
- self.min_num = min_num
- self.max_num = max_num
- self.is_file = 'image' in self.microcontent_category
- self.meta_instance = meta_instance
- self.localized_instance = localized_instance
- self.kwargs = kwargs
-
-
diff --git a/localcosmos_server/online_content/api/urls.py b/localcosmos_server/online_content/api/urls.py
deleted file mode 100644
index d4bb1ae..0000000
--- a/localcosmos_server/online_content/api/urls.py
+++ /dev/null
@@ -1,12 +0,0 @@
-from django.urls import include, path
-from . import views
-from rest_framework.urlpatterns import format_suffix_patterns
-
-
-urlpatterns = [
- path('online-content/', views.GetOnlineContent.as_view(), name='get_online_content'), # HTML ONLY
- path('online-content/list/', views.GetOnlineContentList.as_view(), # JSON ONLY
- name='get_online_content_list'),
-]
-
-urlpatterns = format_suffix_patterns(urlpatterns)
diff --git a/localcosmos_server/online_content/api/views.py b/localcosmos_server/online_content/api/views.py
deleted file mode 100644
index 24a116c..0000000
--- a/localcosmos_server/online_content/api/views.py
+++ /dev/null
@@ -1,155 +0,0 @@
-from rest_framework.views import APIView
-from django.core.exceptions import PermissionDenied
-from rest_framework.renderers import TemplateHTMLRenderer, BrowsableAPIRenderer
-from rest_framework.exceptions import ParseError, NotFound
-
-from localcosmos_server.online_content.models import (TemplateContent, LocalizedTemplateContent,
- TemplateContentFlags, SlugTrail)
-
-from localcosmos_server.models import App
-
-from django.template import Context
-from django.http import HttpResponse, Http404
-
-
-'''
- ONLINE CONTENT
- - fetch online content html and previews
- - always returns html
- - no authentication required
-'''
-'''
- GetOnlineContent
- - fetch TemplateContent by [localized?]slug
-'''
-class GetOnlineContent(APIView):
-
- permission_classes = ()
-
- # the app has to set the ACCEPT header to html only
- # BrowsableAPIRenderer is for testing purposes only
- renderer_classes = (TemplateHTMLRenderer, BrowsableAPIRenderer)
- template_name = None # set during get
-
- def get_object(self):
- # localized template content is fetched by localized_slug
-
- slug = self.request.query_params.get('slug', None)
- preview_token = self.request.query_params.get('preview_token', None)
-
- if not slug:
- raise ParseError('GET param slug is missing')
-
- localized_template_content = LocalizedTemplateContent.objects.filter(slug=slug).first()
-
- # maybe it is an old slug
- if not localized_template_content:
- slug_trail = SlugTrail.objects.filter(old_slug=slug)
-
- while slug_trail and not localized_template_content:
- if slug_trail.count() != 1:
- break
-
- new_slug = slug_trail.first().new_slug
- localized_template_content = LocalizedTemplateContent.objects.filter(slug=new_slug).first()
-
- if not localized_template_content:
- slug_trail = SlugTrail.objects.filter(old_slug=new_slug)
-
-
- if not localized_template_content:
- raise Http404('TemplateContent not found')
-
- if preview_token is None:
- # return the published version
- if not localized_template_content or not localized_template_content.published_version:
- raise Http404('TemplateContent not found')
-
- else:
- # preview qas requested, make security checks
- token_is_valid = localized_template_content.validate_preview_token(preview_token)
-
- # currently, this is disabled: nav entries (links) in the preview of the appbuilder should work
- if not token_is_valid == True:
- raise PermissionDenied('Invalid preview token')
-
-
- return localized_template_content
-
-
- def get_context_data(self, **kwargs):
- self.request.language = self.object.language
- context = {
- 'user': self.request.user,
- 'request' : self.request,
- 'template_content' : self.object.template_content,
- 'localized_template_content' : self.object,
- 'app' : self.object.template_content.app,
- 'preview' : False,
- }
-
- if 'preview_token' in self.request.query_params:
- context['preview'] = True
-
- return context
-
-
- def get(self, request, *args, **kwargs):
- self.object = self.get_object()
-
- # get the template
- template = self.object.get_template()
- context = self.get_context_data(**kwargs)
- c = Context(context)
- rendered = template.render(c)
-
- return HttpResponse(rendered)
-
-
-'''
- GetOnlineContentByFlag
- - get e.g. navigation entries by TemplateContentFlag
- - as multiple apps are possible per user, app-dependant retrieval is necessary
-'''
-class GetOnlineContentList(APIView):
- permission_classes = ()
- renderer_classes = (TemplateHTMLRenderer,)
- template_name = None # set during get
-
-
- def get(self, request, *args, **kwargs):
-
- flag = self.request.query_params.get('flag', None)
- app_uuid = self.request.query_params.get('app_uuid', None)
-
- app_state = 'published'
-
- if flag and app_uuid:
-
- app = App.objects.filter(uuid=app_uuid).first()
-
- if app:
- language = self.request.query_params.get('language', app.primary_language)
-
- if 'preview' in self.request.query_params:
- app_state = 'preview'
-
- flag_tree = TemplateContentFlags.objects.get_tree(app, flag, language, app_state=app_state)
-
- # read the template_name from online_content_settings
- online_content_settings = app.get_online_content_settings()
- template_name = online_content_settings['flags'][flag]['template_name']
-
- template = app.get_online_content_template(template_name)
-
- context = {
- 'app' : app,
- 'flag_tree' : flag_tree,
- }
-
- c = Context(context)
- rendered = template.render(c)
-
- return HttpResponse(rendered)
-
- raise Http404('No Online Content found')
diff --git a/localcosmos_server/online_content/apps.py b/localcosmos_server/online_content/apps.py
deleted file mode 100644
index b30ce7f..0000000
--- a/localcosmos_server/online_content/apps.py
+++ /dev/null
@@ -1,5 +0,0 @@
-from django.apps import AppConfig
-
-
-class OnlineContentConfig(AppConfig):
- name = 'online_content'
diff --git a/localcosmos_server/online_content/fields.py b/localcosmos_server/online_content/fields.py
deleted file mode 100644
index e10dc9a..0000000
--- a/localcosmos_server/online_content/fields.py
+++ /dev/null
@@ -1,74 +0,0 @@
-from django.forms.fields import Field, CharField
-from django.forms.widgets import Textarea
-from django.core.exceptions import ValidationError
-
-from django.utils.translation import gettext_lazy as _
-
-from .widgets import MultiContentWidget
-
-
-"""
- A field accepting multiple data of type CharField
- returns a list with data, or empty list
-"""
-class MultiContentField(Field):
-
- widget = MultiContentWidget
-
- default_error_messages = {
- 'invalid': _('Enter valid text.')
- }
-
- def __deepcopy__(self, memo):
- result = super().__deepcopy__(memo)
- result.fields = tuple(x.__deepcopy__(memo) for x in self.fields)
- return result
-
- def validate(self, value):
- pass
-
- def clean(self, value):
- """
- Validates every value in the given list.
- """
- clean_data = []
- errors = []
- if not value or isinstance(value, (list, tuple)):
- if not value or not [v for v in value if v not in self.empty_values]:
- if self.required:
- raise ValidationError(self.error_messages['required'], code='required')
- else:
- return []
- else:
- raise ValidationError(self.error_messages['invalid'], code='invalid')
-
-
- field = CharField()
-
- for field_value in value:
- try:
- clean_data.append(field.clean(field_value))
- except ValidationError as e:
- # Collect all validation errors in a single list, which we'll
- # raise at the end of clean(), rather than raising a single
- # exception for the first error we encounter. Skip duplicates.
- errors.extend(m for m in e.error_list if m not in errors)
-
- try:
- self.run_validators(field_value)
- except ValidationError as e:
- errors.extend(m for m in e.error_list if m not in errors)
-
- self.validate(clean_data)
-
- if errors:
- raise ValidationError(errors)
-
- return clean_data
-
- def has_changed(self, initial, data):
- for i in initial:
- if i not in data:
- return True
-
- return False
diff --git a/localcosmos_server/online_content/forms.py b/localcosmos_server/online_content/forms.py
deleted file mode 100644
index 1f93ef0..0000000
--- a/localcosmos_server/online_content/forms.py
+++ /dev/null
@@ -1,207 +0,0 @@
-from django.conf import settings
-from django import forms
-from django.utils.translation import gettext_lazy as _
-from django.core.validators import FileExtensionValidator
-
-import os
-
-from .parser import TemplateParser
-from .models import NAVIGATION_LINK_NAME_MAX_LENGTH, TemplateContent
-
-from django.template import loader
-
-from django.contrib.auth import get_user_model
-
-from localcosmos_server.forms import LocalizeableForm
-
-User = get_user_model()
-
-
-class TemplateContentFormCommon(LocalizeableForm):
- draft_title = forms.CharField(label=_('Title'))
- draft_navigation_link_name = forms.CharField(max_length=NAVIGATION_LINK_NAME_MAX_LENGTH,
- label=_('Name for links in navigation menus'),
- help_text=_('Max %(characters)s characters. If this content shows up in a navigation menu, this name will be shown as the link.') % {'characters' : NAVIGATION_LINK_NAME_MAX_LENGTH})
-
- localizeable_fields = ['draft_title', 'draft_navigation_link_name']
-
-
-class CreateTemplateContentForm(TemplateContentFormCommon):
-
- template_name = forms.ChoiceField(label =_('Template'))
-
- def __init__(self, app, template_type, *args, **kwargs):
-
- self.app = app
- self.template_type = template_type
-
- super().__init__(*args, **kwargs)
-
- # load the template_choices according to the cms
- choices = app.get_online_content_templates(template_type)
- self.fields['template_name'].choices = choices
-
-
-'''
- there are global contents without page (eg on base.html)
- and contents bound to a page
-'''
-class ManageMicroContentsForm(TemplateContentFormCommon):
-
- def _append_additional_fields(self):
- pass
-
- def _template(self):
- return self.template_content.get_template()
-
- def __init__(self, template_content, *args, **kwargs):
-
- self.template_content = template_content
-
- for_translation = kwargs.pop('for_translation', False)
-
- super().__init__(*args, **kwargs)
-
- self._append_additional_fields()
-
- self.layoutable_full_fields = set([])
- self.layoutable_simple_fields = set([])
-
- # read the template and find microcontent
- template = self._template()
-
- # find all cms template tags in source
- parser = TemplateParser(template_content.app, template_content, template)
- cms_tags = parser.parse()
-
- # the fields should be in self.fields
- for tag in cms_tags:
-
- # get cms form fields for each tag
- for field in tag.form_fields(self.language, template_content, for_translation=for_translation):
-
- self.fields[field['name']] = field['field']
-
- self.fields[field['name']].language = self.language
-
- if 'layoutable-simple' in tag.args:
- self.layoutable_simple_fields.add(field['name'])
- elif 'layoutable-full' in tag.args:
- self.layoutable_full_fields.add(field['name'])
-
-
-class ManageTemplateContentForm(ManageMicroContentsForm):
-
- #draft_title = forms.CharField(label=_('Title'))
- #draft_navigation_link_name = forms.CharField(max_length=NAVIGATION_LINK_NAME_MAX_LENGTH, label=_('Link name in navigations'),
- # help_text=_('Max %(characters)s characters. If this content shows up in a navigation this name will be shown as the link.') % {'characters' : NAVIGATION_LINK_NAME_MAX_LENGTH})
- page_flags = forms.MultipleChoiceField(label=_('Show link to this online content in'), required=False,
- help_text=_('The title will be the name of the link in the navigations. Long titles will be cut off.'))
-
-
- def _append_additional_fields(self):
- # read the frontend settings and add the page types defined there
- online_content_settings = self.template_content.get_online_content_settings()
-
- page_flag_choices = []
-
- if 'flags' in online_content_settings:
-
- for navigation_type, navigation in online_content_settings['flags'].items():
- page_flag_choices.append(
- (navigation_type, _(navigation['name']))
- )
-
- if self.template_content.template_type == 'page':
- self.fields['page_flags'].choices = page_flag_choices
- else:
- self.fields.pop('page_flags')
-
- if not page_flag_choices and 'page_flags' in self.fields:
- self.fields.pop('page_flags')
-
-
-'''
- Translate Template Content
- - do not include page_flags
-'''
-class TranslateTemplateContentForm(ManageMicroContentsForm):
- pass
-
-
-class ManagePagebaseForm(ManageMicroContentsForm):
-
- def __init__(self, template, *args, **kwargs):
- self.template = template
- super().__init__(None, language, *args, **kwargs)
-
- def _template(self):
- return self.template
-
-
-'''
- currently, this is only used in the primary language and deletes the meta_instance
- if the localized_instance should be deleted only (-> translations), a rewrite is needed
-'''
-class DeleteMicroContentForm(forms.Form):
- meta_pk = forms.IntegerField(widget=forms.HiddenInput)
- localized_pk = forms.IntegerField(widget=forms.HiddenInput)
- microcontent_category = forms.CharField(widget=forms.HiddenInput)
- microcontent_type = forms.CharField(widget=forms.HiddenInput, required=False)
-
-
-class UploadFileForm(forms.Form):
- # used urlparam instead: if form is invalid render an empty input with error message
- # microcontent_category = forms.CharField()
- # microcontent_type = forms.CharField()
- pk = forms.IntegerField(widget=forms.HiddenInput, required=False)
- template_content_id = forms.IntegerField(widget=forms.HiddenInput, required=False)
- language = forms.ChoiceField(widget=forms.HiddenInput, choices=settings.LANGUAGES)
- file = forms.FileField()
-
-
-class UploadImageForm(UploadFileForm):
- file = forms.ImageField()
-
-
-from content_licencing.mixins import LicencingFormMixin
-from collections import OrderedDict
-from localcosmos_server.widgets import ImageInputWithPreview
-from localcosmos_server.forms import ManageContentImageFormCommon
-class UploadImageWithLicenceForm(ManageContentImageFormCommon, LicencingFormMixin, forms.Form):
-
- template_content_id = forms.IntegerField(widget=forms.HiddenInput, required=False)
-
- def get_source_image_field(self):
- # unfortunately, a file field cannot be prepoluated due to html5 restrictions
- # therefore, source_image has to be optional. Otherwise, editing would be impossible
- # check if a new file is required in clean
- source_image_field = forms.ImageField(widget=ImageInputWithPreview, required=False)
- source_image_field.widget.current_image = self.current_image
-
- return source_image_field
-
-
-# check if the template already exists in templates provided by the frontend
-class UploadCustomTemplateForm(forms.Form):
-
- template = forms.FileField(validators=[FileExtensionValidator(allowed_extensions=['html'])])
-
- def __init__(self, app, *args, **kwargs):
- self.app = app
- super().__init__(*args, **kwargs)
-
- def clean_template(self):
-
- template = self.cleaned_data.get('template')
- uploaded_filename = template.name
-
- templates_path = os.path.join(self.app.get_online_content_templates_path(), 'page')
-
- # iterate over templates shipped with the frontend
- for filename in os.listdir(templates_path):
- if filename == uploaded_filename:
- raise forms.ValidationError(_('This template already exists.'))
-
- return template
-
diff --git a/localcosmos_server/online_content/migrations/0001_initial.py b/localcosmos_server/online_content/migrations/0001_initial.py
deleted file mode 100644
index 1c26572..0000000
--- a/localcosmos_server/online_content/migrations/0001_initial.py
+++ /dev/null
@@ -1,197 +0,0 @@
-# Generated by Django 3.1 on 2020-08-27 12:17
-
-from django.conf import settings
-from django.db import migrations, models
-import django.db.models.deletion
-import localcosmos_server.online_content.models
-
-
-class Migration(migrations.Migration):
-
- initial = True
-
- dependencies = [
- ('localcosmos_server', '0001_initial'),
- migrations.swappable_dependency(settings.AUTH_USER_MODEL),
- ]
-
- operations = [
- migrations.CreateModel(
- name='DraftImageMicroContent',
- fields=[
- ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
- ('microcontent_type', models.CharField(max_length=255)),
- ('position', models.IntegerField(default=1)),
- ],
- options={
- 'abstract': False,
- },
- ),
- migrations.CreateModel(
- name='DraftTextMicroContent',
- fields=[
- ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
- ('microcontent_type', models.CharField(max_length=255)),
- ('position', models.IntegerField(default=1)),
- ],
- options={
- 'abstract': False,
- },
- ),
- migrations.CreateModel(
- name='TemplateContent',
- fields=[
- ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
- ('template_name', models.CharField(max_length=255)),
- ('template_type', models.CharField(choices=[('page', 'Page'), ('feature', 'Feature')], max_length=20)),
- ('app', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='localcosmos_server.app')),
- ],
- ),
- migrations.CreateModel(
- name='SlugTrail',
- fields=[
- ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
- ('old_slug', models.SlugField()),
- ('new_slug', models.SlugField()),
- ],
- options={
- 'unique_together': {('old_slug', 'new_slug')},
- },
- ),
- migrations.CreateModel(
- name='PublishedTextMicroContent',
- fields=[
- ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
- ('microcontent_type', models.CharField(max_length=255)),
- ('position', models.IntegerField(default=1)),
- ('template_content', models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, to='online_content.templatecontent')),
- ],
- options={
- 'abstract': False,
- },
- ),
- migrations.CreateModel(
- name='PublishedImageMicroContent',
- fields=[
- ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
- ('microcontent_type', models.CharField(max_length=255)),
- ('position', models.IntegerField(default=1)),
- ('template_content', models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, to='online_content.templatecontent')),
- ],
- options={
- 'abstract': False,
- },
- ),
- migrations.CreateModel(
- name='LocalizedTemplateContent',
- fields=[
- ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
- ('slug', models.SlugField(unique=True)),
- ('language', models.CharField(max_length=15)),
- ('draft_title', models.CharField(max_length=255)),
- ('published_title', models.CharField(max_length=255, null=True)),
- ('draft_navigation_link_name', models.CharField(max_length=30)),
- ('navigation_link_name', models.CharField(max_length=30, null=True)),
- ('created_at', models.DateTimeField(auto_now_add=True)),
- ('last_modified', models.DateTimeField(auto_now=True)),
- ('draft_version', models.IntegerField(default=1)),
- ('published_version', models.IntegerField(null=True)),
- ('published_at', models.DateTimeField(null=True)),
- ('translation_ready', models.BooleanField(default=False)),
- ('preview_token', models.CharField(max_length=255, null=True)),
- ('preview_token_created_at', models.DateTimeField(null=True)),
- ('creator', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='template_content_creator', to=settings.AUTH_USER_MODEL)),
- ('last_modified_by', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, to=settings.AUTH_USER_MODEL)),
- ('template_content', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='online_content.templatecontent')),
- ],
- ),
- migrations.CreateModel(
- name='LocalizedPublishedTextMicroContent',
- fields=[
- ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
- ('language', models.CharField(max_length=15)),
- ('created_at', models.DateTimeField(auto_now_add=True)),
- ('last_modified', models.DateTimeField(auto_now=True)),
- ('content', models.TextField(null=True)),
- ('plain_text', models.TextField(null=True)),
- ('creator', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to=settings.AUTH_USER_MODEL)),
- ('last_modified_by', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to=settings.AUTH_USER_MODEL)),
- ('microcontent', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='online_content.publishedtextmicrocontent')),
- ],
- options={
- 'abstract': False,
- },
- ),
- migrations.CreateModel(
- name='LocalizedPublishedImageMicroContent',
- fields=[
- ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
- ('language', models.CharField(max_length=15)),
- ('created_at', models.DateTimeField(auto_now_add=True)),
- ('last_modified', models.DateTimeField(auto_now=True)),
- ('content', models.ImageField(upload_to=localcosmos_server.online_content.models.content_images_upload_path)),
- ('creator', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to=settings.AUTH_USER_MODEL)),
- ('last_modified_by', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to=settings.AUTH_USER_MODEL)),
- ('microcontent', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='online_content.publishedimagemicrocontent')),
- ],
- options={
- 'abstract': False,
- },
- ),
- migrations.CreateModel(
- name='LocalizedDraftTextMicroContent',
- fields=[
- ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
- ('language', models.CharField(max_length=15)),
- ('created_at', models.DateTimeField(auto_now_add=True)),
- ('last_modified', models.DateTimeField(auto_now=True)),
- ('content', models.TextField(null=True)),
- ('plain_text', models.TextField(null=True)),
- ('creator', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to=settings.AUTH_USER_MODEL)),
- ('last_modified_by', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to=settings.AUTH_USER_MODEL)),
- ('microcontent', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='online_content.drafttextmicrocontent')),
- ],
- options={
- 'abstract': False,
- },
- ),
- migrations.CreateModel(
- name='LocalizedDraftImageMicroContent',
- fields=[
- ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
- ('language', models.CharField(max_length=15)),
- ('created_at', models.DateTimeField(auto_now_add=True)),
- ('last_modified', models.DateTimeField(auto_now=True)),
- ('content', models.ImageField(upload_to=localcosmos_server.online_content.models.content_images_upload_path)),
- ('creator', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to=settings.AUTH_USER_MODEL)),
- ('last_modified_by', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to=settings.AUTH_USER_MODEL)),
- ('microcontent', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='online_content.draftimagemicrocontent')),
- ],
- options={
- 'abstract': False,
- },
- ),
- migrations.AddField(
- model_name='drafttextmicrocontent',
- name='template_content',
- field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, to='online_content.templatecontent'),
- ),
- migrations.AddField(
- model_name='draftimagemicrocontent',
- name='template_content',
- field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, to='online_content.templatecontent'),
- ),
- migrations.CreateModel(
- name='TemplateContentFlags',
- fields=[
- ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
- ('flag', models.CharField(max_length=255)),
- ('position', models.IntegerField(default=1)),
- ('parent_flag', models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, to='online_content.templatecontentflags')),
- ('template_content', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='online_content.templatecontent')),
- ],
- options={
- 'unique_together': {('template_content', 'flag')},
- },
- ),
- ]
diff --git a/localcosmos_server/online_content/mixins.py b/localcosmos_server/online_content/mixins.py
deleted file mode 100644
index 31c592f..0000000
--- a/localcosmos_server/online_content/mixins.py
+++ /dev/null
@@ -1,40 +0,0 @@
-# add self.online_content
-from django.conf import settings
-from django.shortcuts import redirect
-from django.urls import reverse
-from localcosmos_server.models import App
-
-'''
- OnlineContent is available only for installed apps
-'''
-
-class OnlineContentMixin:
-
- def dispatch(self, request, *args, **kwargs):
- if hasattr(request, 'app') and request.app != None:
- self.app = request.app
- else:
- self.app = App.objects.get(uid=kwargs['app_uid'])
-
- if settings.LOCALCOSMOS_PRIVATE == True:
- app_state = 'published'
- else:
- app_state = 'preview'
-
- self.app_disk_path = self.app.get_installed_app_path(app_state=app_state)
-
- if not self.app_disk_path:
- raise FileNotFoundError('[{0}] self.app_disk_path for app_state: {1} is {2}'.format(self.app.uid,
- app_state, self.app_disk_path))
-
- return super().dispatch(request, *args, **kwargs)
-
-
- def get_context_data(self, **kwargs):
- context = super().get_context_data(**kwargs)
- context.update({
- 'app' : self.app,
- })
- return context
-
-
diff --git a/localcosmos_server/online_content/models.py b/localcosmos_server/online_content/models.py
deleted file mode 100644
index 0e59f63..0000000
--- a/localcosmos_server/online_content/models.py
+++ /dev/null
@@ -1,1170 +0,0 @@
-from django.db import models
-from django.conf import settings
-from django.utils.translation import gettext_lazy as _
-from django.contrib.contenttypes.models import ContentType
-from django.template.defaultfilters import slugify
-from django import forms
-from django.utils.html import strip_tags
-from django.utils.safestring import mark_safe
-from django.urls import reverse
-from django.core.files import File
-
-from django.contrib.contenttypes.fields import GenericRelation
-from content_licencing.models import ContentLicenceRegistry
-
-from localcosmos_server.models import App
-
-import os, json, secrets
-
-from django.utils import timezone
-
-from .fields import MultiContentField
-from .widgets import MultiContentWidget
-from .utils import verbosify_template_name
-
-
-
-'''
- TEMPLATE CONTENT AND LOCALIZED TEMPLATE CONTENT
-
- template contents are .html templates with titles
- the templates contain MicroContents which are stored separately
-
- you cannot share single websites (TemplateContent) between apps
- therefore, template contents are tied to OnlineContent
-
- OnlineContent cannot be shared between apps, too
-
- There are two types of TemplateContent:
- - page
- - feature (can be displayed on several pages via include, may not extend a base)
-'''
-
-TEMPLATE_TYPES = (
- ('page', _('Page')), # pages can have flags like "header", "footer" etc
- ('feature', _('Feature')), # e.g. newsbox - can be displayed on several pages
-)
-
-class TemplateContentManager(models.Manager):
-
- def create(self, creator, app, language, draft_title, draft_navigation_link_name, template_name,
- template_type):
-
- template_content = self.model(
- app = app,
- template_name = template_name,
- template_type = template_type,
- )
- template_content.save()
-
- # create the localized template content
- localized_template_content = LocalizedTemplateContent.objects.create(creator, template_content, language,
- draft_title, draft_navigation_link_name)
-
- return template_content
-
-
-class TemplateContent(models.Model):
-
- app = models.ForeignKey(App, on_delete=models.CASCADE)
- template_name = models.CharField(max_length=255) # the .html django template this content uses
- template_type = models.CharField(max_length=20, choices=TEMPLATE_TYPES) # page or feature
-
- objects = TemplateContentManager()
-
- def get_online_content_settings(self):
- return self.app.get_online_content_settings()
-
- def verbose_template_name(self, language_code=None):
- if not language_code:
- language_code = self.app.primary_language
-
- online_content_settings = self.app.get_online_content_settings()
-
- verbose_name = verbosify_template_name(self.template_name)
-
- if self.template_name in online_content_settings['verbose_template_names'] and language_code in online_content_settings['verbose_template_names'][self.template_name]:
- verbose_name = online_content_settings['verbose_template_names'][self.template_name][language_code]
-
- return verbose_name
-
-
- def get_localized(self, language_code):
-
- localized_template_content = LocalizedTemplateContent.objects.filter(template_content=self,
- language=language_code).first()
- return localized_template_content
-
- def locales(self):
- return LocalizedTemplateContent.objects.filter(template_content=self)
-
- # template_path dependant of the apps frontend setting
- def template_path(self, app):
- return os.path.join(self.app.get_online_content_templates_path(), self.template_name)
-
-
- def get_template(self):
- return self.app.get_online_content_template(self.template_name)
-
-
- def primary_title(self):
- language_code = self.app.primary_language
- locale = LocalizedTemplateContent.objects.filter(template_content=self, language=language_code).first()
-
- if locale:
- return locale.draft_title
- else:
- return None
-
-
- # might be obsolete
- def validate(self, app):
-
- result = {
- 'errors' : [],
- 'warnings' : [],
- }
-
- return result
-
-
- '''
- TemplateContent.translation_complete(language_code)
- - checks if all localized_template_contents are set to translation_ready
- - calls LocalizedTemplateContent.translation_complete()
- ---- if language is the primary language: check if all required fields are present
- ---- if language is a secondary language: check if all fields of the primary language are translated
- '''
- def translation_complete(self, language_code):
-
- translation_errors = []
-
- ltc = LocalizedTemplateContent.objects.filter(template_content=self, language=language_code).first()
-
- if not ltc:
- translation_errors.append(_('Translation for the language %(language)s is missing') %{
- 'language':language_code})
-
- else:
- if ltc.translation_ready == False:
- translation_errors.append(_('The translator for the language %(language)s is still working') %{
- 'language':language_code})
-
- translation_errors += ltc.translation_complete()
-
- return translation_errors
-
-
- def publish(self, language='all'):
-
- publication_errors = []
-
- secondary_languages = self.app.secondary_languages()
- primary_language = self.app.primary_language
-
-
- if language == 'all':
- languages = self.app.languages()
- else:
- languages = [language]
-
- # ltc.translation_ready is not set to True by the user if there is only one language
- # skip the check if the "translation" exists and also skip the check if the user has set
- # translation_ready to True, which is not the case because there is only a "publish" button
- # in this case (only 1 language) and no "ready for translation" button
- if not secondary_languages:
- ltc = LocalizedTemplateContent.objects.filter(template_content=self, language=primary_language).first()
- publication_errors += ltc.translation_complete()
-
- # secondary languages exist. these languages need translators and the translation_ready flags are
- # set by the user when he has finished translating
- else:
-
- for language_code in languages:
-
- # translation_complete checks two things:
- # a) if the primary language has filled all required fields
- # b) if all secondary languages are translated completely
- publication_errors += self.translation_complete(language_code)
-
- # below this, no error checks are allowed because published_versions are being set
-
- if not publication_errors:
-
- for language_code in languages:
-
- ltc = LocalizedTemplateContent.objects.filter(template_content=self, language=language_code).first()
- if ltc:
- ltc.publish()
-
- # get all lang independant DraftTextMicroContent and DraftImageMicroContent instances
- # and publish all LocalizedDraft*MicroContent for the given language(s)
- # this cannot be done from the LocalizedTemplateContent as different languages link to
- # the same Published*MicroContent
-
- # ROUTINE: first, publish the Draft*Microcontent, this creates Published*MicroContent
- # then publish all LocalizedPublished*MicroContents and attach them to the already
- # created Published*MicroContent
-
-
- # get all subclasses of PublishedCMSMicrocontent, eg PublishedTextMicroContent or PublishedImageMicroContent
- PublishedMetaClasses = PublishedCMSMicroContent.__subclasses__()
-
- for PublishedMetaClass in PublishedMetaClasses:
- published_microcontents = PublishedMetaClass.objects.filter(template_content=self)
-
- for published_microcontent in published_microcontents:
- published_microcontent.delete()
-
-
- # get all sublasses of DraftCMSMicroContent:
- DraftMetaClasses = DraftCMSMicroContent.__subclasses__()
-
- for DraftMetaClass in DraftMetaClasses:
-
- draft_microcontents = DraftMetaClass.objects.filter(template_content=self)
-
- for draft_microcontent in draft_microcontents:
- draft_microcontent.publish(languages)
-
- return publication_errors
-
-
- def flags(self):
- return list(TemplateContentFlags.objects.filter(template_content=self).values_list('flag', flat=True))
-
- def unpublish(self):
- localizations = LocalizedTemplateContent.objects.filter(template_content=self)
-
- for localization in localizations:
- localization.published_version = None
- localization.published_at = None
- localization.save()
-
-
- @property
- def is_published(self):
- return LocalizedTemplateContent.objects.filter(template_content=self,
- published_version__isnull=False).exists()
-
-
- def __str__(self):
- ltc = self.get_localized(self.app.primary_language)
-
- if ltc:
- return ltc.draft_title
-
- return 'Template Content %s' %self.pk
-
-
-'''
- on db entry per language and content -> LocalizedTemplateContent
- slugs are also localized
-'''
-MAX_SLUG_LENGTH = 100
-class LocalizedTemplateContentManager(models.Manager):
-
- def create(self, creator, template_content, language, draft_title, draft_navigation_link_name):
-
- slug = self.generate_slug(draft_title)
-
- localized_template_content = self.model(
- creator = creator,
- template_content = template_content,
- language = language,
- draft_title = draft_title,
- draft_navigation_link_name = draft_navigation_link_name,
- slug = slug,
- )
-
- localized_template_content.save()
-
- return localized_template_content
-
-
- def generate_slug_base(self, draft_title):
- slug_base = str('%s' % (slugify(draft_title)) )[:MAX_SLUG_LENGTH-1]
-
- return slug_base
-
- def generate_slug(self, draft_title):
-
- slug_base = self.generate_slug_base(draft_title)
-
- slug = slug_base
-
- exists = LocalizedTemplateContent.objects.filter(slug=slug).exists()
-
- i = 2
- while exists:
-
- if len(slug) > 50:
- slug_base = slug_base[:-1]
-
- slug = str('%s-%s' % (slug_base, i))
- i += 1
- exists = LocalizedTemplateContent.objects.filter(slug=slug).exists()
-
- return slug
-
-
-'''
- translation_ready is set by the translator to signal that he has finished the translation
-'''
-# the maximum character count for a link name
-NAVIGATION_LINK_NAME_MAX_LENGTH = 30
-TITLE_MAX_LENGTH = 255
-
-class LocalizedTemplateContent(models.Model):
- template_content = models.ForeignKey(TemplateContent, on_delete=models.CASCADE)
- slug = models.SlugField(unique=True)# localized slug
- language = models.CharField(max_length=15)
-
- # title
- # the title is needed e.g. for displaying a name of the content in the admin
- draft_title = models.CharField(max_length=TITLE_MAX_LENGTH)
- published_title = models.CharField(max_length=TITLE_MAX_LENGTH, null=True)
-
- # link name in navigations
- # link names in navs have to be short. In-Text links can have different names an are set by the editor on demand
- draft_navigation_link_name = models.CharField(max_length=NAVIGATION_LINK_NAME_MAX_LENGTH)
- navigation_link_name = models.CharField(max_length=NAVIGATION_LINK_NAME_MAX_LENGTH, null=True)
-
- created_at = models.DateTimeField(auto_now_add=True)
- creator = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.SET_NULL,
- related_name='template_content_creator', null=True)
- last_modified = models.DateTimeField(auto_now=True)
- last_modified_by = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.SET_NULL, null=True)
- draft_version = models.IntegerField(default=1)
- published_version = models.IntegerField(null=True)
- published_at = models.DateTimeField(null=True)
-
- translation_ready = models.BooleanField(default=False)
-
- # for protecting previews
- preview_token = models.CharField(max_length=255, null=True)
- preview_token_created_at = models.DateTimeField(null=True)
-
- objects = LocalizedTemplateContentManager()
-
-
- '''
- - if the language is the primary language, check if all required fields are presend
- - if the language is a secondart language, check if all fields of the primary language are translated
- '''
- def translation_complete(self):
-
- translation_errors = []
-
- primary_language = self.template_content.app.primary_language
-
- # avoid circular import
- from .parser import TemplateParser
- parser = TemplateParser(self.template_content.app, self.template_content, self.get_template())
-
- cms_tags = parser.parse()
-
- # primary language: check if all required fields are present
- if self.language == primary_language:
- # in this case, the page editor has pushed "translation_complete" button or "publish" button
- # on the creation page, no the translation page
- # check if all required components are presend
- # load the parser
-
- for tag in cms_tags:
- if not 'optional' in tag.args:
-
- draft_microcontent = tag.Model.objects.filter(template_content=self.template_content,
- microcontent_type=tag.microcontent_type).first()
-
- if not draft_microcontent:
- translation_errors.append(_('The component "%(component_name)s" is required but still missing for the language %(language)s') %{'component_name':tag.microcontent_type, 'language':self.language})
-
- else:
- translation_errors += draft_microcontent.translation_complete(self.language)
-
- # secondary languages: check if all fields that are present in the primary language have been translated
- else:
- for tag in cms_tags:
-
- # language independant - fetch all existing drafts
- draft_microcontents = tag.Model.objects.filter(template_content=self.template_content,
- microcontent_type=tag.microcontent_type)
-
- # check all contents, including all 'multi'
- for draft_microcontent in draft_microcontents:
-
- translation_errors += draft_microcontent.translation_complete(self.language)
-
- return translation_errors
-
-
- def flags(self):
- return self.template_content.flags()
-
- # preview token
- def update_preview_token(self):
- self.preview_token = secrets.token_hex(20)
- self.preview_token_created_at = timezone.now()
- self.save(disallow_new_version=True)
-
- def validate_preview_token(self, token, maxage_minutes=240):
-
- if self.preview_token and self.preview_token_created_at:
-
- if self.preview_token != token:
- return False
-
- now = timezone.now()
- timedelta = (now - self.preview_token_created_at).total_seconds() / 60
- # create new token every 5 minutes
- # each token is valid for 60 minutes
- if timedelta > maxage_minutes:
- return False
-
- return True
-
- return False
-
- # shortcut
- def get_template(self):
- return self.template_content.get_template()
-
- # improvement could be: read urls.js from the app and construct the url accordingly
- def in_app_link(self, app_state='published'):
- if app_state == 'preview':
- in_app_link = '/online-content/%s/%s/' %(self.slug, self.preview_token)
- else:
- in_app_link = '/online-content/%s/' %(self.slug)
-
- return in_app_link
-
-
- '''
- publish a LocalizedTemplateContent and all its MicroContents
- '''
- def publish(self):
- # set title
- self.published_title = self.draft_title
- self.navigation_link_name = self.draft_navigation_link_name
-
- if self.published_version != self.draft_version:
-
- self.published_version = self.draft_version
- self.published_at = timezone.now()
-
- self.save(published=True)
-
-
- def save(self, *args, **kwargs):
-
- # indicates, if the save() command came from self.publish
- published = kwargs.pop('published', False)
-
- # if only a new preview token is needed skip the publication protocol
- disallow_new_version = kwargs.pop('disallow_new_version', False)
-
- # first, check the slug - new slug if the title really changed
- slug_base = LocalizedTemplateContent.objects.generate_slug_base(self.draft_title)
- if not self.slug.startswith(slug_base):
- old_slug = self.slug
- self.slug = LocalizedTemplateContent.objects.generate_slug(self.draft_title)
- new_slug = self.slug
-
- # add to SlugTrail - make old links still work
- slug_trail = SlugTrail(
- old_slug = old_slug,
- new_slug = new_slug,
- )
-
- slug_trail.save()
-
- if not self.pk:
-
- if self.language != self.template_content.app.primary_language:
- master_ltc = self.template_content.get_localized(self.template_content.app.primary_language)
- self.draft_version = master_ltc.draft_version
-
- else:
-
- if disallow_new_version == False and published == False:
-
- # the localized_template_content has already been published. start new version
- if self.published_version == self.draft_version:
- self.draft_version += 1
- self.translation_ready = False
-
- super().save(*args, **kwargs)
-
-
-# if a slug is changed, remember the wold slug for a forward
-# e.g. a user changes the title of an entry
-class SlugTrail(models.Model):
- old_slug = models.SlugField()
- new_slug = models.SlugField()
-
- class Meta:
- unique_together = ('old_slug', 'new_slug')
-
-
-'''
- TemplateContentFlags Concept
- - assign flags to template_content, like 'footer' or 'header' or 'nav'
- - multiple assignments possible - one template_content can have multiple flags
- and occur e.g. in header and also in footer
- - nested/multi-level flagging is possible
- - there should not be so many db entries, so a simple parent_flag model should be fast enough
-'''
-
-'''
- The FlagTree class enables iterating over a tree structure
- {
- "name" : "text",
- "children" : {
- "name" : "text"
- }
- }
-
- max_levels is 2 by default
-'''
-class FlagTree:
-
- # flags is a TemplateContentFlags QuerySet
- def __init__(self, flags, language, max_levels=2, **kwargs):
- self.flags = flags.order_by('parent_flag')
- self.language = language
- self.flag_tree = {}
-
- self.app_state = kwargs.get('app_state', 'published')
-
- self.toplevel_count = 0
-
- # preview fetches localized_template_content.draft_title
- title_attr = 'published_title'
- navigation_link_attr = 'navigation_link_name'
- if self.app_state == 'preview':
- title_attr = 'draft_title'
- navigation_link_attr = 'draft_navigation_link_name'
-
-
- for flag in self.flags:
-
- tree_entry = self.get_tree_entry(flag)
-
- if tree_entry is not None:
-
- self.toplevel_count += 1
-
- if flag.parent_flag:
- raise NotImplementedError('navigation nesting is not implemented yet')
-
- localized_template_content = tree_entry['localized_template_content']
-
- self.flag_tree[getattr(localized_template_content, title_attr)] = tree_entry
-
-
- def get_tree_entry(self, flag):
-
- tree_entry = None
-
- # only return unpublished localized_template_contents if preview is set to True
- localized_template_content = flag.template_content.get_localized(self.language)
-
- if localized_template_content:
- if self.app_state == 'preview' or localized_template_content.published_version:
-
- tree_entry = {
- 'localized_template_content' : localized_template_content,
- 'children' :[],
- 'in_app_link' : localized_template_content.in_app_link(app_state=self.app_state),
- 'navigation_link_name' : localized_template_content.navigation_link_name,
- }
-
- if self.app_state == 'preview':
- tree_entry['navigation_link_name'] = localized_template_content.draft_navigation_link_name
-
- return tree_entry
-
-
- def __iter__(self):
- for draft_title, tree_entry in self.flag_tree.items():
- yield tree_entry
-
- def __bool__(self):
- if self.flag_tree.items():
- return True
- return False
-
-
-class TemplateContentFlagsManager(models.Manager):
-
- # do not retrieve flags across apps
- def get_tree(self, app, flag, language, **kwargs):
-
- online_content_settings = app.get_online_content_settings()
- max_flag_levels = online_content_settings.get('max_flag_levels', 2)
-
- flags = self.filter(template_content__app=app, flag=flag)
-
- flag_tree = FlagTree(flags, language, max_levels=max_flag_levels, **kwargs)
-
- return flag_tree
-
-
-class TemplateContentFlags(models.Model):
- template_content = models.ForeignKey(TemplateContent, on_delete=models.CASCADE)
- flag = models.CharField(max_length=255) # e.g. 'footer', 'nav', 'header'
- parent_flag = models.ForeignKey('self', on_delete=models.CASCADE, null=True)
- position = models.IntegerField(default=1)
-
- objects = TemplateContentFlagsManager()
-
- class Meta:
- unique_together = ('template_content', 'flag')
-
-
-'''
- MICROCONTENT
- - linked to a template_content (there can be multiple template_contents of the same type)
- - 1:n relation (TemplateContent : MicroContent)
- - separates layout from text/images
-
- TYPES (microcontent_type) are defined by the template creator
-'''
-class CMSMicroContentManager(models.Manager):
-
- # return LocalizedCMSMicroContent instances in pk order
- def get_language_dependant(self, template_content, microcontent_type, language):
-
- contents = []
-
- # filter the unlocalized model
- contents_ = self.filter(template_content=template_content, microcontent_type=microcontent_type).order_by(
- 'pk', 'position')
-
- for content in contents_:
- LocaleModel = self.model.get_locale_model()
- lmcs = LocaleModel.objects.filter(microcontent=content, language=language)
-
- for lmc in lmcs:
- contents.append(lmc)
-
- return contents
-
-
-class DraftMicroContentManager(CMSMicroContentManager):
-
- def create(self, template_content, language, microcontent_type, content, creator, **kwargs):
-
- draft_microcontent = self.model(
- template_content = template_content,
- microcontent_type = microcontent_type,
- )
-
- draft_microcontent.save()
-
- LocaleModel = self.model.get_locale_model()
-
- localized_draft_microcontent = LocaleModel.objects.create(draft_microcontent, language, content, creator)
-
- return draft_microcontent
-
-
-from django.db.models.fields.files import FileField
-from django.core.files import File
-from django.core.files.base import ContentFile
-class PublishedMicroContentManager(CMSMicroContentManager):
-
- def create(self, draft_microcontent, **kwargs):
-
-
- # first publish the not-localized microcontent
- microcontent_fields = self.model._meta.get_fields(include_parents=False)
- pmc_fields = {}
-
- for mc_field in microcontent_fields:
-
- if mc_field.concrete == False:
- continue
-
- # field.primary_key = bool indicated if the field is the primary key
- if mc_field.primary_key:
- continue
- pmc_fields[mc_field.name] = getattr(draft_microcontent, mc_field.name)
-
- published_microcontent = self.model(**pmc_fields)
- published_microcontent.save()
-
- return published_microcontent
-
-
-'''
- Abstract CMSMicroContent
-'''
-class CMSMicroContent(models.Model):
- template_content = models.ForeignKey(TemplateContent, on_delete=models.CASCADE, null=True) # if template_content is None, it is the same content for every template_content
- microcontent_type = models.CharField(max_length=255)
- position = models.IntegerField(default=1)
-
- @classmethod
- def get_locale_model(self):
- return globals()['Localized%s' % self._meta.model.__name__]
-
-
- def get_content(self, language):
- lmc = self.get_localized(language)
-
- if lmc:
- return lmc.get_content()
-
- return None
-
-
- def get_localized(self, language):
-
- LocaleModel = self.get_locale_model()
- lmc = LocaleModel.objects.filter(microcontent=self, language=language).first()
-
- return lmc
-
-
- class Meta:
- ordering = ['pk', 'position']
- abstract = True
-
-
-
-'''
- Draft and Published Microcontents require some methods and separate Managers
-'''
-class DraftCMSMicroContent(CMSMicroContent):
-
- objects = DraftMicroContentManager()
-
- @classmethod
- def get_publication_model(self):
- return globals()[self._meta.model.__name__.replace('Draft', 'Published')]
-
- def get_form_field(self, app, template_content, widget_attrs, *args, **kwargs):
- raise NotImplementedError('CMSMicroContent need a get_form_field method')
-
- def translation_complete(self, language):
- raise NotImplementedError('CMSMicroContent need a translation_complete method')
-
- def set_content(self, content, editor, language):
- lmc = self.get_localized(language)
- if not lmc:
- LocaleModel = self.get_locale_model()
- lmc = LocaleModel.objects.create(self, language, content, editor)
- else:
- lmc.content = content
- lmc.last_modified_by = editor
- lmc.save()
-
-
- '''
- the licences get published too, for images
- '''
- def publish(self, languages):
-
- PublicationModel = self.get_publication_model()
-
- # create the published microcontent, language independant
- published_microcontent = PublicationModel.objects.create(self)
-
- PublicationLocaleModel = published_microcontent.get_locale_model()
-
- # get the fields
- localized_published_microcontent_fields = PublicationLocaleModel._meta.get_fields(include_parents=False)
-
- # iterate over all languages that should be published and attach the localization to published_microcontent
- for language in languages:
- localized_draft_microcontent = self.get_localized(language)
-
- if localized_draft_microcontent:
-
- lpmc_fields = {
- 'microcontent' : published_microcontent,
- }
- file_field_names = []
-
- for lmc_field in localized_published_microcontent_fields:
-
- if lmc_field.concrete == False:
- continue
-
- if lmc_field.primary_key or lmc_field.name == 'microcontent':
- continue
-
- if isinstance(lmc_field, FileField):
- file_field_names.append(lmc_field.name)
-
- lpmc_fields[lmc_field.name] = getattr(localized_draft_microcontent, lmc_field.name)
-
-
- # model instance without files
- localized_published_microcontent = PublicationLocaleModel(**lpmc_fields)
-
- # work the FileField/Imagefield etc fields
- # copy the file
- for file_field_name in file_field_names:
-
- draft_file = getattr(localized_draft_microcontent, file_field_name)
-
- new_file = ContentFile(draft_file.read())
- new_file.name = os.path.split(draft_file.name)[-1]
-
- setattr(localized_published_microcontent, file_field_name, new_file)
-
- draft_file.close()
-
- localized_published_microcontent.save()
-
- # generate the licences
- if hasattr(localized_draft_microcontent, 'licences'):
- for licence in localized_draft_microcontent.licences.all():
-
- content_licence = licence.content_licence()
- licence_kwargs = {
- 'creator_name' : licence.creator_name,
- 'creator_link' : licence.creator_link,
- 'source_link' : licence.source_link,
- 'language' : licence.language,
- 'sha256' : licence.sha256,
- }
-
- registry_entry = ContentLicenceRegistry.objects.register(localized_published_microcontent,
- licence.model_field, licence.uploader, content_licence.short_name,
- content_licence.version, **licence_kwargs)
-
-
- class Meta:
- abstract = True
-
-
-class PublishedCMSMicroContent(CMSMicroContent):
- objects = PublishedMicroContentManager()
-
- class Meta:
- abstract = True
-
-
-'''
- Abstract LocalizedCMSMicroContent
- - serves both Draft and Published
-'''
-class LocalizedCMSMicroContentManager(models.Manager):
-
- def create(self, microcontent, language, content, creator, **kwargs):
-
- # can be localized_draft_microcontent or localized_published_microcontent
-
- localized_microcontent = self.model(
- language = language,
- microcontent = microcontent,
- content = content,
- creator = creator,
- **kwargs
- )
-
- localized_microcontent.save()
-
- return localized_microcontent
-
-
-class LocalizedCMSMicroContent(models.Model):
-
- language = models.CharField(max_length=15)
-
- creator = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.SET_NULL, related_name='+', null=True)
- created_at = models.DateTimeField(auto_now_add=True)
-
-
- last_modified_by = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.SET_NULL, related_name='+',
- null=True)
- last_modified = models.DateTimeField(auto_now=True)
-
- objects = LocalizedCMSMicroContentManager()
-
-
- def get_content(self):
- if type(self.content) == str:
- return mark_safe(self.content)
- return self.content
-
- class Meta:
- abstract = True
- unique_together = ('microcontent', 'language')
-
-
-'''
- MicroContent
- - are parts of template_contents that the user can fill with text
- - is restricted to one template_content
- - can have one or more images linked to it
- - can be layoutable or not
-'''
-
-
-################################ TEXT MICROCONTENT ######################################
-'''
- Draft Text MicroContent
- - can be deleted and edited
- - deleting these elements does not affect the published content
-'''
-class DraftTextMicroContent(DraftCMSMicroContent):
-
- def get_form_field(self, app, template_content, widget_attrs, *args, **kwargs):
-
- widget = forms.Textarea
- if 'short' in args:
- widget = forms.TextInput
-
-
- if 'multi' in widget_attrs:
- # assign the number
- widget_attrs['widget'] = widget
- return MultiContentField(widget=MultiContentWidget(widget_attrs), **kwargs)
-
- else:
- kwargs['widget'] = widget
- return forms.CharField(**kwargs)
-
-
- def translation_complete(self, language):
-
- translation_errors = []
-
- lmc = self.get_localized(language)
-
- if not lmc or (lmc.content is None or len(lmc.content) == 0):
- translation_errors.append(_('%(component_name)s is missing for the language %(language)s' %{'component_name':self.microcontent_type, 'language':language}))
-
- return translation_errors
-
-
-'''
- Published Text MicroContent
- - cannot be edited
- - is not affected by deletions in the draft
- - is not affected if a whole DraftCMSMicroConent instance is deleted
-'''
-
-class PublishedTextMicroContent(PublishedCMSMicroContent):
- pass
-
-
-'''
- Localizations for Text MicroContents
- content = html
- plain_text = text only
- on each save, the plain text is updated
-'''
-class LocalizedTextMicroContent(LocalizedCMSMicroContent):
-
- content = models.TextField(null=True)
- plain_text = models.TextField(null=True)
-
- def save(self, *args, **kwargs):
-
- if self.content is not None:
- self.plain_text = _generate_plaintext(self.content)
- else:
- self.plain_text = None
-
- return super().save(*args, **kwargs)
-
-
- class Meta:
- abstract = True
-
-
-'''
- localized draft TextMicroContent
-'''
-class LocalizedDraftTextMicroContent(LocalizedTextMicroContent):
- microcontent = models.ForeignKey(DraftTextMicroContent, on_delete=models.CASCADE)
-
-
-'''
- localized published TextMicroContent
-'''
-class LocalizedPublishedTextMicroContent(LocalizedTextMicroContent):
- microcontent = models.ForeignKey(PublishedTextMicroContent, on_delete=models.CASCADE)
-
-
-################################ IMAGE MICROCONTENT ######################################
-
-'''
- ImageMicroContent
- - invoked by {% cms_get_image %}
- - microcontent_category = 'image'
-'''
-
-class DraftImageMicroContent(DraftCMSMicroContent):
-
- def get_form_field(self, app, template_content, widget_attrs, *args, **kwargs):
-
- language = widget_attrs['language']
-
- limc = self.get_localized(language)
- if limc:
- widget_attrs['file'] = limc.content
-
-
- data_url_kwargs = {
- 'app_uid' : app.uid,
- 'template_content_id' : template_content.id,
- 'language' : language,
- 'microcontent_category' : widget_attrs['data-microcontentcategory'],
- }
-
- licenced_url_kwargs = data_url_kwargs.copy()
-
- data_url_kwargs['microcontent_type'] = widget_attrs['data-microcontenttype']
-
- delete_url = None
-
- if self.pk:
-
- licenced_url_kwargs['microcontent_id'] = self.pk
- licenced_url = reverse('update_licenced_image', kwargs=licenced_url_kwargs)
-
- delete_kwargs = {
- 'app_uid' : app.uid,
- 'template_content_id' : template_content.id,
- 'language' : language,
- }
- delete_url = reverse('DeleteFileContent', kwargs=delete_kwargs)
-
- else:
- licenced_url_kwargs['microcontent_type'] = widget_attrs['data-microcontenttype']
- licenced_url = reverse('upload_licenced_image', kwargs=licenced_url_kwargs)
-
- data_url = reverse('upload_image', kwargs=data_url_kwargs)
-
- widget_attrs['data-url'] = data_url
- widget_attrs['accept'] = 'image/*'
-
- form_field = forms.ImageField(widget=forms.FileInput(widget_attrs), **kwargs)
- form_field.licenced_url = licenced_url
- form_field.delete_url = delete_url
- return form_field
-
- # always falls back to primary language image
- def translation_complete(self, language):
- return []
-
- # override set_content oft DraftCMSMicroContent for enabling the deletion of old image file
- def set_content(self, content, editor, language):
-
- limc = self.get_localized(language)
-
- if not limc:
- limc = LocalizedDraftImageMicroContent.objects.create(self, language, content, editor)
- else:
-
- limc.last_modified_by = editor
- old_filepath = limc.content.path
-
- limc.content = content
- limc.save()
-
- # delete old image file
- if old_filepath != limc.content.path:
- if os.path.isfile(old_filepath):
- os.remove(old_filepath)
-
-
-'''
- Published Image MicroContent
- - cannot be edited
- - is not affected by deletions in the draft
- - is not affected if a whole DraftCMSMicroConent instance is deleted
-'''
-class PublishedImageMicroContent(PublishedCMSMicroContent):
-
- licences = GenericRelation(ContentLicenceRegistry)
-
-
-'''
- Localizations for ImageMicroContent
- content = ImageField
-'''
-
-'''
- paths where images (ImageMicroContents) are uploaded to
-'''
-def content_images_upload_path(instance, filename):
-
- # published or draft
- if isinstance(instance, LocalizedPublishedImageMicroContent):
- draft_or_published = 'published'
- else:
- draft_or_published = 'draft'
-
- if hasattr(instance.microcontent, 'template_content') and instance.microcontent.template_content is not None:
- subfolder = str(instance.microcontent.template_content.app.pk)
- else:
- subfolder = 'global'
-
- path = os.path.join('online_content', subfolder, 'content_images', draft_or_published,
- instance.microcontent.microcontent_type, instance.language, filename)
-
- return path
-
-
-class LocalizedImageMicroContent(LocalizedCMSMicroContent):
-
- content = models.ImageField(upload_to=content_images_upload_path)
-
- class Meta:
- abstract = True
-
-
-class LocalizedDraftImageMicroContent(LocalizedImageMicroContent):
- microcontent = models.ForeignKey(DraftImageMicroContent, on_delete=models.CASCADE)
-
- licences = GenericRelation(ContentLicenceRegistry)
-
-
-class LocalizedPublishedImageMicroContent(LocalizedImageMicroContent):
- microcontent = models.ForeignKey(PublishedImageMicroContent, on_delete=models.CASCADE)
-
- licences = GenericRelation(ContentLicenceRegistry)
-
-
-'''
- Helpers
-'''
-def _generate_plaintext(text):
- text = strip_tags(text)
- return ' '.join(text.split())
-
-
-microcontent_category_model_map = {
- 'template_content' : {
- 'draft' : TemplateContent,
- 'published' : TemplateContent,
- },
- 'template_contents' : {
- 'draft' : TemplateContent,
- 'published' : TemplateContent,
- },
- 'microcontent' : {
- 'draft' : DraftTextMicroContent,
- 'published' : PublishedTextMicroContent,
- },
- 'microcontents' : {
- 'draft' : DraftTextMicroContent,
- 'published' : PublishedTextMicroContent,
- },
- 'image' : {
- 'draft' : DraftImageMicroContent,
- 'published' : PublishedImageMicroContent,
- },
- 'images' : {
- 'draft' : DraftImageMicroContent,
- 'published' : PublishedImageMicroContent,
- },
-}
diff --git a/localcosmos_server/online_content/parser.py b/localcosmos_server/online_content/parser.py
deleted file mode 100644
index 75d6af5..0000000
--- a/localcosmos_server/online_content/parser.py
+++ /dev/null
@@ -1,100 +0,0 @@
-"""
- Parser for the Page Admin
- - receives a template
- - extracts all cms microcontent relevant tags into model objects for ModelForm
-
-"""
-
-from .CMSObjects import CMSTag
-
-class TemplateParser:
-
- def __init__(self, app, template_content, template):
- self.app = app
- self.template_content = template_content # a TemplateContent instance
-
- self.template = template
- self.cms_tags = []
-
-
- def get_cms_tag(self, microcontent_category, microcontent_type, *tag_content, **tag_kwargs):
-
- cms_tag = CMSTag(self.app, self.template_content, microcontent_category, microcontent_type,
- *tag_content, **tag_kwargs)
-
- return cms_tag
-
-
- def parse(self):
-
- cms_tags_str = []
-
- tag_open = False
-
- cms_tag_open = False
-
- obj_open = False
- obj_buffer = ""
-
- source = self.template.source
-
- for char in source:
-
- if tag_open:
- obj_buffer += char
- if obj_buffer == "{% cms_":
- cms_tag_open = True
-
- if char == "}":
-
- if cms_tag_open:
- cms_tags_str.append(obj_buffer)
-
- cms_tag_open = False
- tag_open = False
- obj_open = False
-
- obj_buffer = ""
- continue
-
- elif obj_open:
- obj_buffer += char
- if obj_buffer == "{%":
- tag_open = True
-
- elif char == "}":
- obj_open = False
- continue
-
- elif char == "{":
- obj_open = True
- obj_buffer = char
- continue
-
- for tag_str in cms_tags_str:
-
- tag_content = tag_str.lstrip("{%").rstrip("%}").strip().split(" ")
-
- tag = tag_content.pop(0)
-
- tag_content = [t.strip("'") for t in tag_content]
-
- microcontent_category = tag.split("_")[-1]
-
- microcontent_type = tag_content.pop(0)
-
- tag_kwargs = {}
-
- # iterate ofer tag args and create kwargs if necessary
- for arg in tag_content:
- if arg.startswith("min-") or arg.startswith("max-"):
- parts = arg.split("-")
- tag_kwargs[parts[0]] = tag_kwargs[parts[1]]
-
- tagobj = self.get_cms_tag(microcontent_category, microcontent_type, *tag_content, **tag_kwargs)
-
- self.cms_tags.append(tagobj)
-
- return self.cms_tags
-
-
diff --git a/localcosmos_server/online_content/templates/online_content/ajax/image_microcontent_form.html b/localcosmos_server/online_content/templates/online_content/ajax/image_microcontent_form.html
deleted file mode 100644
index 56775ac..0000000
--- a/localcosmos_server/online_content/templates/online_content/ajax/image_microcontent_form.html
+++ /dev/null
@@ -1,43 +0,0 @@
-{% extends 'localcosmos_server/modals/modal_form.html' %}
-
-{% load i18n localcosmos_tags %}
-
-{% block above %}
-
- {% endif %}
-{% endblock %}
-
-{% block script %}
- {% if success %}
-
- {% endif %}
-{% endblock %}
diff --git a/localcosmos_server/online_content/templates/online_content/ajax/upload_custom_template.html b/localcosmos_server/online_content/templates/online_content/ajax/upload_custom_template.html
deleted file mode 100644
index b67f641..0000000
--- a/localcosmos_server/online_content/templates/online_content/ajax/upload_custom_template.html
+++ /dev/null
@@ -1,41 +0,0 @@
-{% extends 'localcosmos_server/modals/modal_form.html' %}
-
-{% load i18n localcosmos_tags %}
-
-{% block above %}
-
-{% endblock %}
-
-{% block footer %}
-
- {% if success %}
- {% else %}
-
- {% endif %}
-{% endblock %}
-
-{% block script %}
-
-{% endblock %}
diff --git a/localcosmos_server/online_content/templates/online_content/ajax_filecontent_field.html b/localcosmos_server/online_content/templates/online_content/ajax_filecontent_field.html
deleted file mode 100644
index e5b2bf7..0000000
--- a/localcosmos_server/online_content/templates/online_content/ajax_filecontent_field.html
+++ /dev/null
@@ -1,21 +0,0 @@
-{% load i18n static online_content_tags imagekit %}
-{% with cms_object=field.field.cms_object %}
-
-
-
- {{ field }}
-
-
-
- {% if field.value %}
-
{% trans 'Delete' %}
- {% else %}
-
- {% endif %}
-
-
-
-
-{% endwith %}
diff --git a/localcosmos_server/online_content/templates/online_content/content_managing_js.html b/localcosmos_server/online_content/templates/online_content/content_managing_js.html
deleted file mode 100644
index 4eedfe4..0000000
--- a/localcosmos_server/online_content/templates/online_content/content_managing_js.html
+++ /dev/null
@@ -1,7 +0,0 @@
-{% if template_content %}
- var template_content_id = {{ template_content.id }};
-{% else %}
- var template_content_id = null;
-{% endif %}
-var language = "{{ language }}";
-cms_manage_image(event, template_content_id, language);
diff --git a/localcosmos_server/online_content/templates/online_content/delete_microcontent.html b/localcosmos_server/online_content/templates/online_content/delete_microcontent.html
deleted file mode 100644
index 213fd61..0000000
--- a/localcosmos_server/online_content/templates/online_content/delete_microcontent.html
+++ /dev/null
@@ -1,45 +0,0 @@
-{% load i18n %}
-{% if success %}
-
-{% else %}
-
-
-{% endif %}
diff --git a/localcosmos_server/online_content/templates/online_content/filecontent_field.html b/localcosmos_server/online_content/templates/online_content/filecontent_field.html
deleted file mode 100644
index 946c8fc..0000000
--- a/localcosmos_server/online_content/templates/online_content/filecontent_field.html
+++ /dev/null
@@ -1,25 +0,0 @@
-{% load i18n static online_content_tags imagekit %}
-{% with cms_object=field.field.cms_tag %}
-
-
-
- {{ field }}
-
-
-
-
-
-
-
-
-
-{% endwith %}
diff --git a/localcosmos_server/online_content/templates/online_content/filecontent_field_form.html b/localcosmos_server/online_content/templates/online_content/filecontent_field_form.html
deleted file mode 100644
index 4ee5b21..0000000
--- a/localcosmos_server/online_content/templates/online_content/filecontent_field_form.html
+++ /dev/null
@@ -1,11 +0,0 @@
-{% for field in fieldform %}
- {% include 'online_content/filecontent_field.html' %}
-
-{% endfor %}
diff --git a/localcosmos_server/online_content/templates/online_content/manage_template_content.html b/localcosmos_server/online_content/templates/online_content/manage_template_content.html
deleted file mode 100644
index a4bb5f3..0000000
--- a/localcosmos_server/online_content/templates/online_content/manage_template_content.html
+++ /dev/null
@@ -1,201 +0,0 @@
-{% extends 'online_content/online_content_base.html' %}
-{% load i18n imagekit static online_content_tags %}
-
-{% block content %}
-
-
-
-
- {{ template_content.primary_title }}
-
- version {{ localized_template_content.draft_version }}
-
- {% if localized_template_content.published_version == localized_template_content.draft_version %}
- {% trans 'published' %}
- {% else %}
- {% if localized_template_content.published_version %}
- {% trans 'unpublished changes' %}
- {% else %}
- {% trans 'not published' %}
- {% endif %}
- {% endif %}
-
- {% if app.secondary_languages %}
- {% if localized_template_content.published_version == localized_template_content.draft_version %}
- {% else %}
- {% if localized_template_content.translation_ready %}
- {% trans 'ready for translation' %}
- {% else %}
- {% trans 'not ready for translation' %}
- {% endif %}
- {% endif %}
- {% endif %}
-
-
- {% if localized_template_content.published_version %}
- {% else %}
- {% if app.secondary_languages %}
- {% if localized_template_content.translation_ready %}
-
- {% trans 'You marked the content in this language as ready for translation.' %}
-
- {% endif %}
- {% endif %}
- {% endif %}
-
-
- {% if saved_as_draft %}
-
-
-
- {% trans 'Successfully saved your content as draft.' %}
-
-
-
- {% endif %}
-
- {% if tried_publication or tried_translation_ready %}
- {% if publication_errors or translation_errors %}
-
-
-
- {% if tried_publication %}
- {% trans 'Publication failed:' %}
- {% for error in publication_errors %}
- {{ error }}
- {% endfor %}
- {% else %}
- {% trans 'Could not set this page as ready for translation:' %}
- {% for error in translation_errors %}
- {{ error }}
- {% endfor %}
- {% endif %}
-
-
-
- {% else %}
- {% if tried_publication %}
-
-
-
- {% trans 'Publication successful.' %}
-
-
-
- {% endif %}
- {% endif %}
- {% endif %}
-
-
-
-
-
{% trans 'Components' %}
-
-
-
- {% if template_content %}
-
-
- {% endif %}
-
-
-
-{% endblock %}
-{% block extra_script %}
- {% include 'online_content/manage_template_content_extra_scripts.html' %}
-
-
-{% endblock %}
diff --git a/localcosmos_server/online_content/templates/online_content/online_content_base.html b/localcosmos_server/online_content/templates/online_content/online_content_base.html
deleted file mode 100644
index d6f62ed..0000000
--- a/localcosmos_server/online_content/templates/online_content/online_content_base.html
+++ /dev/null
@@ -1,69 +0,0 @@
-{% extends request.is_appadmin|yesno:"app_admin/base.html,app_kit/base.html" %}
-{% load i18n static online_content_tags %}
-
-{% block extra_style %}
-
-{% endblock %}
-
-{% block header %}
-
-{% endblock %}
-
-{% block content %}
-
-
-
-
{{ app.name }} {% trans 'OnlineContent' %}
-
-
- {% block subcontent %}
-
{% trans 'Pages' %}
- {% if pages %}
-
- {% for template_content in pages %}
-
- {% include 'online_content/template_content_list_entry.html' %}
-
- {% endfor %}
-
- {% else %}
- {% trans 'You do not have any online pages for this app yet.' %}
- {% endif %}
-
-
-
-
{% trans 'Features' %}
- {% if features %}
-
- {% for template_content in features %}
-
- {% include 'online_content/template_content_list_entry.html' %}
-
- {% endfor %}
-
- {% else %}
- {% trans 'You do not have any online feature content for this app yet.' %}
- {% endif %}
-
-
- {% endblock %}
-
-
-
-{% endblock %}
-
-{% block extra_script %}{% endblock %}
diff --git a/localcosmos_server/online_content/templates/online_content/translate_template_content.html b/localcosmos_server/online_content/templates/online_content/translate_template_content.html
deleted file mode 100644
index 563da2c..0000000
--- a/localcosmos_server/online_content/templates/online_content/translate_template_content.html
+++ /dev/null
@@ -1,106 +0,0 @@
-{% extends 'online_content/online_content_base.html' %}
-{% load i18n static imagekit online_content_tags %}
-
-{% block content %}
-
-
-
-
{{ template_content }}
-
- {% if localized_template_content and localized_template_content.translation_ready %}
-
- {% trans 'This translation is marked as complete. ' %}
-
- {% else %}
-
- {% trans 'This translation is not yet complete.' %}
-
- {% endif %}
- {% if tried_translation_ready %}
- {% if translation_errors %}
-
-
- {% for error_text in translation_errors %}
- - {{ error_text }}
- {% endfor %}
-
-
- {% endif %}
- {% endif %}
-
-
-
-
-
-
-{% endblock %}
-
-{% block extra_script %}
- {% include 'online_content/manage_template_content_extra_scripts.html' %}
-{% endblock %}
diff --git a/localcosmos_server/online_content/templatetags/online_content_tags.py b/localcosmos_server/online_content/templatetags/online_content_tags.py
deleted file mode 100644
index 04857e5..0000000
--- a/localcosmos_server/online_content/templatetags/online_content_tags.py
+++ /dev/null
@@ -1,258 +0,0 @@
-from django.conf import settings
-from django import template
-register = template.Library()
-
-from django.template import loader, Context
-
-from localcosmos_server.online_content.models import (TemplateContent, LocalizedTemplateContent,
- microcontent_category_model_map)
-
-from django.db.models import Q
-
-'''
- get_content_by_type
- - get content by TemplateContent.template_type
- - app object is needed
-'''
-@register.simple_tag(takes_context=True)
-def get_content_by_type(context, template_type, *args, **kwargs):
- app = context['request'].app
- language = context['request'].language
-
- limit = kwargs.get('limit', None)
-
- preview = context.get('preview', False)
- template_content_ids = TemplateContentTypes.objects.filter(template_content__app=app,
- template_type=template_type, template_content__published_at__isnull=preview).order_by('position').values_list('template_content_id', flat=True)
-
- if limit is not None:
- template_content_ids = template_content_ids[:limit]
-
- template_content_ids = list(template_content_ids)
- # does not preserve order
- localized_tcs = LocalizedTemplateContent.objects.filter(template_content_id__in=template_content_ids, language=language)
-
- localized_tcs = list(localized_tcs)
- localized_tcs.sort(key=lambda localized_tc: template_content_ids.index(localized_tc.template_content.id))
-
- return localized_tcs
-
-'''
- returns all pages of a given type
-'''
-@register.simple_tag
-def get_template_content_locale(template_content, language):
- return LocalizedTemplateContent.objects.filter(template_content=template_content, language=language).first()
-
-'''
- fetch content by template_name
-'''
-@register.simple_tag(takes_context=True)
-def get_template_content(context, template_name, *args, **kwargs):
- app = context['app']
-
- language = context['request'].language
- limit = kwargs.get('limit', None)
-
- preview = context.get('preview', False)
- template_content_ids = TemplateContent.objects.filter(app=app, template_name=template_name).values_list(
- 'id', flat=True)
-
- if limit is not None:
- template_content_ids = template_content_ids[:limit]
-
- template_content_ids = list(template_content_ids)
-
- # does not preserve order
- query_kwargs = {
- 'template_content_id__in' : template_content_ids,
- 'language' : language,
- }
-
- if preview == False:
- query_kwargs['published_at__isnull'] = False
-
- localized_tcs = LocalizedTemplateContent.objects.filter(**query_kwargs)
- localized_tcs = list(localized_tcs)
-
- localized_tcs.sort(key=lambda localized_tc: template_content_ids.index(localized_tc.template_content.id))
-
- return localized_tcs
-
-
-'''
- common tag for microcontent(images and html)
-'''
-''' maybe deprecated
-@register.simple_tag(takes_context=True)
-def cms_getall(context, microcontent_category, microcontent_type, *args, **kwargs):
-
- template_content = context['template_content']
- preview_or_published = context.get('preview', 'published')
-
- Model = microcontent_category_model_map[microcontent_category][preview_or_published]
- microcontent = Model.objects.filter(template_content=template_content)
-
- dic = {}
- dic[microcontent_type] = microcontent
-
- return dic
-'''
-
-'''
- cms_get
- - fetch content from db / declare a user-defineable content
- - template_content_specific (template_content in context)
- - base.html (template_content not in context)
-'''
-def cms_get(context, microcontent_category, microcontent_type, *args, **kwargs):
-
- template_content = context.get('template_content', None)
- preview = context.get('preview', False)
- language = context['request'].language
- localized_microcontent = None
-
- microcontent_model_type = 'draft' if preview else 'published'
-
- Model = microcontent_category_model_map[microcontent_category][microcontent_model_type]
-
- _microcontent = Model.objects.filter(Q(template_content=template_content, microcontent_type=microcontent_type) | Q(template_content__isnull=True, microcontent_type=microcontent_type)).first()
-
- if _microcontent:
- #if microcontent.template_content == None:
- # preview = True
- localized_microcontent = _microcontent.get_content(language)
-
- return localized_microcontent
-
-
-'''
- cms_get_multiple
- - fetch multiple contents of the same microcontent_category+microcontent_type from db
-'''
-@register.simple_tag(takes_context=True)
-def cms_get_multiple(context, microcontent_category, microcontent_type, *args, **kwargs):
-
- template_content = context.get('template_content', None)
- preview = context.get('preview', False)
- language = context['request'].language
-
- microcontent_model_type = 'draft' if preview else 'published'
-
- Model = microcontent_category_model_map[microcontent_category][microcontent_model_type]
- _microcontents = Model.objects.filter(Q(template_content=template_content, microcontent_type=microcontent_type) | Q(template_content__isnull=True, microcontent_type=microcontent_type))
-
- microcontents = []
- for microcontent in _microcontents:
- localized_content = microcontent.get_content(language)
-
- if localized_content:
- microcontents.append(localized_content)
-
- return microcontents
-
-
-'''
- shortcuts for getting MicroContent
-'''
-@register.simple_tag(takes_context=True)
-def cms_get_microcontent(context, microcontent_type, *args, **kwargs):
- html = cms_get(context, 'microcontent', microcontent_type, *args, **kwargs)
- return html
-
-
-@register.simple_tag(takes_context=True)
-def cms_get_microcontents(context, microcontent_type, *args, **kwargs):
- html = cms_get_multiple(context, 'microcontents', microcontent_type, *args, **kwargs)
- return html
-
-
-@register.simple_tag(takes_context=True)
-def cms_get_image(context, microcontent_type, *args, **kwargs):
- image = cms_get(context, 'image', microcontent_type, *args, **kwargs)
- return image
-
-'''
- multiple images
-'''
-@register.simple_tag(takes_context=True)
-def cms_get_images(context, microcontent_type, *args, **kwargs):
- image = cms_get_multiple(context, 'image', microcontent_type, *args, **kwargs)
- return image
-
-
-@register.filter
-def template_content_translation_complete(template_content, language):
- ltc = template_content.get_localized(language)
-
- if not ltc:
- return False
-
- else:
- return ltc.translation_complete()
-
-
-@register.simple_tag
-def get_localized_microcontent(instance, language):
- return instance.get_content(language)
-
-@register.simple_tag
-def get_localized_attribute(template_content, language, attr):
-
- ltc = LocalizedTemplateContent.objects.filter(template_content=template_content, language=language).first()
-
- if ltc:
- return getattr(ltc, attr)
-
- return None
-
-'''
- by default fetches latest db entry
- make this configurable
-'''
-@register.simple_tag(takes_context=True)
-def include_latest_content(context, template_name):
- kwargs = {
- 'limit' : 1,
- }
- localized_tcs = get_template_content(context, template_name, **kwargs)
-
- if localized_tcs:
-
- localized_template_content = localized_tcs[0]
-
- template = localized_template_content.get_template()
-
- template_context = Context({
- 'preview' : kwargs.get('preview', False),
- 'request': context['request'],
- 'template_content': localized_template_content.template_content,
- 'localized_template_content' : localized_template_content,
- })
- return template.render(template_context)
-
- return None
-
-
-@register.simple_tag(takes_context=True)
-def include_content(context, localized_template_content):
-
- template = localized_template_content.get_template()
- preview = context.get('preview', False)
-
- # create the correct context
- template_context = Context({
- 'preview' : preview,
- 'request': context['request'],
- 'template_content': localized_template_content.template_content,
- 'localized_template_content' : localized_template_content,
- })
- return template.render(template_context)
-
-
-@register.simple_tag
-def image_url(image):
- try:
- return image.url
- except:
- return ''
diff --git a/localcosmos_server/online_content/tests/test_forms.py b/localcosmos_server/online_content/tests/test_forms.py
deleted file mode 100644
index ba769d6..0000000
--- a/localcosmos_server/online_content/tests/test_forms.py
+++ /dev/null
@@ -1,189 +0,0 @@
-from django.test import TestCase
-from django.conf import settings
-
-from localcosmos_server.tests.mixins import (WithApp, WithUser, WithOnlineContent, WithPowerSetDic,
- WithImageForForm)
-
-from localcosmos_server.tests.common import test_settings
-
-from localcosmos_server.online_content.forms import (CreateTemplateContentForm, ManageMicroContentsForm,
- ManageTemplateContentForm, DeleteMicroContentForm, UploadFileForm, UploadImageForm,
- UploadImageWithLicenceForm)
-
-import os
-
-# available template_type s of Flat frontend
-TEMPLATE_TYPES = ['page', 'feature']
-
-class WithPublishedApp:
-
- # only published apps can access template content settings on lc private
- def setUp(self):
- super().setUp()
- published_version_path = os.path.join(settings.LOCALCOSMOS_APPS_ROOT, self.testapp_relative_www_path)
-
- self.app.published_version_path = published_version_path
-
- self.app.save()
-
-
-@test_settings
-class TestCreateTemplateContentForm(WithPublishedApp, WithApp, TestCase):
-
-
- def test__init__(self):
-
- app_root = settings.LOCALCOSMOS_APPS_ROOT
-
- templates_root = os.path.join(self.app.published_version_path, 'online_content',
- 'templates')
-
- for template_type in TEMPLATE_TYPES:
- form = CreateTemplateContentForm(self.app, template_type)
-
- self.assertEqual(form.app, self.app)
- self.assertEqual(form.template_type, template_type)
-
- for choice in form.fields['template_name'].choices:
-
- template_path = choice[0]
-
- # path has to exist
- path = os.path.join(templates_root, template_path)
-
- self.assertTrue(os.path.isfile(path))
-
-
-@test_settings
-class TestManageMicroContentsForm(WithPublishedApp, WithApp, WithUser, WithOnlineContent, TestCase):
-
- test_page_microcontents = ['simple_content', 'layout1', 'layout2', 'multi-content', 'test_image', 'multi_image']
- test_page_microcontents_no_multi = ['simple_content', 'layout1', 'layout2', 'test_image']
-
- def setUp(self):
- self.user = self.create_user()
- super().setUp()
-
- def test__init__(self):
-
- template_content = self.create_template_content()
- form = ManageMicroContentsForm(template_content, language=self.app.primary_language)
-
- self.assertEqual(form.template_content, template_content)
-
- # check ALL fields of test_page.html
-
- for mc_type in self.test_page_microcontents:
- #field_name = 'pk-{0}-{1}'.format(self.localized_template_content.pk, mc_type)
- self.assertIn(mc_type, form.fields)
-
- self.assertEqual(form.fields[mc_type].language, self.app.primary_language)
-
- self.assertEqual(form.layoutable_simple_fields, set(['layout2']))
- self.assertEqual(form.layoutable_full_fields, set(['layout1']))
-
- # test the template test_page.html
- def test__init__for_translation(self):
-
- for_translation = 'en'
-
- template_content = self.create_template_content()
- form = ManageMicroContentsForm(template_content, language=self.app.primary_language,
- for_translation=for_translation)
-
- # check ALL fields of test_page.html
-
- for mc_type in self.test_page_microcontents_no_multi:
- #field_name = 'pk-{0}-{1}'.format(self.localized_template_content.pk, mc_type)
- self.assertIn(mc_type, form.fields)
-
- self.assertEqual(form.fields[mc_type].language, self.app.primary_language)
-
- self.assertEqual(form.layoutable_simple_fields, set(['layout2']))
- self.assertEqual(form.layoutable_full_fields, set(['layout1']))
-
-
-@test_settings
-class TestManageTemplatecontentForm(WithPublishedApp, WithApp, WithUser, WithOnlineContent, TestCase):
-
- def setUp(self):
- self.user = self.create_user()
- super().setUp()
-
- def test_append_additional_fields(self):
-
- template_content = self.create_template_content()
-
- form = ManageTemplateContentForm(template_content, language=self.app.primary_language)
-
- self.assertIn('page_flags', form.fields)
- self.assertEqual(1, len(form.fields['page_flags'].choices))
- self.assertEqual('main_navigation', form.fields['page_flags'].choices[0][0])
-
-
-@test_settings
-class TestDeleteMicroContentForm(WithPowerSetDic, TestCase):
-
- def test_validation(self):
-
- post_data = {
- 'meta_pk' : 1,
- 'localized_pk' : 2,
- 'microcontent_category' : 'page',
- 'microcontent_type' : 'simple_content',
- }
-
- # for this test case all fields are required
- required_fields = set(['meta_pk', 'localized_pk', 'microcontent_category'])
-
- self.validation_test(post_data, required_fields, DeleteMicroContentForm)
-
-
-@test_settings
-class TestUploadFileFormANDUploadImageForm(WithImageForForm, WithPowerSetDic, TestCase):
-
- def test_validation(self):
-
- image = self.get_image('test_image.jpg')
-
- post_data = {
- 'pk' : 1,
- 'template_content_id' : 2,
- 'language' : 'en',
- 'file' : image,
- }
-
-
- file_keys = ['file']
-
- required_fields = set(['language', 'file'])
-
- for form_class in [UploadFileForm, UploadImageForm]:
-
- self.validation_test(post_data, required_fields, form_class, file_keys=file_keys)
-
-
-@test_settings
-class TestUploadImageWithLicenceForm(WithImageForForm, WithPowerSetDic, TestCase):
-
- def test_validation(self):
-
- image = self.get_image('test_image_2.jpg')
-
- post_data = {
- 'pk' : 1,
- 'template_content_id' : 2,
- 'language' : 'en',
- 'source_image' : image,
- 'image_type' : 'test_image',
- 'creator_name': 'Tester',
- 'licence_0' : 'CC0',
- 'licence_1' : '1.0',
- }
-
-
- file_keys = ['source_image']
-
- required_fields = set(['creator_name', 'licence_0', 'licence_1'])
-
- self.validation_test(post_data, required_fields, UploadImageWithLicenceForm, file_keys=file_keys)
diff --git a/localcosmos_server/online_content/tests/test_models.py b/localcosmos_server/online_content/tests/test_models.py
deleted file mode 100644
index f810341..0000000
--- a/localcosmos_server/online_content/tests/test_models.py
+++ /dev/null
@@ -1,1596 +0,0 @@
-###################################################################################################################
-#
-# TESTS FOR MODELS
-# - this file only covers settings.LOCALCOSMOS_PRIVATE == True
-#
-###################################################################################################################
-from django.conf import settings
-from django.test import TestCase
-from django.template.defaultfilters import slugify
-from django import forms
-from django.core.files.uploadedfile import SimpleUploadedFile
-
-from django.utils import timezone
-
-from localcosmos_server.models import SecondaryAppLanguages
-
-from localcosmos_server.tests.mixins import WithUser, WithApp, WithMedia
-
-from localcosmos_server.tests.common import test_settings, TEST_IMAGE_PATH
-
-
-from localcosmos_server.online_content.models import (TEMPLATE_TYPES, TemplateContent, LocalizedTemplateContent,
- DraftTextMicroContent, LocalizedDraftTextMicroContent, TemplateContentFlags, SlugTrail, FlagTree,
- PublishedTextMicroContent, LocalizedPublishedTextMicroContent, DraftImageMicroContent,
- LocalizedDraftImageMicroContent, PublishedImageMicroContent, LocalizedPublishedImageMicroContent)
-
-from localcosmos_server.online_content.fields import MultiContentField
-from localcosmos_server.online_content.widgets import MultiContentWidget
-
-import os
-
-from random import choice
-from string import ascii_uppercase
-
-# this is just a copy/paste from the config.json file
-TEST_OC_SETTINGS = {
- "verbose_template_names" : {
- "page/free_page.html" : {
- "de" : "Freie Seite",
- "en" : "Free Page"
- },
- "page/test.html" : {
- "de" : "Test Seite",
- "en" : "Test Page"
- },
- "feature/news.html" : {
- "de" : "News Eintrag",
- "en" : "News entry"
- }
- },
- "max_flag_levels" : 2,
- "flags" : {
- "main_navigation" : {
- "template_name" : "flag/main_navigation.html",
- "name" : "Main Navigation"
- }
- }
-}
-
-class WithTemplateContent:
-
- title = 'Test Title'
- navigation_link_name = 'Test Nav name'
-
- template_name = 'page/free_page.html'
- simple_template_name = 'page/free_page.html'
- template_type = 'page'
-
- def create_template_content(self):
-
- self.user = self.create_user()
-
- # type 'page'
- template_type = TEMPLATE_TYPES[0][0]
-
- template_content = TemplateContent.objects.create(self.user, self.app, self.app.primary_language, self.title,
- self.navigation_link_name, self.simple_template_name, template_type)
-
- return template_content
-
-
- def create_freepage_microcontent(self, template_content, language):
-
- draft_content, created = DraftTextMicroContent.objects.get_or_create(
- template_content=template_content,
- microcontent_type = 'freepage_content',
- )
-
-
- ldtmc = LocalizedDraftTextMicroContent(
- microcontent = draft_content,
- content = 'some text content',
- language = language,
- creator = self.user,
- )
-
- ldtmc.save()
-
- return draft_content, ldtmc
-
-
-@test_settings
-class TestTemplateContent(WithUser, WithApp, WithTemplateContent, TestCase):
-
- # only published apps can access template content settings on lc private
- def setUp(self):
- super().setUp()
- published_version_path = os.path.join(settings.LOCALCOSMOS_APPS_ROOT, self.testapp_relative_www_path)
-
- self.app.published_version_path = published_version_path
-
- self.app.save()
-
-
- def test_create(self):
-
- user = self.create_user()
-
- for choice in TEMPLATE_TYPES:
-
- template_type = choice[0]
-
- template_content = TemplateContent.objects.create(user, self.app, self.app.primary_language,
- self.title, self.navigation_link_name, self.template_name, template_type)
-
- self.assertEqual(template_content.app, self.app)
- self.assertEqual(template_content.template_name, self.template_name)
- self.assertEqual(template_content.template_type, template_type)
-
- ltc_exists = LocalizedTemplateContent.objects.filter(template_content=template_content,
- language=self.app.primary_language).exists()
-
- self.assertTrue(ltc_exists)
- # ltc is tested separately
-
-
- def test_get_online_content_settings(self):
-
- template_content = self.create_template_content()
-
- oc_settings = template_content.get_online_content_settings()
-
- self.assertTrue('flags' in oc_settings)
- self.assertTrue('verbose_template_names' in oc_settings)
-
-
- def test_get_verbose_template_name(self):
-
- template_content = self.create_template_content()
- verbose_names = TEST_OC_SETTINGS['verbose_template_names'][template_content.template_name]
-
- verbose_name = template_content.verbose_template_name()
- expected_name = verbose_names[self.app.primary_language]
- self.assertEqual(verbose_name, expected_name)
-
- # test en
- verbose_name_en = template_content.verbose_template_name(language_code='en')
- expected_name_en = verbose_names['en']
- self.assertEqual(verbose_name_en, expected_name_en)
-
- # test non-existant fr
- verbose_name_fr = template_content.verbose_template_name(language_code='fr')
- expected_name_fr = 'Free page'#template_content.template_name
- self.assertEqual(verbose_name_fr, expected_name_fr)
-
-
- def test_get_localized(self):
-
- template_content = self.create_template_content()
-
- ltc = template_content.get_localized(self.app.primary_language)
- self.assertEqual(ltc.language, self.app.primary_language)
-
- ltc = template_content.get_localized('fr')
- self.assertEqual(ltc, None)
-
-
- def test_locales(self):
-
- template_content = self.create_template_content()
- ltc = LocalizedTemplateContent.objects.create(self.user, template_content, 'en', self.title,
- self.navigation_link_name)
-
- locales = template_content.locales()
- self.assertEqual(locales.count(), 2)
-
-
- def test_template_path(self):
-
- template_content = self.create_template_content()
-
- template_path = template_content.template_path(self.app)
-
- self.assertTrue(template_path.endswith(template_content.template_name))
-
- # tested by localcosmos_server.TestApp
- def test_get_template(self):
- template_content = self.create_template_content()
- template_content.get_template()
-
-
- def test_primary_title(self):
- template_content = self.create_template_content()
-
- title = template_content.primary_title()
- self.assertEqual(title, self.title)
-
- # test none
- self.app.primary_language = 'jp'
- self.app.save()
-
- title = template_content.primary_title()
- self.assertEqual(title, None)
-
-
- def test_validate(self):
- template_content = self.create_template_content()
-
- result = template_content.validate(self.app)
- self.assertEqual(result['errors'], [])
- self.assertEqual(result['warnings'], [])
-
-
- def test_translation_complete(self):
-
- # test incomplete primary language
- template_content = self.create_template_content()
-
- errors = template_content.translation_complete(self.app.primary_language)
-
- # 2 errors: translatin is not ready, ltc is incomplete
- self.assertEqual(len(errors), 2)
- self.assertIn('still working', str(errors[0]))
- # ltc component is not complete
- self.assertIn('required but still missing', str(errors[1]))
-
-
- errors_2 = template_content.translation_complete('fr')
- self.assertEqual(len(errors_2), 1)
- self.assertIn('is missing', str(errors_2[0]))
-
- # complete, but not translation_ready
- draft_content, ltdmc = self.create_freepage_microcontent(template_content, self.app.primary_language)
-
- errors_3 = template_content.translation_complete(self.app.primary_language)
- self.assertEqual(len(errors_3), 1)
- self.assertIn('still working', str(errors_3[0]))
-
- # complete and translation_ready = True
- ltc = template_content.get_localized(self.app.primary_language)
- ltc.translation_ready=True
- ltc.save()
-
- errors_4 = template_content.translation_complete(self.app.primary_language)
- self.assertEqual(errors_4, [])
-
-
- def test_publish_no_secondary_languages(self):
-
- template_content = self.create_template_content()
-
- secondary_languages = SecondaryAppLanguages.objects.filter(app=self.app)
- for language in secondary_languages:
- language.delete()
-
- # check errors : unready
- errors = template_content.publish()
- self.assertEqual(len(errors), 1)
-
- ltc = template_content.get_localized(self.app.primary_language)
- self.assertEqual(ltc.published_version, None)
- self.assertEqual(ltc.published_at, None)
-
-
- draft_content, ldtmc = self.create_freepage_microcontent(template_content, self.app.primary_language)
-
- # everything is ready for publication now
- # translation_ready is not needed if there are no secondary languages
- errors_2 = template_content.publish()
- self.assertEqual(errors_2, [])
-
- ltc.refresh_from_db()
- self.assertEqual(ltc.published_version, 1)
- self.assertEqual(ltc.draft_version, 1)
- self.assertTrue(ltc.published_at != None)
-
-
- def test_publish_with_secondary_languages(self):
-
- template_content = self.create_template_content()
-
- errors = template_content.publish()
- self.assertEqual(len(errors), 3)
-
- # primary language: missing component
- self.assertIn('is still working', errors[0])
- self.assertIn('required but still missing', errors[1])
- self.assertIn('Translation for the language', errors[2])
-
- # create microcontent
- draft_content, ldtmc = self.create_freepage_microcontent(template_content, self.app.primary_language)
-
- errors_2 = template_content.publish()
- self.assertEqual(len(errors_2), 2)
- self.assertIn('is still working', errors_2[0])
- self.assertIn('Translation for the language', errors_2[1])
-
- # set translation ready
- ltc = template_content.get_localized(self.app.primary_language)
- ltc.translation_ready=True
- ltc.save()
-
- errors_3 = template_content.publish()
- self.assertEqual(len(errors_3), 1)
- self.assertIn('Translation for the language', errors_3[0])
-
- # create ltc for secondary languages
- # translator is still working
- for language in self.app.secondary_languages():
- ltc = LocalizedTemplateContent.objects.create(self.user, template_content, language,
- 'Titel {}'.format(language), 'Link name {}'.format(language))
-
- ltc.save()
-
- errors_4 = template_content.publish()
- self.assertEqual(len(errors_4), 2)
- self.assertIn('is still working', errors_4[0])
- self.assertIn('is missing for the language', errors_4[1])
-
-
- for language in self.app.secondary_languages():
- draft_content, ldtmc = self.create_freepage_microcontent(template_content, language)
-
-
- errors_5 = template_content.publish()
- self.assertEqual(len(errors_5), 1)
- self.assertIn('is still working', errors_5[0])
-
- for language in self.app.secondary_languages():
- ltc = template_content.get_localized(language)
- ltc.translation_ready = True
- ltc.save()
-
-
- errors_6 = template_content.publish()
- self.assertEqual([], errors_6)
-
- for language in self.app.languages():
- ltc = template_content.get_localized(language)
- self.assertEqual(ltc.published_version, 1)
- self.assertTrue(ltc.published_at != None)
- self.assertEqual(ltc.draft_version, 1)
-
-
- def test_set_translated_localized_template_content_version(self):
- # case: seoncdary language is added pimary language already is version 2
-
- template_content = self.create_template_content()
-
- secondary_languages = SecondaryAppLanguages.objects.filter(app=self.app)
- for language in secondary_languages:
- language.delete()
-
-
- draft_content, ldtmc = self.create_freepage_microcontent(template_content, self.app.primary_language)
-
- errors = template_content.publish()
-
- ltc = template_content.get_localized(self.app.primary_language)
- self.assertEqual(ltc.published_version, 1)
- self.assertEqual(ltc.draft_version, 1)
-
- ltc.save()
-
- ltc.refresh_from_db()
- self.assertEqual(ltc.draft_version, 2)
-
-
- secondary_language = SecondaryAppLanguages(
- app=self.app,
- language_code='en'
- )
-
- secondary_language.save()
-
- language_code = secondary_language.language_code
- secondary_ltc = LocalizedTemplateContent.objects.create(self.user, template_content, language_code,
- 'Titel {}'.format(language_code), 'Link name {}'.format(language_code))
-
- secondary_ltc.save()
-
- self.assertEqual(secondary_ltc.draft_version, 2)
-
-
- def test_flags(self):
- template_content = self.create_template_content()
-
- flag_name = 'test_flag'
-
- flag = TemplateContentFlags(
- template_content=template_content,
- flag=flag_name,
- )
-
- flag.save()
-
- flags = template_content.flags()
-
- self.assertEqual(flags,[flag_name])
-
-
- def test_unpublish(self):
-
- template_content = self.create_template_content()
-
- draft_content, ldtmc = self.create_freepage_microcontent(template_content, self.app.primary_language)
-
- primary_ltc = template_content.get_localized(self.app.primary_language)
- primary_ltc.translation_ready = True
- primary_ltc.save()
-
- for language in self.app.secondary_languages():
- ltc = LocalizedTemplateContent.objects.create(self.user, template_content, language,
- 'Titel {}'.format(language), 'Link name {}'.format(language))
- draft_content, ldtmc = self.create_freepage_microcontent(template_content, language)
-
- ltc.translation_ready = True
- ltc.save()
-
- errors = template_content.publish()
-
- for language in self.app.languages():
- ltc = template_content.get_localized(language)
- self.assertEqual(ltc.published_version, 1)
-
- template_content.unpublish()
-
- for language in self.app.languages():
- ltc = template_content.get_localized(language)
- self.assertEqual(ltc.published_version, None)
- self.assertEqual(ltc.published_at, None)
-
-
- def test_is_published(self):
-
- template_content = self.create_template_content()
-
- draft_content, ldtmc = self.create_freepage_microcontent(template_content, self.app.primary_language)
-
- primary_ltc = template_content.get_localized(self.app.primary_language)
- primary_ltc.translation_ready = True
- primary_ltc.save()
-
- for language in self.app.secondary_languages():
- ltc = LocalizedTemplateContent.objects.create(self.user, template_content, language,
- 'Titel {}'.format(language), 'Link name {}'.format(language))
- draft_content, ldtmc = self.create_freepage_microcontent(template_content, language)
-
- ltc.translation_ready = True
- ltc.save()
-
- self.assertFalse(template_content.is_published)
-
- errors = template_content.publish()
- self.assertTrue(template_content.is_published)
-
-
- def test_str(self):
-
- template_content = self.create_template_content()
-
- ltc = template_content.get_localized(self.app.primary_language)
-
- self.assertEqual(str(template_content), ltc.draft_title)
-
- ltc.delete()
-
- self.assertEqual(str(template_content), 'Template Content %s' % template_content.pk)
-
-
-
-@test_settings
-class TestLocalizedTemplateContent(WithUser, WithApp, WithTemplateContent, TestCase):
-
- # only published apps can access template content settings on lc private
- def setUp(self):
- super().setUp()
- published_version_path = os.path.join(settings.LOCALCOSMOS_APPS_ROOT, self.testapp_relative_www_path)
-
- self.app.published_version_path = published_version_path
-
- self.app.save()
-
-
- def test_create(self):
-
- user = self.create_user()
-
- template_content = TemplateContent(
- app = self.app,
- template_name = self.template_name,
- template_type = self.template_type,
- )
- template_content.save()
-
- ltc = LocalizedTemplateContent.objects.create(user, template_content, self.app.primary_language,
- self.title, self.navigation_link_name)
-
- ltc = LocalizedTemplateContent.objects.get(pk=ltc.pk)
- self.assertEqual(ltc.draft_title, self.title)
- self.assertEqual(ltc.draft_navigation_link_name, self.navigation_link_name)
- self.assertEqual(ltc.language, self.app.primary_language)
- self.assertEqual(ltc.template_content, template_content)
- self.assertEqual(ltc.creator, user)
-
-
- def test_generate_slug_base(self):
-
- title_1 = 'TestTitle'
- slug_base = LocalizedTemplateContent.objects.generate_slug_base(title_1)
-
- self.assertEqual(len(slug_base), len(title_1))
-
- title_2 = ''.join(choice(ascii_uppercase) for i in range(200))
-
- slug_base_2 = LocalizedTemplateContent.objects.generate_slug_base(title_2)
- self.assertEqual(len(slug_base_2), 99)
-
-
- def test_generate_slug(self):
-
- slug_1 = LocalizedTemplateContent.objects.generate_slug(self.title)
- self.assertEqual(slug_1, slugify(self.title))
-
- template_content = self.create_template_content()
-
- slug_2 = LocalizedTemplateContent.objects.generate_slug(self.title)
- expected_slug = '{0}-2'.format(slugify(self.title))
- self.assertEqual(slug_2, expected_slug)
-
-
- def test_translation_complete(self):
-
- template_content = self.create_template_content()
-
- ltc = template_content.get_localized(self.app.primary_language)
-
- errors = ltc.translation_complete()
-
- self.assertEqual(len(errors), 1)
- self.assertIn('is required but still missing', errors[0])
-
- draft_content, ldtmc = self.create_freepage_microcontent(template_content, self.app.primary_language)
-
- errors_2 = ltc.translation_complete()
-
- self.assertEqual(errors_2, [])
-
- ltc_en = ltc = LocalizedTemplateContent.objects.create(self.user, template_content, 'en', self.title,
- self.navigation_link_name)
-
- errors_3 = ltc_en.translation_complete()
- self.assertEqual(len(errors_3), 1)
- self.assertIn('is missing for the language', errors_3[0])
-
-
- def test_flags(self):
-
- template_content = self.create_template_content()
-
- flag_name = 'test_flag'
-
- flag = TemplateContentFlags(
- template_content=template_content,
- flag=flag_name,
- )
-
- flag.save()
-
- ltc = template_content.get_localized(self.app.primary_language)
-
- flags = ltc.flags()
-
- self.assertEqual(flags,[flag_name])
-
-
- def test_update_preview_token(self):
-
- # this saves the ltc -> no version adjustment
- template_content = self.create_template_content()
-
- ltc = template_content.get_localized(self.app.primary_language)
-
- draft_version = ltc.draft_version
- published_version = ltc.published_version
-
- now = timezone.now()
- ltc.update_preview_token()
-
- self.assertEqual(ltc.draft_version, draft_version)
- self.assertEqual(ltc.published_version, published_version)
-
- delta = ltc.preview_token_created_at - now
- self.assertTrue(delta.seconds < 1)
- self.assertTrue(ltc.preview_token != None)
-
-
- def test_validate_preview_token(self):
-
- template_content = self.create_template_content()
-
- ltc = template_content.get_localized(self.app.primary_language)
-
- token = 'test'
- is_valid = ltc.validate_preview_token(token)
- self.assertFalse(is_valid)
-
- ltc.update_preview_token()
- is_valid_2 = ltc.validate_preview_token(token)
- self.assertFalse(is_valid_2)
-
- is_valid_3 = ltc.validate_preview_token(ltc.preview_token)
- self.assertTrue(is_valid_3)
-
- is_valid_4 = ltc.validate_preview_token(ltc.preview_token, maxage_minutes = -1)
- self.assertFalse(is_valid_4)
-
-
- def test_get_template(self):
-
- template_content = self.create_template_content()
-
- ltc = template_content.get_localized(self.app.primary_language)
- template = ltc.get_template()
- self.assertTrue(template != None)
-
-
- def test_in_app_link(self):
-
- template_content = self.create_template_content()
- ltc = template_content.get_localized(self.app.primary_language)
- ltc.update_preview_token()
-
- in_app_link = ltc.in_app_link()
- self.assertEqual(in_app_link, '/online-content/{0}/'.format(ltc.slug))
-
- in_app_link = ltc.in_app_link(app_state='preview')
- self.assertEqual(in_app_link, '/online-content/{0}/{1}/'.format(ltc.slug, ltc.preview_token))
-
-
- def test_publish(self):
-
- template_content = self.create_template_content()
-
- ltc = template_content.get_localized(self.app.primary_language)
-
- self.assertEqual(ltc.published_at, None)
- self.assertEqual(ltc.navigation_link_name, None)
- self.assertEqual(ltc.published_title, None)
- self.assertEqual(ltc.published_version, None)
-
- ltc.publish()
-
- self.assertEqual(ltc.draft_navigation_link_name, ltc.navigation_link_name)
- self.assertEqual(ltc.draft_title, ltc.published_title)
- self.assertEqual(ltc.draft_version, ltc.published_version)
- self.assertTrue(ltc.published_at != None)
-
-
- def test_save(self):
-
- template_content = self.create_template_content()
-
- ltc = template_content.get_localized(self.app.primary_language)
-
- old_slug = ltc.slug
-
- ltc.save()
- self.assertEqual(old_slug, ltc.slug)
-
- # check: new slug
- new_title = 'Completely different'
- ltc.draft_title = new_title
-
- ltc.save()
-
- self.assertFalse(old_slug == ltc.slug)
- slug_trail = SlugTrail.objects.get(old_slug=old_slug)
- self.assertEqual(slug_trail.new_slug, ltc.slug)
-
- ltc.publish()
-
- # check: unready translation, increment version
- ltc.translation_ready = True
- ltc.save(disallow_new_version=True)
- self.assertTrue(ltc.draft_version == 1)
- self.assertTrue(ltc.translation_ready)
-
- ltc.save()
- self.assertTrue(ltc.draft_version == 2)
- self.assertFalse(ltc.translation_ready)
-
- # check: no pk, master ltc.version > 1
- # covered by: TestTemplateContent.test_set_translated_localized_template_content_version
-
-
-
-class WithFlags:
-
- flag_names = ['nav', 'footer']
-
- def create_flags(self, template_content):
-
-
- for flag_name in self.flag_names:
- flag = TemplateContentFlags(
- template_content = template_content,
- flag = flag_name,
- )
- flag.save()
-
-@test_settings
-class TestFlagTree(WithUser, WithApp, WithTemplateContent, WithFlags, TestCase):
-
- def test_init(self):
-
- template_content = self.create_template_content()
-
- self.create_flags(template_content)
-
- flags = TemplateContentFlags.objects.filter(flag=self.flag_names[0])
- flag_tree = FlagTree(flags, self.app.primary_language)
-
- self.assertEqual(set(flag_tree.flags.values_list('flag', flat=True)), set(flags.values_list('flag', flat=True)))
- self.assertEqual(flag_tree.language, self.app.primary_language)
- self.assertEqual(flag_tree.app_state, 'published')
- self.assertEqual(flag_tree.toplevel_count, 0)
-
- # set preview to True to generate 1 flag
- ltc = template_content.get_localized(self.app.primary_language)
- flag_tree = FlagTree(flags, self.app.primary_language, app_state='preview')
- self.assertEqual(flag_tree.app_state, 'preview')
- self.assertEqual(flag_tree.toplevel_count, 1)
- self.assertIn(ltc.draft_title, flag_tree.flag_tree)
-
- # published flag tree
- ltc.publish()
- flag_tree = FlagTree(flags, self.app.primary_language)
- self.assertEqual(flag_tree.app_state, 'published')
- self.assertEqual(flag_tree.toplevel_count, 1)
- self.assertIn(ltc.published_title, flag_tree.flag_tree)
-
-
-
- def test_get_tree_entry(self):
-
- template_content = self.create_template_content()
-
- self.create_flags(template_content)
-
- flags = TemplateContentFlags.objects.filter(flag=self.flag_names[0])
- flag_tree = FlagTree(flags, self.app.primary_language)
-
- tree_entry = flag_tree.get_tree_entry(flags.first())
- self.assertEqual(tree_entry, None)
-
- # preview
- ltc = template_content.get_localized(self.app.primary_language)
- flag_tree = FlagTree(flags, self.app.primary_language, app_state='preview')
- tree_entry = flag_tree.get_tree_entry(flags.first())
- self.assertEqual(tree_entry['children'], [])
- self.assertEqual(tree_entry['localized_template_content'], ltc)
- self.assertEqual(tree_entry['navigation_link_name'], ltc.draft_navigation_link_name)
- self.assertIn('in_app_link', tree_entry)
-
- ltc.publish()
- flag_tree = FlagTree(flags, self.app.primary_language)
- tree_entry = flag_tree.get_tree_entry(flags.first())
- self.assertEqual(tree_entry['children'], [])
- self.assertEqual(tree_entry['localized_template_content'], ltc)
- self.assertEqual(tree_entry['navigation_link_name'], ltc.navigation_link_name)
- self.assertIn('in_app_link', tree_entry)
-
-
- def test_iter(self):
-
- template_content = self.create_template_content()
-
- ltc = template_content.get_localized(self.app.primary_language)
- ltc.publish()
-
- self.create_flags(template_content)
-
- flags = TemplateContentFlags.objects.filter(flag=self.flag_names[0])
- flag_tree = FlagTree(flags, self.app.primary_language)
-
- expected_tree_entry = flag_tree.get_tree_entry(flags.first())
-
- for tree_entry in flag_tree:
- self.assertEqual(tree_entry, expected_tree_entry)
-
-
- def test_bool(self):
-
- template_content = self.create_template_content()
-
- self.create_flags(template_content)
-
- flags = TemplateContentFlags.objects.filter(flag=self.flag_names[0])
- flag_tree = FlagTree(flags, self.app.primary_language)
-
- self.assertFalse(bool(flag_tree))
-
-
- flag_tree = FlagTree(flags, self.app.primary_language, app_state='preview')
- self.assertTrue(bool(flag_tree))
-
-
- ltc = template_content.get_localized(self.app.primary_language)
- ltc.publish()
-
- flag_tree = FlagTree(flags, self.app.primary_language)
- self.assertTrue(bool(flag_tree))
-
-
-@test_settings
-class TestTemplateContentFlagsManager(WithUser, WithApp, WithTemplateContent, WithFlags, TestCase):
-
-
- # only published apps can access template content settings on lc private
- def setUp(self):
- super().setUp()
- published_version_path = os.path.join(settings.LOCALCOSMOS_APPS_ROOT, self.testapp_relative_www_path)
-
- self.app.published_version_path = published_version_path
-
- self.app.save()
-
-
- def test_get_tree(self):
-
- template_content = self.create_template_content()
-
- self.create_flags(template_content)
-
- flag_tree = TemplateContentFlags.objects.get_tree(self.app, self.flag_names[0], self.app.primary_language)
-
- self.assertEqual(flag_tree.language, self.app.primary_language)
- self.assertEqual(flag_tree.app_state, 'published')
- self.assertEqual(flag_tree.toplevel_count, 0)
-
-
- flag_tree = TemplateContentFlags.objects.get_tree(self.app, self.flag_names[0], self.app.primary_language,
- app_state='preview')
-
- self.assertEqual(flag_tree.language, self.app.primary_language)
- self.assertEqual(flag_tree.app_state, 'preview')
- self.assertEqual(flag_tree.toplevel_count, 1)
-
-
- ltc = template_content.get_localized(self.app.primary_language)
- ltc.publish()
- flag_tree = TemplateContentFlags.objects.get_tree(self.app, self.flag_names[0], self.app.primary_language)
- self.assertEqual(flag_tree.language, self.app.primary_language)
- self.assertEqual(flag_tree.app_state, 'published')
- self.assertEqual(flag_tree.toplevel_count, 1)
-
-
-# nothing to test currently
-@test_settings
-class TestTemplatecontentFlags(TestCase):
-
- def test(self):
- pass
-
-
-class WithDraftTextMicroContent:
-
- microcontent_type = 'freepage_content'
-
- content = 'Freedom from the heart'
-
- def create_draft_text_mc(self, template_content):
-
- draft_text_mc = DraftTextMicroContent.objects.create(template_content, self.app.primary_language,
- self.microcontent_type, self.content, self.user)
-
- return draft_text_mc
-
-
-@test_settings
-class TestDraftTextMicroContent(WithUser, WithApp, WithTemplateContent, WithDraftTextMicroContent, TestCase):
-
-
- def test_create(self):
-
- template_content = self.create_template_content()
-
- draft_text_mc = self.create_draft_text_mc(template_content)
-
- draft_text_mc.refresh_from_db()
- self.assertEqual(draft_text_mc.template_content, template_content)
- self.assertEqual(draft_text_mc.microcontent_type, self.microcontent_type)
-
- # local creation
- ldtmc = LocalizedDraftTextMicroContent.objects.get(microcontent=draft_text_mc)
- self.assertEqual(ldtmc.content, self.content)
- self.assertEqual(ldtmc.language, self.app.primary_language)
- self.assertEqual(ldtmc.creator, self.user)
- self.assertEqual(ldtmc.microcontent, draft_text_mc)
-
-
- def test_get_language_dependant(self):
-
- template_content = self.create_template_content()
-
- draft_text_mc = self.create_draft_text_mc(template_content)
-
- contents = DraftTextMicroContent.objects.get_language_dependant(template_content, self.microcontent_type,
- self.app.primary_language)
-
- ldtmc = LocalizedDraftTextMicroContent.objects.get(microcontent=draft_text_mc)
-
- self.assertEqual(len(contents), 1)
- self.assertEqual(contents[0], ldtmc)
-
-
- def test_get_locale_model(self):
-
- LocaleModel = DraftTextMicroContent.get_locale_model()
- self.assertEqual(LocaleModel, LocalizedDraftTextMicroContent)
-
-
- def test_get_publication_model(self):
-
- PublicationModel = DraftTextMicroContent.get_publication_model()
- self.assertEqual(PublicationModel, PublishedTextMicroContent)
-
-
- def test_get_content(self):
-
- template_content = self.create_template_content()
-
- draft_text_mc = self.create_draft_text_mc(template_content)
-
- content = draft_text_mc.get_content(self.app.primary_language)
- self.assertEqual(content, self.content)
-
- content_2 = draft_text_mc.get_content('en')
- self.assertEqual(content_2, None)
-
-
- def test_get_localized(self):
-
- template_content = self.create_template_content()
- draft_text_mc = self.create_draft_text_mc(template_content)
-
- ldtmc = draft_text_mc.get_localized(self.app.primary_language)
-
- expected_ldtmc = LocalizedDraftTextMicroContent.objects.get(microcontent=draft_text_mc,
- language=self.app.primary_language)
-
- self.assertEqual(ldtmc, expected_ldtmc)
-
- non_existant_ldtmc = draft_text_mc.get_localized('fr')
- self.assertEqual(non_existant_ldtmc, None)
-
-
- def test_get_form_field(self):
-
- template_content = self.create_template_content()
- draft_text_mc = self.create_draft_text_mc(template_content)
-
- form_field = draft_text_mc.get_form_field(self.app, template_content, {})
-
- self.assertTrue(isinstance(form_field.widget, forms.Textarea))
- self.assertTrue(isinstance(form_field, forms.CharField))
-
- form_field = draft_text_mc.get_form_field(self.app, template_content, {}, 'short')
-
- self.assertTrue(isinstance(form_field.widget, forms.TextInput))
- self.assertTrue(isinstance(form_field, forms.CharField))
-
- form_field = draft_text_mc.get_form_field(self.app, template_content, {'multi':True})
-
- self.assertTrue(isinstance(form_field.widget, MultiContentWidget))
- self.assertTrue(isinstance(form_field, MultiContentField))
-
-
- def test_translation_complete(self):
-
- template_content = self.create_template_content()
- draft_text_mc = self.create_draft_text_mc(template_content)
-
- errors = draft_text_mc.translation_complete(self.app.primary_language)
- self.assertEqual(errors, [])
-
- errors_2 = draft_text_mc.translation_complete('en')
- self.assertEqual(len(errors_2), 1)
- self.assertIn('is missing for the language', errors_2[0])
-
-
- def test_set_content(self):
-
- template_content = self.create_template_content()
- draft_text_mc = self.create_draft_text_mc(template_content)
-
- content_2 = 'Edited content'
- draft_text_mc.set_content(content_2, self.user, self.app.primary_language)
-
- ldtmc = draft_text_mc.get_localized(self.app.primary_language)
- self.assertEqual(ldtmc.content, content_2)
-
- en_content = 'Translated content'
- draft_text_mc.set_content(en_content, self.user, 'en')
-
- ldtmc_en = draft_text_mc.get_localized('en')
- self.assertEqual(ldtmc_en.content, en_content)
-
-
- def test_publish(self):
-
- template_content = self.create_template_content()
- draft_text_mc = self.create_draft_text_mc(template_content)
-
- en_content = 'Translated content'
- draft_text_mc.set_content(en_content, self.user, 'en')
-
- draft_text_mc.publish(['de', 'en'])
-
- published_text_mc = PublishedTextMicroContent.objects.get(template_content=template_content,
- microcontent_type=draft_text_mc.microcontent_type)
-
- ldtmc = draft_text_mc.get_localized(self.app.primary_language)
- lptmc = published_text_mc.get_localized(self.app.primary_language)
-
- self.assertEqual(ldtmc.content, lptmc.content)
-
-
- ldtmc_en = draft_text_mc.get_localized('en')
- lptmc_en = published_text_mc.get_localized('en')
-
- self.assertEqual(ldtmc_en.content, lptmc_en.content)
-
-
-@test_settings
-class TestLocalizedDraftTextMicroContent(WithUser, WithApp, WithTemplateContent, WithDraftTextMicroContent,
- TestCase):
-
- def test_create(self):
-
- template_content = self.create_template_content()
-
- draft_text_mc = DraftTextMicroContent(
- template_content = template_content,
- microcontent_type = self.microcontent_type,
- )
-
- draft_text_mc.save()
-
- ldtmc = LocalizedDraftTextMicroContent.objects.create(draft_text_mc, self.app.primary_language,
- self.content, self.user)
-
- ldtmc.refresh_from_db()
-
- self.assertEqual(ldtmc.microcontent, draft_text_mc)
- self.assertEqual(ldtmc.language, self.app.primary_language)
- self.assertEqual(ldtmc.content, self.content)
- self.assertEqual(ldtmc.creator, self.user)
-
-
- def test_get_content(self):
-
- template_content = self.create_template_content()
- draft_text_mc = self.create_draft_text_mc(template_content)
-
- ldtmc = draft_text_mc.get_localized(self.app.primary_language)
-
- content = ldtmc.get_content()
- self.assertEqual(content, self.content)
-
-
- def test_save(self):
-
- template_content = self.create_template_content()
- draft_text_mc = self.create_draft_text_mc(template_content)
-
- ldtmc = draft_text_mc.get_localized(self.app.primary_language)
- ldtmc.content = None
-
- ldtmc.save()
-
- self.assertEqual(ldtmc.plain_text, None)
-
- ldtmc.content = 'Test Content
'
- ldtmc.save()
-
- self.assertEqual(ldtmc.plain_text, 'Test Content')
-
-
-
-class TestPublishedTextMicroContent(WithUser, WithApp, WithTemplateContent, WithDraftTextMicroContent, TestCase):
-
-
- def test_create(self):
-
- template_content = self.create_template_content()
- draft_text_mc = self.create_draft_text_mc(template_content)
-
- published_text_mc = PublishedTextMicroContent.objects.create(draft_text_mc)
-
- published_text_mc.refresh_from_db()
-
- self.assertEqual(published_text_mc.template_content, template_content)
- self.assertEqual(published_text_mc.microcontent_type, self.microcontent_type)
-
-
- def test_get_language_dependant(self):
-
- template_content = self.create_template_content()
-
- draft_text_mc = self.create_draft_text_mc(template_content)
-
- draft_text_mc.publish([self.app.primary_language])
-
- published_text_mc = PublishedTextMicroContent.objects.get(template_content=template_content,
- microcontent_type=self.microcontent_type)
-
- contents = PublishedTextMicroContent.objects.get_language_dependant(template_content, self.microcontent_type,
- self.app.primary_language)
-
- lptmc = LocalizedPublishedTextMicroContent.objects.get(microcontent=published_text_mc)
-
- self.assertEqual(len(contents), 1)
- self.assertEqual(contents[0], lptmc)
-
-
- def test_get_locale_model(self):
-
- LocaleModel = PublishedTextMicroContent.get_locale_model()
- self.assertEqual(LocaleModel, LocalizedPublishedTextMicroContent)
-
-
- def test_get_content(self):
-
- template_content = self.create_template_content()
- draft_text_mc = self.create_draft_text_mc(template_content)
-
- draft_text_mc.publish([self.app.primary_language])
-
- published_text_mc = PublishedTextMicroContent.objects.get(template_content=template_content,
- microcontent_type=self.microcontent_type)
-
- content = published_text_mc.get_content(self.app.primary_language)
- self.assertEqual(content, self.content)
-
-
- def test_get_localized(self):
-
- template_content = self.create_template_content()
- draft_text_mc = self.create_draft_text_mc(template_content)
-
- draft_text_mc.publish([self.app.primary_language])
-
- published_text_mc = PublishedTextMicroContent.objects.get(template_content=template_content,
- microcontent_type=self.microcontent_type)
-
- lptmc = published_text_mc.get_localized(self.app.primary_language)
- expected_lptmc = LocalizedPublishedTextMicroContent.objects.get(microcontent=published_text_mc,
- language=self.app.primary_language)
-
- self.assertEqual(lptmc, expected_lptmc)
-
- non_existant_lptmc = published_text_mc.get_localized('fr')
- self.assertEqual(non_existant_lptmc, None)
-
-
-
-class TestLocalizedPublishedTextMicroContent(WithUser, WithApp, WithTemplateContent, WithDraftTextMicroContent,
- TestCase):
-
-
- def create_localized_published_mc(self, template_content):
-
- draft_text_mc = self.create_draft_text_mc(template_content)
-
- published_text_mc = PublishedTextMicroContent.objects.create(draft_text_mc)
-
-
- lptmc = LocalizedPublishedTextMicroContent.objects.create(published_text_mc, self.app.primary_language,
- self.content, self.user)
-
- return lptmc, published_text_mc
-
-
- def test_create(self):
-
- template_content = self.create_template_content()
-
- lptmc, published_text_mc = self.create_localized_published_mc(template_content)
-
- lptmc.refresh_from_db()
-
- self.assertEqual(lptmc.language, self.app.primary_language)
- self.assertEqual(lptmc.microcontent, published_text_mc)
- self.assertEqual(lptmc.content, self.content)
- self.assertEqual(lptmc.creator, self.user)
-
-
- def test_get_content(self):
-
- template_content = self.create_template_content()
-
- lptmc, published_text_mc = self.create_localized_published_mc(template_content)
-
- lptmc.refresh_from_db()
-
- content = lptmc.get_content()
- self.assertEqual(content, self.content)
-
-
- def test_save(self):
-
- template_content = self.create_template_content()
-
- lptmc, published_text_mc = self.create_localized_published_mc(template_content)
-
- lptmc.content = None
- lptmc.save()
-
- self.assertEqual(lptmc.plain_text, None)
-
- lptmc.content = 'Test Content
'
- lptmc.save()
-
- self.assertEqual(lptmc.plain_text, 'Test Content')
-
-
-# Testing Images
-# -> take care of licences
-from content_licencing.models import ContentLicenceRegistry
-
-class WithImageMicroContent:
-
- image_microcontent_type = 'test_image'
-
- licence = 'CC0'
- creator_name = 'Someone With a Name'
-
- filename = 'test_image.jpg'
-
- def get_content(self):
- content = SimpleUploadedFile(name=self.filename, content=open(TEST_IMAGE_PATH, 'rb').read(),
- content_type='image/jpeg')
-
- return content
-
-
- def create_draft_image_mc(self, template_content):
-
- image = self.get_content()
-
- draft_image_mc = DraftImageMicroContent.objects.create(template_content, self.app.primary_language,
- self.image_microcontent_type, image, self.user)
-
- ldimc = draft_image_mc.get_localized(self.app.primary_language)
- # set licence
- licence_kwargs = {
- 'creator_name' : self.creator_name,
- }
- registry_entry = ContentLicenceRegistry.objects.register(ldimc, 'content', self.user, self.licence, '1.0',
- **licence_kwargs)
-
- return draft_image_mc
-
-
-@test_settings
-class TestDraftImageMicroContent(WithUser, WithApp, WithTemplateContent, WithMedia, WithImageMicroContent, TestCase):
-
- def test_create(self):
-
- template_content = self.create_template_content()
-
- image = self.get_content()
-
- draft_image_mc = DraftImageMicroContent.objects.create(template_content, self.app.primary_language,
- self.image_microcontent_type, image, self.user)
-
- draft_image_mc.refresh_from_db()
-
- self.assertEqual(draft_image_mc.template_content, template_content)
- self.assertEqual(draft_image_mc.microcontent_type, self.image_microcontent_type)
-
- ldimc = LocalizedDraftImageMicroContent.objects.get(microcontent=draft_image_mc)
-
- self.assertEqual(ldimc.language, self.app.primary_language)
- self.assertEqual(ldimc.creator, self.user)
-
-
- def test_get_language_dependant(self):
-
- template_content = self.create_template_content()
-
- draft_image_mc = self.create_draft_image_mc(template_content)
-
- contents = DraftImageMicroContent.objects.get_language_dependant(template_content,
- self.image_microcontent_type, self.app.primary_language)
-
- ldimc = LocalizedDraftImageMicroContent.objects.get(microcontent=draft_image_mc)
-
- self.assertEqual(len(contents), 1)
- self.assertEqual(contents[0], ldimc)
-
-
- def test_get_form_field(self):
- template_content = self.create_template_content()
- draft_image_mc = self.create_draft_image_mc(template_content)
- ldimc = LocalizedDraftImageMicroContent.objects.get(microcontent=draft_image_mc)
-
- widget_attrs_default = {
- 'language' : self.app.primary_language,
- 'data-microcontentcategory' : 'image',
- 'data-microcontenttype' : 'single_image',
- }
-
- widget_attrs = widget_attrs_default.copy()
- form_field = draft_image_mc.get_form_field(self.app, template_content, widget_attrs)
-
- self.assertTrue(isinstance(form_field, forms.ImageField))
- self.assertTrue(isinstance(form_field.widget, forms.FileInput))
- self.assertEqual(form_field.widget.attrs['file'], ldimc.content)
-
- # without ldimc
- ldimc.delete()
- draft_image_mc.refresh_from_db()
-
- widget_attrs = widget_attrs_default.copy()
- form_field_2 = draft_image_mc.get_form_field(self.app, template_content, widget_attrs)
- self.assertTrue(isinstance(form_field_2, forms.ImageField))
-
- self.assertTrue(isinstance(form_field_2.widget, forms.FileInput))
- self.assertFalse('file' in form_field_2.widget.attrs)
-
-
- def test_translation_complete(self):
- template_content = self.create_template_content()
- draft_image_mc = self.create_draft_image_mc(template_content)
-
- errors = draft_image_mc.translation_complete(self.app.primary_language)
- self.assertEqual(errors, [])
-
-
- def test_set_content(self):
-
- template_content = self.create_template_content()
- draft_image_mc = self.create_draft_image_mc(template_content)
-
- ldimc = LocalizedDraftImageMicroContent.objects.get(microcontent=draft_image_mc)
-
- old_path = ldimc.content.path
- self.assertTrue(os.path.isfile(old_path))
-
- image = SimpleUploadedFile(name='new_test_image.jpg', content=open(TEST_IMAGE_PATH, 'rb').read(),
- content_type='image/jpeg')
-
- draft_image_mc.set_content(image, self.user, self.app.primary_language)
-
- ldimc.refresh_from_db()
- self.assertFalse(os.path.isfile(old_path))
- self.assertTrue(os.path.isfile(ldimc.content.path))
-
-
- draft_image_mc.set_content(image, self.user, 'en')
- ldimc_en = LocalizedDraftImageMicroContent.objects.get(microcontent=draft_image_mc, language='en')
-
- self.assertTrue(os.path.isfile(ldimc_en.content.path))
-
-
- def test_get_publication_model(self):
- PublicationModel = DraftImageMicroContent.get_publication_model()
- self.assertEqual(PublicationModel, PublishedImageMicroContent)
-
-
- def test_get_locale_model(self):
- LocaleModel = DraftImageMicroContent.get_locale_model()
- self.assertEqual(LocaleModel, LocalizedDraftImageMicroContent)
-
-
- def test_publish(self):
- # also check the licences
-
- template_content = self.create_template_content()
- draft_image_mc = self.create_draft_image_mc(template_content)
-
-
- draft_image_mc.publish([self.app.primary_language])
-
- published_image_mc = PublishedImageMicroContent.objects.get(template_content=template_content,
- microcontent_type=draft_image_mc.microcontent_type)
-
- ldimc = draft_image_mc.get_localized(self.app.primary_language)
- lpimc = published_image_mc.get_localized(self.app.primary_language)
-
- self.assertEqual(os.path.basename(ldimc.content.name), os.path.basename(lpimc.content.name))
- self.assertTrue(os.path.isfile(lpimc.content.path))
-
- draft_licence = ldimc.licences.all().first()
- published_licence = lpimc.licences.all().first()
-
- self.assertEqual(draft_licence.content_licence().short_name, published_licence.content_licence().short_name)
- self.assertEqual(draft_licence.creator_name, published_licence.creator_name)
-
-
- def test_get_content(self):
-
- template_content = self.create_template_content()
- draft_image_mc = self.create_draft_image_mc(template_content)
-
- content = draft_image_mc.get_content(self.app.primary_language)
- ldimc = draft_image_mc.get_localized(self.app.primary_language)
-
- self.assertEqual(content, ldimc.content)
-
- content_en = draft_image_mc.get_content('en')
- self.assertEqual(content_en, None)
-
-
- def test_get_localized(self):
-
- template_content = self.create_template_content()
- draft_image_mc = self.create_draft_image_mc(template_content)
-
- expected_ldimc = LocalizedDraftImageMicroContent.objects.get(microcontent=draft_image_mc)
-
- ldimc = draft_image_mc.get_localized(self.app.primary_language)
-
- self.assertEqual(ldimc, expected_ldimc)
-
-
-@test_settings
-class TestLocalizedDraftImageMicroContent(WithUser, WithApp, WithTemplateContent, WithMedia, WithImageMicroContent,
- TestCase):
-
- def test_create(self):
-
- template_content = self.create_template_content()
-
- draft_image_mc = DraftImageMicroContent(
- template_content = template_content,
- microcontent_type = self.image_microcontent_type,
- )
-
- draft_image_mc.save()
-
- content = self.get_content()
- ldimc = LocalizedDraftImageMicroContent.objects.create(draft_image_mc, self.app.primary_language, content,
- self.user)
-
- ldimc.refresh_from_db()
-
- self.assertEqual(ldimc.microcontent, draft_image_mc)
- self.assertEqual(ldimc.language, self.app.primary_language)
- self.assertEqual(os.path.basename(ldimc.content.name), self.filename)
- self.assertEqual(ldimc.creator, self.user)
-
-
- def test_get_content(self):
-
- template_content = self.create_template_content()
- draft_image_mc = self.create_draft_image_mc(template_content)
-
- ldimc = draft_image_mc.get_localized(self.app.primary_language)
-
- content = ldimc.get_content()
- self.assertEqual(content, ldimc.content)
-
-
-@test_settings
-class TestPublishedImageMicroContent(WithUser, WithApp, WithTemplateContent, WithMedia, WithImageMicroContent,
- TestCase):
-
- def test_create(self):
- template_content = self.create_template_content()
- draft_image_mc = self.create_draft_image_mc(template_content)
-
- published_image_mc = PublishedImageMicroContent.objects.create(draft_image_mc)
-
- published_image_mc.refresh_from_db()
-
- self.assertEqual(published_image_mc.template_content, template_content)
- self.assertEqual(published_image_mc.microcontent_type, self.image_microcontent_type)
-
-
- def test_get_language_dependant(self):
-
- template_content = self.create_template_content()
-
- draft_image_mc = self.create_draft_image_mc(template_content)
-
- draft_image_mc.publish([self.app.primary_language])
-
- published_image_mc = PublishedImageMicroContent.objects.get(template_content=template_content,
- microcontent_type=self.image_microcontent_type)
-
- contents = PublishedImageMicroContent.objects.get_language_dependant(template_content,
- self.image_microcontent_type, self.app.primary_language)
-
- lpimc = LocalizedPublishedImageMicroContent.objects.get(microcontent=published_image_mc)
-
- self.assertEqual(len(contents), 1)
- self.assertEqual(contents[0], lpimc)
-
-
- def test_get_locale_model(self):
- LocaleModel = PublishedImageMicroContent.get_locale_model()
- self.assertEqual(LocaleModel, LocalizedPublishedImageMicroContent)
-
-
- def test_get_content(self):
-
- template_content = self.create_template_content()
-
- draft_image_mc = self.create_draft_image_mc(template_content)
-
- draft_image_mc.publish([self.app.primary_language])
-
- published_image_mc = PublishedImageMicroContent.objects.get(template_content=template_content,
- microcontent_type=self.image_microcontent_type)
-
- content = published_image_mc.get_content(self.app.primary_language)
-
- lpimc = published_image_mc.get_localized(self.app.primary_language)
-
- self.assertEqual(content, lpimc.content)
-
-
- def test_get_localized(self):
-
- template_content = self.create_template_content()
-
- draft_image_mc = self.create_draft_image_mc(template_content)
-
- draft_image_mc.publish([self.app.primary_language])
-
- published_image_mc = PublishedImageMicroContent.objects.get(template_content=template_content,
- microcontent_type=self.image_microcontent_type)
-
- lpimc = published_image_mc.get_localized(self.app.primary_language)
-
- expected_lpimc = LocalizedPublishedImageMicroContent.objects.get(microcontent=published_image_mc,
- language=self.app.primary_language)
-
- self.assertEqual(lpimc, expected_lpimc)
-
-
-@test_settings
-class TestLocalizedPublishedImageMicroContent(WithUser, WithApp, WithTemplateContent, WithMedia,
- WithImageMicroContent, TestCase):
-
- def create_localized_published_mc(self, template_content):
-
- draft_image_mc = self.create_draft_image_mc(template_content)
-
- published_image_mc = PublishedImageMicroContent.objects.create(draft_image_mc)
-
- content = self.get_content()
- lpimc = LocalizedPublishedImageMicroContent.objects.create(published_image_mc, self.app.primary_language,
- content, self.user)
-
- return lpimc, published_image_mc
-
-
- def test_create(self):
-
- template_content = self.create_template_content()
-
- lpimc, published_image_mc = self.create_localized_published_mc(template_content)
-
- lpimc.refresh_from_db()
-
- self.assertEqual(lpimc.language, self.app.primary_language)
- self.assertEqual(lpimc.microcontent, published_image_mc)
- self.assertEqual(os.path.basename(lpimc.content.name), self.filename)
- self.assertEqual(lpimc.creator, self.user)
-
-
- def test_get_content(self):
-
- template_content = self.create_template_content()
-
- lpimc, published_image_mc = self.create_localized_published_mc(template_content)
-
- lpimc.refresh_from_db()
-
- content = lpimc.get_content()
- self.assertEqual(content, lpimc.content)
diff --git a/localcosmos_server/online_content/tests/test_views.py b/localcosmos_server/online_content/tests/test_views.py
deleted file mode 100644
index 62941a4..0000000
--- a/localcosmos_server/online_content/tests/test_views.py
+++ /dev/null
@@ -1,1678 +0,0 @@
-from django.test import TestCase, RequestFactory
-from django.urls import reverse
-from django.conf import settings
-
-from localcosmos_server.models import SecondaryAppLanguages
-
-from localcosmos_server.online_content.views import (ManageOnlineContent, CreateTemplateContent, ManageMicroContents,
- ManageTemplateContent, TranslateTemplateContent, PublishTemplateContent, DeleteTemplateContent,
- UnpublishTemplateContent, DeleteMicroContent, DeleteFileContent, UploadFile, UploadImage, ManageImageUpload,
- GetFormField)
-
-from localcosmos_server.online_content.mixins import OnlineContentMixin
-
-from localcosmos_server.online_content.models import (TemplateContent, LocalizedDraftTextMicroContent,
- DraftTextMicroContent, TemplateContentFlags, LocalizedTemplateContent, DraftImageMicroContent,
- LocalizedDraftImageMicroContent)
-
-from localcosmos_server.online_content.forms import ManageTemplateContentForm, TranslateTemplateContentForm
-
-
-from localcosmos_server.tests.mixins import (WithDataset, WithApp, WithUser, WithMedia, WithOnlineContent,
- CommonSetUp, WithImageForForm)
-
-from localcosmos_server.tests.common import test_settings, MockPost
-
-import os
-
-class WithPublishedApp:
-
- # only published apps can access template content settings on lc private
- def setUp(self):
- super().setUp()
- published_version_path = os.path.join(settings.LOCALCOSMOS_APPS_ROOT, self.testapp_relative_www_path)
-
- self.app.published_version_path = published_version_path
-
- self.app.save()
-
-
-@test_settings
-class TestManageOnlineContent(WithPublishedApp, CommonSetUp, WithUser, WithApp, WithOnlineContent, TestCase):
-
-
- def get_url_kwargs(self):
- url_kwargs = {
- 'app_uid' : self.app.uid,
- }
-
- return url_kwargs
-
-
- def test_get_context_data(self):
-
- self.create_template_content()
-
- view, request = self.get_view(ManageOnlineContent, 'manage_onlinecontent')
-
- context = view.get_context_data(**{})
- self.assertEqual(list(context['features']), [])
- self.assertEqual(context['pages'].first(), self.template_content)
-
- def test_get(self):
-
- url_kwargs = self.get_url_kwargs()
- response = self.client.get(reverse('manage_onlinecontent', kwargs=url_kwargs))
-
- self.assertEqual(response.status_code, 200)
-
-
-@test_settings
-class TestCreateTemplateContent(WithPublishedApp, CommonSetUp, WithUser, WithApp, WithOnlineContent, TestCase):
-
-
- def get_post_data(self):
-
- post_data = {
- 'draft_title' : 'Test draft title',
- 'draft_navigation_link_name' : 'Test link name',
- 'template_name' : 'page/test.html',
- 'template_type' : 'page',
- 'input_language' : self.app.primary_language
- }
-
- return post_data
-
-
- def get_url_kwargs(self):
-
- url_kwargs = {
- 'app_uid' : self.app.uid,
- 'template_type' : 'page',
- }
-
- return url_kwargs
-
-
- def test_get_context_data(self):
-
-
- view, request = self.get_view(CreateTemplateContent, 'create_template_content')
-
- context = view.get_context_data(**{})
-
- self.assertEqual(context['template_type'], 'page')
-
-
- def test_get_form_kwargs(self):
-
-
- view, request = self.get_view(CreateTemplateContent, 'create_template_content')
-
- form_kwargs = view.get_form_kwargs(**{})
-
- self.assertEqual(form_kwargs['language'], self.app.primary_language)
-
-
- def test_get_form(self):
-
-
- view, request = self.get_view(CreateTemplateContent, 'create_template_content')
-
- form = view.get_form()
-
- self.assertEqual(form.__class__, view.form_class)
-
-
- def test_form_valid(self):
-
- post_data = self.get_post_data()
-
- view, request = self.get_view(CreateTemplateContent, 'create_template_content')
-
- form = view.form_class(self.app, view.kwargs['template_type'], data=post_data,
- **view.get_form_kwargs())
-
- form.is_valid()
-
- self.assertEqual(form.errors, {})
-
- response = view.form_valid(form)
-
- self.assertEqual(response.status_code, 302)
-
- created_tc = TemplateContent.objects.filter(app=self.app, template_name=post_data['template_name'],
- template_type=post_data['template_type']).first()
-
-
- self.assertEqual(created_tc.__class__, TemplateContent)
-
-
- def test_get(self):
-
- response = self.client.get(reverse('create_template_content', kwargs=self.get_url_kwargs()))
-
- self.assertEqual(response.status_code, 200)
-
-
- def test_post(self):
-
- post_data = self.get_post_data()
-
- response = self.client.post(reverse('create_template_content', kwargs=self.get_url_kwargs()), post_data)
-
- self.assertEqual(response.status_code, 302)
-
- created_tc = TemplateContent.objects.filter(app=self.app, template_name=post_data['template_name'],
- template_type=post_data['template_type']).first()
-
-
- self.assertEqual(created_tc.__class__, TemplateContent)
-
-
-class ManageMicroContentsMixin:
-
- cms_fields = ['simple_content', 'layout1', 'layout2', 'multi-content']
-
- app_secondary_languages = ['en', 'fr']
-
- def get_post_data(self):
- post_data = {
- 'draft_title' : 'Test draft title - edited',
- 'draft_navigation_link_name' : 'Test link name - edited',
- 'page_flags' : [],
- 'simple_content' : 'Test simple content',
- 'layout1' : 'layout 1',
- 'layout2' : '',
- 'multi-content' : ['a', 'b', 'c'],
- 'input_language' : self.app.primary_language
- }
-
- return post_data
-
-
- def get_url_kwargs(self):
-
- url_kwargs = {
- 'app_uid' : self.app.uid,
- 'pk' : self.template_content.pk,
- }
-
- return url_kwargs
-
-
- def validate_saved_microcontents(self, template_content, form, language=None):
-
- if language is None:
- language = self.app.primary_language
-
- for field_ in form:
-
- field = field_.field
-
- if field_.name in self.cms_fields:
- self.assertTrue(hasattr(field, 'cms_object'))
-
- data = form.cleaned_data[field_.name]
-
- if field_.name == 'multi-content':
-
- for counter, content in enumerate(data, 0):
- created_meta_instance = field.cms_object.Model.objects.filter(
- template_content=template_content,
- microcontent_type=field.cms_object.microcontent_type).order_by('pk')[counter]
-
- self.assertTrue(created_meta_instance is not None)
-
- created_content = created_meta_instance.get_content(language)
- self.assertEqual(created_content, content)
- locale = created_meta_instance.get_localized(language)
- self.assertEqual(locale.creator, self.user)
-
- else:
- created_meta_instance = field.cms_object.Model.objects.get(
- template_content=template_content, microcontent_type=field.cms_object.microcontent_type)
-
- self.assertTrue(created_meta_instance is not None)
-
- created_content = created_meta_instance.get_content(language)
- self.assertEqual(created_content, data)
- locale = created_meta_instance.get_localized(language)
- self.assertEqual(locale.creator, self.user)
-
-
-@test_settings
-class TestManageMicroContents(ManageMicroContentsMixin, WithPublishedApp, CommonSetUp, WithUser, WithApp,
- WithOnlineContent, WithMedia, TestCase):
-
- def test_dispatch(self):
-
- template_content = self.create_template_content()
-
- view, request = self.get_view(ManageMicroContents, 'manage_template_content')
-
- view.form_class = ManageTemplateContentForm
-
- response = view.dispatch(request, **self.get_url_kwargs())
-
- self.assertEqual(response.status_code, 200)
- self.assertEqual(view.template_content, self.template_content)
- self.assertEqual(view.language, self.app.primary_language)
-
- def test_set_template_content(self):
-
- template_content = self.create_template_content()
-
- view, request = self.get_view(ManageMicroContents, 'manage_template_content')
-
- url_kwargs = self.get_url_kwargs()
-
- view.set_template_content(request, **url_kwargs)
-
- self.assertEqual(view.template_content, template_content)
-
- def test_set_language(self):
- template_content = self.create_template_content()
-
- view, request = self.get_view(ManageMicroContents, 'manage_template_content')
- view.template_content = template_content
-
- url_kwargs = self.get_url_kwargs()
-
- view.set_language(request, **url_kwargs)
-
- self.assertEqual(view.language, self.app.primary_language)
-
-
- def test_get_context_data(self):
-
- template_content = self.create_template_content()
-
- view, request = self.get_view(ManageMicroContents, 'manage_template_content')
- view.template_content = template_content
- view.language = self.app.primary_language
- view.form_class = ManageTemplateContentForm
-
- context = view.get_context_data(**{})
- self.assertEqual(context['language'], self.app.primary_language)
- self.assertEqual(context['template_content'], template_content)
-
-
- def validate_saved_content(self, view, template_content, field, data, user):
-
- created_meta_instance = field.cms_object.Model.objects.filter(template_content=template_content,
- microcontent_type=field.cms_object.microcontent_type).order_by('pk').last()
-
- self.assertTrue(created_meta_instance is not None)
-
- created_content = created_meta_instance.get_content(self.app.primary_language)
- self.assertEqual(created_content, data)
- locale = created_meta_instance.get_localized(self.app.primary_language)
- self.assertEqual(locale.creator, self.user)
-
- def test_save_content(self):
-
- template_content = self.create_template_content()
- view, request = self.get_view(ManageMicroContents, 'manage_template_content')
- view.template_content = template_content
- view.language = self.app.primary_language
- view.form_class = ManageTemplateContentForm
-
- post = MockPost(self.get_post_data())
- form = view.form_class(template_content, post)
-
- form.is_valid()
-
- self.assertEqual(form.errors, {})
-
- for field_ in form:
-
- field = field_.field
-
- if field_.name in self.cms_fields:
- self.assertTrue(hasattr(field, 'cms_object'))
-
- data = form.cleaned_data[field_.name]
-
- if field_.name == 'multi-content':
-
- for content in data:
-
- view._save_content(template_content, self.app.primary_language, field, content, self.user)
- self.validate_saved_content(view, template_content, field, content, self.user)
-
- else:
- view._save_content(template_content, self.app.primary_language, field, data, self.user)
- self.validate_saved_content(view, template_content, field, data, self.user)
-
-
- def test_get_form_kwargs(self):
-
- template_content = self.create_template_content()
- view, request = self.get_view(ManageMicroContents, 'manage_template_content')
- view.template_content = template_content
- view.language = self.app.primary_language
- view.form_class = ManageTemplateContentForm
-
- form_kwargs = view.get_form_kwargs()
-
- self.assertEqual(form_kwargs['language'], self.app.primary_language)
-
-
- def test_get_form(self):
-
- template_content = self.create_template_content()
- view, request = self.get_view(ManageMicroContents, 'manage_template_content')
- view.template_content = template_content
- view.language = self.app.primary_language
- view.form_class = ManageTemplateContentForm
-
- form = view.get_form()
-
- self.assertEqual(form.__class__, view.form_class)
-
-
- def test_get_form_force_initial(self):
-
- template_content = self.create_template_content()
- view, request = self.get_view(ManageMicroContents, 'manage_template_content')
- view.template_content = template_content
- view.language = self.app.primary_language
- view.form_class = ManageTemplateContentForm
-
- form_initial = view.get_form_force_initial()
-
- self.assertEqual(form_initial.__class__, view.form_class)
- self.assertEqual(form_initial.is_bound, False)
-
- # save primary ltc, unready all translations
- def test_save_localized_template_content(self):
-
- template_content = self.create_template_content()
- view, request = self.get_view(ManageMicroContents, 'manage_template_content')
- view.template_content = template_content
- view.form_class = ManageTemplateContentForm
-
- localized_template_content = template_content.get_localized(self.app.primary_language)
- localized_template_content.translation_ready = True
- localized_template_content.save()
-
- secondary_languages = self.app.secondary_languages()
-
- self.assertTrue(len(secondary_languages) > 0)
-
- self.create_secondary_language_ltcs()
-
- for language in secondary_languages:
-
- ltc = template_content.get_localized(language)
- ltc.translation_ready = True
- ltc.save()
-
-
- view.language = self.app.primary_language
-
- post_data = self.get_post_data()
- post = MockPost(post_data)
- form = view.form_class(template_content, post)
-
- form.is_valid()
-
- self.assertEqual(form.errors, {})
-
- view.localized_template_content = localized_template_content
- view.save_localized_template_content(form)
-
- localized_template_content.refresh_from_db()
- self.assertEqual(localized_template_content.draft_title, post_data['draft_title'])
- self.assertEqual(localized_template_content.draft_navigation_link_name,
- post_data['draft_navigation_link_name'])
-
- self.assertFalse(localized_template_content.translation_ready)
-
- for language in secondary_languages:
-
- ltc = template_content.get_localized(language)
- self.assertFalse(ltc.translation_ready)
-
-
- # save secondary ltc, unready only submitted language
- def test_save_secondary_localized_template_content(self):
-
- template_content = self.create_template_content()
- view, request = self.get_view(ManageMicroContents, 'manage_template_content')
- view.template_content = template_content
- view.form_class = ManageTemplateContentForm
-
- primary_ltc = template_content.get_localized(self.app.primary_language)
- primary_ltc.translation_ready = True
- primary_ltc.save()
-
- secondary_languages = self.app.secondary_languages()
-
- self.assertTrue(len(secondary_languages) > 0)
-
- self.create_secondary_language_ltcs()
-
- for language in secondary_languages:
-
- ltc = template_content.get_localized(language)
- ltc.translation_ready = True
- ltc.save()
-
- test_language = secondary_languages[0]
-
- view.language = test_language
-
- post_data = self.get_post_data()
- post = MockPost(post_data)
- form = view.form_class(template_content, post)
-
- form.is_valid()
-
- self.assertEqual(form.errors, {})
-
- localized_template_content = template_content.get_localized(test_language)
- view.localized_template_content = localized_template_content
- view.save_localized_template_content(form)
-
- self.assertEqual(localized_template_content.draft_title, post_data['draft_title'])
- self.assertEqual(localized_template_content.draft_navigation_link_name,
- post_data['draft_navigation_link_name'])
-
- self.assertFalse(localized_template_content.translation_ready)
-
- primary_ltc.refresh_from_db()
- self.assertTrue(primary_ltc.translation_ready)
-
- for language in secondary_languages:
-
- if language != test_language:
- ltc = template_content.get_localized(language)
- self.assertTrue(ltc.translation_ready)
-
-
- def test_save_microcontent_fields(self):
-
- template_content = self.create_template_content()
- view, request = self.get_view(ManageMicroContents, 'manage_template_content')
- view.template_content = template_content
- view.language = self.app.primary_language
- view.form_class = ManageTemplateContentForm
-
- post_data = self.get_post_data()
- post = MockPost(post_data)
- form = view.form_class(template_content, post)
-
- form.is_valid()
-
- self.assertEqual(form.errors, {})
-
- view.save_microcontent_fields(form)
-
- self.validate_saved_microcontents(template_content, form)
-
-
- # test deletion of meta instance
- # edit the post data
-
- post_data['simple_content'] = ''
- post = MockPost(post_data)
- form = view.form_class(template_content, post)
-
- form.is_valid()
-
- self.assertEqual(form.errors, {})
-
- field = form.fields['simple_content']
-
- qry = field.cms_object.Model.objects.filter(template_content=template_content,
- microcontent_type=field.cms_object.microcontent_type)
-
- self.assertTrue(qry.exists())
-
- view.save_microcontent_fields(form)
-
- self.assertFalse(qry.exists())
-
-
- # test deletion of localized instances
- layout_1 = 'layout1'
- secondary_language = 'fr'
- field = form.fields[layout_1]
-
- layout_1_qry = field.cms_object.Model.objects.filter(template_content=template_content,
- microcontent_type=field.cms_object.microcontent_type)
-
- # creater translatoin of layout1
- LocaleModel = field.cms_object.Model.get_locale_model()
-
- localized_draft_microcontent = LocaleModel.objects.create(layout_1_qry.first(), secondary_language,
- 'test fr', self.user)
-
- localized_qry = LocalizedDraftTextMicroContent.objects.filter(microcontent=layout_1_qry.first(),
- language=secondary_language)
-
- self.assertTrue(layout_1_qry.exists())
-
- self.assertTrue(localized_qry.exists())
-
- post_data[layout_1] = ''
- post_data['input_language'] = secondary_language
- post = MockPost(post_data)
- form = view.form_class(template_content, post, for_translation=True, language=secondary_language)
-
- form.is_valid()
-
- self.assertEqual(form.errors, {})
-
- view.save_microcontent_fields(form, for_translation=True)
-
- self.assertTrue(layout_1_qry.exists())
-
- self.assertFalse(localized_qry.exists())
-
-
- def test_post_form_invalid(self):
- # a required field is 'draft_title'
-
- template_content = self.create_template_content()
-
- post_data = self.get_post_data()
- del post_data['draft_title']
-
- response = self.client.post(reverse('manage_template_content', kwargs=self.get_url_kwargs()), post_data)
-
- self.assertEqual(response.status_code, 200)
- self.assertFalse(response.context_data['saved_as_draft'])
- self.assertFalse(response.context_data['form'].is_valid())
-
-
- def validate_valid_post(self, response, template_content):
- self.assertEqual(response.context_data['form'].errors, {})
- self.assertEqual(response.context_data['form'].data, {})
- self.assertFalse(response.context_data['form'].is_bound)
- self.assertEqual(response.context_data['language'], self.app.primary_language)
- self.assertEqual(response.context_data['template_content'], template_content)
-
-
- def test_post_form_valid_secondary_languages_present(self):
-
- template_content = self.create_template_content()
-
- post_data = self.get_post_data()
-
- response = self.client.post(reverse('manage_template_content', kwargs=self.get_url_kwargs()), post_data)
-
- self.validate_valid_post(response, template_content)
- self.assertTrue(response.context_data['saved_as_draft'])
-
-
- def test_post_form_valid_translation_ready_secondary_languages_present_no_translation_errors(self):
-
- template_content = self.create_template_content()
-
- # create test_image and multi_image
- self.create_draft_image_microcontent(template_content, 'test_image', self.app.primary_language)
- self.create_draft_image_microcontent(template_content, 'multi_image', self.app.primary_language)
-
- post_data = self.get_post_data()
-
- url = '{0}?translation-ready=1'.format(reverse('manage_template_content', kwargs=self.get_url_kwargs()))
- response = self.client.post(url, post_data)
-
- self.validate_valid_post(response, template_content)
- self.assertFalse(response.context_data['saved_as_draft'])
- self.assertTrue(response.context_data['tried_translation_ready'])
- self.assertEqual(response.context_data['translation_errors'], [])
-
- # no translation errors
- ltc = template_content.get_localized(self.app.primary_language)
- self.assertTrue(ltc.translation_ready)
-
-
- def test_post_form_valid_translation_ready_secondary_languages_present_with_translation_errors(self):
-
- template_content = self.create_template_content()
-
- post_data = self.get_post_data()
-
- url = '{0}?translation-ready=1'.format(reverse('manage_template_content', kwargs=self.get_url_kwargs()))
- response = self.client.post(url, post_data)
-
- self.validate_valid_post(response, template_content)
- self.assertFalse(response.context_data['saved_as_draft'])
- self.assertTrue(response.context_data['tried_translation_ready'])
- self.assertEqual(len(response.context_data['translation_errors']), 2)
-
- # translation errors: 2 images are missing
- ltc = template_content.get_localized(self.app.primary_language)
- self.assertFalse(ltc.translation_ready)
-
-
- def test_post_form_valid_translation_ready_no_secondary_languages_present_no_publication_errors(self):
-
- for language in SecondaryAppLanguages.objects.filter(app=self.app):
- language.delete()
-
- template_content = self.create_template_content()
-
- # create test_image and multi_image
- self.create_draft_image_microcontent(template_content, 'test_image', self.app.primary_language)
- self.create_draft_image_microcontent(template_content, 'multi_image', self.app.primary_language)
-
- post_data = self.get_post_data()
-
- url = '{0}?translation-ready=1'.format(reverse('manage_template_content', kwargs=self.get_url_kwargs()))
- response = self.client.post(url, post_data)
-
- self.validate_valid_post(response, template_content)
- self.assertFalse(response.context_data['saved_as_draft'])
- self.assertTrue(response.context_data['tried_publication'])
- self.assertEqual(response.context_data['publication_errors'], [])
-
- # no publication errors
- ltc = template_content.get_localized(self.app.primary_language)
- self.assertEqual(ltc.published_version, 1)
-
-
- def test_post_form_valid_translation_ready_no_secondary_languages_present_with_publication_errors(self):
-
- for language in SecondaryAppLanguages.objects.filter(app=self.app):
- language.delete()
-
- template_content = self.create_template_content()
-
- post_data = self.get_post_data()
-
- url = '{0}?translation-ready=1'.format(reverse('manage_template_content', kwargs=self.get_url_kwargs()))
- response = self.client.post(url, post_data)
-
- self.validate_valid_post(response, template_content)
- self.assertFalse(response.context_data['saved_as_draft'])
- self.assertTrue(response.context_data['tried_publication'])
- self.assertEqual(len(response.context_data['publication_errors']), 2)
-
- # no publication errors
- ltc = template_content.get_localized(self.app.primary_language)
- self.assertEqual(ltc.published_version, None)
-
-
-@test_settings
-class TestManageTemplateContent(ManageMicroContentsMixin, WithPublishedApp, CommonSetUp, WithUser, WithApp,
- WithOnlineContent, TestCase):
-
- def test_dispatch(self):
-
- template_content = self.create_template_content()
-
- ltc = template_content.get_localized(self.app.primary_language)
-
- view, request = self.get_view(ManageTemplateContent, 'manage_template_content')
-
- view.form_class = ManageTemplateContentForm
-
- response = view.dispatch(request, **self.get_url_kwargs())
-
- self.assertEqual(response.status_code, 200)
- self.assertEqual(view.template_content, self.template_content)
- self.assertEqual(view.language, self.app.primary_language)
- self.assertEqual(view.localized_template_content, ltc)
-
-
- def test_get_context_data(self):
-
- template_content = self.create_template_content()
- localized_template_content = template_content.get_localized(self.app.primary_language)
-
- view, request = self.get_view(ManageTemplateContent, 'manage_template_content')
- view.template_content = template_content
- view.language = self.app.primary_language
- view.form_class = ManageTemplateContentForm
- view.localized_template_content = localized_template_content
-
- context = view.get_context_data(**{})
- self.assertEqual(context['localized_template_content'], localized_template_content)
- self.assertIn('/online-content/{0}'.format(localized_template_content.slug), context['preview_url'])
- self.assertTrue(context['preview'])
-
-
- def test_get_initial(self):
-
- template_content = self.create_template_content()
- localized_template_content = template_content.get_localized(self.app.primary_language)
-
- view, request = self.get_view(ManageTemplateContent, 'manage_template_content')
- view.template_content = template_content
- view.language = self.app.primary_language
- view.form_class = ManageTemplateContentForm
- view.localized_template_content = localized_template_content
-
- initial = view.get_initial()
-
- self.assertEqual(initial['draft_title'], localized_template_content.draft_title)
- self.assertEqual(initial['draft_navigation_link_name'],
- localized_template_content.draft_navigation_link_name)
- self.assertEqual(initial['input_language'], localized_template_content.language)
- self.assertEqual(initial['page_flags'], [])
-
-
- def test_form_valid(self):
-
- template_content = self.create_template_content()
- localized_template_content = template_content.get_localized(self.app.primary_language)
-
- view, request = self.get_view(ManageTemplateContent, 'manage_template_content')
- view.template_content = template_content
- view.language = self.app.primary_language
- view.form_class = ManageTemplateContentForm
- view.localized_template_content = localized_template_content
-
- post_data = self.get_post_data()
- post_data['page_flags'] = ['main_navigation']
-
- post = MockPost(post_data)
- form = view.form_class(template_content, post)
-
- form.is_valid()
-
- self.assertEqual(form.errors, {})
-
- view.form_valid(form)
-
- self.validate_saved_microcontents(template_content, form)
-
- # validate page flag add
- flag = TemplateContentFlags.objects.filter(template_content=self.template_content)
- self.assertEqual(flag.first().flag, 'main_navigation')
-
-
- # test flag deletion
- post_data['page_flags'] = []
-
- post = MockPost(post_data)
- form = view.form_class(template_content, post)
-
- form.is_valid()
-
- self.assertEqual(form.errors, {})
-
- view.form_valid(form)
- self.assertFalse(flag.exists())
-
-
-@test_settings
-class TestTranslateTemplateContent(ManageMicroContentsMixin, WithPublishedApp, CommonSetUp, WithUser, WithApp,
- WithOnlineContent, TestCase):
-
- def setUp(self):
- super().setUp()
- self.secondary_language = self.app_secondary_languages[0]
-
- def get_url_kwargs(self):
-
- url_kwargs = super().get_url_kwargs()
-
- url_kwargs['language'] = self.secondary_language
-
- return url_kwargs
-
- def test_dispatch(self):
- template_content = self.create_template_content()
-
- ltc = template_content.get_localized(self.secondary_language)
-
- view, request = self.get_view(TranslateTemplateContent, 'translate_template_content')
-
- view.form_class = TranslateTemplateContentForm
-
- response = view.dispatch(request, **self.get_url_kwargs())
-
- self.assertEqual(response.status_code, 200)
- self.assertEqual(view.template_content, self.template_content)
- self.assertEqual(view.language, self.secondary_language)
- self.assertEqual(view.localized_template_content, None)
-
-
- def test_get_context_data(self):
-
- template_content = self.create_template_content()
-
- primary_ltc = template_content.get_localized(self.app.primary_language)
-
- view, request = self.get_view(TranslateTemplateContent, 'translate_template_content')
- view.template_content = template_content
- view.language = self.secondary_language
- view.form_class = TranslateTemplateContentForm
- view.localized_template_content = None
-
- context = view.get_context_data(**{})
- self.assertEqual(context['localized_template_content'], None)
- self.assertEqual(context['source_page'], primary_ltc)
- self.assertTrue(context['preview'])
-
- def test_get_initial_no_ltc(self):
-
- template_content = self.create_template_content()
-
- primary_ltc = template_content.get_localized(self.app.primary_language)
-
- view, request = self.get_view(TranslateTemplateContent, 'translate_template_content')
- view.template_content = template_content
- view.language = self.secondary_language
- view.form_class = TranslateTemplateContentForm
- view.localized_template_content = None
-
- initial = view.get_initial()
- self.assertEqual(initial, {})
-
-
- def test_get_initial_with_ltc(self):
-
- template_content = self.create_template_content()
-
- localized_title = 'draft title en'
- localized_link_name = 'draft navigation link name en'
-
- secondary_ltc = LocalizedTemplateContent.objects.create( self.user, template_content,
- self.secondary_language, localized_title, localized_link_name)
-
- view, request = self.get_view(TranslateTemplateContent, 'translate_template_content')
- view.template_content = template_content
- view.language = self.secondary_language
- view.form_class = TranslateTemplateContentForm
- view.localized_template_content = secondary_ltc
-
- initial = view.get_initial()
- self.assertEqual(initial['draft_title'], localized_title)
- self.assertEqual(initial['draft_navigation_link_name'], localized_link_name)
-
-
- def test_get_form_kwargs(self):
-
- template_content = self.create_template_content()
-
- view, request = self.get_view(TranslateTemplateContent, 'translate_template_content')
- view.template_content = template_content
- view.language = self.secondary_language
- view.form_class = TranslateTemplateContentForm
- view.localized_template_content = None
-
- form_kwargs = view.get_form_kwargs()
- self.assertTrue(form_kwargs['for_translation'])
-
-
- def test_get_form_force_initial(self):
-
- template_content = self.create_template_content()
-
- view, request = self.get_view(TranslateTemplateContent, 'translate_template_content')
- view.template_content = template_content
- view.language = self.secondary_language
- view.form_class = TranslateTemplateContentForm
- view.localized_template_content = None
-
- form = view.get_form_force_initial()
- self.assertFalse(form.is_bound)
- self.assertEqual(form.data, {})
-
-
- def test_form_valid(self):
-
- template_content = self.create_template_content()
-
- view, request = self.get_view(TranslateTemplateContent, 'translate_template_content')
- view.template_content = template_content
- view.language = self.secondary_language
- view.form_class = TranslateTemplateContentForm
- view.localized_template_content = None
-
- post_data = self.get_post_data()
- post_data['input_language'] = self.secondary_language
- post = MockPost(post_data)
- form = view.form_class(template_content, post, for_translation=True)
-
- form.is_valid()
-
- self.assertEqual(form.errors, {})
-
- view.form_valid(form)
-
- ltc = template_content.get_localized(self.secondary_language)
- self.assertEqual(ltc.draft_title, post_data['draft_title'])
- self.assertEqual(ltc.draft_navigation_link_name, post_data['draft_navigation_link_name'])
-
- self.validate_saved_microcontents(template_content, form, language=self.secondary_language)
-
- # test with existing ltc
- post_data['simple_content'] = 'edited simple content'
- post_data['draft_title'] = 'edited draft title'
-
- view.localized_template_content = ltc
-
- post = MockPost(post_data)
- form = view.form_class(template_content, post, for_translation=True)
-
- form.is_valid()
-
- self.assertEqual(form.errors, {})
-
- view.form_valid(form)
-
- ltc.refresh_from_db()
-
- self.assertEqual(ltc.draft_title, post_data['draft_title'])
- self.assertEqual(ltc.draft_navigation_link_name, post_data['draft_navigation_link_name'])
-
- self.validate_saved_microcontents(template_content, form, language=self.secondary_language)
-
-
-@test_settings
-class TestPublishTemplateContent(ManageMicroContentsMixin, WithPublishedApp, CommonSetUp, WithUser, WithApp,
- WithOnlineContent, TestCase):
-
- def get_url_kwargs(self):
-
- url_kwargs = {
- 'app_uid' : self.app.uid,
- 'template_content_id' : self.template_content.id,
- }
-
- return url_kwargs
-
- def test_dispatch(self):
-
- template_content = self.create_template_content()
- view, request = self.get_view(PublishTemplateContent, 'publish_template_content')
-
- response = view.dispatch(request, **self.get_url_kwargs())
- self.assertEqual(response.status_code, 200)
- self.assertEqual(view.template_content, template_content)
-
- def test_get(self):
-
- template_content = self.create_template_content()
-
- response = self.client.get(reverse('publish_template_content', kwargs=self.get_url_kwargs()))
- self.assertEqual(response.status_code, 200)
- self.assertEqual(response.context_data['template_content'], template_content)
- self.assertTrue(response.context_data['publication'])
- self.assertTrue(len(response.context_data['publication_errors']) > 0)
-
- # create complete ltc
- post_data = self.get_post_data()
- post_kwargs = {
- 'app_uid' : self.app.uid,
- 'pk' : self.template_content.id,
- }
- post_url = reverse('manage_template_content', kwargs=post_kwargs)
- self.client.post(post_url, post_data)
- # create test_image and multi_image
- self.create_draft_image_microcontent(template_content, 'test_image', self.app.primary_language)
- self.create_draft_image_microcontent(template_content, 'multi_image', self.app.primary_language)
-
- for language in SecondaryAppLanguages.objects.filter(app=self.app):
- language.delete()
-
- response = self.client.get(reverse('publish_template_content', kwargs=self.get_url_kwargs()))
- self.assertEqual(response.status_code, 200)
- self.assertEqual(response.context_data['template_content'], template_content)
- self.assertTrue(response.context_data['publication'])
- self.assertEqual(response.context_data['publication_errors'], [])
-
- ltc = template_content.get_localized(self.app.primary_language)
- self.assertEqual(ltc.published_version, 1)
-
-
-@test_settings
-class TestUnpublishTemplateContent(ManageMicroContentsMixin, WithPublishedApp, CommonSetUp, WithUser, WithApp,
- WithOnlineContent, WithMedia, TestCase):
-
- def get_url_kwargs(self):
-
- url_kwargs = {
- 'app_uid' : self.app.uid,
- 'template_content_id' : self.template_content.id,
- }
-
- return url_kwargs
-
- def test_dispatch_no_ajax(self):
- template_content = self.create_template_content()
- view, request = self.get_view(UnpublishTemplateContent, 'unpublish_template_content')
-
- response = view.dispatch(request, **self.get_url_kwargs())
- self.assertEqual(response.status_code, 400)
-
-
- def test_dispatch(self):
-
- template_content = self.create_template_content()
- view, request = self.get_view(UnpublishTemplateContent, 'unpublish_template_content')
- request.META['HTTP_X_REQUESTED_WITH'] = 'XMLHttpRequest'
-
- response = view.dispatch(request, **self.get_url_kwargs())
- self.assertEqual(response.status_code, 200)
- self.assertEqual(view.template_content, template_content)
-
-
- def test_get_context_data(self):
-
- template_content = self.create_template_content()
- view, request = self.get_view(UnpublishTemplateContent, 'unpublish_template_content')
- view.template_content = template_content
-
- context = view.get_context_data(**{})
- self.assertEqual(context['template_content'], template_content)
- self.assertFalse(context['success'])
-
- def test_post(self):
-
- template_content = self.create_template_content()
-
- response = self.client.get(reverse('publish_template_content', kwargs=self.get_url_kwargs()))
-
- # create complete ltc
- post_data = self.get_post_data()
- post_kwargs = {
- 'app_uid' : self.app.uid,
- 'pk' : self.template_content.id,
- }
- post_url = reverse('manage_template_content', kwargs=post_kwargs)
- self.client.post(post_url, post_data)
- # create test_image and multi_image
- self.create_draft_image_microcontent(template_content, 'test_image', self.app.primary_language)
- self.create_draft_image_microcontent(template_content, 'multi_image', self.app.primary_language)
-
- for language in SecondaryAppLanguages.objects.filter(app=self.app):
- language.delete()
-
- ltc = template_content.get_localized(self.app.primary_language)
-
- response = self.client.get(reverse('publish_template_content', kwargs=self.get_url_kwargs()))
-
- ltc.refresh_from_db()
- self.assertEqual(ltc.published_version, 1)
-
- # test unpublish
- response = self.client.post(reverse('unpublish_template_content', kwargs=self.get_url_kwargs()), {},
- HTTP_X_REQUESTED_WITH='XMLHttpRequest')
-
- self.assertEqual(response.status_code, 200)
- self.assertTrue(response.context_data['success'])
-
- ltc.refresh_from_db()
- self.assertEqual(ltc.published_version, None)
-
-
-@test_settings
-class TestDeleteMicroContent(WithPublishedApp, CommonSetUp, WithUser, WithApp, WithOnlineContent, TestCase):
-
- def get_url_kwargs(self):
-
- url_kwargs = {
- 'app_uid' : self.app.uid,
- 'template_content_id' : self.template_content.id,
- 'language' : self.app.primary_language,
- }
-
- return url_kwargs
-
-
- def test_dispatch(self):
- template_content = self.create_template_content()
- view, request = self.get_view(DeleteMicroContent, 'DeleteMicroContent')
-
- get_data = {
- 'meta_pk' : 1,
- 'localized_pk' : 2,
- 'microcontentcategory' : 'microcontent',
- 'microcontenttype' : 'test',
- }
- request.GET = get_data
-
- response = view.dispatch(request, **self.get_url_kwargs())
- self.assertEqual(response.status_code, 200)
- self.assertEqual(view.language, self.app.primary_language)
- self.assertEqual(view.template_content, template_content)
-
-
- def test_get_context_data(self):
-
- template_content = self.create_template_content()
- view, request = self.get_view(DeleteMicroContent, 'DeleteMicroContent')
-
- view.template_content = template_content
- view.language = self.app.primary_language
-
- context = view.get_context_data(**{})
- self.assertEqual(context['template_content'], template_content)
- self.assertEqual(context['language'], self.app.primary_language)
- self.assertEqual(context['view_name'], view.__class__.__name__)
-
-
- def test_delete_microcontent_primary_language(self):
-
- template_content = self.create_template_content()
- microcontent_type = 'simple_content'
- #create microcontent
- lmc = self.create_draft_microcontent(template_content, microcontent_type, self.app.primary_language)
- microcontent = lmc.microcontent
-
- view, request = self.get_view(DeleteMicroContent, 'DeleteMicroContent')
-
- qry = DraftTextMicroContent.objects.filter(template_content=template_content,
- microcontent_type=microcontent_type)
-
- self.assertTrue(qry.exists())
-
- view.delete_microcontent(microcontent, self.app.primary_language)
-
- self.assertFalse(qry.exists())
-
-
- def test_delete_microcontent_secondary_language(self):
-
- secondary_language = 'en'
-
- template_content = self.create_template_content()
- microcontent_type = 'simple_content'
- #create microcontent
- lmc = self.create_draft_microcontent(template_content, microcontent_type, self.app.primary_language)
- microcontent = lmc.microcontent
-
- secondary_lmc = self.create_localized_draft_microcontent(microcontent, secondary_language)
-
- view, request = self.get_view(DeleteMicroContent, 'DeleteMicroContent')
-
- qry = DraftTextMicroContent.objects.filter(template_content=template_content,
- microcontent_type=microcontent_type)
-
- locale_qry = LocalizedDraftTextMicroContent.objects.filter(microcontent=microcontent,
- language=self.app.primary_language)
- secondary_locale_qry = LocalizedDraftTextMicroContent.objects.filter(microcontent=microcontent,
- language=secondary_language)
-
- self.assertTrue(qry.exists())
- self.assertTrue(locale_qry.exists())
- self.assertTrue(secondary_locale_qry.exists())
-
- view.delete_microcontent(microcontent, secondary_language)
-
- self.assertTrue(qry.exists())
- self.assertTrue(locale_qry.exists())
- self.assertFalse(secondary_locale_qry.exists())
-
-
- def test_get(self):
-
- template_content = self.create_template_content()
-
- url = reverse('DeleteMicroContent', kwargs=self.get_url_kwargs())
-
- get_data = {
- 'meta_pk' : 1,
- 'localized_pk' : 2,
- 'microcontentcategory' : 'microcontent',
- 'microcontenttype' : 'test',
- }
-
- response = self.client.get(url, get_data)
-
- self.assertEqual(response.context_data['template_content'], template_content)
- self.assertEqual(response.context_data['language'], self.app.primary_language)
- self.assertEqual(response.context_data['view_name'], 'DeleteMicroContent')
-
- form = response.context_data['form']
- self.assertEqual(form.initial['localized_pk'], str(get_data['localized_pk']))
- self.assertEqual(form.initial['microcontent_category'], get_data['microcontentcategory'])
- self.assertEqual(form.initial['microcontent_type'], get_data['microcontenttype'])
-
-
- def test_post_invalid(self):
-
- template_content = self.create_template_content()
-
- microcontent_type = 'simple_content'
-
- lmc = self.create_draft_microcontent(template_content, microcontent_type, self.app.primary_language)
- microcontent = lmc.microcontent
-
- post_data = {
- 'meta_pk' : microcontent.pk,
- 'microcontent_category' : 'microcontent',
- 'microcontent_type' : microcontent.microcontent_type,
- }
-
- url = reverse('DeleteMicroContent', kwargs=self.get_url_kwargs())
-
- response = self.client.post(url, post_data)
-
- self.assertEqual(response.status_code, 200)
- self.assertFalse(response.context_data['success'])
-
- qry = DraftTextMicroContent.objects.filter(template_content=template_content,
- microcontent_type=microcontent_type)
-
- self.assertTrue(qry.exists())
-
-
- def test_post_valid(self):
-
- template_content = self.create_template_content()
-
- ltc = template_content.get_localized(self.app.primary_language)
- ltc.translation_ready = True
- ltc.save()
-
- microcontent_type = 'simple_content'
-
- lmc = self.create_draft_microcontent(template_content, microcontent_type, self.app.primary_language)
- microcontent = lmc.microcontent
-
- post_data = {
- 'meta_pk' : microcontent.pk,
- 'localized_pk' : lmc.pk,
- 'microcontent_category' : 'microcontent',
- 'microcontent_type' : microcontent.microcontent_type,
- }
-
- url = reverse('DeleteMicroContent', kwargs=self.get_url_kwargs())
-
- response = self.client.post(url, post_data)
-
- self.assertEqual(response.status_code, 200)
- self.assertTrue(response.context_data['success'])
-
- qry = DraftTextMicroContent.objects.filter(template_content=template_content,
- microcontent_type=microcontent_type)
-
- self.assertFalse(qry.exists())
-
- ltc.refresh_from_db()
- self.assertFalse(ltc.translation_ready)
-
-
-@test_settings
-class TestDeleteFileContent(WithPublishedApp, CommonSetUp, WithUser, WithApp, WithOnlineContent, WithMedia,
- TestCase):
-
- def get_url_kwargs(self):
-
- url_kwargs = {
- 'app_uid' : self.app.uid,
- 'template_content_id' : self.template_content.id,
- 'language' : self.app.primary_language,
- }
-
- return url_kwargs
-
-
- def test_on_success(self):
-
- template_content = self.create_template_content()
-
- microcontent_type = 'image'
- #create microcontent
- limc = self.create_draft_image_microcontent(template_content, microcontent_type, self.app.primary_language)
- microcontent = limc.microcontent
-
- view, request = self.get_view(DeleteFileContent, 'DeleteFileContent')
-
- post_data = {
- 'meta_pk' : microcontent.pk,
- 'localized_pk' : limc.pk,
- 'microcontent_category' : 'image',
- 'microcontent_type' : microcontent.microcontent_type,
- }
-
- form = view.form_class(data=post_data)
-
- form.is_valid()
-
- self.assertEqual(form.errors, {})
-
- context = {}
- response = view.on_success(context, form)
-
- self.assertEqual(response.context_data['microcontent_category'], 'image')
- self.assertEqual(response.context_data['microcontent_type'], microcontent_type)
-
-
-@test_settings
-class TestUploadFile(WithPublishedApp, CommonSetUp, WithUser, WithApp, WithOnlineContent, WithMedia,
- WithImageForForm, TestCase):
-
- microcontent_type = 'image'
- microcontent_category = 'image'
-
- def get_url_kwargs(self):
-
- url_kwargs = {
- 'app_uid' : self.app.uid,
- 'template_content_id' : self.template_content.id,
- 'microcontent_category' : self.microcontent_category,
- 'microcontent_type' : self.microcontent_type,
- 'language' : self.app.primary_language,
- }
-
- return url_kwargs
-
-
- def get_post_data(self):
-
- image = self.get_image('test_image.jpg')
-
- post_data = {
- 'pk' : '',
- 'template_content_id' : self.template_content.id,
- 'language' : self.app.primary_language,
- 'file' : image,
- }
-
- return post_data
-
-
- def test_post_invalid(self):
-
- template_content = self.create_template_content()
-
- limc = self.create_draft_image_microcontent(template_content, self.microcontent_type,
- self.app.primary_language)
-
- url = reverse('upload_image', kwargs=self.get_url_kwargs())
-
- post_data = self.get_post_data()
- del post_data['file']
-
- response = self.client.post(url, post_data, HTTP_X_REQUESTED_WITH='XMLHttpRequest')
- self.assertEqual(response.status_code, 200)
- self.assertEqual(response.context_data['template_content'], template_content)
- self.assertEqual(response.context_data['language'], self.app.primary_language)
- self.assertEqual(response.context_data['form'].__class__, UploadImage.form_class)
- self.assertFalse(response.context_data['form'].is_valid())
-
-
- def test_post_valid(self):
-
- template_content = self.create_template_content()
-
- limc = self.create_draft_image_microcontent(template_content, self.microcontent_type,
- self.app.primary_language)
-
- url = reverse('upload_image', kwargs=self.get_url_kwargs())
-
- post_data = self.get_post_data()
-
- response = self.client.post(url, post_data, HTTP_X_REQUESTED_WITH='XMLHttpRequest')
- self.assertEqual(response.status_code, 200)
- self.assertEqual(response.context_data['template_content'], template_content)
- self.assertEqual(response.context_data['language'], self.app.primary_language)
- self.assertEqual(response.context_data['form'].__class__, UploadImage.form_class)
- self.assertTrue(response.context_data['form'].is_valid())
-
- qry = DraftImageMicroContent.objects.filter(template_content=template_content)
- self.assertTrue(qry.exists())
-
- microcontent = qry.first()
- locale_qry = LocalizedDraftImageMicroContent.objects.filter(microcontent=microcontent)
-
- self.assertTrue(locale_qry.exists())
-
-
-@test_settings
-class TestManageImageUpload(WithPublishedApp, CommonSetUp, WithUser, WithApp, WithOnlineContent, WithMedia,
- WithImageForForm, TestCase):
-
- microcontent_category = 'image'
- microcontent_type = 'image'
-
- def get_url_kwargs(self):
-
- url_kwargs = {
- 'app_uid' : self.app.uid,
- 'template_content_id' : self.template_content.id,
- 'microcontent_category' : self.microcontent_category,
- 'language' : self.app.primary_language,
- }
-
- if self.microcontent:
- url_kwargs['microcontent_id'] = self.microcontent.id
- else:
- url_kwargs['microcontent_type'] = self.microcontent_type
-
- return url_kwargs
-
-
- def get_post_data(self):
-
- post_data = {
- 'creator_name' : 'image creator',
- 'licence_0' : 'CC0',
- 'licence_1' : '1.0',
- 'template_content_id' : self.template_content.id,
- 'source_image' : self.get_image('test_image.jpg'),
- 'image_type' : self.microcontent_type,
- }
-
- return post_data
-
-
- def test_dispatch(self):
-
- template_content = self.create_template_content()
- self.microcontent = None
-
- view, request = self.get_view(ManageImageUpload, 'upload_licenced_image')
- request.META['HTTP_X_REQUESTED_WITH'] = 'XMLHttpRequest'
-
- response = view.dispatch(request, **self.get_url_kwargs())
-
- self.assertEqual(response.status_code, 200)
- self.assertEqual(view.language, self.app.primary_language)
- self.assertEqual(view.template_content, template_content)
- self.assertEqual(view.microcontent_category, self.microcontent_category)
- self.assertEqual(view.localized_instance, None)
- self.assertEqual(view.meta_instance, None)
- self.assertEqual(view.MetaModel, DraftImageMicroContent)
- self.assertEqual(view.LocaleModel, LocalizedDraftImageMicroContent)
-
-
-
- def test_dispatch_with_microcontent_id(self):
-
- template_content = self.create_template_content()
-
- limc = self.create_draft_image_microcontent(template_content, self.microcontent_type,
- self.app.primary_language)
- self.microcontent = limc.microcontent
-
- view, request = self.get_view(ManageImageUpload, 'update_licenced_image')
- request.META['HTTP_X_REQUESTED_WITH'] = 'XMLHttpRequest'
-
- url_kwargs = self.get_url_kwargs()
-
- response = view.dispatch(request, **url_kwargs)
-
- self.assertEqual(response.status_code, 200)
- self.assertEqual(view.language, self.app.primary_language)
- self.assertEqual(view.template_content, template_content)
- self.assertEqual(view.microcontent_category, self.microcontent_category)
- self.assertEqual(view.localized_instance, limc)
- self.assertEqual(view.meta_instance, self.microcontent)
- self.assertEqual(view.MetaModel, DraftImageMicroContent)
- self.assertEqual(view.LocaleModel, LocalizedDraftImageMicroContent)
-
- def test_get_context_data(self):
-
- template_content = self.create_template_content()
- self.microcontent = None
-
- view, request = self.get_view(ManageImageUpload, 'upload_licenced_image')
- view.template_content = template_content
- view.microcontent_category = self.microcontent_category
- view.microcontent_type = self.microcontent_type
- view.meta_instance = None
- view.localized_instance = None
- view.language = self.app.primary_language
-
- context = view.get_context_data(**{})
-
- self.assertEqual(context['language'], self.app.primary_language)
- self.assertEqual(context['template_content'], template_content)
- self.assertEqual(context['microcontent_category'], self.microcontent_category)
- self.assertEqual(context['microcontent_type'], self.microcontent_type)
- self.assertEqual(context['localized_instance'], None)
- self.assertEqual(context['meta_instance'], None)
-
-
- def test_get_initial(self):
-
- template_content = self.create_template_content()
-
- limc = self.create_draft_image_microcontent(template_content, self.microcontent_type,
- self.app.primary_language)
- self.microcontent = limc.microcontent
-
- view, request = self.get_view(ManageImageUpload, 'update_licenced_image')
- view.localized_instance = limc
- view.licence_registry_entry = None
-
- initial = view.get_initial()
-
- self.assertEqual(initial['source_image'], limc.content)
-
-
- def test_get_form_kwargs(self):
-
- template_content = self.create_template_content()
-
- limc = self.create_draft_image_microcontent(template_content, self.microcontent_type,
- self.app.primary_language)
- self.microcontent = limc.microcontent
-
- view, request = self.get_view(ManageImageUpload, 'update_licenced_image')
- view.localized_instance = limc
- view.licence_registry_entry = None
-
- form_kwargs = view.get_form_kwargs()
-
- self.assertEqual(form_kwargs['current_image'], limc.content)
-
-
- def test_form_valid(self):
-
- template_content = self.create_template_content()
-
- self.microcontent = None
-
- url = reverse('upload_licenced_image', kwargs=self.get_url_kwargs())
-
- post_data = self.get_post_data()
-
- response = self.client.post(url, post_data, HTTP_X_REQUESTED_WITH='XMLHttpRequest')
- self.assertEqual(response.status_code, 200)
- form = response.context_data['form']
- self.assertEqual(form.errors, {})
-
- qry = DraftImageMicroContent.objects.filter(template_content=template_content,
- microcontent_type=self.microcontent_type)
- self.assertTrue(qry.exists())
-
- microcontent = qry.first()
- locale_qry = LocalizedDraftImageMicroContent.objects.filter(microcontent=microcontent)
-
- self.assertTrue(locale_qry.exists())
-
- def test_form_valid_update(self):
-
- template_content = self.create_template_content()
-
- limc = self.create_draft_image_microcontent(template_content, self.microcontent_type,
- self.app.primary_language)
-
- self.microcontent = limc.microcontent
-
- url = reverse('update_licenced_image', kwargs=self.get_url_kwargs())
-
- post_data = self.get_post_data()
-
- response = self.client.post(url, post_data, HTTP_X_REQUESTED_WITH='XMLHttpRequest')
- self.assertEqual(response.status_code, 200)
- form = response.context_data['form']
- self.assertEqual(form.errors, {})
-
- qry = DraftImageMicroContent.objects.filter(template_content=template_content,
- microcontent_type=self.microcontent_type)
-
- self.assertEqual(len(qry), 1)
- self.assertEqual(qry.first(), self.microcontent)
-
- locale_qry = LocalizedDraftImageMicroContent.objects.filter(microcontent=self.microcontent)
-
- self.assertTrue(locale_qry.exists())
-
-
-@test_settings
-class TestGetFormField(WithPublishedApp, CommonSetUp, WithUser, WithApp, WithOnlineContent, WithMedia,
- WithImageForForm, TestCase):
-
- microcontent_category = 'image'
- microcontent_type = 'test_image'
-
- def get_url_kwargs(self):
-
- url_kwargs = {
- 'app_uid' : self.app.uid,
- 'template_content_id' : self.template_content.id,
- 'microcontent_category' : self.microcontent_category,
- 'microcontent_type' : self.microcontent_type,
- 'language' : self.app.primary_language
- }
-
- return url_kwargs
-
-
- def test_dispatch(self):
-
- template_content = self.create_template_content()
-
- view, request = self.get_view(GetFormField, 'get_form_field')
- request.META['HTTP_X_REQUESTED_WITH'] = 'XMLHttpRequest'
-
- response = view.dispatch(request, **self.get_url_kwargs())
-
- self.assertEqual(response.status_code, 200)
- self.assertEqual(view.language, self.app.primary_language)
- self.assertEqual(view.template_content, template_content)
- self.assertEqual(view.microcontent_category, self.microcontent_category)
- self.assertEqual(view.microcontent_type, self.microcontent_type)
-
-
- def test_get_context_data(self):
-
- template_content = self.create_template_content()
-
- view, request = self.get_view(GetFormField, 'get_form_field')
- view.language = self.app.primary_language
- view.template_content = template_content
- view.microcontent_category = self.microcontent_category
- view.microcontent_type = self.microcontent_type
-
- context = view.get_context_data(**{})
- self.assertEqual(context['language'], self.app.primary_language)
- self.assertEqual(context['template_content'], template_content)
-
- def test_get_form(self):
-
- template_content = self.create_template_content()
-
- view, request = self.get_view(GetFormField, 'get_form_field')
- view.language = self.app.primary_language
- view.template_content = template_content
- view.microcontent_type = self.microcontent_type
- view.microcontent_category = self.microcontent_category
-
- form = view.get_form()
-
- self.assertTrue('test_image' in form.fields)
diff --git a/localcosmos_server/online_content/urls.py b/localcosmos_server/online_content/urls.py
deleted file mode 100644
index 4f6e830..0000000
--- a/localcosmos_server/online_content/urls.py
+++ /dev/null
@@ -1,46 +0,0 @@
-from django.urls import path, re_path
-
-from django.contrib.auth.decorators import login_required
-
-from . import views
-
-
-urlpatterns = [
- path('/manage-online-content/',
- login_required(views.ManageOnlineContent.as_view()), name='manage_onlinecontent'),
- path('/create-template-content//',
- login_required(views.CreateTemplateContent.as_view()), name='create_template_content'),
- path('/manage-template-content//', login_required(views.ManageTemplateContent.as_view()),
- name='manage_template_content'),
- # translating template content
- path('/translate-template-content///',
- login_required(views.TranslateTemplateContent.as_view()), name='translate_template_content'),
- # publishing template content
- path('/publish-template-content//',
- login_required(views.PublishTemplateContent.as_view()), name='publish_template_content'),
- path('/unpublish-template-content//',
- login_required(views.UnpublishTemplateContent.as_view()), name='unpublish_template_content'),
- # DELETING CONTENT
- path('/delete-templatecontent//',
- login_required(views.DeleteTemplateContent.as_view()), name='delete_template_content'),
- path('/delete-microcontent///',
- login_required(views.DeleteMicroContent.as_view()), name='DeleteMicroContent'),
- path('/delete-filecontent///',
- login_required(views.DeleteFileContent.as_view()), name='DeleteFileContent'),
- # IMAGES - template content required
- path('/online-content/upload-image/////',
- login_required(views.UploadImage.as_view()), name='upload_image'),
- # LICENCED IMAGES
- # upload new image
- path('/online-content/upload-licenced-image/////',
- login_required(views.ManageImageUpload.as_view()), name='upload_licenced_image'),
- # edit localized image or upload new locale
- path('/online-content/update-licenced-image/////',
- login_required(views.ManageImageUpload.as_view()), name='update_licenced_image'),
- # GET EMPTY FIELDS
- path('/online-content/get-form-field/////',
- login_required(views.GetFormField.as_view()), name='get_form_field'),
- # Upload custom template
- path('/upload-custom-template/', login_required(views.UploadCustomTemplate.as_view()),
- name='upload_custom_template'),
-]
diff --git a/localcosmos_server/online_content/utils.py b/localcosmos_server/online_content/utils.py
deleted file mode 100644
index bf8732e..0000000
--- a/localcosmos_server/online_content/utils.py
+++ /dev/null
@@ -1,4 +0,0 @@
-def verbosify_template_name(template_name):
- verbose_name = ' '.join(template_name.replace('.html','').split('/')[-1].split('_')).capitalize()
-
- return verbose_name
diff --git a/localcosmos_server/online_content/views.py b/localcosmos_server/online_content/views.py
deleted file mode 100644
index 3d0052e..0000000
--- a/localcosmos_server/online_content/views.py
+++ /dev/null
@@ -1,881 +0,0 @@
-from django.shortcuts import render, redirect
-from django.views.generic import TemplateView
-from django.views.generic.edit import FormView
-from django.conf import settings
-from django.urls import reverse
-from django import forms
-from django.utils.translation import gettext as _
-from django.contrib.contenttypes.models import ContentType
-
-from localcosmos_server.decorators import ajax_required
-from django.utils.decorators import method_decorator
-
-from localcosmos_server.generic_views import AjaxDeleteView
-
-from .mixins import OnlineContentMixin
-
-from .forms import (CreateTemplateContentForm, ManageTemplateContentForm, DeleteMicroContentForm, UploadFileForm,
- UploadImageForm, UploadImageWithLicenceForm, TranslateTemplateContentForm,
- UploadCustomTemplateForm)
-
-from .models import (TemplateContent, LocalizedTemplateContent, TemplateContentFlags,
- microcontent_category_model_map)
-
-from .CMSObjects import CMSTag
-
-import os
-
-
-class ManageOnlineContent(OnlineContentMixin, TemplateView):
-
- template_name = 'online_content/online_content_base.html'
-
- def get_context_data(self, **kwargs):
- context = super().get_context_data(**kwargs)
-
- pages = TemplateContent.objects.filter(app=self.app, template_type='page')
- context['pages'] = pages
-
- features = TemplateContent.objects.filter(app=self.app, template_type='feature')
- context['features'] = features
- return context
-
-
-'''
- Creating a template_content consists of
- - selecting a template
- - supplying a title
- - the title is always in the current language
-'''
-class CreateTemplateContent(OnlineContentMixin, FormView):
-
- template_name = 'online_content/create_template_content.html'
- form_class = CreateTemplateContentForm
-
- def get_context_data(self, **kwargs):
- context = super().get_context_data(**kwargs)
- context['template_type'] = self.kwargs['template_type']
- return context
-
-
- def get_form_kwargs(self):
- form_kwargs = super().get_form_kwargs()
- form_kwargs['language'] = self.app.primary_language
- return form_kwargs
-
-
- def get_form(self, form_class=None):
- """Return an instance of the form to be used in this view."""
- if form_class is None:
- form_class = self.get_form_class()
- return form_class(self.app, self.kwargs['template_type'], **self.get_form_kwargs())
-
-
- def form_valid(self, form):
- # create a new template_content for this online content (which is app specific)
-
- template_content = TemplateContent.objects.create(
- self.request.user,
- self.app,
- self.app.primary_language,
- form.cleaned_data['draft_title'],
- form.cleaned_data['draft_navigation_link_name'],
- form.cleaned_data['template_name'],
- self.kwargs['template_type'],
- )
-
- template_content.save()
-
- return redirect('manage_template_content', app_uid=self.app.uid, pk=template_content.pk)
-
-
-
-'''
- Manage a Localized TemplateContent
- - The template is read and the content elements which the user has to fill are detected and presented in a form
-'''
-
-'''
- ManageMicroContents
- - Abstract View
- - Superclass for ManageTemplateContent and TranslateTemplateContent
- - self.language is not always the apps primary language, it can be the language of the translation
-'''
-class ManageMicroContents(OnlineContentMixin, FormView):
-
- template_name = 'online_content/manage_template_content.html'
-
- empty_text_microcontent_values = ['', '
', None] #
is an empty ckeditor field
-
- def dispatch(self, request, *args, **kwargs):
- self.set_template_content(request, *args, **kwargs)
- self.set_language(request, *args, **kwargs)
- return super().dispatch(request, *args, **kwargs)
-
-
- def set_template_content(self, request, *args, **kwargs):
- if not hasattr(self, 'template_content'):
- self.template_content = TemplateContent.objects.get(pk=kwargs['pk'])
-
- def set_language(self, request, *args, **kwargs):
- if not hasattr(self, 'language'):
- self.language = kwargs.get('language', self.template_content.app.primary_language)
-
- def get_context_data(self, **kwargs):
- context = super().get_context_data(**kwargs)
- context['language'] = self.language
- context['template_content'] = self.template_content
- return context
-
- # this only saves in the draft models
- def _save_content(self, template_content, language, field, content, user):
- if hasattr(field, 'meta_instance') and field.meta_instance.pk:
- # translations always have the meta_instance, so no new meta_instance is created
- field.meta_instance.set_content(content, user, language)
- else:
- # the meta instance is created, which also triggers the creation of localized_instance
- # this should only be triggered if NOT translating
- meta_instance = field.cms_object.Model.objects.create(
- template_content,
- language,
- field.cms_object.microcontent_type,
- content,
- user,
- )
-
- # a multifield cant have ONE instance
- #if field.cms_object.multi == False:
- # field.instance = instance
-
- def get_form_kwargs(self):
- form_kwargs = super().get_form_kwargs()
- form_kwargs['language'] = self.language
- return form_kwargs
-
- def get_form(self):
- return self.form_class(self.template_content, **self.get_form_kwargs())
-
- # needed for reloading a clean form after POSTing
- def get_form_force_initial(self):
- form_kwargs = {
- 'initial' : self.get_initial(),
- 'language' : self.language,
- }
- return self.form_class(self.template_content, **form_kwargs)
-
-
- def save_localized_template_content(self, form):
-
- # translation_ready can only be set AFTER the microcontents have been saved
- # which is done after localized_template_content is saved
-
- self.localized_template_content.translation_ready = False
- self.localized_template_content.draft_title = form.cleaned_data['draft_title']
- self.localized_template_content.draft_navigation_link_name = form.cleaned_data['draft_navigation_link_name']
- self.localized_template_content.last_modified_by = self.request.user
- self.localized_template_content.save()
-
- secondary_languages = self.app.secondary_languages()
-
- if secondary_languages:
- # if the language is the primary language -> unready all other translations
- if self.localized_template_content.language == self.app.primary_language:
-
- for language in secondary_languages:
- ltc = LocalizedTemplateContent.objects.filter(template_content=self.template_content,
- language=language).first()
- if ltc:
- ltc.translation_ready = False
- ltc.save()
-
- # save the microcontent text fields
- def save_microcontent_fields(self, form, for_translation=False):
-
- language = form.cleaned_data['input_language']
-
- for field_ in form:
-
- field = field_.field
- if hasattr(field, 'cms_object'):
-
- data = form.cleaned_data[field_.name]
-
- if data and type(data) in [str, list] and len(data) > 0 and data not in self.empty_text_microcontent_values:
-
- if type(data) == list:
- for content in data:
- self._save_content(self.template_content, language, field, content, self.request.user)
- else:
- self._save_content(self.template_content, language, field, data, self.request.user)
-
- else:
- # do not delete image fields
- if field.cms_object.microcontent_category not in ['image', 'images']:
- # the user has submitted an empty field
-
- # only delete the locale during translation
- if for_translation == True:
- if hasattr(field, 'localized_instance') and field.localized_instance.pk:
- field.localized_instance.delete()
- else:
- # delete the field together with all translations
- # if the user edits the primary language
- if hasattr(field, 'meta_instance') and field.meta_instance.pk:
- field.meta_instance.delete()
-
-
- def post(self, request, *args, **kwargs):
- context = {
- 'saved_as_draft' : False,
- }
-
- form = self.get_form()
-
- if form.is_valid():
-
- # this saves localized_template_content (creates if necessary AND it is a translation)
- # and then the microcontents
- # now, translation readyness can be checked
- self.form_valid(form)
-
- # optionally publish the template_content - if there are no secondary languages
- secondary_languages = self.app.secondary_languages()
- # if there are no secondary languages and translation-ready has been set, publish right away
- if 'translation-ready' in self.request.GET:
-
- if not secondary_languages:
- # only one language exists -> try publication
- publication_errors = self.localized_template_content.template_content.publish()
- context['tried_publication'] = True
- context['publication_errors'] = publication_errors
-
- else:
- # more than one language present -> try to set translation_complete
- translation_errors = self.localized_template_content.translation_complete()
- context['tried_translation_ready'] = True
- context['translation_errors'] = translation_errors
-
- if not translation_errors:
- self.localized_template_content.translation_ready = True
- self.localized_template_content.save()
-
- else:
- context['saved_as_draft'] = True
-
- # necessary but not nice
- # using get_form_kwargs() will add 'data' and 'files' to the form
- # the form will then ignore the field.initial setting set by CMSObject
- form = self.get_form_force_initial()
-
- context.update(self.get_context_data(**kwargs))
- context['form'] = form
- return self.render_to_response(context)
-
-
-'''
- fill a template content with microcontents in the primary language
-'''
-class ManageTemplateContent(ManageMicroContents):
-
- form_class = ManageTemplateContentForm
-
- def dispatch(self, request, *args, **kwargs):
- self.set_template_content(request, *args, **kwargs)
- self.set_language(request, *args, **kwargs)
-
- # this is not for translations, so the localized template content exists
- self.localized_template_content = LocalizedTemplateContent.objects.get(
- template_content=self.template_content, language=self.language)
-
- # update preview_token if necessary
- token_is_valid = self.localized_template_content.validate_preview_token(
- self.localized_template_content.preview_token, 5) # 5 is maxminutes
-
- if token_is_valid == False:
- self.localized_template_content.update_preview_token()
-
- return super().dispatch(request, *args, **kwargs)
-
-
- def get_context_data(self, **kwargs):
- context = super().get_context_data(**kwargs)
-
- # make sure all data is up to date (!important)
- self.localized_template_content.refresh_from_db()
- context['localized_template_content'] = self.localized_template_content
-
-
- app_preview_url_suffix = '/online-content/%s/%s/' % (self.localized_template_content.slug,
- self.localized_template_content.preview_token)
-
- # the relative preview url
- unschemed_preview_url = '{0}#{1}'.format(self.app.get_preview_url(), app_preview_url_suffix)
-
- # the host where the preview is served. on LCOS it is simply the website
- if unschemed_preview_url.startswith('http://') or unschemed_preview_url.startswith('https://'):
- preview_url = unschemed_preview_url
- else:
- preview_url = '{0}://{1}'.format(self.request.scheme, unschemed_preview_url)
-
- context['preview_url'] = preview_url
- context['preview'] = True
-
- return context
-
-
- def get_initial(self):
-
- initial = {
- 'draft_title' : self.localized_template_content.draft_title,
- 'draft_navigation_link_name' : self.localized_template_content.draft_navigation_link_name,
- 'input_language' : self.localized_template_content.language,
- 'page_flags' : self.localized_template_content.flags(),
- }
- return initial
-
-
- def form_valid(self, form):
- # save the template_content, publication errors
- self.save_localized_template_content(form)
-
- flags = form.cleaned_data.get('page_flags', [])
- for page_flag in TemplateContentFlags.objects.filter(template_content=self.template_content):
- if page_flag.flag in flags:
- # remove from add_list
- flags.pop(flags.index(page_flag.flag))
- else:
- # delete db entry
- page_flag.delete()
-
- for f in flags:
- page_flag = TemplateContentFlags(
- template_content = self.template_content,
- flag = f,
- )
- page_flag.save()
-
- # save the microcontent
- self.save_microcontent_fields(form)
-
-
-
-'''
- above each field the source text/image needs to be shown
-'''
-class TranslateTemplateContent(ManageMicroContents):
-
- template_name = 'online_content/translate_template_content.html'
- form_class = TranslateTemplateContentForm
-
- def dispatch(self, request, *args, **kwargs):
- self.set_template_content(request, *args, **kwargs)
-
- # set_language reads the language from the kwargs first - in this case the target language
- self.set_language(request, *args, **kwargs)
-
- # fetch the new localized template_content if it already exists
- self.localized_template_content = LocalizedTemplateContent.objects.filter(
- template_content=self.template_content, language=self.language).first()
-
- return super().dispatch(request, *args, **kwargs)
-
-
- def get_context_data(self, **kwargs):
- context = super().get_context_data(**kwargs)
- context['source_page'] = LocalizedTemplateContent.objects.get(
- language=self.app.primary_language, template_content=self.template_content)
-
- context['localized_template_content'] = self.localized_template_content
-
- # always show the draft content for translations
- context['preview'] = True
- return context
-
-
- # initial has to be overridden - localized_template_content mighjt not exist
- # initial['input_language'] is set by the LocalizeableForm and get_form_kwargs which
- # sets form_kwargs['language'] = self.language
- def get_initial(self):
- initial = {}
-
- if self.localized_template_content:
- initial['draft_title'] = self.localized_template_content.draft_title
- initial['draft_navigation_link_name'] = self.localized_template_content.draft_navigation_link_name
-
- return initial
-
-
- def get_form_kwargs(self):
- form_kwargs = super().get_form_kwargs()
- form_kwargs['for_translation'] = True
- return form_kwargs
-
-
- # needed for reloading a clean form after POSTing
- # override to inlude for_translation
- def get_form_force_initial(self):
- form_kwargs = {
- 'initial' : self.get_initial(),
- 'language' : self.language,
- 'for_translation' : True,
- }
- return self.form_class(self.template_content, **form_kwargs)
-
-
- def form_valid(self, form):
-
- # self.language is the target language
-
- # fetch or create the content
- self.localized_template_content = LocalizedTemplateContent.objects.filter(
- template_content=self.template_content, language=self.language).first()
-
- if not self.localized_template_content:
- self.localized_template_content = LocalizedTemplateContent.objects.create(
- self.request.user, self.template_content, self.language, form.cleaned_data['draft_title'],
- form.cleaned_data['draft_navigation_link_name'])
-
- # save the template_content
- self.save_localized_template_content(form)
-
- self.save_microcontent_fields(form, for_translation=True)
-
-
-'''
- publish all languages at once, or one language
-'''
-class PublishTemplateContent(OnlineContentMixin, TemplateView):
-
- template_name = 'online_content/template_content_list_entry.html'
-
- def dispatch(self, request, *args, **kwargs):
-
- self.template_content = TemplateContent.objects.get(pk=kwargs['template_content_id'])
- self.language = kwargs.get('language', 'all')
-
- return super().dispatch(request, *args, **kwargs)
-
- def get(self, request, *args, **kwargs):
- context = self.get_context_data(**kwargs)
- context['template_content'] = self.template_content
- context['publication'] = True
- context['publication_errors'] = self.template_content.publish(language=self.language)
-
- return self.render_to_response(context)
-
-
-class DeleteTemplateContent(AjaxDeleteView):
- model = TemplateContent
-
-
-class UnpublishTemplateContent(OnlineContentMixin, TemplateView):
-
- template_name = 'online_content/ajax/unpublish_template_content.html'
-
- @method_decorator(ajax_required)
- def dispatch(self, request, *args, **kwargs):
- self.template_content = TemplateContent.objects.get(pk=kwargs['template_content_id'])
- return super().dispatch(request, *args, **kwargs)
-
-
- def get_context_data(self, **kwargs):
- context = super().get_context_data(**kwargs)
- context['template_content'] = self.template_content
- context['success'] = False
- return context
-
-
- def post(self, request, *args, **kwargs):
- self.template_content.unpublish()
- context = self.get_context_data(**kwargs)
- context['success'] = True
- return self.render_to_response(context)
-
-
-"""
- this deletes draft_content of Textarea or TextInput fields, including CKEditor
- ajax is not used
-"""
-class DeleteMicroContent(OnlineContentMixin, TemplateView):
-
- template_name = 'online_content/delete_microcontent.html'
- form_class = DeleteMicroContentForm
-
- def dispatch(self, request, *args, **kwargs):
- self.kwargs = kwargs
- self.request = request
-
- self.language = kwargs['language']
-
- template_content_id = kwargs.get('template_content_id', None)
- self.template_content = None
- if template_content_id is not None:
- self.template_content = TemplateContent.objects.get(pk=template_content_id)
-
- return super().dispatch(request, *args, **kwargs)
-
- def get_context_data(self, **kwargs):
- context = super().get_context_data(**kwargs)
- context['template_content'] = self.template_content
- context['language'] = self.language
- context['view_name'] = self.__class__.__name__
- return context
-
-
- def get(self, request, *args, **kwargs):
- context = self.get_context_data(**kwargs)
-
- initial = {
- 'meta_pk' : request.GET['meta_pk'],
- 'localized_pk' : request.GET['localized_pk'],
- 'microcontent_category' : request.GET['microcontentcategory'],
- 'microcontent_type' : request.GET.get('microcontenttype', None),
- }
-
- form = self.form_class(initial=initial)
-
- context.update({
- 'form' : form,
- })
-
- return self.render_to_response(context)
-
-
- def delete_microcontent(self, microcontent, language):
- # decide if only a translation is deleted - or if the content is deleted
- # if the language == primary_language we are not in the translation process, but
- # the main editor is editing the page -> delete microcontent and not localized_microcontent
- primary_language = microcontent.template_content.app.primary_language
-
- if language == primary_language:
- microcontent.delete()
- else:
- localized_microcontent = microcontent.get_localized(language)
- localized_microcontent.delete()
-
-
- def post(self, request, *args, **kwargs):
- context = self.get_context_data(**kwargs)
-
- form = self.form_class(request.POST)
-
- success = False
-
- if form.is_valid():
-
- # published content has no delete button, it can only be unpublished
- Model = microcontent_category_model_map[form.cleaned_data['microcontent_category']]['draft']
- microcontent = Model.objects.filter(pk=form.cleaned_data['meta_pk']).first()
-
- if microcontent:
- localized_template_content = microcontent.template_content.get_localized(self.language)
-
- self.delete_microcontent(microcontent, self.language)
-
- # unready the translation
- localized_template_content.translation_ready = False
- localized_template_content.save()
-
- success = True
-
- context['success'] = success
- self.on_success(context, form)
-
- context['form'] = form
- context['success'] = success
-
- return self.render_to_response(context)
-
-
- def on_success(self, context, form):
- pass
-
-
-
-# files are handled via ajax
-class DeleteFileContent(DeleteMicroContent):
-
-
- def on_success(self, context, form):
-
- microcontent_category = form.cleaned_data['microcontent_category']
- microcontent_type = form.cleaned_data['microcontent_type']
-
- context['microcontent_category'] = microcontent_category
- context['microcontent_type'] = microcontent_type
-
- return self.render_to_response(context)
-
-
-"""
- ajax upload file
- - in the future, language specific files should be possible
- - essential view kwargs are app_uid and template_content_id
- - the form contains [pk],[template_content_id], language, file
- - the url kwargs contain microcontent_type, microcontent_category, language
-"""
-class UploadFile(OnlineContentMixin, TemplateView):
-
- template_name = 'online_content/filecontent_field_form.html'
-
- form_class = UploadFileForm
-
- @method_decorator(ajax_required)
- def dispatch(self, request, *args, **kwargs):
- self.template_content = TemplateContent.objects.get(pk=kwargs['template_content_id'])
- return super().dispatch(request, *args, **kwargs)
-
- def get_context_data(self, **kwargs):
- context = super().get_context_data(**kwargs)
-
- context['template_content'] = self.template_content
-
- return context
-
- def post(self, request, *args, **kwargs):
-
- microcontent_type = kwargs['microcontent_type'] # the microcontent_type as defined in the template
- microcontent_category = kwargs['microcontent_category']
-
- # cms_tag
- cms_tag = CMSTag(self.app, self.template_content, microcontent_category, microcontent_type)
- widget_attrs = cms_tag._get_widget_attrs()
-
- # Model is the Meta model: DraftImageMicroContent
- Model = microcontent_category_model_map[microcontent_category]['draft']
-
- # if the form is invalid, language cannot be read from the form
- language = kwargs['language']
-
- # try to get the meta_instance
- meta_instance_pk = request.POST.get('pk', None)
-
- meta_instance = None
- if meta_instance_pk:
- meta_instance = Model.objects.get(pk=meta_instance_pk)
-
-
- form = self.form_class(request.POST, request.FILES)
-
- if form.is_valid():
- pk = form.cleaned_data.get('pk', None)
- file = form.cleaned_data['file']
-
- language = form.cleaned_data['language']
-
- if meta_instance is not None:
- meta_instance.set_content(file, request.user, language)
-
- else:
- meta_instance = Model.objects.create(self.template_content, language, microcontent_type, file,
- request.user)
-
- localized_instance = meta_instance.get_localized(language)
-
- field = cms_tag._create_field(language, meta_instance, localized_instance, widget_attrs=widget_attrs)
-
- # unready the localized translation/start a new version if published
- localized_template_content = self.template_content.get_localized(language)
- localized_template_content.save()
-
- else:
- if meta_instance is None:
- meta_instance = Model()
- localized_instance = cms_tag.get_empty_localized_instance()
- else:
- localized_instance = meta_instance.get_localized(language)
- field = cms_tag._create_field(language, meta_instance, localized_instance, widget_attrs=widget_attrs)
-
- fieldform = forms.Form()
- fieldform.fields[field['name']] = field['field']
-
- # fields do not render outside forms, we have to pass a form to the template
-
- context = self.get_context_data(**kwargs)
- context.update({
- 'fieldform' : fieldform,
- 'form' : form,
- 'language' : language,
- })
-
- return self.render_to_response(context)
-
-
-class UploadImage(UploadFile):
- form_class = UploadImageForm
-
-
-'''
- ManageFileUpload
- - a two-step process: opens a modal with image+licence input
- - requires the input of licences
-'''
-from content_licencing.view_mixins import LicencingFormViewMixin
-class ManageImageUpload(LicencingFormViewMixin, OnlineContentMixin, FormView):
-
- template_name = 'online_content/ajax/image_microcontent_form.html'
- form_class = UploadImageWithLicenceForm
-
- @method_decorator(ajax_required)
- def dispatch(self, request, *args, **kwargs):
-
- self.microcontent_category = kwargs['microcontent_category']
-
- self.MetaModel = microcontent_category_model_map[self.microcontent_category]['draft']
- self.LocaleModel = self.MetaModel.get_locale_model()
-
- self.template_content = None
- self.localized_instance = None
- self.meta_instance = None
- self.language = kwargs['language']
-
- if 'microcontent_id' in kwargs:
- self.meta_instance = self.MetaModel.objects.filter(pk=kwargs['microcontent_id']).first()
- self.localized_instance = self.meta_instance.get_localized(self.language)
- self.template_content = self.meta_instance.template_content
- self.microcontent_type = self.meta_instance.microcontent_type
- else:
- self.microcontent_type = kwargs['microcontent_type']
- self.template_content = TemplateContent.objects.get(pk=kwargs['template_content_id'])
-
- self.set_licence_registry_entry(self.localized_instance, 'content')
- return super().dispatch(request, *args, **kwargs)
-
-
- def get_context_data(self, **kwargs):
- context = super().get_context_data(**kwargs)
- context['template_content'] = self.template_content
- context['microcontent_category'] = self.microcontent_category
- context['microcontent_type'] = self.microcontent_type
- context['meta_instance'] = self.meta_instance
- context['localized_instance'] = self.localized_instance
- context['language'] = self.language
- return context
-
-
- def get_initial(self):
- initial = super().get_initial()
-
- if self.localized_instance:
- initial['source_image'] = self.localized_instance.content
- licencing_initial = self.get_licencing_initial()
- initial.update(licencing_initial)
-
- return initial
-
-
- def get_form_kwargs(self):
- form_kwargs = super().get_form_kwargs()
- if self.localized_instance:
- form_kwargs['current_image'] = self.localized_instance.content
- return form_kwargs
-
-
- def form_valid(self, form):
-
- image_file = form.cleaned_data['source_image']
-
- if not self.meta_instance:
- # this creates the localized_instance
- self.meta_instance = self.MetaModel.objects.create(self.template_content, self.language,
- self.microcontent_type, image_file, self.request.user)
-
- else:
- if not self.localized_instance or self.localized_instance.content != image_file:
- self.meta_instance.set_content(image_file, self.request.user, self.language)
-
- self.localized_instance = self.meta_instance.get_localized(self.language)
- # register content_licence
- self.register_content_licence(form, self.localized_instance, 'content')
-
- # render a response with refreshed data
- self.localized_instance.refresh_from_db()
- self.meta_instance.refresh_from_db()
- context = self.get_context_data(**self.kwargs)
- context['form'] = form
-
- return self.render_to_response(context)
-
-
-'''
- get all fields for a microcontent_type
- ajax only
- for successful image deletions and uploads
- reloads all fields if field is multi
-'''
-class GetFormField(OnlineContentMixin, FormView):
-
- template_name = 'online_content/ajax/reloaded_file_fields.html'
- form_class = forms.Form
-
- @method_decorator(ajax_required)
- def dispatch(self, request, *args, **kwargs):
- self.template_content = TemplateContent.objects.get(pk=kwargs['template_content_id'])
- self.microcontent_category = kwargs['microcontent_category']
- self.microcontent_type = kwargs['microcontent_type']
- self.language = kwargs['language']
-
- return super().dispatch(request, *args, **kwargs)
-
- def get_context_data(self, **kwargs):
- context = super().get_context_data(**kwargs)
- context['language'] = self.language
- context['template_content'] = self.template_content
- return context
-
- def get_form(self, form_class=None):
- if form_class is None:
- form_class = forms.Form
-
- cms_tag = CMSTag(self.app, self.template_content, self.microcontent_category, self.microcontent_type)
-
- form = form_class(**self.get_form_kwargs())
-
- for field in cms_tag.form_fields(self.language, self.template_content):
- form.fields[field['name']] = field['field']
- form.fields[field['name']].language = self.language
-
- return form
-
-
-class UploadCustomTemplate(OnlineContentMixin, FormView):
-
- template_name = 'online_content/ajax/upload_custom_template.html'
- form_class = UploadCustomTemplateForm
-
- @method_decorator(ajax_required)
- def dispatch(self, request, *args, **kwargs):
- return super().dispatch(request, *args, **kwargs)
-
- def get_form(self, form_class=None):
- """Return an instance of the form to be used in this view."""
- if form_class is None:
- form_class = self.get_form_class()
- return form_class(self.app, **self.get_form_kwargs())
-
-
- def get_context_data(self, **kwargs):
- context = super().get_context_data(**kwargs)
- context['success'] = False
- return context
-
-
- def form_valid(self, form):
-
- template_folder = os.path.join(self.app.get_user_uploaded_online_content_templates_path(), 'page')
-
- if not os.path.isdir(template_folder):
- os.makedirs(template_folder)
-
- file = form.cleaned_data['template']
- template_path = os.path.join(template_folder, file.name)
-
- with open(template_path, 'wb+') as destination:
- for chunk in file.chunks():
- destination.write(chunk)
-
- context = self.get_context_data(**self.kwargs)
- context['form'] = form
- context['success'] = True
-
- return self.render_to_response(context)
diff --git a/localcosmos_server/online_content/widgets.py b/localcosmos_server/online_content/widgets.py
deleted file mode 100644
index 06af02c..0000000
--- a/localcosmos_server/online_content/widgets.py
+++ /dev/null
@@ -1,117 +0,0 @@
-from django.forms.widgets import FileInput, Widget, TextInput, Textarea
-from django.forms.utils import flatatt
-from django.template import loader, Context
-from django.utils.encoding import (
- force_str, force_text
-)
-
-from django.utils.html import conditional_escape, format_html, html_safe
-from django.utils.safestring import mark_safe
-
-import copy
-
-
-"""
- A widget displaying multiple TextInputs
- - still needs custom RENDER method
- REWRITE THIS - use MultiWidget as blueprint
-"""
-class MultiContentWidget(Widget):
-
- # template_name = 'online_content/widgets/multi_content_input.html'
-
- def __init__(self, attrs={}):
- max_ = attrs.get('maxnum', None)
-
- number = 1
-
- if max_ and max_ < number:
- number = max_
-
- widget = attrs.pop('widget', Textarea)
- self.widgets = [widget() for i in range(0,number)]
- super().__init__(attrs)
-
- @property
- def is_hidden(self):
- return all(w.is_hidden for w in self.widgets)
-
- def render(self, name, value, attrs=None, renderer=None):
- if self.is_localized:
- for widget in self.widgets:
- widget.is_localized = self.is_localized
- # value is a list of values, each corresponding to a widget
- # in self.widgets.
- if not isinstance(value, list):
- value = self.decompress(value)
- output = []
-
- # multi content fields may not have an id
- # the user can add fields via javascript
- id_ = attrs.pop('id')
-
-
- final_attrs = self.build_attrs(attrs)
- id_ = final_attrs.get('id')
- for i, widget in enumerate(self.widgets):
- try:
- widget_value = value[i]
- except IndexError:
- widget_value = None
- if id_:
- final_attrs = dict(final_attrs, id='%s_%s' % (id_, i))
- output.append(widget.render(name, widget_value, final_attrs))
-
- widgets_html = mark_safe(self.format_output(output))
-
- return widgets_html
-
-
- def id_for_label(self, id_):
- # See the comment for RadioSelect.id_for_label()
- if id_:
- id_ += '_0'
- return id_
-
- def value_from_datadict(self, data, files, name):
- data = data.getlist(name,[])
- return data
-
- def value_omitted_from_data(self, data, files, name):
- return all(
- widget.value_omitted_from_data(data, files, name)
- for i, widget in enumerate(self.widgets)
- )
-
- def format_output(self, rendered_widgets):
- """
- Given a list of rendered widgets (as strings), returns a Unicode string
- representing the HTML for the whole lot.
-
- This hook allows you to format the HTML design of the widgets, if
- needed.
- """
-
- return ''.join(rendered_widgets)
-
- def decompress(self, value):
- if value:
- return value
- return []
-
- def _get_media(self):
- "Media for a multiwidget is the combination of all media of the subwidgets"
- media = Media()
- for w in self.widgets:
- media = media + w.media
- return media
- media = property(_get_media)
-
- def __deepcopy__(self, memo):
- obj = super().__deepcopy__(memo)
- obj.widgets = copy.deepcopy(self.widgets)
- return obj
-
- @property
- def needs_multipart_form(self):
- return any(w.needs_multipart_form for w in self.widgets)
diff --git a/localcosmos_server/server_control_panel/templates/server_control_panel/base.html b/localcosmos_server/server_control_panel/templates/server_control_panel/base.html
index abf7538..6de975b 100644
--- a/localcosmos_server/server_control_panel/templates/server_control_panel/base.html
+++ b/localcosmos_server/server_control_panel/templates/server_control_panel/base.html
@@ -41,9 +41,6 @@
{% trans 'Install app' %}
-
diff --git a/localcosmos_server/template_content/Templates.py b/localcosmos_server/template_content/Templates.py
new file mode 100644
index 0000000..6c08bc1
--- /dev/null
+++ b/localcosmos_server/template_content/Templates.py
@@ -0,0 +1,216 @@
+from django.conf import settings
+import os, json
+
+TEMPLATE_CONTENT_FOLDER_NAME = 'template_content'
+UPLOADED_TEMPLATES_ROOT = os.path.join(settings.MEDIA_ROOT, TEMPLATE_CONTENT_FOLDER_NAME)
+
+'''
+definition :
+
+ {
+ "templateName" : "Neobiota", # a verbose name, constant
+ "version" : 1, # integer version
+ "templateFileName": "filename.xy",
+ "templateUrl": "/fact-sheet/neobiota/{slug}", # the url supplied by the frontend for the preview
+ "contents": {
+ "headline": {
+ "type": "text",
+ "widget" : "TextInput"
+ },
+ "titleImage": {
+ "type": "image"
+ },
+ "description": {
+ "type": "text",
+ "format": "layoutable-full"
+ },
+ "images": {
+ "type": "multi-image"
+ }
+ }
+ }
+'''
+
+
+class TemplatePaths:
+
+ @property
+ def frontend_templates_path(self):
+ app_root = self.app.get_installed_app_path(app_state='preview')
+ return os.path.join(app_root, TEMPLATE_CONTENT_FOLDER_NAME)
+
+ @property
+ def uploaded_templates_path(self):
+ return os.path.join(UPLOADED_TEMPLATES_ROOT, self.app.uid)
+
+
+
+'''
+ A template consists of 2 files:
+ - a .json file as a definition
+ - a template file containing the template as a string
+ - draft should use the newest file
+ (-> check version if the template exists in both the frontend and the uploaded folder)
+'''
+class Template(TemplatePaths):
+
+ # name is used to identify the folder of the template files
+ def __init__(self, app, name, template_filepath=None, template_definition_filepath=None):
+
+ self.app = app
+ self.name = name
+
+ self.template_filepath = None
+ self.template_definition_filepath = None
+
+ if template_filepath and template_definition_filepath:
+ self.template_filepath = template_filepath
+ self.template_definition_filepath = template_definition_filepath
+
+ self.template, self.definition = self.read_template_files(self.template_filepath,
+ self.template_definition_filepath)
+
+ else:
+ self.load_template_and_definition_from_files()
+
+ @property
+ def frontend_template_folder(self):
+ return os.path.join(self.frontend_templates_path, self.name)
+
+ @property
+ def uploaded_template_folder(self):
+ return os.path.join(self.uploaded_templates_path, self.name)
+
+
+ def get_template_filepaths(self, template_folder):
+
+ template_filepath = None
+ template_definition_filepath = None
+
+ filecount = 0
+
+ if os.path.isdir(template_folder):
+
+ for filename in os.listdir(template_folder):
+
+ filepath = os.path.join(template_folder, filename)
+
+ if os.path.isfile(filepath):
+
+ filecount += 1
+ basename, ext = os.path.splitext(filepath)
+
+ if ext == '.json':
+ template_definition_filepath = filepath
+ else:
+ template_filepath = filepath
+
+ if filecount == 2:
+ return template_filepath, template_definition_filepath
+
+ return None, None
+
+ def read_template_files(self, template_filepath, template_definition_filepath):
+
+ template = None
+ definition = None
+
+ if template_filepath and template_definition_filepath:
+
+ with open(template_filepath, 'r') as template_file:
+ template = template_file.read()
+
+
+ with open(template_definition_filepath, 'r') as template_definition_file:
+ definition = json.loads(template_definition_file.read())
+
+
+ return template, definition
+
+ '''
+ - first, look up the frontend template_content folder for the template
+ - second, check uploaded templates, compare version with frontend's template
+ '''
+ def load_template_and_definition_from_files(self):
+
+ template = None
+ definition = None
+ template_filepath = None
+ template_definition_filepath = None
+
+
+ frontend_template_filepath, frontend_template_definition_filepath = self.get_template_filepaths(
+ self.frontend_template_folder)
+
+ frontend_template, frontend_template_definition = self.read_template_files(frontend_template_filepath,
+ frontend_template_definition_filepath)
+
+
+ if frontend_template and frontend_template_definition:
+
+ template = frontend_template
+ definition = frontend_template_definition
+ template_filepath = frontend_template_filepath
+ template_definition_filepath = frontend_template_definition_filepath
+
+ uploaded_template_filepath, uploaded_template_definition_filepath = self.get_template_filepaths(
+ self.uploaded_template_folder)
+
+ uploaded_template, uploaded_template_definition = self.read_template_files(uploaded_template_filepath,
+ uploaded_template_definition_filepath)
+
+ if uploaded_template and uploaded_template_definition:
+
+ if uploaded_template_definition['version'] > frontend_template_definition['version']:
+ template = uploaded_template
+ definition = uploaded_template_definition
+ template_filepath = uploaded_template_filepath
+ template_definition_filepath = uploaded_template_definition_filepath
+
+ if not template or not definition:
+ msg = 'Template not found: {0}. Looked in: {1} , {2}'.format(self.name, self.frontend_templates_path,
+ self.uploaded_templates_path)
+ raise FileNotFoundError(msg)
+
+ self.template = template
+ self.definition = definition
+
+ self.template_filepath = template_filepath
+ self.template_definition_filepath = template_definition_filepath
+
+
+'''
+ there are several app builds:
+ - preview
+ - review
+ - published
+
+ preview and review have the same frontend version
+'''
+class Templates(TemplatePaths):
+
+ def __init__(self, app):
+ self.app = app
+
+
+ def get_all_templates(self):
+
+ templates = {}
+
+ # iterate over all directories of frontend_templates_folder
+ if os.path.isdir(self.frontend_templates_path):
+ for frontend_dirname in os.listdir(self.frontend_templates_path):
+ template = Template(self.app, frontend_dirname)
+ templates[frontend_dirname] = template
+
+ if os.path.isdir(self.uploaded_templates_path):
+ for dirname in os.listdir(self.uploaded_templates_path):
+ template = Template(self.app, dirname)
+
+ if dirname in templates:
+ if template.definition['version'] < templates[dirname].definition['version']:
+ continue
+
+ templates[dirname] = template
+
+ return templates
\ No newline at end of file
diff --git a/localcosmos_server/online_content/__init__.py b/localcosmos_server/template_content/__init__.py
similarity index 100%
rename from localcosmos_server/online_content/__init__.py
rename to localcosmos_server/template_content/__init__.py
diff --git a/localcosmos_server/online_content/admin.py b/localcosmos_server/template_content/admin.py
similarity index 100%
rename from localcosmos_server/online_content/admin.py
rename to localcosmos_server/template_content/admin.py
diff --git a/localcosmos_server/online_content/api/__init__.py b/localcosmos_server/template_content/api/__init__.py
similarity index 100%
rename from localcosmos_server/online_content/api/__init__.py
rename to localcosmos_server/template_content/api/__init__.py
diff --git a/localcosmos_server/template_content/api/serializers.py b/localcosmos_server/template_content/api/serializers.py
new file mode 100644
index 0000000..9678256
--- /dev/null
+++ b/localcosmos_server/template_content/api/serializers.py
@@ -0,0 +1,134 @@
+from rest_framework import serializers
+
+from localcosmos_server.template_content.models import LocalizedTemplateContent, PUBLISHED_IMAGE_TYPE_PREFIX
+
+from content_licencing.models import ContentLicenceRegistry
+
+class LocalizedTemplateContentSerializer(serializers.ModelSerializer):
+
+ title = serializers.SerializerMethodField()
+ navigationLinkName = serializers.SerializerMethodField()
+ templateName = serializers.SerializerMethodField()
+ version = serializers.SerializerMethodField()
+ templateFileName = serializers.SerializerMethodField()
+ templateUrl = serializers.SerializerMethodField()
+ contents = serializers.SerializerMethodField()
+
+ template_definition = None
+
+ def get_from_definition(self, localized_template_content, key):
+ template_definition = self.get_template_definition(localized_template_content)
+ return template_definition[key]
+
+
+ def get_template_definition(self, localized_template_content):
+ preview = self.context.get('preview', True)
+ if not self.template_definition:
+ if preview == True:
+ self.template_definition = localized_template_content.template_content.draft_template.definition
+ else:
+ self.template_definition = localized_template_content.template_content.template.definition
+ return self.template_definition
+
+ def get_title(self, localized_template_content):
+ preview = self.context.get('preview', True)
+ if preview == True:
+ return localized_template_content.draft_title
+ return localized_template_content.published_title
+
+ def get_navigationLinkName(self, localized_template_content):
+ preview = self.context.get('preview', True)
+ if preview == True:
+ return localized_template_content.draft_navigation_link_name
+
+ return localized_template_content.published_navigation_link_name
+
+ def get_templateName(self, localized_template_content):
+ return self.get_from_definition(localized_template_content, 'templateName')
+
+ def get_version(self, localized_template_content):
+ return self.get_from_definition(localized_template_content, 'version')
+
+ def get_templateFileName(self, localized_template_content):
+ return self.get_from_definition(localized_template_content, 'templateFileName')
+
+ def get_templateUrl(self, localized_template_content):
+ return self.get_from_definition(localized_template_content, 'templateUrl')
+
+ def get_contents(self, localized_template_content):
+ preview = self.context.get('preview', True)
+
+ if preview == True:
+ supplied_contents = localized_template_content.draft_contents
+ else:
+ supplied_contents = localized_template_content.published_contents
+
+
+ template_definition = self.get_template_definition(localized_template_content)
+
+ contents = template_definition['contents'].copy()
+
+ for content_key, content in supplied_contents.items():
+ contents[content_key]['value'] = content
+
+ # add images to contents, according to the template definition
+ for content_key, content_definition in template_definition['contents'].items():
+
+ image_type = content_key
+
+ if preview == False:
+ image_type = '{0}{1}'.format(PUBLISHED_IMAGE_TYPE_PREFIX, content_key)
+
+ if content_definition['type'] == 'image':
+
+ content_image = localized_template_content.image(image_type=image_type)
+ if content_image:
+
+ serializer = ContentImageSerializer(content_image)
+ contents[content_key]['value'] = serializer.data
+
+ elif content_definition['type'] == 'multi-image':
+ content_images = localized_template_content.images(image_type=image_type)
+ contents[content_key]['value'] = []
+ for content_image in content_images:
+
+ serializer = ContentImageSerializer(content_image)
+ contents[content_key]['value'].append(serializer.data)
+ # add images to contents, according to the template definition
+
+ return contents
+
+
+ class Meta:
+ model = LocalizedTemplateContent
+ fields = ['title', 'navigationLinkName', 'templateName', 'version', 'templateFileName', 'templateUrl', 'contents']
+
+
+class ContentLicenceSerializer(serializers.ModelSerializer):
+
+ licenceVersion = serializers.CharField(source='licence_version')
+ creatorName = serializers.CharField(source='creator_name')
+ creatorLink = serializers.CharField(source='creator_link')
+ sourceLink = serializers.CharField(source='source_link')
+
+ class Meta:
+ model = ContentLicenceRegistry
+ fields = ('licence', 'licenceVersion', 'creatorName', 'creatorLink', 'sourceLink')
+
+
+class ContentImageSerializer(serializers.Serializer):
+
+ imageUrl = serializers.SerializerMethodField()
+ licence = serializers.SerializerMethodField()
+
+ def get_imageUrl(self, content_image):
+ return content_image.image_url()
+
+ def get_licence(self, content_image):
+
+ image_store = content_image.image_store
+ licence = image_store.licences.first()
+
+ serializer = ContentLicenceSerializer(licence)
+
+ return serializer.data
\ No newline at end of file
diff --git a/localcosmos_server/template_content/api/urls.py b/localcosmos_server/template_content/api/urls.py
new file mode 100644
index 0000000..ca7e4e5
--- /dev/null
+++ b/localcosmos_server/template_content/api/urls.py
@@ -0,0 +1,12 @@
+from django.urls import include, path
+from . import views
+from rest_framework.urlpatterns import format_suffix_patterns
+
+
+urlpatterns = [
+ path('template-content//', views.GetTemplateContent.as_view(), name='get_template_content'), # JSON ONLY
+ path('template-content-preview//', views.GetTemplateContentPreview.as_view(), # JSON ONLY
+ name='get_template_content_preview'),
+]
+
+urlpatterns = format_suffix_patterns(urlpatterns, allowed=['json',])
\ No newline at end of file
diff --git a/localcosmos_server/template_content/api/views.py b/localcosmos_server/template_content/api/views.py
new file mode 100644
index 0000000..5d7d82d
--- /dev/null
+++ b/localcosmos_server/template_content/api/views.py
@@ -0,0 +1,33 @@
+from rest_framework.generics import GenericAPIView
+from rest_framework import mixins
+
+from localcosmos_server.template_content.models import LocalizedTemplateContent
+
+
+from .serializers import LocalizedTemplateContentSerializer
+
+class GetTemplateContentCommon:
+
+ queryset = LocalizedTemplateContent.objects.all()
+ serializer_class = LocalizedTemplateContentSerializer
+ lookup_url_kwarg = 'slug'
+ lookup_field = 'slug'
+
+ def get(self, request, *args, **kwargs):
+ return self.retrieve(request, *args, **kwargs)
+
+
+class GetTemplateContent(GetTemplateContentCommon, mixins.RetrieveModelMixin, GenericAPIView):
+
+ def get_serializer_context(self):
+ context = super().get_serializer_context()
+ context['preview'] = False
+ return context
+
+
+class GetTemplateContentPreview(GetTemplateContentCommon, mixins.RetrieveModelMixin, GenericAPIView):
+
+ def get_serializer_context(self):
+ context = super().get_serializer_context()
+ context['preview'] = True
+ return context
diff --git a/localcosmos_server/template_content/apps.py b/localcosmos_server/template_content/apps.py
new file mode 100644
index 0000000..603a539
--- /dev/null
+++ b/localcosmos_server/template_content/apps.py
@@ -0,0 +1,5 @@
+from django.apps import AppConfig
+
+
+class TemplateContentConfig(AppConfig):
+ name = 'template_content'
diff --git a/localcosmos_server/template_content/forms.py b/localcosmos_server/template_content/forms.py
new file mode 100644
index 0000000..b6228e3
--- /dev/null
+++ b/localcosmos_server/template_content/forms.py
@@ -0,0 +1,278 @@
+from django import forms
+from django.urls import reverse
+from django.utils.translation import gettext_lazy as _
+from django.contrib.contenttypes.models import ContentType
+
+from .models import NAVIGATION_LINK_NAME_MAX_LENGTH
+
+from .Templates import Template, Templates
+
+from django.contrib.auth import get_user_model
+
+from localcosmos_server.forms import LocalizeableForm
+
+User = get_user_model()
+
+
+class TemplateContentFormCommon(LocalizeableForm):
+ draft_title = forms.CharField(label=_('Title'))
+ draft_navigation_link_name = forms.CharField(max_length=NAVIGATION_LINK_NAME_MAX_LENGTH,
+ label=_('Name for links in navigation menus'),
+ help_text=_('Max %(characters)s characters. If this content shows up in a navigation menu, this name will be shown as the link.') % {'characters' : NAVIGATION_LINK_NAME_MAX_LENGTH})
+
+ localizeable_fields = ['draft_title', 'draft_navigation_link_name']
+
+
+class CreateTemplateContentForm(TemplateContentFormCommon):
+
+ template_name = forms.ChoiceField(label =_('Template'))
+
+ def __init__(self, app, template_type, *args, **kwargs):
+
+ self.app = app
+ self.template_type = template_type
+
+ super().__init__(*args, **kwargs)
+
+ # load the template_choices
+ templates = Templates(self.app)
+ available_templates = templates.get_all_templates()
+
+ choices = []
+
+ if available_templates:
+ for template_name, template in available_templates.items():
+ choice = (template_name, template.definition['templateName'])
+ choices.append(choice)
+ self.fields['template_name'].choices = choices
+
+
+class TemplateContentFormFieldManager:
+
+ def __init__(self, localized_template_content):
+ self.localized_template_content = localized_template_content
+
+ def get_form_fields(self, content_key, content_definition, instances):
+
+ form_fields = []
+
+ draft_contents = self.localized_template_content.draft_contents
+
+ if content_definition['type'] == 'multi-image':
+
+ max_number = content_definition.get('max_number', None)
+
+ is_first = True
+ is_last = False
+ field_count = 0
+
+ for current_image in instances:
+
+ field_count += 1
+
+ form_field = self.get_image_form_field(content_key, content_definition, current_image)
+
+ if field_count == max_number:
+ is_last = True
+
+ form_field.is_first = is_first
+ form_field.is_last = is_last
+
+ field_name = '{0}-{1}'.format(content_key, field_count)
+
+ field = {
+ 'name' : field_name,
+ 'field' : form_field,
+ }
+
+ form_fields.append(field)
+
+ if is_first == True:
+ is_first = False
+
+
+ # optionally add empty field
+ if max_number is None or field_count < max_number:
+ # is_last is False
+ is_last = True
+
+ form_field = self.get_image_form_field(content_key, content_definition)
+
+ form_field.is_first = is_first
+ form_field.is_last = is_last
+
+ field = {
+ 'name' : content_key,
+ 'field' : form_field,
+ }
+
+ form_fields.append(field)
+
+
+ elif content_definition['type'] == 'image':
+ current_image = None
+
+ if instances:
+ current_image = instances[0]
+
+ form_field = self.get_image_form_field(content_key, content_definition, current_image)
+
+ field = {
+ 'name' : content_key,
+ 'field' : form_field,
+ }
+
+ form_fields.append(field)
+
+
+
+ elif content_definition['type'] == 'text':
+
+ label = self._get_label(content_key, content_definition)
+
+ field_kwargs = {
+ 'required' : False,
+ 'label' : label
+ }
+
+ widget = forms.Textarea
+ if content_definition.get('widget', None) == 'TextInput':
+ widget = forms.TextInput
+
+ initial = ''
+
+ if draft_contents and content_key in draft_contents:
+ initial = draft_contents[content_key]
+
+ field_kwargs.update({
+ 'widget' : widget,
+ 'initial' : initial,
+ })
+
+ form_field = forms.CharField(**field_kwargs)
+ form_field.cms_tag = self
+
+ field = {
+ 'name' : content_key,
+ 'field' : form_field,
+ }
+
+ form_fields.append(field)
+
+
+ return form_fields
+
+
+ def get_image_form_field(self, content_key, content_definition, current_image=None):
+
+ widget_attrs = self._get_widget_attrs(content_key, content_definition)
+
+ label = self._get_label(content_key, content_definition)
+
+ field_kwargs = {
+ 'required' : False,
+ 'label' : label
+ }
+
+ delete_url = None
+
+ if current_image:
+
+ data_url_kwargs = {
+ 'content_image_id' : current_image.id,
+ }
+
+ data_url = reverse('manage_template_content_image', kwargs=data_url_kwargs)
+
+ delete_kwargs = {
+ 'pk' : current_image.pk,
+ }
+
+ delete_url = reverse('delete_template_content_image', kwargs=delete_kwargs)
+
+ else :
+ ltc_content_type = ContentType.objects.get_for_model(self.localized_template_content)
+
+ data_url_kwargs = {
+ 'content_type_id' : ltc_content_type.id,
+ 'object_id' : self.localized_template_content.id,
+ 'image_type' : content_key
+ }
+
+ data_url = reverse('manage_template_content_image', kwargs=data_url_kwargs)
+
+ widget_attrs['data-url'] = data_url
+ widget_attrs['accept'] = 'image/*'
+
+ form_field = forms.ImageField(widget=forms.FileInput(widget_attrs), **field_kwargs)
+ form_field.current_image = current_image
+
+ form_field.licenced_url = data_url
+ form_field.delete_url = delete_url
+
+ return form_field
+
+
+ def _get_label(self, content_key, content_definition):
+ fallback_label = content_key.replace('_', ' ').capitalize()
+ label = content_definition.get('label', fallback_label)
+ return label
+
+ def _get_widget_attrs(self, content_key, content_definition):
+
+ widget_attrs = {
+ 'data-content-key' : content_key,
+ 'data-type' : content_definition['type'],
+ }
+
+ if content_definition['type'] == 'multi-images':
+ widget_attrs.update({
+ 'multi' : True,
+ })
+
+ return widget_attrs
+
+
+
+class ManageLocalizedTemplateContentForm(TemplateContentFormCommon):
+
+ def __init__(self, app, localized_template_content, *args, **kwargs):
+
+ super().__init__(*args, **kwargs)
+
+ self.localized_template_content = localized_template_content
+ self.template_content = self.localized_template_content.template_content
+
+ self.layoutable_full_fields = set([])
+ self.layoutable_simple_fields = set([])
+
+
+ template_definition = self.template_content.draft_template.definition
+
+ field_manager = TemplateContentFormFieldManager(self.localized_template_content)
+
+ # content_key is the key in the json
+ for content_key, content_definition in template_definition['contents'].items():
+
+ instances = []
+
+ if content_definition['type'] in ['image', 'multi-image']:
+ instances = self.localized_template_content.images(image_type=content_key).order_by('pk')
+
+
+ form_fields = field_manager.get_form_fields(content_key, content_definition, instances)
+
+ # get form fields for each content_id
+ for field in form_fields:
+
+ field['field'].content_definition = content_definition
+ field['field'].content_key = content_key
+
+ self.fields[field['name']] = field['field']
+
+ self.fields[field['name']].language = app.primary_language
+
+ if content_definition.get('format', None) == 'layoutable-simple':
+ self.layoutable_simple_fields.add(field['name'])
+ elif content_definition.get('format', None) == 'layoutable-full':
+ self.layoutable_full_fields.add(field['name'])
\ No newline at end of file
diff --git a/localcosmos_server/template_content/migrations/0001_initial.py b/localcosmos_server/template_content/migrations/0001_initial.py
new file mode 100644
index 0000000..daddaa8
--- /dev/null
+++ b/localcosmos_server/template_content/migrations/0001_initial.py
@@ -0,0 +1,61 @@
+# Generated by Django 3.1.14 on 2022-12-14 15:57
+
+from django.conf import settings
+from django.db import migrations, models
+import django.db.models.deletion
+import localcosmos_server.models
+import localcosmos_server.template_content.models
+import uuid
+
+
+class Migration(migrations.Migration):
+
+ initial = True
+
+ dependencies = [
+ ('localcosmos_server', '0002_servercontentimage_serverimagestore'),
+ migrations.swappable_dependency(settings.AUTH_USER_MODEL),
+ ]
+
+ operations = [
+ migrations.CreateModel(
+ name='TemplateContent',
+ fields=[
+ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+ ('uuid', models.UUIDField(default=uuid.uuid4, editable=False, unique=True)),
+ ('template_type', models.CharField(choices=[('page', 'Page')], max_length=20)),
+ ('tag', models.CharField(editable=False, max_length=100)),
+ ('draft_template_name', models.CharField(max_length=355)),
+ ('published_template', models.FileField(null=True, upload_to=localcosmos_server.template_content.models.get_published_template_path)),
+ ('published_template_definition', models.FileField(null=True, upload_to=localcosmos_server.template_content.models.get_published_template_definition_path)),
+ ('app', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='localcosmos_server.app')),
+ ],
+ ),
+ migrations.CreateModel(
+ name='LocalizedTemplateContent',
+ fields=[
+ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+ ('language', models.CharField(max_length=15)),
+ ('draft_title', models.CharField(max_length=255)),
+ ('published_title', models.CharField(max_length=255, null=True)),
+ ('draft_navigation_link_name', models.CharField(max_length=30)),
+ ('published_navigation_link_name', models.CharField(max_length=30, null=True)),
+ ('slug', models.SlugField(unique=True)),
+ ('draft_contents', models.JSONField(null=True)),
+ ('published_contents', models.JSONField(null=True)),
+ ('translation_ready', models.BooleanField(default=False)),
+ ('created_at', models.DateTimeField(auto_now_add=True)),
+ ('last_modified', models.DateTimeField(auto_now=True)),
+ ('draft_version', models.IntegerField(default=1)),
+ ('published_version', models.IntegerField(null=True)),
+ ('published_at', models.DateTimeField(null=True)),
+ ('created_by', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='template_content_creator', to=settings.AUTH_USER_MODEL)),
+ ('last_modified_by', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, to=settings.AUTH_USER_MODEL)),
+ ('template_content', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='template_content.templatecontent')),
+ ],
+ options={
+ 'unique_together': {('template_content', 'language')},
+ },
+ bases=(localcosmos_server.models.ServerContentImageMixin, models.Model),
+ ),
+ ]
diff --git a/localcosmos_server/online_content/migrations/__init__.py b/localcosmos_server/template_content/migrations/__init__.py
similarity index 100%
rename from localcosmos_server/online_content/migrations/__init__.py
rename to localcosmos_server/template_content/migrations/__init__.py
diff --git a/localcosmos_server/template_content/models.py b/localcosmos_server/template_content/models.py
new file mode 100644
index 0000000..e1a3593
--- /dev/null
+++ b/localcosmos_server/template_content/models.py
@@ -0,0 +1,425 @@
+from django.db import models
+from django.conf import settings
+from django.utils.translation import gettext_lazy as _
+from django.template.defaultfilters import slugify
+from django.core.files import File
+from django.contrib.contenttypes.models import ContentType
+
+from localcosmos_server.models import App, ServerContentImageMixin, TaxonomicRestriction
+
+from django.utils import timezone
+
+import os, json, uuid
+
+from.Templates import Template
+
+PUBLISHED_IMAGE_TYPE_PREFIX = 'published-'
+
+TEMPLATE_TYPES = (
+ ('page', _('Page')),
+)
+
+NAVIGATION_LINK_NAME_MAX_LENGTH = 30
+TITLE_MAX_LENGTH = 255
+
+def get_template_content_root(template_content):
+
+ template_definition = template_content.draft_template.definition
+
+ template_content_folder_name = '{0}-{1}'.format(template_definition['templateName'], template_content.pk)
+
+ path = '/'.join(['localcosmos-server', 'template-content', 'published', template_content_folder_name])
+
+ return path
+
+# store published template here
+def get_published_template_path(template_content, filename):
+ template_content_root = get_template_content_root(template_content)
+ unchanged_filename = os.path.basename(template_content.draft_template.template_filepath)
+ path = '/'.join([template_content_root, 'template', filename])
+
+ return path
+
+def get_published_template_definition_path(template_content, filename):
+ template_content_root = get_template_content_root(template_content)
+ unchanged_filename = os.path.basename(template_content.draft_template.template_definition_filepath)
+ path = '/'.join([template_content_root, 'template', unchanged_filename])
+
+ return path
+
+'''
+ Templates and their definitions can rely in two different paths
+'''
+class TemplateContentManager(models.Manager):
+
+ def create(self, creator, app, language, draft_title, draft_navigation_link_name, template_name,
+ template_type):
+
+ template_content = self.model(
+ app = app,
+ draft_template_name = template_name,
+ template_type = template_type,
+ tag = slugify(draft_title),
+ )
+ template_content.save()
+
+ # create the localized template content
+ localized_template_content = LocalizedTemplateContent.objects.create(creator, template_content, language,
+ draft_title, draft_navigation_link_name)
+
+ return template_content
+
+
+ def filter_by_taxon(self, lazy_taxon, ascendants=False):
+
+ template_contents = []
+
+ if ascendants == False:
+
+ template_content_content_type = ContentType.objects.get_for_model(TemplateContent)
+
+ taxon_latname = lazy_taxon.taxon_latname
+ taxon_author=lazy_taxon.taxon_author
+ taxon_source = lazy_taxon.taxon_source
+
+ template_content_links = TaxonomicRestriction.objects.filter(content_type=template_content_content_type,
+ taxon_source=taxon_source, taxon_latname=taxon_latname, taxon_author=taxon_author)
+
+ template_content_ids = template_content_links.values_list('object_id', flat=True)
+
+ template_contents = self.filter(pk__in=template_content_ids)
+
+
+ else:
+ # get for all nuids, not implemented yet
+ taxon_nuid = lazy_taxon.taxon_nuid
+
+
+ return template_contents
+
+
+'''
+ TemplateContent
+ - a "hybrid" component: during build, all template contens are built for offline use
+ - template content which is not available offline can be fetched using the API
+ - this can only work if some sort of menu is being fetched from the server
+ - template content does not have to be part of a menu
+ - offline (build) template content is never newer than the online version
+ - the app can query online to get new contents, should also fetch template then
+
+ published_template:
+ The actual template and its definition are stored in the database rather then store a path
+ to the files. The user might upload a newer template version (as a file). This should not
+ have any effect on the stored pages.
+
+ draft_template:
+ only the name is saved. the Template class looks up actual files. The draf always uses the template
+ which is currently available as a file (with its definition as a file)
+'''
+class TemplateContent(models.Model):
+
+ uuid = models.UUIDField(default=uuid.uuid4, unique=True, editable=False)
+
+ app = models.ForeignKey(App, on_delete=models.CASCADE)
+
+ template_type = models.CharField(max_length=20, choices=TEMPLATE_TYPES)
+
+ # if a taxon is added to this content, it receives this tag during build
+ tag = models.CharField(max_length=100, editable=False)
+
+ draft_template_name = models.CharField(max_length=355) # stores the actual template (e.g. .html)
+
+ published_template = models.FileField(null=True, upload_to=get_published_template_path)
+ published_template_definition = models.FileField(null=True, upload_to=get_published_template_definition_path)
+
+ objects = TemplateContentManager()
+
+ def __init__(self, *args, **kwargs):
+ super().__init__(*args, **kwargs)
+ # use the templates on disk
+ self.draft_template = Template(self.app, self.draft_template_name)
+
+ if self.published_template:
+ # the published template, use the template data (definition&template) stored in the db
+ self.template = Template(self.app, self.name,
+ self.published_template.path, self.published_template_definition.path)
+
+ @property
+ def name (self):
+ return self.draft_template.definition['templateName']
+
+ def get_locale(self, language_code):
+ return LocalizedTemplateContent.objects.filter(template_content=self, language=language_code).first()
+
+
+ def publish(self, language='all'):
+
+ publication_errors = []
+
+ primary_language = self.app.primary_language
+ secondary_languages = self.app.secondary_languages()
+
+ if language == 'all':
+ languages = self.app.languages()
+ else:
+ languages = [language]
+
+
+ # ltc.translation_ready is not set to True by the user if there is only one language
+ # skip the check if the "translation" exists and also skip the check if the user has set
+ # translation_ready to True, which is not the case because there is only a "publish" button
+ # in this case (only 1 language) and no "ready for translation" button
+ if not secondary_languages:
+ ltc = LocalizedTemplateContent.objects.filter(template_content=self, language=primary_language).first()
+ publication_errors += ltc.translation_complete()
+
+ # secondary languages exist. these languages need translators and the translation_ready flags are
+ # set by the user when he has finished translating
+ else:
+
+ for language_code in languages:
+
+ # translation_complete checks two things:
+ # a) if the primary language has filled all required fields
+ # b) if all secondary languages are translated completely
+ publication_errors += self.translation_complete(language_code)
+
+
+ # below this, no error checks are allowed because published_versions are being set
+ if not publication_errors:
+
+ for language_code in languages:
+
+ ltc = LocalizedTemplateContent.objects.filter(template_content=self, language=language_code).first()
+ if ltc:
+ ltc.publish()
+
+ # store TemplateContent.published_template and TemplateContent.published_template_definition
+ filepaths = [self.draft_template.template_filepath, self.draft_template.template_definition_filepath]
+
+ for filepath in filepaths:
+
+ with open(filepath, 'r') as template_file:
+ filename = os.path.basename(filepath)
+ djangofile = File(template_file)
+
+ if filepath == self.draft_template.template_filepath:
+ self.published_template.save(filename, djangofile)
+
+ if filepath == self.draft_template.template_definition_filepath:
+ self.published_template_definition.save(filename, djangofile)
+
+ return publication_errors
+
+
+ def unpublish(self):
+ localizations = LocalizedTemplateContent.objects.filter(template_content=self)
+
+ for localization in localizations:
+ localization.published_version = None
+ localization.published_at = None
+ localization.save()
+
+
+ @property
+ def is_published(self):
+ return LocalizedTemplateContent.objects.filter(template_content=self,
+ published_version__isnull=False).exists()
+
+
+ def __str__(self):
+ return self.name
+
+
+MAX_SLUG_LENGTH = 100
+class LocalizedTemplateContentManager(models.Manager):
+
+ def create(self, creator, template_content, language, draft_title, draft_navigation_link_name):
+
+ slug = self.generate_slug(draft_title)
+
+ localized_template_content = self.model(
+ created_by = creator,
+ template_content = template_content,
+ language = language,
+ draft_title = draft_title,
+ draft_navigation_link_name = draft_navigation_link_name,
+ slug = slug,
+ )
+
+ localized_template_content.save()
+
+ return localized_template_content
+
+
+ def generate_slug_base(self, draft_title):
+ slug_base = str('{0}'.format(slugify(draft_title)) )[:MAX_SLUG_LENGTH-1]
+
+ return slug_base
+
+ def generate_slug(self, draft_title):
+
+ slug_base = self.generate_slug_base(draft_title)
+
+ slug = slug_base
+
+ exists = LocalizedTemplateContent.objects.filter(slug=slug).exists()
+
+ i = 2
+ while exists:
+
+ if len(slug) > 50:
+ slug_base = slug_base[:-1]
+
+ slug = str('{0}-{1}'.format(slug_base, i))
+ i += 1
+ exists = LocalizedTemplateContent.objects.filter(slug=slug).exists()
+
+ return slug
+
+
+class LocalizedTemplateContent(ServerContentImageMixin, models.Model):
+
+ language = models.CharField(max_length=15)
+
+ template_content = models.ForeignKey(TemplateContent, on_delete=models.CASCADE)
+
+ draft_title = models.CharField(max_length=TITLE_MAX_LENGTH)
+ published_title = models.CharField(max_length=TITLE_MAX_LENGTH, null=True)
+
+ draft_navigation_link_name = models.CharField(max_length=NAVIGATION_LINK_NAME_MAX_LENGTH)
+ published_navigation_link_name = models.CharField(max_length=NAVIGATION_LINK_NAME_MAX_LENGTH, null=True)
+
+ slug = models.SlugField(unique=True)# localized slug
+
+ draft_contents = models.JSONField(null=True)
+ published_contents = models.JSONField(null=True)
+
+ translation_ready = models.BooleanField(default=False)
+
+ created_at = models.DateTimeField(auto_now_add=True)
+ created_by = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.SET_NULL,
+ related_name='template_content_creator', null=True)
+
+ last_modified = models.DateTimeField(auto_now=True)
+ last_modified_by = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.SET_NULL, null=True)
+
+ draft_version = models.IntegerField(default=1)
+ published_version = models.IntegerField(null=True)
+ published_at = models.DateTimeField(null=True)
+
+ objects = LocalizedTemplateContentManager()
+
+
+ '''
+ - if the language is the primary language, check if all required fields are presend
+ - if the language is a secondart language, check if all fields of the primary language are translated
+ '''
+ def translation_complete(self):
+
+ translation_errors = []
+
+ template_definition = self.template_content.draft_template.definition
+ contents = template_definition['contents']
+
+ for content_key, content_definition in contents.items():
+
+ if content_definition['type'] == 'text':
+
+ if 'required' in content_definition and content_definition['required'] == False:
+ continue
+
+ content = self.draft_contents.get(content_key, None)
+
+ if not content:
+ translation_errors.append(_('The component "%(component_name)s" is required but still missing for the language %(language)s') %{'component_name':content_key, 'language':self.language})
+
+ return translation_errors
+
+
+ def publish_images(self):
+
+ template_definition = self.template_content.draft_template.definition
+ contents = template_definition['contents']
+
+ for content_key, content_definition in contents.items():
+
+ content_images = []
+
+ if content_definition['type'] == 'image':
+
+ content_images = [self.image(image_type=content_key)]
+
+ elif content_definition['type'] == 'multi-image':
+
+ content_images = self.images(image_type=content_key)
+
+
+ published_image_type = '{0}{1}'.format(PUBLISHED_IMAGE_TYPE_PREFIX, content_key)
+
+ old_published_images = self.images(image_type=published_image_type)
+ old_published_images.delete()
+
+ for content_image in content_images:
+ published_content_image = content_image
+ published_content_image.pk = None
+ published_content_image.image_type = published_image_type
+ published_content_image.save()
+
+
+ def publish(self):
+ # set title
+ self.published_title = self.draft_title
+ self.navigation_link_name = self.draft_navigation_link_name
+ self.published_contents = self.draft_contents
+
+ self.publish_images()
+
+ if self.published_version != self.draft_version:
+
+ self.published_version = self.draft_version
+ self.published_at = timezone.now()
+
+ self.save(published=True)
+
+
+ def save(self, *args, **kwargs):
+
+ # indicates, if the save() command came from self.publish
+ published = kwargs.pop('published', False)
+
+ if not self.pk:
+
+ if self.language != self.template_content.app.primary_language:
+ master_ltc = self.template_content.get_localized(self.template_content.app.primary_language)
+ self.draft_version = master_ltc.draft_version
+
+ else:
+
+ if published == False:
+
+ # the localized_template_content has already been published. start new version
+ if self.published_version == self.draft_version:
+ self.draft_version += 1
+ self.translation_ready = False
+
+ super().save(*args, **kwargs)
+
+
+ class Meta:
+ unique_together=('template_content', 'language')
+
+
+'''
+class Navigation(models.Model):
+
+ app = models.ForeignKey(App, on_delete=models.CASCADE)
+ name = models.CharField(max_length=355)
+
+
+class NavigationEntry(models.Model):
+
+ navigation = models.ForeignKey(Navigation, on_delete=models.CASCADE)
+ template_content = models.ForeignKey(TemplateContent, on_delete=models.CASCADE)
+ parent_entry = models.ForeignKey('self', on_delete=models.CASCADE)
+'''
\ No newline at end of file
diff --git a/localcosmos_server/online_content/static/online_content/ckeditor5-build-classic/LICENSE.md b/localcosmos_server/template_content/static/template_content/ckeditor5-build-classic/LICENSE.md
similarity index 100%
rename from localcosmos_server/online_content/static/online_content/ckeditor5-build-classic/LICENSE.md
rename to localcosmos_server/template_content/static/template_content/ckeditor5-build-classic/LICENSE.md
diff --git a/localcosmos_server/online_content/static/online_content/ckeditor5-build-classic/README.md b/localcosmos_server/template_content/static/template_content/ckeditor5-build-classic/README.md
similarity index 100%
rename from localcosmos_server/online_content/static/online_content/ckeditor5-build-classic/README.md
rename to localcosmos_server/template_content/static/template_content/ckeditor5-build-classic/README.md
diff --git a/localcosmos_server/online_content/static/online_content/ckeditor5-build-classic/ckeditor.js b/localcosmos_server/template_content/static/template_content/ckeditor5-build-classic/ckeditor.js
similarity index 100%
rename from localcosmos_server/online_content/static/online_content/ckeditor5-build-classic/ckeditor.js
rename to localcosmos_server/template_content/static/template_content/ckeditor5-build-classic/ckeditor.js
diff --git a/localcosmos_server/online_content/static/online_content/ckeditor5-build-classic/ckeditor.js.map b/localcosmos_server/template_content/static/template_content/ckeditor5-build-classic/ckeditor.js.map
similarity index 100%
rename from localcosmos_server/online_content/static/online_content/ckeditor5-build-classic/ckeditor.js.map
rename to localcosmos_server/template_content/static/template_content/ckeditor5-build-classic/ckeditor.js.map
diff --git a/localcosmos_server/online_content/static/online_content/ckeditor5-build-classic/index.html b/localcosmos_server/template_content/static/template_content/ckeditor5-build-classic/index.html
similarity index 100%
rename from localcosmos_server/online_content/static/online_content/ckeditor5-build-classic/index.html
rename to localcosmos_server/template_content/static/template_content/ckeditor5-build-classic/index.html
diff --git a/localcosmos_server/online_content/static/online_content/ckeditor5-build-classic/old/LICENSE.md b/localcosmos_server/template_content/static/template_content/ckeditor5-build-classic/old/LICENSE.md
similarity index 100%
rename from localcosmos_server/online_content/static/online_content/ckeditor5-build-classic/old/LICENSE.md
rename to localcosmos_server/template_content/static/template_content/ckeditor5-build-classic/old/LICENSE.md
diff --git a/localcosmos_server/online_content/static/online_content/ckeditor5-build-classic/old/README.md b/localcosmos_server/template_content/static/template_content/ckeditor5-build-classic/old/README.md
similarity index 100%
rename from localcosmos_server/online_content/static/online_content/ckeditor5-build-classic/old/README.md
rename to localcosmos_server/template_content/static/template_content/ckeditor5-build-classic/old/README.md
diff --git a/localcosmos_server/online_content/static/online_content/ckeditor5-build-classic/old/ckeditor.js b/localcosmos_server/template_content/static/template_content/ckeditor5-build-classic/old/ckeditor.js
similarity index 100%
rename from localcosmos_server/online_content/static/online_content/ckeditor5-build-classic/old/ckeditor.js
rename to localcosmos_server/template_content/static/template_content/ckeditor5-build-classic/old/ckeditor.js
diff --git a/localcosmos_server/online_content/static/online_content/ckeditor5-build-classic/old/ckeditor.js.map b/localcosmos_server/template_content/static/template_content/ckeditor5-build-classic/old/ckeditor.js.map
similarity index 100%
rename from localcosmos_server/online_content/static/online_content/ckeditor5-build-classic/old/ckeditor.js.map
rename to localcosmos_server/template_content/static/template_content/ckeditor5-build-classic/old/ckeditor.js.map
diff --git a/localcosmos_server/online_content/static/online_content/ckeditor5-build-classic/old/translations/ar.js b/localcosmos_server/template_content/static/template_content/ckeditor5-build-classic/old/translations/ar.js
similarity index 100%
rename from localcosmos_server/online_content/static/online_content/ckeditor5-build-classic/old/translations/ar.js
rename to localcosmos_server/template_content/static/template_content/ckeditor5-build-classic/old/translations/ar.js
diff --git a/localcosmos_server/online_content/static/online_content/ckeditor5-build-classic/old/translations/ast.js b/localcosmos_server/template_content/static/template_content/ckeditor5-build-classic/old/translations/ast.js
similarity index 100%
rename from localcosmos_server/online_content/static/online_content/ckeditor5-build-classic/old/translations/ast.js
rename to localcosmos_server/template_content/static/template_content/ckeditor5-build-classic/old/translations/ast.js
diff --git a/localcosmos_server/online_content/static/online_content/ckeditor5-build-classic/old/translations/bg.js b/localcosmos_server/template_content/static/template_content/ckeditor5-build-classic/old/translations/bg.js
similarity index 100%
rename from localcosmos_server/online_content/static/online_content/ckeditor5-build-classic/old/translations/bg.js
rename to localcosmos_server/template_content/static/template_content/ckeditor5-build-classic/old/translations/bg.js
diff --git a/localcosmos_server/online_content/static/online_content/ckeditor5-build-classic/old/translations/cs.js b/localcosmos_server/template_content/static/template_content/ckeditor5-build-classic/old/translations/cs.js
similarity index 100%
rename from localcosmos_server/online_content/static/online_content/ckeditor5-build-classic/old/translations/cs.js
rename to localcosmos_server/template_content/static/template_content/ckeditor5-build-classic/old/translations/cs.js
diff --git a/localcosmos_server/online_content/static/online_content/ckeditor5-build-classic/old/translations/da.js b/localcosmos_server/template_content/static/template_content/ckeditor5-build-classic/old/translations/da.js
similarity index 100%
rename from localcosmos_server/online_content/static/online_content/ckeditor5-build-classic/old/translations/da.js
rename to localcosmos_server/template_content/static/template_content/ckeditor5-build-classic/old/translations/da.js
diff --git a/localcosmos_server/online_content/static/online_content/ckeditor5-build-classic/old/translations/de.js b/localcosmos_server/template_content/static/template_content/ckeditor5-build-classic/old/translations/de.js
similarity index 100%
rename from localcosmos_server/online_content/static/online_content/ckeditor5-build-classic/old/translations/de.js
rename to localcosmos_server/template_content/static/template_content/ckeditor5-build-classic/old/translations/de.js
diff --git a/localcosmos_server/online_content/static/online_content/ckeditor5-build-classic/old/translations/el.js b/localcosmos_server/template_content/static/template_content/ckeditor5-build-classic/old/translations/el.js
similarity index 100%
rename from localcosmos_server/online_content/static/online_content/ckeditor5-build-classic/old/translations/el.js
rename to localcosmos_server/template_content/static/template_content/ckeditor5-build-classic/old/translations/el.js
diff --git a/localcosmos_server/online_content/static/online_content/ckeditor5-build-classic/old/translations/en-au.js b/localcosmos_server/template_content/static/template_content/ckeditor5-build-classic/old/translations/en-au.js
similarity index 100%
rename from localcosmos_server/online_content/static/online_content/ckeditor5-build-classic/old/translations/en-au.js
rename to localcosmos_server/template_content/static/template_content/ckeditor5-build-classic/old/translations/en-au.js
diff --git a/localcosmos_server/online_content/static/online_content/ckeditor5-build-classic/old/translations/eo.js b/localcosmos_server/template_content/static/template_content/ckeditor5-build-classic/old/translations/eo.js
similarity index 100%
rename from localcosmos_server/online_content/static/online_content/ckeditor5-build-classic/old/translations/eo.js
rename to localcosmos_server/template_content/static/template_content/ckeditor5-build-classic/old/translations/eo.js
diff --git a/localcosmos_server/online_content/static/online_content/ckeditor5-build-classic/old/translations/es.js b/localcosmos_server/template_content/static/template_content/ckeditor5-build-classic/old/translations/es.js
similarity index 100%
rename from localcosmos_server/online_content/static/online_content/ckeditor5-build-classic/old/translations/es.js
rename to localcosmos_server/template_content/static/template_content/ckeditor5-build-classic/old/translations/es.js
diff --git a/localcosmos_server/online_content/static/online_content/ckeditor5-build-classic/old/translations/et.js b/localcosmos_server/template_content/static/template_content/ckeditor5-build-classic/old/translations/et.js
similarity index 100%
rename from localcosmos_server/online_content/static/online_content/ckeditor5-build-classic/old/translations/et.js
rename to localcosmos_server/template_content/static/template_content/ckeditor5-build-classic/old/translations/et.js
diff --git a/localcosmos_server/online_content/static/online_content/ckeditor5-build-classic/old/translations/eu.js b/localcosmos_server/template_content/static/template_content/ckeditor5-build-classic/old/translations/eu.js
similarity index 100%
rename from localcosmos_server/online_content/static/online_content/ckeditor5-build-classic/old/translations/eu.js
rename to localcosmos_server/template_content/static/template_content/ckeditor5-build-classic/old/translations/eu.js
diff --git a/localcosmos_server/online_content/static/online_content/ckeditor5-build-classic/old/translations/fi.js b/localcosmos_server/template_content/static/template_content/ckeditor5-build-classic/old/translations/fi.js
similarity index 100%
rename from localcosmos_server/online_content/static/online_content/ckeditor5-build-classic/old/translations/fi.js
rename to localcosmos_server/template_content/static/template_content/ckeditor5-build-classic/old/translations/fi.js
diff --git a/localcosmos_server/online_content/static/online_content/ckeditor5-build-classic/old/translations/fr.js b/localcosmos_server/template_content/static/template_content/ckeditor5-build-classic/old/translations/fr.js
similarity index 100%
rename from localcosmos_server/online_content/static/online_content/ckeditor5-build-classic/old/translations/fr.js
rename to localcosmos_server/template_content/static/template_content/ckeditor5-build-classic/old/translations/fr.js
diff --git a/localcosmos_server/online_content/static/online_content/ckeditor5-build-classic/old/translations/gl.js b/localcosmos_server/template_content/static/template_content/ckeditor5-build-classic/old/translations/gl.js
similarity index 100%
rename from localcosmos_server/online_content/static/online_content/ckeditor5-build-classic/old/translations/gl.js
rename to localcosmos_server/template_content/static/template_content/ckeditor5-build-classic/old/translations/gl.js
diff --git a/localcosmos_server/online_content/static/online_content/ckeditor5-build-classic/old/translations/gu.js b/localcosmos_server/template_content/static/template_content/ckeditor5-build-classic/old/translations/gu.js
similarity index 100%
rename from localcosmos_server/online_content/static/online_content/ckeditor5-build-classic/old/translations/gu.js
rename to localcosmos_server/template_content/static/template_content/ckeditor5-build-classic/old/translations/gu.js
diff --git a/localcosmos_server/online_content/static/online_content/ckeditor5-build-classic/old/translations/hr.js b/localcosmos_server/template_content/static/template_content/ckeditor5-build-classic/old/translations/hr.js
similarity index 100%
rename from localcosmos_server/online_content/static/online_content/ckeditor5-build-classic/old/translations/hr.js
rename to localcosmos_server/template_content/static/template_content/ckeditor5-build-classic/old/translations/hr.js
diff --git a/localcosmos_server/online_content/static/online_content/ckeditor5-build-classic/old/translations/hu.js b/localcosmos_server/template_content/static/template_content/ckeditor5-build-classic/old/translations/hu.js
similarity index 100%
rename from localcosmos_server/online_content/static/online_content/ckeditor5-build-classic/old/translations/hu.js
rename to localcosmos_server/template_content/static/template_content/ckeditor5-build-classic/old/translations/hu.js
diff --git a/localcosmos_server/online_content/static/online_content/ckeditor5-build-classic/old/translations/it.js b/localcosmos_server/template_content/static/template_content/ckeditor5-build-classic/old/translations/it.js
similarity index 100%
rename from localcosmos_server/online_content/static/online_content/ckeditor5-build-classic/old/translations/it.js
rename to localcosmos_server/template_content/static/template_content/ckeditor5-build-classic/old/translations/it.js
diff --git a/localcosmos_server/online_content/static/online_content/ckeditor5-build-classic/old/translations/ja.js b/localcosmos_server/template_content/static/template_content/ckeditor5-build-classic/old/translations/ja.js
similarity index 100%
rename from localcosmos_server/online_content/static/online_content/ckeditor5-build-classic/old/translations/ja.js
rename to localcosmos_server/template_content/static/template_content/ckeditor5-build-classic/old/translations/ja.js
diff --git a/localcosmos_server/online_content/static/online_content/ckeditor5-build-classic/old/translations/km.js b/localcosmos_server/template_content/static/template_content/ckeditor5-build-classic/old/translations/km.js
similarity index 100%
rename from localcosmos_server/online_content/static/online_content/ckeditor5-build-classic/old/translations/km.js
rename to localcosmos_server/template_content/static/template_content/ckeditor5-build-classic/old/translations/km.js
diff --git a/localcosmos_server/online_content/static/online_content/ckeditor5-build-classic/old/translations/kn.js b/localcosmos_server/template_content/static/template_content/ckeditor5-build-classic/old/translations/kn.js
similarity index 100%
rename from localcosmos_server/online_content/static/online_content/ckeditor5-build-classic/old/translations/kn.js
rename to localcosmos_server/template_content/static/template_content/ckeditor5-build-classic/old/translations/kn.js
diff --git a/localcosmos_server/online_content/static/online_content/ckeditor5-build-classic/old/translations/ko.js b/localcosmos_server/template_content/static/template_content/ckeditor5-build-classic/old/translations/ko.js
similarity index 100%
rename from localcosmos_server/online_content/static/online_content/ckeditor5-build-classic/old/translations/ko.js
rename to localcosmos_server/template_content/static/template_content/ckeditor5-build-classic/old/translations/ko.js
diff --git a/localcosmos_server/online_content/static/online_content/ckeditor5-build-classic/old/translations/ku.js b/localcosmos_server/template_content/static/template_content/ckeditor5-build-classic/old/translations/ku.js
similarity index 100%
rename from localcosmos_server/online_content/static/online_content/ckeditor5-build-classic/old/translations/ku.js
rename to localcosmos_server/template_content/static/template_content/ckeditor5-build-classic/old/translations/ku.js
diff --git a/localcosmos_server/online_content/static/online_content/ckeditor5-build-classic/old/translations/nb.js b/localcosmos_server/template_content/static/template_content/ckeditor5-build-classic/old/translations/nb.js
similarity index 100%
rename from localcosmos_server/online_content/static/online_content/ckeditor5-build-classic/old/translations/nb.js
rename to localcosmos_server/template_content/static/template_content/ckeditor5-build-classic/old/translations/nb.js
diff --git a/localcosmos_server/online_content/static/online_content/ckeditor5-build-classic/old/translations/ne.js b/localcosmos_server/template_content/static/template_content/ckeditor5-build-classic/old/translations/ne.js
similarity index 100%
rename from localcosmos_server/online_content/static/online_content/ckeditor5-build-classic/old/translations/ne.js
rename to localcosmos_server/template_content/static/template_content/ckeditor5-build-classic/old/translations/ne.js
diff --git a/localcosmos_server/online_content/static/online_content/ckeditor5-build-classic/old/translations/nl.js b/localcosmos_server/template_content/static/template_content/ckeditor5-build-classic/old/translations/nl.js
similarity index 100%
rename from localcosmos_server/online_content/static/online_content/ckeditor5-build-classic/old/translations/nl.js
rename to localcosmos_server/template_content/static/template_content/ckeditor5-build-classic/old/translations/nl.js
diff --git a/localcosmos_server/online_content/static/online_content/ckeditor5-build-classic/old/translations/oc.js b/localcosmos_server/template_content/static/template_content/ckeditor5-build-classic/old/translations/oc.js
similarity index 100%
rename from localcosmos_server/online_content/static/online_content/ckeditor5-build-classic/old/translations/oc.js
rename to localcosmos_server/template_content/static/template_content/ckeditor5-build-classic/old/translations/oc.js
diff --git a/localcosmos_server/online_content/static/online_content/ckeditor5-build-classic/old/translations/pl.js b/localcosmos_server/template_content/static/template_content/ckeditor5-build-classic/old/translations/pl.js
similarity index 100%
rename from localcosmos_server/online_content/static/online_content/ckeditor5-build-classic/old/translations/pl.js
rename to localcosmos_server/template_content/static/template_content/ckeditor5-build-classic/old/translations/pl.js
diff --git a/localcosmos_server/online_content/static/online_content/ckeditor5-build-classic/old/translations/pt-br.js b/localcosmos_server/template_content/static/template_content/ckeditor5-build-classic/old/translations/pt-br.js
similarity index 100%
rename from localcosmos_server/online_content/static/online_content/ckeditor5-build-classic/old/translations/pt-br.js
rename to localcosmos_server/template_content/static/template_content/ckeditor5-build-classic/old/translations/pt-br.js
diff --git a/localcosmos_server/online_content/static/online_content/ckeditor5-build-classic/old/translations/pt.js b/localcosmos_server/template_content/static/template_content/ckeditor5-build-classic/old/translations/pt.js
similarity index 100%
rename from localcosmos_server/online_content/static/online_content/ckeditor5-build-classic/old/translations/pt.js
rename to localcosmos_server/template_content/static/template_content/ckeditor5-build-classic/old/translations/pt.js
diff --git a/localcosmos_server/online_content/static/online_content/ckeditor5-build-classic/old/translations/ro.js b/localcosmos_server/template_content/static/template_content/ckeditor5-build-classic/old/translations/ro.js
similarity index 100%
rename from localcosmos_server/online_content/static/online_content/ckeditor5-build-classic/old/translations/ro.js
rename to localcosmos_server/template_content/static/template_content/ckeditor5-build-classic/old/translations/ro.js
diff --git a/localcosmos_server/online_content/static/online_content/ckeditor5-build-classic/old/translations/ru.js b/localcosmos_server/template_content/static/template_content/ckeditor5-build-classic/old/translations/ru.js
similarity index 100%
rename from localcosmos_server/online_content/static/online_content/ckeditor5-build-classic/old/translations/ru.js
rename to localcosmos_server/template_content/static/template_content/ckeditor5-build-classic/old/translations/ru.js
diff --git a/localcosmos_server/online_content/static/online_content/ckeditor5-build-classic/old/translations/si.js b/localcosmos_server/template_content/static/template_content/ckeditor5-build-classic/old/translations/si.js
similarity index 100%
rename from localcosmos_server/online_content/static/online_content/ckeditor5-build-classic/old/translations/si.js
rename to localcosmos_server/template_content/static/template_content/ckeditor5-build-classic/old/translations/si.js
diff --git a/localcosmos_server/online_content/static/online_content/ckeditor5-build-classic/old/translations/sk.js b/localcosmos_server/template_content/static/template_content/ckeditor5-build-classic/old/translations/sk.js
similarity index 100%
rename from localcosmos_server/online_content/static/online_content/ckeditor5-build-classic/old/translations/sk.js
rename to localcosmos_server/template_content/static/template_content/ckeditor5-build-classic/old/translations/sk.js
diff --git a/localcosmos_server/online_content/static/online_content/ckeditor5-build-classic/old/translations/sq.js b/localcosmos_server/template_content/static/template_content/ckeditor5-build-classic/old/translations/sq.js
similarity index 100%
rename from localcosmos_server/online_content/static/online_content/ckeditor5-build-classic/old/translations/sq.js
rename to localcosmos_server/template_content/static/template_content/ckeditor5-build-classic/old/translations/sq.js
diff --git a/localcosmos_server/online_content/static/online_content/ckeditor5-build-classic/old/translations/sv.js b/localcosmos_server/template_content/static/template_content/ckeditor5-build-classic/old/translations/sv.js
similarity index 100%
rename from localcosmos_server/online_content/static/online_content/ckeditor5-build-classic/old/translations/sv.js
rename to localcosmos_server/template_content/static/template_content/ckeditor5-build-classic/old/translations/sv.js
diff --git a/localcosmos_server/online_content/static/online_content/ckeditor5-build-classic/old/translations/tr.js b/localcosmos_server/template_content/static/template_content/ckeditor5-build-classic/old/translations/tr.js
similarity index 100%
rename from localcosmos_server/online_content/static/online_content/ckeditor5-build-classic/old/translations/tr.js
rename to localcosmos_server/template_content/static/template_content/ckeditor5-build-classic/old/translations/tr.js
diff --git a/localcosmos_server/online_content/static/online_content/ckeditor5-build-classic/old/translations/tt.js b/localcosmos_server/template_content/static/template_content/ckeditor5-build-classic/old/translations/tt.js
similarity index 100%
rename from localcosmos_server/online_content/static/online_content/ckeditor5-build-classic/old/translations/tt.js
rename to localcosmos_server/template_content/static/template_content/ckeditor5-build-classic/old/translations/tt.js
diff --git a/localcosmos_server/online_content/static/online_content/ckeditor5-build-classic/old/translations/ug.js b/localcosmos_server/template_content/static/template_content/ckeditor5-build-classic/old/translations/ug.js
similarity index 100%
rename from localcosmos_server/online_content/static/online_content/ckeditor5-build-classic/old/translations/ug.js
rename to localcosmos_server/template_content/static/template_content/ckeditor5-build-classic/old/translations/ug.js
diff --git a/localcosmos_server/online_content/static/online_content/ckeditor5-build-classic/old/translations/uk.js b/localcosmos_server/template_content/static/template_content/ckeditor5-build-classic/old/translations/uk.js
similarity index 100%
rename from localcosmos_server/online_content/static/online_content/ckeditor5-build-classic/old/translations/uk.js
rename to localcosmos_server/template_content/static/template_content/ckeditor5-build-classic/old/translations/uk.js
diff --git a/localcosmos_server/online_content/static/online_content/ckeditor5-build-classic/old/translations/zh-cn.js b/localcosmos_server/template_content/static/template_content/ckeditor5-build-classic/old/translations/zh-cn.js
similarity index 100%
rename from localcosmos_server/online_content/static/online_content/ckeditor5-build-classic/old/translations/zh-cn.js
rename to localcosmos_server/template_content/static/template_content/ckeditor5-build-classic/old/translations/zh-cn.js
diff --git a/localcosmos_server/online_content/static/online_content/ckeditor5-build-classic/old/translations/zh.js b/localcosmos_server/template_content/static/template_content/ckeditor5-build-classic/old/translations/zh.js
similarity index 100%
rename from localcosmos_server/online_content/static/online_content/ckeditor5-build-classic/old/translations/zh.js
rename to localcosmos_server/template_content/static/template_content/ckeditor5-build-classic/old/translations/zh.js
diff --git a/localcosmos_server/online_content/static/online_content/ckeditor5-build-classic/sample/css/sample.css b/localcosmos_server/template_content/static/template_content/ckeditor5-build-classic/sample/css/sample.css
similarity index 100%
rename from localcosmos_server/online_content/static/online_content/ckeditor5-build-classic/sample/css/sample.css
rename to localcosmos_server/template_content/static/template_content/ckeditor5-build-classic/sample/css/sample.css
diff --git a/localcosmos_server/online_content/static/online_content/ckeditor5-build-classic/sample/img/bg.png b/localcosmos_server/template_content/static/template_content/ckeditor5-build-classic/sample/img/bg.png
similarity index 100%
rename from localcosmos_server/online_content/static/online_content/ckeditor5-build-classic/sample/img/bg.png
rename to localcosmos_server/template_content/static/template_content/ckeditor5-build-classic/sample/img/bg.png
diff --git a/localcosmos_server/online_content/static/online_content/ckeditor5-build-classic/sample/img/github.svg b/localcosmos_server/template_content/static/template_content/ckeditor5-build-classic/sample/img/github.svg
similarity index 100%
rename from localcosmos_server/online_content/static/online_content/ckeditor5-build-classic/sample/img/github.svg
rename to localcosmos_server/template_content/static/template_content/ckeditor5-build-classic/sample/img/github.svg
diff --git a/localcosmos_server/online_content/static/online_content/ckeditor5-build-classic/sample/img/logo.svg b/localcosmos_server/template_content/static/template_content/ckeditor5-build-classic/sample/img/logo.svg
similarity index 100%
rename from localcosmos_server/online_content/static/online_content/ckeditor5-build-classic/sample/img/logo.svg
rename to localcosmos_server/template_content/static/template_content/ckeditor5-build-classic/sample/img/logo.svg
diff --git a/localcosmos_server/online_content/static/online_content/ckeditor5-build-classic/sample/img/umbrellas.jpg b/localcosmos_server/template_content/static/template_content/ckeditor5-build-classic/sample/img/umbrellas.jpg
similarity index 100%
rename from localcosmos_server/online_content/static/online_content/ckeditor5-build-classic/sample/img/umbrellas.jpg
rename to localcosmos_server/template_content/static/template_content/ckeditor5-build-classic/sample/img/umbrellas.jpg
diff --git a/localcosmos_server/online_content/static/online_content/ckeditor5-build-classic/translations/af.js b/localcosmos_server/template_content/static/template_content/ckeditor5-build-classic/translations/af.js
similarity index 100%
rename from localcosmos_server/online_content/static/online_content/ckeditor5-build-classic/translations/af.js
rename to localcosmos_server/template_content/static/template_content/ckeditor5-build-classic/translations/af.js
diff --git a/localcosmos_server/online_content/static/online_content/ckeditor5-build-classic/translations/ar.js b/localcosmos_server/template_content/static/template_content/ckeditor5-build-classic/translations/ar.js
similarity index 100%
rename from localcosmos_server/online_content/static/online_content/ckeditor5-build-classic/translations/ar.js
rename to localcosmos_server/template_content/static/template_content/ckeditor5-build-classic/translations/ar.js
diff --git a/localcosmos_server/online_content/static/online_content/ckeditor5-build-classic/translations/ast.js b/localcosmos_server/template_content/static/template_content/ckeditor5-build-classic/translations/ast.js
similarity index 100%
rename from localcosmos_server/online_content/static/online_content/ckeditor5-build-classic/translations/ast.js
rename to localcosmos_server/template_content/static/template_content/ckeditor5-build-classic/translations/ast.js
diff --git a/localcosmos_server/online_content/static/online_content/ckeditor5-build-classic/translations/az.js b/localcosmos_server/template_content/static/template_content/ckeditor5-build-classic/translations/az.js
similarity index 100%
rename from localcosmos_server/online_content/static/online_content/ckeditor5-build-classic/translations/az.js
rename to localcosmos_server/template_content/static/template_content/ckeditor5-build-classic/translations/az.js
diff --git a/localcosmos_server/online_content/static/online_content/ckeditor5-build-classic/translations/bg.js b/localcosmos_server/template_content/static/template_content/ckeditor5-build-classic/translations/bg.js
similarity index 100%
rename from localcosmos_server/online_content/static/online_content/ckeditor5-build-classic/translations/bg.js
rename to localcosmos_server/template_content/static/template_content/ckeditor5-build-classic/translations/bg.js
diff --git a/localcosmos_server/online_content/static/online_content/ckeditor5-build-classic/translations/ca.js b/localcosmos_server/template_content/static/template_content/ckeditor5-build-classic/translations/ca.js
similarity index 100%
rename from localcosmos_server/online_content/static/online_content/ckeditor5-build-classic/translations/ca.js
rename to localcosmos_server/template_content/static/template_content/ckeditor5-build-classic/translations/ca.js
diff --git a/localcosmos_server/online_content/static/online_content/ckeditor5-build-classic/translations/cs.js b/localcosmos_server/template_content/static/template_content/ckeditor5-build-classic/translations/cs.js
similarity index 100%
rename from localcosmos_server/online_content/static/online_content/ckeditor5-build-classic/translations/cs.js
rename to localcosmos_server/template_content/static/template_content/ckeditor5-build-classic/translations/cs.js
diff --git a/localcosmos_server/online_content/static/online_content/ckeditor5-build-classic/translations/da.js b/localcosmos_server/template_content/static/template_content/ckeditor5-build-classic/translations/da.js
similarity index 100%
rename from localcosmos_server/online_content/static/online_content/ckeditor5-build-classic/translations/da.js
rename to localcosmos_server/template_content/static/template_content/ckeditor5-build-classic/translations/da.js
diff --git a/localcosmos_server/online_content/static/online_content/ckeditor5-build-classic/translations/de-ch.js b/localcosmos_server/template_content/static/template_content/ckeditor5-build-classic/translations/de-ch.js
similarity index 100%
rename from localcosmos_server/online_content/static/online_content/ckeditor5-build-classic/translations/de-ch.js
rename to localcosmos_server/template_content/static/template_content/ckeditor5-build-classic/translations/de-ch.js
diff --git a/localcosmos_server/online_content/static/online_content/ckeditor5-build-classic/translations/de.js b/localcosmos_server/template_content/static/template_content/ckeditor5-build-classic/translations/de.js
similarity index 100%
rename from localcosmos_server/online_content/static/online_content/ckeditor5-build-classic/translations/de.js
rename to localcosmos_server/template_content/static/template_content/ckeditor5-build-classic/translations/de.js
diff --git a/localcosmos_server/online_content/static/online_content/ckeditor5-build-classic/translations/el.js b/localcosmos_server/template_content/static/template_content/ckeditor5-build-classic/translations/el.js
similarity index 100%
rename from localcosmos_server/online_content/static/online_content/ckeditor5-build-classic/translations/el.js
rename to localcosmos_server/template_content/static/template_content/ckeditor5-build-classic/translations/el.js
diff --git a/localcosmos_server/online_content/static/online_content/ckeditor5-build-classic/translations/en-au.js b/localcosmos_server/template_content/static/template_content/ckeditor5-build-classic/translations/en-au.js
similarity index 100%
rename from localcosmos_server/online_content/static/online_content/ckeditor5-build-classic/translations/en-au.js
rename to localcosmos_server/template_content/static/template_content/ckeditor5-build-classic/translations/en-au.js
diff --git a/localcosmos_server/online_content/static/online_content/ckeditor5-build-classic/translations/en-gb.js b/localcosmos_server/template_content/static/template_content/ckeditor5-build-classic/translations/en-gb.js
similarity index 100%
rename from localcosmos_server/online_content/static/online_content/ckeditor5-build-classic/translations/en-gb.js
rename to localcosmos_server/template_content/static/template_content/ckeditor5-build-classic/translations/en-gb.js
diff --git a/localcosmos_server/online_content/static/online_content/ckeditor5-build-classic/translations/eo.js b/localcosmos_server/template_content/static/template_content/ckeditor5-build-classic/translations/eo.js
similarity index 100%
rename from localcosmos_server/online_content/static/online_content/ckeditor5-build-classic/translations/eo.js
rename to localcosmos_server/template_content/static/template_content/ckeditor5-build-classic/translations/eo.js
diff --git a/localcosmos_server/online_content/static/online_content/ckeditor5-build-classic/translations/es.js b/localcosmos_server/template_content/static/template_content/ckeditor5-build-classic/translations/es.js
similarity index 100%
rename from localcosmos_server/online_content/static/online_content/ckeditor5-build-classic/translations/es.js
rename to localcosmos_server/template_content/static/template_content/ckeditor5-build-classic/translations/es.js
diff --git a/localcosmos_server/online_content/static/online_content/ckeditor5-build-classic/translations/et.js b/localcosmos_server/template_content/static/template_content/ckeditor5-build-classic/translations/et.js
similarity index 100%
rename from localcosmos_server/online_content/static/online_content/ckeditor5-build-classic/translations/et.js
rename to localcosmos_server/template_content/static/template_content/ckeditor5-build-classic/translations/et.js
diff --git a/localcosmos_server/online_content/static/online_content/ckeditor5-build-classic/translations/eu.js b/localcosmos_server/template_content/static/template_content/ckeditor5-build-classic/translations/eu.js
similarity index 100%
rename from localcosmos_server/online_content/static/online_content/ckeditor5-build-classic/translations/eu.js
rename to localcosmos_server/template_content/static/template_content/ckeditor5-build-classic/translations/eu.js
diff --git a/localcosmos_server/online_content/static/online_content/ckeditor5-build-classic/translations/fa.js b/localcosmos_server/template_content/static/template_content/ckeditor5-build-classic/translations/fa.js
similarity index 100%
rename from localcosmos_server/online_content/static/online_content/ckeditor5-build-classic/translations/fa.js
rename to localcosmos_server/template_content/static/template_content/ckeditor5-build-classic/translations/fa.js
diff --git a/localcosmos_server/online_content/static/online_content/ckeditor5-build-classic/translations/fi.js b/localcosmos_server/template_content/static/template_content/ckeditor5-build-classic/translations/fi.js
similarity index 100%
rename from localcosmos_server/online_content/static/online_content/ckeditor5-build-classic/translations/fi.js
rename to localcosmos_server/template_content/static/template_content/ckeditor5-build-classic/translations/fi.js
diff --git a/localcosmos_server/online_content/static/online_content/ckeditor5-build-classic/translations/fr.js b/localcosmos_server/template_content/static/template_content/ckeditor5-build-classic/translations/fr.js
similarity index 100%
rename from localcosmos_server/online_content/static/online_content/ckeditor5-build-classic/translations/fr.js
rename to localcosmos_server/template_content/static/template_content/ckeditor5-build-classic/translations/fr.js
diff --git a/localcosmos_server/online_content/static/online_content/ckeditor5-build-classic/translations/gl.js b/localcosmos_server/template_content/static/template_content/ckeditor5-build-classic/translations/gl.js
similarity index 100%
rename from localcosmos_server/online_content/static/online_content/ckeditor5-build-classic/translations/gl.js
rename to localcosmos_server/template_content/static/template_content/ckeditor5-build-classic/translations/gl.js
diff --git a/localcosmos_server/online_content/static/online_content/ckeditor5-build-classic/translations/gu.js b/localcosmos_server/template_content/static/template_content/ckeditor5-build-classic/translations/gu.js
similarity index 100%
rename from localcosmos_server/online_content/static/online_content/ckeditor5-build-classic/translations/gu.js
rename to localcosmos_server/template_content/static/template_content/ckeditor5-build-classic/translations/gu.js
diff --git a/localcosmos_server/online_content/static/online_content/ckeditor5-build-classic/translations/he.js b/localcosmos_server/template_content/static/template_content/ckeditor5-build-classic/translations/he.js
similarity index 100%
rename from localcosmos_server/online_content/static/online_content/ckeditor5-build-classic/translations/he.js
rename to localcosmos_server/template_content/static/template_content/ckeditor5-build-classic/translations/he.js
diff --git a/localcosmos_server/online_content/static/online_content/ckeditor5-build-classic/translations/hr.js b/localcosmos_server/template_content/static/template_content/ckeditor5-build-classic/translations/hr.js
similarity index 100%
rename from localcosmos_server/online_content/static/online_content/ckeditor5-build-classic/translations/hr.js
rename to localcosmos_server/template_content/static/template_content/ckeditor5-build-classic/translations/hr.js
diff --git a/localcosmos_server/online_content/static/online_content/ckeditor5-build-classic/translations/hu.js b/localcosmos_server/template_content/static/template_content/ckeditor5-build-classic/translations/hu.js
similarity index 100%
rename from localcosmos_server/online_content/static/online_content/ckeditor5-build-classic/translations/hu.js
rename to localcosmos_server/template_content/static/template_content/ckeditor5-build-classic/translations/hu.js
diff --git a/localcosmos_server/online_content/static/online_content/ckeditor5-build-classic/translations/id.js b/localcosmos_server/template_content/static/template_content/ckeditor5-build-classic/translations/id.js
similarity index 100%
rename from localcosmos_server/online_content/static/online_content/ckeditor5-build-classic/translations/id.js
rename to localcosmos_server/template_content/static/template_content/ckeditor5-build-classic/translations/id.js
diff --git a/localcosmos_server/online_content/static/online_content/ckeditor5-build-classic/translations/it.js b/localcosmos_server/template_content/static/template_content/ckeditor5-build-classic/translations/it.js
similarity index 100%
rename from localcosmos_server/online_content/static/online_content/ckeditor5-build-classic/translations/it.js
rename to localcosmos_server/template_content/static/template_content/ckeditor5-build-classic/translations/it.js
diff --git a/localcosmos_server/online_content/static/online_content/ckeditor5-build-classic/translations/ja.js b/localcosmos_server/template_content/static/template_content/ckeditor5-build-classic/translations/ja.js
similarity index 100%
rename from localcosmos_server/online_content/static/online_content/ckeditor5-build-classic/translations/ja.js
rename to localcosmos_server/template_content/static/template_content/ckeditor5-build-classic/translations/ja.js
diff --git a/localcosmos_server/online_content/static/online_content/ckeditor5-build-classic/translations/km.js b/localcosmos_server/template_content/static/template_content/ckeditor5-build-classic/translations/km.js
similarity index 100%
rename from localcosmos_server/online_content/static/online_content/ckeditor5-build-classic/translations/km.js
rename to localcosmos_server/template_content/static/template_content/ckeditor5-build-classic/translations/km.js
diff --git a/localcosmos_server/online_content/static/online_content/ckeditor5-build-classic/translations/kn.js b/localcosmos_server/template_content/static/template_content/ckeditor5-build-classic/translations/kn.js
similarity index 100%
rename from localcosmos_server/online_content/static/online_content/ckeditor5-build-classic/translations/kn.js
rename to localcosmos_server/template_content/static/template_content/ckeditor5-build-classic/translations/kn.js
diff --git a/localcosmos_server/online_content/static/online_content/ckeditor5-build-classic/translations/ko.js b/localcosmos_server/template_content/static/template_content/ckeditor5-build-classic/translations/ko.js
similarity index 100%
rename from localcosmos_server/online_content/static/online_content/ckeditor5-build-classic/translations/ko.js
rename to localcosmos_server/template_content/static/template_content/ckeditor5-build-classic/translations/ko.js
diff --git a/localcosmos_server/online_content/static/online_content/ckeditor5-build-classic/translations/ku.js b/localcosmos_server/template_content/static/template_content/ckeditor5-build-classic/translations/ku.js
similarity index 100%
rename from localcosmos_server/online_content/static/online_content/ckeditor5-build-classic/translations/ku.js
rename to localcosmos_server/template_content/static/template_content/ckeditor5-build-classic/translations/ku.js
diff --git a/localcosmos_server/online_content/static/online_content/ckeditor5-build-classic/translations/lt.js b/localcosmos_server/template_content/static/template_content/ckeditor5-build-classic/translations/lt.js
similarity index 100%
rename from localcosmos_server/online_content/static/online_content/ckeditor5-build-classic/translations/lt.js
rename to localcosmos_server/template_content/static/template_content/ckeditor5-build-classic/translations/lt.js
diff --git a/localcosmos_server/online_content/static/online_content/ckeditor5-build-classic/translations/lv.js b/localcosmos_server/template_content/static/template_content/ckeditor5-build-classic/translations/lv.js
similarity index 100%
rename from localcosmos_server/online_content/static/online_content/ckeditor5-build-classic/translations/lv.js
rename to localcosmos_server/template_content/static/template_content/ckeditor5-build-classic/translations/lv.js
diff --git a/localcosmos_server/online_content/static/online_content/ckeditor5-build-classic/translations/ms.js b/localcosmos_server/template_content/static/template_content/ckeditor5-build-classic/translations/ms.js
similarity index 100%
rename from localcosmos_server/online_content/static/online_content/ckeditor5-build-classic/translations/ms.js
rename to localcosmos_server/template_content/static/template_content/ckeditor5-build-classic/translations/ms.js
diff --git a/localcosmos_server/online_content/static/online_content/ckeditor5-build-classic/translations/nb.js b/localcosmos_server/template_content/static/template_content/ckeditor5-build-classic/translations/nb.js
similarity index 100%
rename from localcosmos_server/online_content/static/online_content/ckeditor5-build-classic/translations/nb.js
rename to localcosmos_server/template_content/static/template_content/ckeditor5-build-classic/translations/nb.js
diff --git a/localcosmos_server/online_content/static/online_content/ckeditor5-build-classic/translations/ne.js b/localcosmos_server/template_content/static/template_content/ckeditor5-build-classic/translations/ne.js
similarity index 100%
rename from localcosmos_server/online_content/static/online_content/ckeditor5-build-classic/translations/ne.js
rename to localcosmos_server/template_content/static/template_content/ckeditor5-build-classic/translations/ne.js
diff --git a/localcosmos_server/online_content/static/online_content/ckeditor5-build-classic/translations/nl.js b/localcosmos_server/template_content/static/template_content/ckeditor5-build-classic/translations/nl.js
similarity index 100%
rename from localcosmos_server/online_content/static/online_content/ckeditor5-build-classic/translations/nl.js
rename to localcosmos_server/template_content/static/template_content/ckeditor5-build-classic/translations/nl.js
diff --git a/localcosmos_server/online_content/static/online_content/ckeditor5-build-classic/translations/no.js b/localcosmos_server/template_content/static/template_content/ckeditor5-build-classic/translations/no.js
similarity index 100%
rename from localcosmos_server/online_content/static/online_content/ckeditor5-build-classic/translations/no.js
rename to localcosmos_server/template_content/static/template_content/ckeditor5-build-classic/translations/no.js
diff --git a/localcosmos_server/online_content/static/online_content/ckeditor5-build-classic/translations/oc.js b/localcosmos_server/template_content/static/template_content/ckeditor5-build-classic/translations/oc.js
similarity index 100%
rename from localcosmos_server/online_content/static/online_content/ckeditor5-build-classic/translations/oc.js
rename to localcosmos_server/template_content/static/template_content/ckeditor5-build-classic/translations/oc.js
diff --git a/localcosmos_server/online_content/static/online_content/ckeditor5-build-classic/translations/pl.js b/localcosmos_server/template_content/static/template_content/ckeditor5-build-classic/translations/pl.js
similarity index 100%
rename from localcosmos_server/online_content/static/online_content/ckeditor5-build-classic/translations/pl.js
rename to localcosmos_server/template_content/static/template_content/ckeditor5-build-classic/translations/pl.js
diff --git a/localcosmos_server/online_content/static/online_content/ckeditor5-build-classic/translations/pt-br.js b/localcosmos_server/template_content/static/template_content/ckeditor5-build-classic/translations/pt-br.js
similarity index 100%
rename from localcosmos_server/online_content/static/online_content/ckeditor5-build-classic/translations/pt-br.js
rename to localcosmos_server/template_content/static/template_content/ckeditor5-build-classic/translations/pt-br.js
diff --git a/localcosmos_server/online_content/static/online_content/ckeditor5-build-classic/translations/pt.js b/localcosmos_server/template_content/static/template_content/ckeditor5-build-classic/translations/pt.js
similarity index 100%
rename from localcosmos_server/online_content/static/online_content/ckeditor5-build-classic/translations/pt.js
rename to localcosmos_server/template_content/static/template_content/ckeditor5-build-classic/translations/pt.js
diff --git a/localcosmos_server/online_content/static/online_content/ckeditor5-build-classic/translations/ro.js b/localcosmos_server/template_content/static/template_content/ckeditor5-build-classic/translations/ro.js
similarity index 100%
rename from localcosmos_server/online_content/static/online_content/ckeditor5-build-classic/translations/ro.js
rename to localcosmos_server/template_content/static/template_content/ckeditor5-build-classic/translations/ro.js
diff --git a/localcosmos_server/online_content/static/online_content/ckeditor5-build-classic/translations/ru.js b/localcosmos_server/template_content/static/template_content/ckeditor5-build-classic/translations/ru.js
similarity index 100%
rename from localcosmos_server/online_content/static/online_content/ckeditor5-build-classic/translations/ru.js
rename to localcosmos_server/template_content/static/template_content/ckeditor5-build-classic/translations/ru.js
diff --git a/localcosmos_server/online_content/static/online_content/ckeditor5-build-classic/translations/si.js b/localcosmos_server/template_content/static/template_content/ckeditor5-build-classic/translations/si.js
similarity index 100%
rename from localcosmos_server/online_content/static/online_content/ckeditor5-build-classic/translations/si.js
rename to localcosmos_server/template_content/static/template_content/ckeditor5-build-classic/translations/si.js
diff --git a/localcosmos_server/online_content/static/online_content/ckeditor5-build-classic/translations/sk.js b/localcosmos_server/template_content/static/template_content/ckeditor5-build-classic/translations/sk.js
similarity index 100%
rename from localcosmos_server/online_content/static/online_content/ckeditor5-build-classic/translations/sk.js
rename to localcosmos_server/template_content/static/template_content/ckeditor5-build-classic/translations/sk.js
diff --git a/localcosmos_server/online_content/static/online_content/ckeditor5-build-classic/translations/sl.js b/localcosmos_server/template_content/static/template_content/ckeditor5-build-classic/translations/sl.js
similarity index 100%
rename from localcosmos_server/online_content/static/online_content/ckeditor5-build-classic/translations/sl.js
rename to localcosmos_server/template_content/static/template_content/ckeditor5-build-classic/translations/sl.js
diff --git a/localcosmos_server/online_content/static/online_content/ckeditor5-build-classic/translations/sq.js b/localcosmos_server/template_content/static/template_content/ckeditor5-build-classic/translations/sq.js
similarity index 100%
rename from localcosmos_server/online_content/static/online_content/ckeditor5-build-classic/translations/sq.js
rename to localcosmos_server/template_content/static/template_content/ckeditor5-build-classic/translations/sq.js
diff --git a/localcosmos_server/online_content/static/online_content/ckeditor5-build-classic/translations/sr-latn.js b/localcosmos_server/template_content/static/template_content/ckeditor5-build-classic/translations/sr-latn.js
similarity index 100%
rename from localcosmos_server/online_content/static/online_content/ckeditor5-build-classic/translations/sr-latn.js
rename to localcosmos_server/template_content/static/template_content/ckeditor5-build-classic/translations/sr-latn.js
diff --git a/localcosmos_server/online_content/static/online_content/ckeditor5-build-classic/translations/sr.js b/localcosmos_server/template_content/static/template_content/ckeditor5-build-classic/translations/sr.js
similarity index 100%
rename from localcosmos_server/online_content/static/online_content/ckeditor5-build-classic/translations/sr.js
rename to localcosmos_server/template_content/static/template_content/ckeditor5-build-classic/translations/sr.js
diff --git a/localcosmos_server/online_content/static/online_content/ckeditor5-build-classic/translations/sv.js b/localcosmos_server/template_content/static/template_content/ckeditor5-build-classic/translations/sv.js
similarity index 100%
rename from localcosmos_server/online_content/static/online_content/ckeditor5-build-classic/translations/sv.js
rename to localcosmos_server/template_content/static/template_content/ckeditor5-build-classic/translations/sv.js
diff --git a/localcosmos_server/online_content/static/online_content/ckeditor5-build-classic/translations/th.js b/localcosmos_server/template_content/static/template_content/ckeditor5-build-classic/translations/th.js
similarity index 100%
rename from localcosmos_server/online_content/static/online_content/ckeditor5-build-classic/translations/th.js
rename to localcosmos_server/template_content/static/template_content/ckeditor5-build-classic/translations/th.js
diff --git a/localcosmos_server/online_content/static/online_content/ckeditor5-build-classic/translations/tr.js b/localcosmos_server/template_content/static/template_content/ckeditor5-build-classic/translations/tr.js
similarity index 100%
rename from localcosmos_server/online_content/static/online_content/ckeditor5-build-classic/translations/tr.js
rename to localcosmos_server/template_content/static/template_content/ckeditor5-build-classic/translations/tr.js
diff --git a/localcosmos_server/online_content/static/online_content/ckeditor5-build-classic/translations/tt.js b/localcosmos_server/template_content/static/template_content/ckeditor5-build-classic/translations/tt.js
similarity index 100%
rename from localcosmos_server/online_content/static/online_content/ckeditor5-build-classic/translations/tt.js
rename to localcosmos_server/template_content/static/template_content/ckeditor5-build-classic/translations/tt.js
diff --git a/localcosmos_server/online_content/static/online_content/ckeditor5-build-classic/translations/ug.js b/localcosmos_server/template_content/static/template_content/ckeditor5-build-classic/translations/ug.js
similarity index 100%
rename from localcosmos_server/online_content/static/online_content/ckeditor5-build-classic/translations/ug.js
rename to localcosmos_server/template_content/static/template_content/ckeditor5-build-classic/translations/ug.js
diff --git a/localcosmos_server/online_content/static/online_content/ckeditor5-build-classic/translations/uk.js b/localcosmos_server/template_content/static/template_content/ckeditor5-build-classic/translations/uk.js
similarity index 100%
rename from localcosmos_server/online_content/static/online_content/ckeditor5-build-classic/translations/uk.js
rename to localcosmos_server/template_content/static/template_content/ckeditor5-build-classic/translations/uk.js
diff --git a/localcosmos_server/online_content/static/online_content/ckeditor5-build-classic/translations/vi.js b/localcosmos_server/template_content/static/template_content/ckeditor5-build-classic/translations/vi.js
similarity index 100%
rename from localcosmos_server/online_content/static/online_content/ckeditor5-build-classic/translations/vi.js
rename to localcosmos_server/template_content/static/template_content/ckeditor5-build-classic/translations/vi.js
diff --git a/localcosmos_server/online_content/static/online_content/ckeditor5-build-classic/translations/zh-cn.js b/localcosmos_server/template_content/static/template_content/ckeditor5-build-classic/translations/zh-cn.js
similarity index 100%
rename from localcosmos_server/online_content/static/online_content/ckeditor5-build-classic/translations/zh-cn.js
rename to localcosmos_server/template_content/static/template_content/ckeditor5-build-classic/translations/zh-cn.js
diff --git a/localcosmos_server/online_content/static/online_content/ckeditor5-build-classic/translations/zh.js b/localcosmos_server/template_content/static/template_content/ckeditor5-build-classic/translations/zh.js
similarity index 100%
rename from localcosmos_server/online_content/static/online_content/ckeditor5-build-classic/translations/zh.js
rename to localcosmos_server/template_content/static/template_content/ckeditor5-build-classic/translations/zh.js
diff --git a/localcosmos_server/online_content/static/online_content/online_content.css b/localcosmos_server/template_content/static/template_content/template_content.css
similarity index 100%
rename from localcosmos_server/online_content/static/online_content/online_content.css
rename to localcosmos_server/template_content/static/template_content/template_content.css
diff --git a/localcosmos_server/online_content/static/online_content/online_content.js b/localcosmos_server/template_content/static/template_content/template_content.js
similarity index 100%
rename from localcosmos_server/online_content/static/online_content/online_content.js
rename to localcosmos_server/template_content/static/template_content/template_content.js
diff --git a/localcosmos_server/template_content/templates/template_content/ajax/delete_template_content_image.html b/localcosmos_server/template_content/templates/template_content/ajax/delete_template_content_image.html
new file mode 100644
index 0000000..0a918a9
--- /dev/null
+++ b/localcosmos_server/template_content/templates/template_content/ajax/delete_template_content_image.html
@@ -0,0 +1,17 @@
+{% extends 'localcosmos_server/ajax/delete_server_content_image.html' %}
+
+{% block success_js %}
+
+{% endblock %}
diff --git a/localcosmos_server/online_content/templates/online_content/manage_template_content_extra_scripts.html b/localcosmos_server/template_content/templates/template_content/ajax/manage_template_content_extra_scripts.html
similarity index 60%
rename from localcosmos_server/online_content/templates/online_content/manage_template_content_extra_scripts.html
rename to localcosmos_server/template_content/templates/template_content/ajax/manage_template_content_extra_scripts.html
index 61c15ee..c636d50 100644
--- a/localcosmos_server/online_content/templates/online_content/manage_template_content_extra_scripts.html
+++ b/localcosmos_server/template_content/templates/template_content/ajax/manage_template_content_extra_scripts.html
@@ -1,13 +1,13 @@
{% load static %}
-
-
+
+
+
+
+
+
+{% endblock %}
\ No newline at end of file
diff --git a/localcosmos_server/template_content/templates/template_content/ajax/reload_template_content_images.html b/localcosmos_server/template_content/templates/template_content/ajax/reload_template_content_images.html
new file mode 100644
index 0000000..0cccb51
--- /dev/null
+++ b/localcosmos_server/template_content/templates/template_content/ajax/reload_template_content_images.html
@@ -0,0 +1,21 @@
+var cms_images_container = null;
+
+var current_edit = document.getElementById("content_image_current_edit");
+if (current_edit != null && current_edit.value && current_edit.value.length) {
+ cms_images_container_id = current_edit.value;
+
+ let element = document.getElementById(cms_images_container_id);
+ if (element != null) {
+
+ var cms_container_id = element.getAttribute("data-container-id");
+ cms_images_container = element;
+ }
+}
+
+var url = "{% url 'get_template_content_form_fields' localized_template_content.id content_key %}";
+$.get(url, function(html){
+ $(cms_images_container).html(html);
+ ajaxify(cms_images_container.id);
+});
+
+reloadPreview();
\ No newline at end of file
diff --git a/localcosmos_server/template_content/templates/template_content/ajax/reloaded_file_fields.html b/localcosmos_server/template_content/templates/template_content/ajax/reloaded_file_fields.html
new file mode 100644
index 0000000..aca1d1b
--- /dev/null
+++ b/localcosmos_server/template_content/templates/template_content/ajax/reloaded_file_fields.html
@@ -0,0 +1,3 @@
+{% for field in form %}
+ {% include 'template_content/widgets/filecontent_field.html' %}
+{% endfor %}
diff --git a/localcosmos_server/online_content/templates/online_content/ckeditor/layout-complex.js b/localcosmos_server/template_content/templates/template_content/ckeditor/layout-complex.js
similarity index 100%
rename from localcosmos_server/online_content/templates/online_content/ckeditor/layout-complex.js
rename to localcosmos_server/template_content/templates/template_content/ckeditor/layout-complex.js
diff --git a/localcosmos_server/online_content/templates/online_content/ckeditor/layout-simple.js b/localcosmos_server/template_content/templates/template_content/ckeditor/layout-simple.js
similarity index 100%
rename from localcosmos_server/online_content/templates/online_content/ckeditor/layout-simple.js
rename to localcosmos_server/template_content/templates/template_content/ckeditor/layout-simple.js
diff --git a/localcosmos_server/online_content/templates/online_content/create_template_content.html b/localcosmos_server/template_content/templates/template_content/create_template_content.html
similarity index 78%
rename from localcosmos_server/online_content/templates/online_content/create_template_content.html
rename to localcosmos_server/template_content/templates/template_content/create_template_content.html
index 5057aa7..d17ef74 100644
--- a/localcosmos_server/online_content/templates/online_content/create_template_content.html
+++ b/localcosmos_server/template_content/templates/template_content/create_template_content.html
@@ -1,4 +1,4 @@
-{% extends 'online_content/online_content_base.html' %}
+{% extends 'template_content/template_content_base.html' %}
{% load i18n localcosmos_tags %}
{% block subcontent %}
@@ -15,18 +15,19 @@ {% blocktrans %}Add new {{ template_type }}{% endblocktrans %}
{% else %}
- {% blocktrans %}Your frontend does not supply any templates for online content.{% endblocktrans %}
+ {% blocktrans %}Your frontend does not supply any templates.{% endblocktrans %}
{% endif %}
+
{% endblock %}
diff --git a/localcosmos_server/template_content/templates/template_content/manage_localized_template_content.html b/localcosmos_server/template_content/templates/template_content/manage_localized_template_content.html
new file mode 100644
index 0000000..a4c9764
--- /dev/null
+++ b/localcosmos_server/template_content/templates/template_content/manage_localized_template_content.html
@@ -0,0 +1,132 @@
+{% extends 'template_content/template_content_base.html' %}
+{% load i18n imagekit static localcosmos_tags %}
+
+{% block extra_style %}
+
+{% endblock %}
+
+{% block content %}
+
+
+
+
+ {{ localized_template_content.draft_title }}
+
+
+ {% if saved %}
+
+
+
+ {% trans 'Successfully saved your content.' %}
+
+
+
+ {% endif %}
+
+
+
+
+
+
+
+
+
+
+
+
+ {% render_taxonomic_restriction app localized_template_content.template_content %}
+
+
+
+
+
+
+
+
{% trans 'Components' %}
+
+
+
+
+
+
+{% endblock %}
+{% block extra_script %}
+ {% include 'template_content/ajax/manage_template_content_extra_scripts.html' %}
+
+
+{% endblock %}
diff --git a/localcosmos_server/template_content/templates/template_content/template_content_base.html b/localcosmos_server/template_content/templates/template_content/template_content_base.html
new file mode 100644
index 0000000..3741593
--- /dev/null
+++ b/localcosmos_server/template_content/templates/template_content/template_content_base.html
@@ -0,0 +1,52 @@
+{% extends request.is_appadmin|yesno:"app_admin/base.html,app_kit/base.html" %}
+{% load i18n static %}
+
+{% block extra_style %}
+
+{% endblock %}
+
+{% block header %}
+
+{% endblock %}
+
+{% block content %}
+
+
+
+
{{ app.name }} {% trans 'TemplateContent' %}
+
+
+ {% block subcontent %}
+
{% trans 'Pages' %}
+ {% if localized_template_contents %}
+
+ {% for localized_template_content in localized_template_contents %}
+ {% with template_content=localized_template_content.template_content %}
+
+ {% include 'template_content/template_content_list_entry.html' %}
+
+ {% endwith %}
+ {% endfor %}
+
+ {% else %}
+ {% trans 'You do not have any online pages for this app yet.' %}
+ {% endif %}
+
+
+ {% endblock %}
+
+
+
+{% endblock %}
+
+{% block extra_script %}{% endblock %}
diff --git a/localcosmos_server/online_content/templates/online_content/template_content_list_entry.html b/localcosmos_server/template_content/templates/template_content/template_content_list_entry.html
similarity index 62%
rename from localcosmos_server/online_content/templates/online_content/template_content_list_entry.html
rename to localcosmos_server/template_content/templates/template_content/template_content_list_entry.html
index 88d8512..fbe39dd 100644
--- a/localcosmos_server/online_content/templates/online_content/template_content_list_entry.html
+++ b/localcosmos_server/template_content/templates/template_content/template_content_list_entry.html
@@ -1,35 +1,34 @@
-{% load i18n static localcosmos_tags online_content_tags %}
+{% load i18n static localcosmos_tags template_content_tags %}
- {% get_template_content_locale template_content app.primary_language as primary_localized_template_content %}
{% for language in app.languages %}
- {% get_template_content_locale template_content language as localized_template_content %}
+ {% get_template_content_locale template_content language as locale %}
- {% if localized_template_content %}
+ {% if locale %}
- {% if localized_template_content.published_version %}
-
{% blocktrans with version=localized_template_content.published_version %}published version: {{ version }}{% endblocktrans %}
+ {% if locale.published_version %}
+
{% blocktrans with version=locale.published_version %}published version: {{ version }}{% endblocktrans %}
{% else %}
{% trans 'not published' %}
{% endif %}
-
{% blocktrans with version=localized_template_content.draft_version %}current version: {{ version }}{% endblocktrans %}
+
{% blocktrans with version=locale.draft_version %}current version: {{ version }}{% endblocktrans %}
{% if app.secondary_languages %}
- {% if localized_template_content.translation_ready %}
- {% if localized_template_content.draft_version != primary_localized_template_content.draft_version %}
+ {% if locale.translation_ready %}
+ {% if locale.draft_version != localized_template_content.draft_version %}
{% trans 'old version' %}
{% else %}
{% trans 'translation complete' %}
@@ -46,6 +45,7 @@
{% endfor %}
{% if publication %}
+
{% if publication_errors %}
@@ -73,17 +73,17 @@
{% trans 'Manage' %}
diff --git a/localcosmos_server/template_content/templates/template_content/widgets/filecontent_field.html b/localcosmos_server/template_content/templates/template_content/widgets/filecontent_field.html
new file mode 100644
index 0000000..0c2fbfd
--- /dev/null
+++ b/localcosmos_server/template_content/templates/template_content/widgets/filecontent_field.html
@@ -0,0 +1,30 @@
+{% load i18n static imagekit %}
+{% with cms_object=field.field.cms_tag %}
+
+
+
+ {{ field }}
+
+
+
+
+
+
+
+
+
+
+{% endwith %}
\ No newline at end of file
diff --git a/localcosmos_server/online_content/templates/online_content/textcontent_field.html b/localcosmos_server/template_content/templates/template_content/widgets/textcontent_field.html
similarity index 100%
rename from localcosmos_server/online_content/templates/online_content/textcontent_field.html
rename to localcosmos_server/template_content/templates/template_content/widgets/textcontent_field.html
diff --git a/localcosmos_server/online_content/templatetags/__init__.py b/localcosmos_server/template_content/templatetags/__init__.py
similarity index 100%
rename from localcosmos_server/online_content/templatetags/__init__.py
rename to localcosmos_server/template_content/templatetags/__init__.py
diff --git a/localcosmos_server/template_content/templatetags/template_content_tags.py b/localcosmos_server/template_content/templatetags/template_content_tags.py
new file mode 100644
index 0000000..baf71fc
--- /dev/null
+++ b/localcosmos_server/template_content/templatetags/template_content_tags.py
@@ -0,0 +1,8 @@
+from django import template
+register = template.Library()
+
+from localcosmos_server.template_content.models import LocalizedTemplateContent
+
+@register.simple_tag
+def get_template_content_locale(template_content, language):
+ return LocalizedTemplateContent.objects.filter(template_content=template_content, language=language).first()
\ No newline at end of file
diff --git a/localcosmos_server/template_content/tests.py b/localcosmos_server/template_content/tests.py
new file mode 100644
index 0000000..7ce503c
--- /dev/null
+++ b/localcosmos_server/template_content/tests.py
@@ -0,0 +1,3 @@
+from django.test import TestCase
+
+# Create your tests here.
diff --git a/localcosmos_server/template_content/urls.py b/localcosmos_server/template_content/urls.py
new file mode 100644
index 0000000..2ffd269
--- /dev/null
+++ b/localcosmos_server/template_content/urls.py
@@ -0,0 +1,29 @@
+from django.urls import path, re_path
+
+from django.contrib.auth.decorators import login_required
+
+from . import views
+
+
+urlpatterns = [
+ path('
/template-content-list/',
+ login_required(views.TemplateContentList.as_view()), name='template_content_home'),
+ path('/create-template-content//',
+ login_required(views.CreateTemplateContent.as_view()), name='create_template_content'),
+ path('/manage-localized-template-content//',
+ login_required(views.ManageLocalizedTemplateContent.as_view()),
+ name='manage_localized_template_content'),
+ # images
+ path('manage-template-content-image//',
+ views.ManageTemplateContentImage.as_view(), name='manage_template_content_image'),
+ path('manage-template-content-image////',
+ views.ManageTemplateContentImage.as_view(),
+ name='manage_template_content_image'),
+ path('delete-template-content-image//',
+ views.DeleteTemplateContentImage.as_view(), name='delete_template_content_image'),
+ path('get-template-content-formfields///',
+ views.GetTemplateContentFormFileFields.as_view(), name='get_template_content_form_fields'),
+ # publishing template content
+ path('/publish-template-content//',
+ login_required(views.PublishTemplateContent.as_view()), name='publish_template_content'),
+]
diff --git a/localcosmos_server/template_content/views.py b/localcosmos_server/template_content/views.py
new file mode 100644
index 0000000..d0fdeb1
--- /dev/null
+++ b/localcosmos_server/template_content/views.py
@@ -0,0 +1,284 @@
+from django.shortcuts import redirect
+from django.views.generic import TemplateView, FormView
+from django import forms
+
+from localcosmos_server.models import App
+
+from localcosmos_server.views import ManageServerContentImage, DeleteServerContentImage
+
+from localcosmos_server.decorators import ajax_required
+from django.utils.decorators import method_decorator
+
+from .models import TemplateContent, LocalizedTemplateContent
+from .forms import CreateTemplateContentForm, ManageLocalizedTemplateContentForm
+
+from urllib.parse import urljoin
+
+class TemplateContentMixin:
+
+ def dispatch(self, request, *args, **kwargs):
+
+ self.app = App.objects.get(uid=kwargs['app_uid'])
+
+ return super().dispatch(request, *args, **kwargs)
+
+
+ def get_context_data(self, **kwargs):
+ context = super().get_context_data(**kwargs)
+ context.update({
+ 'app' : self.app,
+ })
+ return context
+
+
+class TemplateContentList(TemplateContentMixin, TemplateView):
+
+ template_name = 'template_content/template_content_base.html'
+
+ def get_context_data(self, **kwargs):
+ context = super().get_context_data(**kwargs)
+
+ localized_template_contents = LocalizedTemplateContent.objects.filter(template_content__app=self.app,
+ template_content__template_type='page', language=self.app.primary_language)
+ context['localized_template_contents'] = localized_template_contents
+
+ return context
+
+
+'''
+ Creating a template_content consists of
+ - selecting a template
+ - supplying a title
+ - the title is always in the current language
+'''
+class CreateTemplateContent(TemplateContentMixin, FormView):
+
+ template_name = 'template_content/create_template_content.html'
+ form_class = CreateTemplateContentForm
+
+ def get_context_data(self, **kwargs):
+ context = super().get_context_data(**kwargs)
+ context['template_type'] = self.kwargs['template_type']
+ return context
+
+
+ def get_form_kwargs(self):
+ form_kwargs = super().get_form_kwargs()
+ form_kwargs['language'] = self.app.primary_language
+ return form_kwargs
+
+
+ def get_form(self, form_class=None):
+ """Return an instance of the form to be used in this view."""
+ if form_class is None:
+ form_class = self.get_form_class()
+ return form_class(self.app, self.kwargs['template_type'], **self.get_form_kwargs())
+
+
+ def form_valid(self, form):
+ # create a new template_content for this online content (which is app specific)
+
+ template_content = TemplateContent.objects.create(
+ self.request.user,
+ self.app,
+ self.app.primary_language,
+ form.cleaned_data['draft_title'],
+ form.cleaned_data['draft_navigation_link_name'],
+ form.cleaned_data['template_name'],
+ self.kwargs['template_type'],
+ )
+
+ template_content.save()
+
+ localized_template_content = template_content.get_locale(self.app.primary_language)
+
+ return redirect('manage_localized_template_content', app_uid=self.app.uid, pk=localized_template_content.pk)
+
+
+class ManageLocalizedTemplateContent(TemplateContentMixin, FormView):
+
+ template_name = 'template_content/manage_localized_template_content.html'
+ form_class = ManageLocalizedTemplateContentForm
+
+ empty_values = ['', '
', None]
+
+ def dispatch(self, request, *args, **kwargs):
+ self.localized_template_content = LocalizedTemplateContent.objects.get(pk=kwargs['localized_template_content_id'])
+ return super().dispatch(request, *args, **kwargs)
+
+ def get_form(self, form_class=None):
+ if form_class is None:
+ form_class = self.get_form_class()
+ return form_class(self.app, self.localized_template_content, **self.get_form_kwargs())
+
+
+ def get_initial(self):
+
+ initial = {
+ 'draft_title' : self.localized_template_content.draft_title,
+ 'draft_navigation_link_name' : self.localized_template_content.draft_navigation_link_name,
+ 'input_language' : self.localized_template_content.language,
+ }
+
+ if self.localized_template_content.draft_contents:
+ for content_key, data in self.localized_template_content.draft_contents.items():
+ initial[content_key] = data
+
+ return initial
+
+
+ def get_preview_url(self):
+
+ template = self.localized_template_content.template_content.draft_template
+
+ template_url = template.definition['templateUrl'].replace('{slug}', self.localized_template_content.slug)
+
+ # the relative preview url
+ app_preview_url = self.app.get_preview_url()
+
+ unschemed_preview_url = urljoin(app_preview_url, template_url.lstrip('/'))
+
+ # the host where the preview is served. on LCOS it is simply the website
+ if unschemed_preview_url.startswith('http://') or unschemed_preview_url.startswith('https://'):
+ preview_url = unschemed_preview_url
+ else:
+ preview_url = '{0}://{1}'.format(self.request.scheme, unschemed_preview_url)
+
+ return preview_url
+
+
+ def get_context_data(self, **kwargs):
+ context = super().get_context_data(**kwargs)
+ context['localized_template_content'] = self.localized_template_content
+ context['preview_url'] = self.get_preview_url()
+ return context
+
+
+ def form_valid(self, form):
+
+ self.localized_template_content.draft_title = form.cleaned_data['draft_title']
+ self.localized_template_content.draft_navigation_link_name = form.cleaned_data['draft_navigation_link_name']
+
+ if not self.localized_template_content.draft_contents:
+ self.localized_template_content.draft_contents = {}
+
+
+ # existing keys in JSON - content that already has been saved
+ old_keys = list(self.localized_template_content.draft_contents.keys())
+
+ template_definition = self.localized_template_content.template_content.draft_template.definition
+
+ for content_key, content_definition in template_definition['contents'].items():
+
+ content = form.cleaned_data.get(content_key, None)
+
+ if content and type(content) in [str, list] and len(content) > 0 and content not in self.empty_values:
+ self.localized_template_content.draft_contents[content_key] = content
+
+ if content_key in old_keys:
+ old_keys.remove(content_key)
+
+ self.localized_template_content.draft_contents[content_key] = content
+
+ # remove keys/data that do not occur anymore in the template
+ for old_key in old_keys:
+ del self.localized_template_content.draft_contents[old_key]
+
+ self.localized_template_content.save()
+
+ context = self.get_context_data(**self.kwargs)
+ return self.render_to_response(context)
+
+
+'''
+ get all fields for a content_key
+ ajax only
+ for successful image deletions and uploads
+ reloads all fields if field is multi
+'''
+from .forms import TemplateContentFormFieldManager
+class GetTemplateContentFormFileFields(FormView):
+
+ template_name = 'template_content/ajax/reloaded_file_fields.html'
+ form_class = forms.Form
+
+ @method_decorator(ajax_required)
+ def dispatch(self, request, *args, **kwargs):
+ self.set_content(**kwargs)
+ return super().dispatch(request, *args, **kwargs)
+
+
+ def set_content(self, **kwargs):
+ self.localized_template_content = LocalizedTemplateContent.objects.get(pk=kwargs['localized_template_content_id'])
+ self.content_key = kwargs['content_key']
+
+
+ def get_form(self, form_class=None):
+ if form_class is None:
+ form_class = forms.Form
+
+ form = form_class(**self.get_form_kwargs())
+
+ template_definition = self.localized_template_content.template_content.draft_template.definition
+
+ content_definition = template_definition['contents'][self.content_key]
+
+ instances = self.localized_template_content.images(image_type=self.content_key).order_by('pk')
+ field_manager = TemplateContentFormFieldManager(self.localized_template_content)
+ form_fields = field_manager.get_form_fields(self.content_key, content_definition, instances)
+
+ for field in form_fields:
+ form.fields[field['name']] = field['field']
+
+ return form
+
+
+
+class ManageTemplateContentImage(ManageServerContentImage):
+
+ template_name = 'template_content/ajax/manage_template_content_image.html'
+
+ def get_context_data(self, **kwargs):
+ context = super().get_context_data(**kwargs)
+ context['localized_template_content'] = self.content_instance
+ context['content_key'] = self.image_type
+
+ return context
+
+
+
+class DeleteTemplateContentImage(DeleteServerContentImage):
+
+ template_name = 'template_content/ajax/delete_template_content_image.html'
+
+ def get_context_data(self, **kwargs):
+ context = super().get_context_data(**kwargs)
+ context['localized_template_content'] = self.object.content
+ context['content_key'] = self.object.image_type
+ return context
+
+
+'''
+ publish all languages at once, or one language
+'''
+class PublishTemplateContent(TemplateContentMixin, TemplateView):
+
+ template_name = 'template_content/template_content_list_entry.html'
+
+ def dispatch(self, request, *args, **kwargs):
+
+ self.template_content = TemplateContent.objects.get(pk=kwargs['template_content_id'])
+ self.localized_template_content = self.template_content.get_locale(
+ self.template_content.app.primary_language)
+ self.language = kwargs.get('language', 'all')
+
+ return super().dispatch(request, *args, **kwargs)
+
+ def get_context_data(self, **kwargs):
+ context = super().get_context_data(**kwargs)
+ context['localized_template_content'] = self.localized_template_content
+ context['template_content'] = self.template_content
+ context['publication'] = True
+ context['publication_errors'] = self.template_content.publish(language=self.language)
+
+ return context
\ No newline at end of file
diff --git a/localcosmos_server/templates/localcosmos_server/ajax/delete_server_content_image.html b/localcosmos_server/templates/localcosmos_server/ajax/delete_server_content_image.html
new file mode 100644
index 0000000..baacde4
--- /dev/null
+++ b/localcosmos_server/templates/localcosmos_server/ajax/delete_server_content_image.html
@@ -0,0 +1,21 @@
+{% extends 'localcosmos_server/generic/delete_object.html' %}
+{% load i18n static localcosmos_tags %}
+
+{% block success_js %}
+
+
+ {% block extra_success_js %}{% endblock %}
+{% endblock %}
+
+{% block title %}{% blocktrans %}Delete image{% endblocktrans %}{% endblock %}
+
+{% block deletion_text %}
+ {% trans 'Do you really want to delete this image?' %}
+{% endblock %}
diff --git a/localcosmos_server/templates/localcosmos_server/ajax/server_content_image_form.html b/localcosmos_server/templates/localcosmos_server/ajax/server_content_image_form.html
new file mode 100644
index 0000000..d5b6fe0
--- /dev/null
+++ b/localcosmos_server/templates/localcosmos_server/ajax/server_content_image_form.html
@@ -0,0 +1,68 @@
+{% extends 'localcosmos_server/modals/large_modal_form.html' %}
+
+{% load i18n localcosmos_tags %}
+
+{% block above %}
+
diff --git a/localcosmos_server/templates/localcosmos_server/widgets/crop_image_input.html b/localcosmos_server/templates/localcosmos_server/widgets/crop_image_input.html
index 5e51b36..5d14dcb 100644
--- a/localcosmos_server/templates/localcosmos_server/widgets/crop_image_input.html
+++ b/localcosmos_server/templates/localcosmos_server/widgets/crop_image_input.html
@@ -11,27 +11,29 @@