From 8dfafa9404afcca798fc92cd61d1e638e54c368e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lo=C3=AFc=20Gremaud?= Date: Fri, 27 Sep 2024 18:09:38 +0200 Subject: [PATCH] Working v0 --- k356/app/settings.py | 101 ++++--- k356/app/utils/api/api_list.py | 6 +- k356/app/utils/extra_context.py | 23 +- k356/app/utils/models.py | 37 ++- .../components/ItemList/template.html | 1 + .../templates/components/ItemList/vue.js | 7 + .../components/ItemView/template.html | 40 +++ .../templates/components/ItemView/vue.js | 282 ++++++++++++++++++ .../templates/components/item/template.html | 5 - k356/items/templates/components/item/vue.js | 4 - .../components/item_list/template.html | 88 ------ .../templates/components/item_list/vue.js | 191 ------------ k356/items/urls.py | 4 +- k356/items/views/{item.py => item_view.py} | 9 +- k356/items/views/type_view.py | 67 +++++ .../template.html | 0 .../vue.js | 4 +- .../{k356-loading => Loading}/template.html | 0 .../{k356-loading => Loading}/vue.js | 4 +- k356/main/templates/main/home.html | 10 - k356/main/templatetags/__init__.py | 0 k356/main/templatetags/main.py | 9 + k356/templates/base.html | 46 ++- .../base_components/glist/template.html | 104 +++++++ k356/templates/base_components/glist/vue.js | 87 ++++++ 25 files changed, 740 insertions(+), 389 deletions(-) create mode 100644 k356/items/templates/components/ItemList/template.html create mode 100644 k356/items/templates/components/ItemList/vue.js create mode 100644 k356/items/templates/components/ItemView/template.html create mode 100644 k356/items/templates/components/ItemView/vue.js delete mode 100644 k356/items/templates/components/item/template.html delete mode 100644 k356/items/templates/components/item/vue.js delete mode 100644 k356/items/templates/components/item_list/template.html delete mode 100644 k356/items/templates/components/item_list/vue.js rename k356/items/views/{item.py => item_view.py} (88%) create mode 100644 k356/items/views/type_view.py rename k356/main/templates/components/{encryption-testing => EncryptionTesting}/template.html (100%) rename k356/main/templates/components/{encryption-testing => EncryptionTesting}/vue.js (92%) rename k356/main/templates/components/{k356-loading => Loading}/template.html (100%) rename k356/main/templates/components/{k356-loading => Loading}/vue.js (98%) delete mode 100644 k356/main/templates/main/home.html create mode 100644 k356/main/templatetags/__init__.py create mode 100644 k356/main/templatetags/main.py create mode 100644 k356/templates/base_components/glist/template.html create mode 100644 k356/templates/base_components/glist/vue.js diff --git a/k356/app/settings.py b/k356/app/settings.py index 9efcc7d..d3823dd 100644 --- a/k356/app/settings.py +++ b/k356/app/settings.py @@ -10,12 +10,10 @@ For the full list of settings and their values, see https://docs.djangoproject.com/en/5.1/ref/settings/ """ -from pathlib import Path -from django.utils.module_loading import import_module - - import os +from pathlib import Path +from django.utils.module_loading import import_module BASE_DIR = Path(__file__).resolve().parent.parent @@ -24,7 +22,7 @@ BASE_DIR = Path(__file__).resolve().parent.parent # See https://docs.djangoproject.com/en/5.1/howto/deployment/checklist/ # SECURITY WARNING: keep the secret key used in production secret! -SECRET_KEY = 'django-insecure-$440wv7cqb$-umfo-x%w_@p3g5kuuk1(!rv#=7*gzndx4_h4ds' +SECRET_KEY = "django-insecure-$440wv7cqb$-umfo-x%w_@p3g5kuuk1(!rv#=7*gzndx4_h4ds" # SECURITY WARNING: don't run with debug turned on in production! DEBUG = True @@ -35,18 +33,34 @@ ALLOWED_HOSTS = [] # Application definition INSTALLED_APPS = [ - 'django.contrib.admin', - 'django.contrib.auth', - 'django.contrib.contenttypes', - 'django.contrib.sessions', - 'django.contrib.messages', - 'django.contrib.staticfiles', - - 'main', - 'users', - 'items', + "django.contrib.admin", + "django.contrib.auth", + "django.contrib.contenttypes", + "django.contrib.sessions", + "django.contrib.messages", + "django.contrib.staticfiles", + "django_js_reverse", + "djangobower", + "main", + "users", + "items", +] - 'django_js_reverse', +BOWER_COMPONENT_ROOT = os.path.join(BASE_DIR, "components") +BOWER_INSTALLED_APPS = [ + "https://code.jquery.com/jquery-3.7.1.min.js", + "https://cdn.jsdelivr.net/npm/vue@2.7.16/dist/vue.js", + "https://cdn.jsdelivr.net/npm/sweetalert2@11", + "https://cdn.jsdelivr.net/npm/vue-resource@1.5.3", + "https://cdn.jsdelivr.net/npm/js-cookie@3.0.5/dist/js.cookie.min.js", + "https://unpkg.com/vue-router@3/dist/vue-router.js", + "https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css", + "https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js", + "https://cdn.jsdelivr.net/npm/bootswatch@5.3.3/dist/lux/bootstrap.min.css", + "https://cdn.jsdelivr.net/npm/vuetify@2.x/dist/vuetify.min.css", + "https://cdn.jsdelivr.net/npm/vuetify@2.x/dist/vuetify.js", + "https://cdn.jsdelivr.net/npm/@mdi/font@7.4.47/css/materialdesignicons.min.css", + "https://fonts.cdnfonts.com/css/jetbrains-mono", ] STATIC_URL = "/static/" @@ -56,7 +70,7 @@ STATICFILES_DIRS = (os.path.join(BASE_DIR, "static_source"),) STATICFILES_FINDERS = ( "django.contrib.staticfiles.finders.FileSystemFinder", "django.contrib.staticfiles.finders.AppDirectoriesFinder", - # "djangobower.finders.BowerFinder", + "djangobower.finders.BowerFinder", # "compressor.finders.CompressorFinder", ) @@ -70,37 +84,37 @@ STORAGES = { } MIDDLEWARE = [ - 'django.middleware.security.SecurityMiddleware', - 'django.contrib.sessions.middleware.SessionMiddleware', - 'django.middleware.common.CommonMiddleware', - 'django.middleware.csrf.CsrfViewMiddleware', - 'django.contrib.auth.middleware.AuthenticationMiddleware', - 'django.contrib.messages.middleware.MessageMiddleware', - 'django.middleware.clickjacking.XFrameOptionsMiddleware', + "django.middleware.security.SecurityMiddleware", + "django.contrib.sessions.middleware.SessionMiddleware", + "django.middleware.common.CommonMiddleware", + "django.middleware.csrf.CsrfViewMiddleware", + "django.contrib.auth.middleware.AuthenticationMiddleware", + "django.contrib.messages.middleware.MessageMiddleware", + "django.middleware.clickjacking.XFrameOptionsMiddleware", ] -ROOT_URLCONF = 'app.urls' +ROOT_URLCONF = "app.urls" TEMPLATES = [ { - 'BACKEND': 'django.template.backends.django.DjangoTemplates', - 'DIRS': [ + "BACKEND": "django.template.backends.django.DjangoTemplates", + "DIRS": [ os.path.join(BASE_DIR, "templates"), ], - 'APP_DIRS': True, - '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', - 'app.utils.extra_context.extra_context', + "APP_DIRS": True, + "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", + "app.utils.extra_context.extra_context", ], }, }, ] -WSGI_APPLICATION = 'app.wsgi.application' +WSGI_APPLICATION = "app.wsgi.application" # Database @@ -114,16 +128,16 @@ DATABASES = {} AUTH_PASSWORD_VALIDATORS = [ { - 'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator', + "NAME": "django.contrib.auth.password_validation.UserAttributeSimilarityValidator", }, { - 'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator', + "NAME": "django.contrib.auth.password_validation.MinimumLengthValidator", }, { - 'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator', + "NAME": "django.contrib.auth.password_validation.CommonPasswordValidator", }, { - 'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator', + "NAME": "django.contrib.auth.password_validation.NumericPasswordValidator", }, ] @@ -131,9 +145,9 @@ AUTH_PASSWORD_VALIDATORS = [ # Internationalization # https://docs.djangoproject.com/en/5.1/topics/i18n/ -LANGUAGE_CODE = 'en-us' +LANGUAGE_CODE = "en-us" -TIME_ZONE = 'UTC' +TIME_ZONE = "UTC" USE_I18N = True @@ -142,12 +156,11 @@ USE_TZ = True # Default primary key field type # https://docs.djangoproject.com/en/5.1/ref/settings/#default-auto-field -DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField' +DEFAULT_AUTO_FIELD = "django.db.models.BigAutoField" from app.settingsLocal import * - for extra_app in EXTRA_APPS: INSTALLED_APPS.append(extra_app) diff --git a/k356/app/utils/api/api_list.py b/k356/app/utils/api/api_list.py index f3ceefb..a662697 100644 --- a/k356/app/utils/api/api_list.py +++ b/k356/app/utils/api/api_list.py @@ -4,8 +4,8 @@ def header_for_table(model): return [ { - "text": value, "value": key, + **value, } for key, value in headers.items() ] + [ @@ -15,7 +15,3 @@ def header_for_table(model): "sortable": False, }, ] - - -def encrypted_fields(model): - return model.Encryption.fields diff --git a/k356/app/utils/extra_context.py b/k356/app/utils/extra_context.py index 2562200..8a5b44d 100644 --- a/k356/app/utils/extra_context.py +++ b/k356/app/utils/extra_context.py @@ -1,10 +1,10 @@ -from django.conf import settings -from django.apps import apps - - -from users.models import UserSettings from pathlib import Path +from django.apps import apps +from django.conf import settings +from rich import print +from users.models import UserSettings + def extra_context(request): @@ -21,17 +21,16 @@ def extra_context(request): for path in p.iterdir(): components.append(path.name) - return { + ret = { "user_settings": user_settings, "templates": { component: f"components/{component}/template.html" for component in components }, "components": { - component: { - "path": f"components/{component}/vue.js", - "flat_name": component.replace("-", "_").lower(), - } - for component in components - } + component: f"components/{component}/vue.js" for component in components + }, } + + print(ret) + return ret diff --git a/k356/app/utils/models.py b/k356/app/utils/models.py index d72d45f..35be261 100644 --- a/k356/app/utils/models.py +++ b/k356/app/utils/models.py @@ -11,15 +11,44 @@ class BaseQuerySet(models.QuerySet): def headers(self): """Return the list of header for a list.""" + def _field(field): + ret = { + field.name: { + "text": field.verbose_name.capitalize(), + "align": "", + "encrypted": field.name in self.model.Encryption.fields, + "editable": field.name + not in self.model.Serialization.excluded_fields_edit, + "field_widget": "v-textarea", + } + } + + if isinstance(field, RelatedField): + ret[f"{field.name}__name"] = { + "text": f"{field.verbose_name.capitalize()} - Name", + "align": "", + "encrypted": True, + } + ret[f"{field.name}__name"] = { + "text": f"{field.verbose_name.capitalize()} - Identifier", + "align": "", + "encrypted": True, + } + + ret[field.name].update( + align=" d-none", + testing=123, + field_widget="v-select", + ) + + return ret + fields = {} for field in self.model._meta.fields: if field.name in self.model.Serialization.excluded_fields: continue - # if isinstance(field, RelatedField): - # fields[f"{field.name}__name"] = field.verbose_name.capitalize() - - fields[field.name] = field.verbose_name.capitalize() + fields.update(_field(field)) return fields diff --git a/k356/items/templates/components/ItemList/template.html b/k356/items/templates/components/ItemList/template.html new file mode 100644 index 0000000..fb816ff --- /dev/null +++ b/k356/items/templates/components/ItemList/template.html @@ -0,0 +1 @@ +{% extends "base_components/glist/template.html" %} diff --git a/k356/items/templates/components/ItemList/vue.js b/k356/items/templates/components/ItemList/vue.js new file mode 100644 index 0000000..335a17a --- /dev/null +++ b/k356/items/templates/components/ItemList/vue.js @@ -0,0 +1,7 @@ +{% extends "base_components/glist/vue.js" %} +{% load main %} + +{% block component %} + {% define 'items' 'items' %} + {{ block.super }} +{% endblock %} diff --git a/k356/items/templates/components/ItemView/template.html b/k356/items/templates/components/ItemView/template.html new file mode 100644 index 0000000..f3d2078 --- /dev/null +++ b/k356/items/templates/components/ItemView/template.html @@ -0,0 +1,40 @@ +{% load i18n %} + +
+
+
{% trans "Your items" %}
+
+ + + +
+
+ +
+
{% trans "Your types" %}
+
+ + + +
+
+ +
diff --git a/k356/items/templates/components/ItemView/vue.js b/k356/items/templates/components/ItemView/vue.js new file mode 100644 index 0000000..99c35d7 --- /dev/null +++ b/k356/items/templates/components/ItemView/vue.js @@ -0,0 +1,282 @@ +ItemView = { + template: "#ItemView", + delimiters: ["[[", "]]"], + props: ["crypto_key"], + + data: function() { + return { + search: "", + items: [], + items_headers: [], + items_relations: [], + types: [], + types_headers: [], + types_relations: [], + } + }, + + mounted: function() { + this.reload() + }, + + computed: { + items_encrypted_fields: function() { + return this.items_headers.filter(e => e.encrypted).map(e => e.value) + }, + + types_encrypted_fields: function() { + return this.types_headers.filter(e => e.encrypted).map(e => e.value) + }, + }, + + methods: { + + reload () { + + var self = this + + this.$http.get(Urls["items:list"]()).then(response => { + + Object.keys(response.data.result).forEach(name => { + self.$set(self, name, response.data.result[name]) + }) + + self.items.forEach(item => { + self.decryptObject(self.items_encrypted_fields, item) + }) + + self.types.forEach(type => { + self.decryptObject(self.types_encrypted_fields, type) + }) + + }).catch(err => { + + Swal.fire({title: "{{_('Error during loading of items') | escapejs}}", icon: "error", position:"top-end", showConfirmButton: false, toast: true, timer: 1000}) + + }) + + }, + + async aDecryptObject (encrypted_fields, obj) { + + var self = this + + return new Promise((resolve) => { + + let promises = encrypted_fields.map(field => { + // Encrypt all necessary fields + if (obj[field] == null) { + return null + } + + return new Promise((resolve) => { + return decryptWithKey(self.crypto_key, obj[field]).then(dec => { + resolve({field: field, value: dec}) + }) + }) + + }).filter(e => e != null) + + Promise.all(promises).then(values => { + + values.forEach(value => { + obj[value.field] = value.value + }) + + resolve(obj) + + }) + + }) + + }, + + decryptObject (encrypted_fields, obj) { + + var self = this + + return new Promise((resolve) => { + + let promises = encrypted_fields.map(field => { + // Encrypt all necessary fields + if (obj[field] == null) { + return null + } + + return new Promise((resolve) => { + return decryptWithKey(self.crypto_key, obj[field]).then(dec => { + resolve({field: field, value: dec}) + }) + }) + + }).filter(e => e != null) + + Promise.all(promises).then(values => { + + values.forEach(value => { + obj[value.field] = value.value + }) + + resolve(obj) + + }) + + }) + + }, + + object_edition (url_edit, url_create, encrypted_fields, method, obj) { + // Return a Promise + var self = this + + return new Promise((resolve) => { + + let url = Urls[url_edit](obj.id) + + if (obj.id == undefined || obj.id == null) { + url = Urls[url_create]() + } + + let promises = encrypted_fields.map(field => { + // Encrypt all necessary fields + if (obj[field] == null) { + return null + } + + return new Promise((resolve) => { + return encryptWithKey(self.crypto_key, obj[field]).then(enc => { + resolve({field: field, value: enc}) + }) + }) + + }).filter(e => e != null) + + Promise.all(promises).then(values => { + + values.forEach(value => { + obj[value.field] = value.value + }) + + self.$http[method](url, obj).then(response => { + + if (method == "delete") { + + resolve() + + } else { + + self.decryptObject(encrypted_fields, response.data.object).then(new_obj => { + resolve(new_obj) + }) + + } + + }).catch(err => { + + let msg = "{{_('Error during edition') | escapejs}}" + if (method == "delete") { + msg = "{{_('Error during deletion') | escapejs}}" + } + + Swal.fire({title: msg, icon: "error", position:"top-end", showConfirmButton: false, toast: true, timer: 1000}) + + }) + + }) + }) + }, + + item_edition (method, item) { + return this.object_edition("items:edit", "items:create", this.items_encrypted_fields, method, item) + }, + + type_edition (method, item) { + return this.object_edition("items:type.edit", "items:type.create", this.items_encrypted_fields, method, item) + }, + + createItem (item) { + + var self = this + + this.item_edition("post", item).then(new_item => { + + self.items.push(new_item) + + Swal.fire({title: "{{_('Item successfully created!') | escapejs}}", icon: "success", position:"top-end", showConfirmButton: false, toast: true, timer: 1000}) + + }) + + }, + + editItem (index, item) { + + var self = this + + this.item_edition("post", item).then(new_item => { + + // Remove the 'current' (non edited) item from the list + self.items.splice(index, 1) + self.items.push(new_item) + + Swal.fire({title: "{{_('Item successfully edited') | escapejs}}", icon: "success", position:"top-end", showConfirmButton: false, toast: true, timer: 1000}) + + }) + }, + + deleteItem (index) { + + var self = this + var item = this.items[index] + + this.item_edition("delete", item).then(() => { + self.items.splice(this.items.indexOf(item), 1) + + Swal.fire({title: "{{_('Item successfully deleted!') | escapejs}}", icon: "success", position:"top-end", showConfirmButton: false, toast: true, timer: 1000}) + + }) + }, + + createType (type) { + + var self = this + + this.type_edition("post", type).then(new_type => { + + self.types.push(new_type) + + Swal.fire({title: "{{_('Type successfully created!') | escapejs}}", icon: "success", position:"top-end", showConfirmButton: false, toast: true, timer: 1000}) + + }) + + }, + + editType (index, type) { + + var self = this + + this.type_edition("post", type).then(new_type => { + + // Remove the 'current' (non edited) item from the list + self.types.splice(index, 1) + self.types.push(new_type) + + Swal.fire({title: "{{_('Type successfully edited') | escapejs}}", icon: "success", position:"top-end", showConfirmButton: false, toast: true, timer: 1000}) + + }) + }, + + deleteType (index) { + + var self = this + var type = this.types[index] + + this.type_edition("delete", type).then(() => { + self.types.splice(this.types.indexOf(type), 1) + + Swal.fire({title: "{{_('Type successfully deleted!') | escapejs}}", icon: "success", position:"top-end", showConfirmButton: false, toast: true, timer: 1000}) + + }) + }, + + }, +} diff --git a/k356/items/templates/components/item/template.html b/k356/items/templates/components/item/template.html deleted file mode 100644 index d4d2074..0000000 --- a/k356/items/templates/components/item/template.html +++ /dev/null @@ -1,5 +0,0 @@ -{% load i18n %} - -
- -
diff --git a/k356/items/templates/components/item/vue.js b/k356/items/templates/components/item/vue.js deleted file mode 100644 index 1b8a100..0000000 --- a/k356/items/templates/components/item/vue.js +++ /dev/null @@ -1,4 +0,0 @@ -item = { - template: "#item", - props: ["crypto_key", "item"], -} diff --git a/k356/items/templates/components/item_list/template.html b/k356/items/templates/components/item_list/template.html deleted file mode 100644 index b389e4f..0000000 --- a/k356/items/templates/components/item_list/template.html +++ /dev/null @@ -1,88 +0,0 @@ -{% load i18n %} - -
-
-
{% trans "Your items" %}
-
- - - - - - - - - - -
-
- -
diff --git a/k356/items/templates/components/item_list/vue.js b/k356/items/templates/components/item_list/vue.js deleted file mode 100644 index 3fbc8a3..0000000 --- a/k356/items/templates/components/item_list/vue.js +++ /dev/null @@ -1,191 +0,0 @@ -item_list = { - template: "#item_list", - delimiters: ["[[", "]]"], - props: ["crypto_key", "locked"], - - data: function() { - return { - dialog: false, - dialogDelete: false, - editedIndex: -1, - defaultItem: {}, - editedItem: {}, - search: null, - data: {}, - } - }, - - mounted: function() { - - var self = this; - - this.$http.get(Urls["items:list"]()).then(response => { - - Object.keys(response.data.result).forEach(name => { - self.$set(self.data, name, response.data.result[name]); - }); - - self.data.items.forEach(item => { - self.decryptItem(item); - }); - - }).catch(err => { - - Swal.fire({title: "{{_('Error during loading of items.') | escapejs}}", icon: "error", position:"top-end", showConfirmButton: false, toast: true, timer: 1000}); - - }); - - }, - - computed: { - formTitle () { - return this.editedIndex === -1 ? "{{_('New item') | escapejs}}" : "{{_('Edit item') | escapejs}}" - }, - }, - - watch: { - dialog (val) { - val || this.close() - }, - dialogDelete (val) { - val || this.closeDelete() - }, - }, - - methods: { - - decryptItem (item) { - this.data.items_encrypted.forEach(field => { - decryptWithKey(this.crypto_key, item[field]).then(dec => { - item[field] = dec; - }) - }); - return item; - }, - - editItem (item) { - this.editedIndex = this.data.items.indexOf(item) - this.editedItem = Object.assign({}, item) - this.dialog = true - }, - - deleteItem (item) { - this.editedIndex = this.data.items.indexOf(item) - this.editedItem = Object.assign({}, item) - this.dialogDelete = true - }, - - deleteItemConfirm () { - var item = this.data.items[this.editedIndex]; - - this.item_edition("delete", item).then(response => { - this.data.items.splice(this.data.items.indexOf(item), 1) - Swal.fire({title: "{{_('Item successfully deleted!') | escapejs}}", icon: "success", position:"top-end", showConfirmButton: false, toast: true, timer: 1000}); - }); - - this.closeDelete() - }, - - close () { - this.dialog = false - this.$nextTick(() => { - this.editedItem = Object.assign({}, this.defaultItem) - this.editedIndex = -1 - }) - }, - - closeDelete () { - this.dialogDelete = false - this.$nextTick(() => { - this.editedItem = Object.assign({}, this.defaultItem) - this.editedIndex = -1 - }) - }, - - item_edition (method, item) { - // Return a Promise - var self = this; - - return new Promise((resolve) => { - - let url = Urls["items:edit"](item.id) - - if (item.id == undefined || item.id == null) { - url = Urls["items:create"]() - } - - let promises = self.data.items_encrypted.map(field => { - // Encrypt all necessary fields - if (item[field] == null) { - return null; - } - - return new Promise((resolve) => { - return encryptWithKey(self.crypto_key, item[field]).then(enc => { - resolve({field: field, value: enc}); - }); - }); - - }).filter(e => e != null); - - Promise.all(promises).then(values => { - - values.forEach(value => { - item[value.field] = value.value; - }); - - self.$http[method](url, item).then(response => { - - resolve(response.data); - - }).catch(err => { - - let msg = "{{_('Error during edition of item') | escapejs}}"; - if (method == "delete") { - msg = "{{_('Error during deletion of item') | escapejs}}"; - } - - Swal.fire({title: msg, icon: "error", position:"top-end", showConfirmButton: false, toast: true, timer: 1000}); - - }); - - }); - }); - }, - - save () { - if (this.editedIndex > -1) { - - var self = this; - - this.item_edition("post", this.editedItem).then(data => { - - self.data.items.splice(this.data.items.indexOf(self.editedItem), 1) - console.log('pre edit', data.item) - new_item = self.decryptItem(data.item); - console.log('edited item', new_item); - // self.data.items.push(self.decryptItem(data.item)); - - Swal.fire({title: "{{_('Item successfully edited') | escapejs}}", icon: "success", position:"top-end", showConfirmButton: false, toast: true, timer: 1000}); - - }); - - } else { - - var self = this; - - this.item_edition("post", this.editedItem).then(data => { - new_item = self.decryptItem(data.item); - console.log('new item', new_item); - // self.data.items.push(self.decryptItem(data.item)); - - // console.log(data.item); - - Swal.fire({title: "{{_('Item successfully created!') | escapejs}}", icon: "success", position:"top-end", showConfirmButton: false, toast: true, timer: 1000}); - }); - - } - this.close() - }, - } -} diff --git a/k356/items/urls.py b/k356/items/urls.py index a8cdee7..cebf85f 100644 --- a/k356/items/urls.py +++ b/k356/items/urls.py @@ -1,5 +1,5 @@ from django.urls import path -from items.views import item as item_view +from items.views import item_view, type_view app_name = "items" @@ -7,4 +7,6 @@ urlpatterns = [ path("", item_view.item_list, name="list"), path("", item_view.item_edit, name="edit"), path("create", item_view.item_edit, {"id": None}, name="create"), + path("type/", type_view.type_edit, name="type.edit"), + path("type/create", type_view.type_edit, {"id": None}, name="type.create"), ] diff --git a/k356/items/views/item.py b/k356/items/views/item_view.py similarity index 88% rename from k356/items/views/item.py rename to k356/items/views/item_view.py index a4c3068..031ccd4 100644 --- a/k356/items/views/item.py +++ b/k356/items/views/item_view.py @@ -1,6 +1,6 @@ import json -from app.utils.api.api_list import encrypted_fields, header_for_table +from app.utils.api.api_list import header_for_table from django.contrib.auth.decorators import login_required from django.db import models from django.db.models.fields.related import RelatedField @@ -12,18 +12,15 @@ from items.models import Item, ItemType def item_list(request): items = Item.objects.filter(author=request.user.setting) - types = ItemType.objects.filter(author=request.user.setting) return JsonResponse( { "result": { "items": list(items.serialize()), - "types": list(types.serialize()), "items_headers": header_for_table(Item), + "types": list(types.serialize()), "types_headers": header_for_table(ItemType), - "items_encrypted": encrypted_fields(Item), - "types_encrypted": encrypted_fields(ItemType), }, "count": items.count(), } @@ -85,6 +82,6 @@ def item_edit(request, id=None): return JsonResponse( { - "item": Item.objects.filter(id=item.id).serialize().first(), + "object": Item.objects.filter(id=item.id).serialize().first(), } ) diff --git a/k356/items/views/type_view.py b/k356/items/views/type_view.py new file mode 100644 index 0000000..35bfd6e --- /dev/null +++ b/k356/items/views/type_view.py @@ -0,0 +1,67 @@ +import json + +from django.contrib.auth.decorators import login_required +from django.db import models +from django.db.models.fields.related import RelatedField +from django.http import JsonResponse +from items.models import ItemType + + +@login_required +def type_edit(request, id=None): + """Create/edit type view.""" + + if id: + item = ItemType.objects.filter(id=id, author=request.user.setting).first() + + else: + item = ItemType(author=request.user.setting) + + if not item: + return JsonResponse({}, status=404) + + if request.method == "DELETE": + try: + item.delete() + + except Exception: + return JsonResponse({"error": "INVALID_DELETE"}, status=401) + + return JsonResponse({}) + + if request.method != "POST": + return JsonResponse({}, status=405) + + try: + data = json.loads(request.body) + + except Exception: + return JsonResponse({"error": "INVALID_DATA"}, status=401) + + for field in item._meta.fields: + if field.name in item.Serialization.excluded_fields_edit: + continue + + if isinstance(field, RelatedField): + # For now, disregard related field (fk, m2m, 1-1) + if isinstance(field, models.ForeignKey): + setattr(item, f"{field.name}_id", data[field.name]) + + continue + + if field.name not in data: + continue + + setattr(item, field.name, data[field.name]) + + try: + item.save() + + except Exception: + return JsonResponse({"error": "DATA_INVALID"}, status=401) + + return JsonResponse( + { + "object": ItemType.objects.filter(id=item.id).serialize().first(), + } + ) diff --git a/k356/main/templates/components/encryption-testing/template.html b/k356/main/templates/components/EncryptionTesting/template.html similarity index 100% rename from k356/main/templates/components/encryption-testing/template.html rename to k356/main/templates/components/EncryptionTesting/template.html diff --git a/k356/main/templates/components/encryption-testing/vue.js b/k356/main/templates/components/EncryptionTesting/vue.js similarity index 92% rename from k356/main/templates/components/encryption-testing/vue.js rename to k356/main/templates/components/EncryptionTesting/vue.js index 9824ec7..860f964 100644 --- a/k356/main/templates/components/encryption-testing/vue.js +++ b/k356/main/templates/components/EncryptionTesting/vue.js @@ -1,5 +1,5 @@ -encryption_testing = { - template: "#encryption-testing", +EncryptionTesting = { + template: "#EncryptionTesting", props: ["crypto_key"], data: function() { return { diff --git a/k356/main/templates/components/k356-loading/template.html b/k356/main/templates/components/Loading/template.html similarity index 100% rename from k356/main/templates/components/k356-loading/template.html rename to k356/main/templates/components/Loading/template.html diff --git a/k356/main/templates/components/k356-loading/vue.js b/k356/main/templates/components/Loading/vue.js similarity index 98% rename from k356/main/templates/components/k356-loading/vue.js rename to k356/main/templates/components/Loading/vue.js index 36b6126..f388af4 100644 --- a/k356/main/templates/components/k356-loading/vue.js +++ b/k356/main/templates/components/Loading/vue.js @@ -1,7 +1,7 @@ const rvalidate = Vue.resource(Urls["users:k356.validate"]); -k356_loading = { - template: "#k356-loading", +Loading = { + template: "#Loading", props: ["crypto_key"], data: function() { diff --git a/k356/main/templates/main/home.html b/k356/main/templates/main/home.html deleted file mode 100644 index 8594a28..0000000 --- a/k356/main/templates/main/home.html +++ /dev/null @@ -1,10 +0,0 @@ -{% extends "base.html" %} - -{% load i18n %} - -{% block title %}{% trans "Home" %}{% endblock %} -{% block body %} - - - -{% endblock %} diff --git a/k356/main/templatetags/__init__.py b/k356/main/templatetags/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/k356/main/templatetags/main.py b/k356/main/templatetags/main.py new file mode 100644 index 0000000..b61709a --- /dev/null +++ b/k356/main/templatetags/main.py @@ -0,0 +1,9 @@ +from django import template + +register = template.Library() + + +@register.simple_tag(takes_context=True) +def define(context, key, value): + context.dicts[0][key] = value + return "" diff --git a/k356/templates/base.html b/k356/templates/base.html index ea05565..197f52f 100644 --- a/k356/templates/base.html +++ b/k356/templates/base.html @@ -11,9 +11,7 @@ - - @@ -28,14 +26,22 @@ + + + - - + +
- + @@ -101,17 +107,17 @@ Vue.config.delimiters = ["[[", "]]"]; - {% for name, value in components.items %} - {% include value.path %} - Vue.component("{{ name }}", {{ value.flat_name }}); + {% for name, path in components.items %} + {% include path %} + Vue.component("{{ name }}", {{ name }}); {% endfor %} const routes = [ { path: '/', component: null }, - {% for name, value in components.items %} + {% for name, path in components.items %} { path: "/{{ name }}", - component: {{ value.flat_name }}, + component: {{ name }}, }, {% endfor %} ]; @@ -131,7 +137,6 @@ }, methods: { update_key: function(key) { - console.log(key); this.key.key = key; this.locked = key == null; }, @@ -143,9 +148,9 @@ } }); - router.beforeEach((to, from) => { + router.beforeEach((to, from, next) => { // Prevent from routing if key is not present. - return approuter.key.key != null; + next(approuter.key.key != null); }); @@ -159,5 +164,16 @@ refresh_csrftoken(); + + + diff --git a/k356/templates/base_components/glist/template.html b/k356/templates/base_components/glist/template.html new file mode 100644 index 0000000..a1a82a8 --- /dev/null +++ b/k356/templates/base_components/glist/template.html @@ -0,0 +1,104 @@ +{% load i18n %} + +{% block component %} + + + + + + + + + +{% endblock %} diff --git a/k356/templates/base_components/glist/vue.js b/k356/templates/base_components/glist/vue.js new file mode 100644 index 0000000..e37d615 --- /dev/null +++ b/k356/templates/base_components/glist/vue.js @@ -0,0 +1,87 @@ +{% block component %} +{{ name }} = { + template: "#{{ name }}", + delimiters: ["[[", "]]"], + props: ["crypto_key", "items", "items_headers", "items_relations", "group_by"], + + data: function() { + return { + dialog: false, + dialogDelete: false, + editedIndex: -1, + defaultItem: {}, + editedItem: {}, + search: null, + } + }, + + computed: { + editable_fields: function() { + return this.items_headers.filter(e => e.editable) + }, + }, + + watch: { + dialog (val) { + val || this.close() + }, + dialogDelete (val) { + val || this.closeDelete() + }, + }, + + methods: { + + editItem (item) { + this.editedIndex = this.items.indexOf(item) + this.editedItem = Object.assign({}, item) + this.dialog = true + }, + + deleteItem (item) { + this.editedIndex = this.items.indexOf(item) + this.editedItem = Object.assign({}, item) + this.dialogDelete = true + }, + + deleteItemConfirm () { + var self = this + var item = this.items[this.editedIndex] + + this.$emit("deleteItem", this.editedIndex) + this.closeDelete() + + }, + + close () { + this.dialog = false + this.editedItem = Object.assign({}, this.defaultItem) + this.editedIndex = -1 + }, + + closeDelete () { + this.dialogDelete = false + this.editedItem = Object.assign({}, this.defaultItem) + this.editedIndex = -1 + }, + + + save () { + + if (this.editedIndex > -1) { + + this.$emit("editItem", this.editedIndex, this.editedItem) + + } else { + + console.log('createItem emit', this.editedItem) + this.$emit("createItem", this.editedItem) + + } + + this.close() + }, + } +} + +{% endblock %}