Working v0
This commit is contained in:
parent
f3116a6876
commit
8dfafa9404
@ -10,12 +10,10 @@ For the full list of settings and their values, see
|
|||||||
https://docs.djangoproject.com/en/5.1/ref/settings/
|
https://docs.djangoproject.com/en/5.1/ref/settings/
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from pathlib import Path
|
|
||||||
from django.utils.module_loading import import_module
|
|
||||||
|
|
||||||
|
|
||||||
import os
|
import os
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
from django.utils.module_loading import import_module
|
||||||
|
|
||||||
BASE_DIR = Path(__file__).resolve().parent.parent
|
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/
|
# See https://docs.djangoproject.com/en/5.1/howto/deployment/checklist/
|
||||||
|
|
||||||
# SECURITY WARNING: keep the secret key used in production secret!
|
# 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!
|
# SECURITY WARNING: don't run with debug turned on in production!
|
||||||
DEBUG = True
|
DEBUG = True
|
||||||
@ -35,18 +33,34 @@ ALLOWED_HOSTS = []
|
|||||||
# Application definition
|
# Application definition
|
||||||
|
|
||||||
INSTALLED_APPS = [
|
INSTALLED_APPS = [
|
||||||
'django.contrib.admin',
|
"django.contrib.admin",
|
||||||
'django.contrib.auth',
|
"django.contrib.auth",
|
||||||
'django.contrib.contenttypes',
|
"django.contrib.contenttypes",
|
||||||
'django.contrib.sessions',
|
"django.contrib.sessions",
|
||||||
'django.contrib.messages',
|
"django.contrib.messages",
|
||||||
'django.contrib.staticfiles',
|
"django.contrib.staticfiles",
|
||||||
|
"django_js_reverse",
|
||||||
'main',
|
"djangobower",
|
||||||
'users',
|
"main",
|
||||||
'items',
|
"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/"
|
STATIC_URL = "/static/"
|
||||||
@ -56,7 +70,7 @@ STATICFILES_DIRS = (os.path.join(BASE_DIR, "static_source"),)
|
|||||||
STATICFILES_FINDERS = (
|
STATICFILES_FINDERS = (
|
||||||
"django.contrib.staticfiles.finders.FileSystemFinder",
|
"django.contrib.staticfiles.finders.FileSystemFinder",
|
||||||
"django.contrib.staticfiles.finders.AppDirectoriesFinder",
|
"django.contrib.staticfiles.finders.AppDirectoriesFinder",
|
||||||
# "djangobower.finders.BowerFinder",
|
"djangobower.finders.BowerFinder",
|
||||||
# "compressor.finders.CompressorFinder",
|
# "compressor.finders.CompressorFinder",
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -70,37 +84,37 @@ STORAGES = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
MIDDLEWARE = [
|
MIDDLEWARE = [
|
||||||
'django.middleware.security.SecurityMiddleware',
|
"django.middleware.security.SecurityMiddleware",
|
||||||
'django.contrib.sessions.middleware.SessionMiddleware',
|
"django.contrib.sessions.middleware.SessionMiddleware",
|
||||||
'django.middleware.common.CommonMiddleware',
|
"django.middleware.common.CommonMiddleware",
|
||||||
'django.middleware.csrf.CsrfViewMiddleware',
|
"django.middleware.csrf.CsrfViewMiddleware",
|
||||||
'django.contrib.auth.middleware.AuthenticationMiddleware',
|
"django.contrib.auth.middleware.AuthenticationMiddleware",
|
||||||
'django.contrib.messages.middleware.MessageMiddleware',
|
"django.contrib.messages.middleware.MessageMiddleware",
|
||||||
'django.middleware.clickjacking.XFrameOptionsMiddleware',
|
"django.middleware.clickjacking.XFrameOptionsMiddleware",
|
||||||
]
|
]
|
||||||
|
|
||||||
ROOT_URLCONF = 'app.urls'
|
ROOT_URLCONF = "app.urls"
|
||||||
|
|
||||||
TEMPLATES = [
|
TEMPLATES = [
|
||||||
{
|
{
|
||||||
'BACKEND': 'django.template.backends.django.DjangoTemplates',
|
"BACKEND": "django.template.backends.django.DjangoTemplates",
|
||||||
'DIRS': [
|
"DIRS": [
|
||||||
os.path.join(BASE_DIR, "templates"),
|
os.path.join(BASE_DIR, "templates"),
|
||||||
],
|
],
|
||||||
'APP_DIRS': True,
|
"APP_DIRS": True,
|
||||||
'OPTIONS': {
|
"OPTIONS": {
|
||||||
'context_processors': [
|
"context_processors": [
|
||||||
'django.template.context_processors.debug',
|
"django.template.context_processors.debug",
|
||||||
'django.template.context_processors.request',
|
"django.template.context_processors.request",
|
||||||
'django.contrib.auth.context_processors.auth',
|
"django.contrib.auth.context_processors.auth",
|
||||||
'django.contrib.messages.context_processors.messages',
|
"django.contrib.messages.context_processors.messages",
|
||||||
'app.utils.extra_context.extra_context',
|
"app.utils.extra_context.extra_context",
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
|
|
||||||
WSGI_APPLICATION = 'app.wsgi.application'
|
WSGI_APPLICATION = "app.wsgi.application"
|
||||||
|
|
||||||
|
|
||||||
# Database
|
# Database
|
||||||
@ -114,16 +128,16 @@ DATABASES = {}
|
|||||||
|
|
||||||
AUTH_PASSWORD_VALIDATORS = [
|
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
|
# Internationalization
|
||||||
# https://docs.djangoproject.com/en/5.1/topics/i18n/
|
# 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
|
USE_I18N = True
|
||||||
|
|
||||||
@ -142,12 +156,11 @@ USE_TZ = True
|
|||||||
# Default primary key field type
|
# Default primary key field type
|
||||||
# https://docs.djangoproject.com/en/5.1/ref/settings/#default-auto-field
|
# 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 *
|
from app.settingsLocal import *
|
||||||
|
|
||||||
|
|
||||||
for extra_app in EXTRA_APPS:
|
for extra_app in EXTRA_APPS:
|
||||||
INSTALLED_APPS.append(extra_app)
|
INSTALLED_APPS.append(extra_app)
|
||||||
|
|
||||||
|
|||||||
@ -4,8 +4,8 @@ def header_for_table(model):
|
|||||||
|
|
||||||
return [
|
return [
|
||||||
{
|
{
|
||||||
"text": value,
|
|
||||||
"value": key,
|
"value": key,
|
||||||
|
**value,
|
||||||
}
|
}
|
||||||
for key, value in headers.items()
|
for key, value in headers.items()
|
||||||
] + [
|
] + [
|
||||||
@ -15,7 +15,3 @@ def header_for_table(model):
|
|||||||
"sortable": False,
|
"sortable": False,
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
def encrypted_fields(model):
|
|
||||||
return model.Encryption.fields
|
|
||||||
|
|||||||
@ -1,10 +1,10 @@
|
|||||||
from django.conf import settings
|
|
||||||
from django.apps import apps
|
|
||||||
|
|
||||||
|
|
||||||
from users.models import UserSettings
|
|
||||||
from pathlib import Path
|
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):
|
def extra_context(request):
|
||||||
|
|
||||||
@ -21,17 +21,16 @@ def extra_context(request):
|
|||||||
for path in p.iterdir():
|
for path in p.iterdir():
|
||||||
components.append(path.name)
|
components.append(path.name)
|
||||||
|
|
||||||
return {
|
ret = {
|
||||||
"user_settings": user_settings,
|
"user_settings": user_settings,
|
||||||
"templates": {
|
"templates": {
|
||||||
component: f"components/{component}/template.html"
|
component: f"components/{component}/template.html"
|
||||||
for component in components
|
for component in components
|
||||||
},
|
},
|
||||||
"components": {
|
"components": {
|
||||||
component: {
|
component: f"components/{component}/vue.js" for component in components
|
||||||
"path": f"components/{component}/vue.js",
|
},
|
||||||
"flat_name": component.replace("-", "_").lower(),
|
|
||||||
}
|
|
||||||
for component in components
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
print(ret)
|
||||||
|
return ret
|
||||||
|
|||||||
@ -11,15 +11,44 @@ class BaseQuerySet(models.QuerySet):
|
|||||||
def headers(self):
|
def headers(self):
|
||||||
"""Return the list of header for a list."""
|
"""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 = {}
|
fields = {}
|
||||||
for field in self.model._meta.fields:
|
for field in self.model._meta.fields:
|
||||||
if field.name in self.model.Serialization.excluded_fields:
|
if field.name in self.model.Serialization.excluded_fields:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
# if isinstance(field, RelatedField):
|
fields.update(_field(field))
|
||||||
# fields[f"{field.name}__name"] = field.verbose_name.capitalize()
|
|
||||||
|
|
||||||
fields[field.name] = field.verbose_name.capitalize()
|
|
||||||
|
|
||||||
return fields
|
return fields
|
||||||
|
|
||||||
|
|||||||
1
k356/items/templates/components/ItemList/template.html
Normal file
1
k356/items/templates/components/ItemList/template.html
Normal file
@ -0,0 +1 @@
|
|||||||
|
{% extends "base_components/glist/template.html" %}
|
||||||
7
k356/items/templates/components/ItemList/vue.js
Normal file
7
k356/items/templates/components/ItemList/vue.js
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
{% extends "base_components/glist/vue.js" %}
|
||||||
|
{% load main %}
|
||||||
|
|
||||||
|
{% block component %}
|
||||||
|
{% define 'items' 'items' %}
|
||||||
|
{{ block.super }}
|
||||||
|
{% endblock %}
|
||||||
40
k356/items/templates/components/ItemView/template.html
Normal file
40
k356/items/templates/components/ItemView/template.html
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
{% load i18n %}
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<div class="card mt-4 pt-2 ps-lg-2">
|
||||||
|
<h5 class="card-header">{% trans "Your items" %}</h5>
|
||||||
|
<div class="card-body">
|
||||||
|
|
||||||
|
<ItemList
|
||||||
|
:crypto_key="crypto_key"
|
||||||
|
:items="items"
|
||||||
|
:items_headers="items_headers"
|
||||||
|
:items_relations="{'type': types}"
|
||||||
|
group_by="type__name"
|
||||||
|
@deleteItem="deleteItem"
|
||||||
|
@createItem="createItem"
|
||||||
|
@editItem="editItem"
|
||||||
|
></ItemList>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card mt-4 pt-2 ps-lg-2">
|
||||||
|
<h5 class="card-header">{% trans "Your types" %}</h5>
|
||||||
|
<div class="card-body">
|
||||||
|
|
||||||
|
<ItemList
|
||||||
|
:crypto_key="crypto_key"
|
||||||
|
:items="types"
|
||||||
|
:items_headers="types_headers"
|
||||||
|
:items_relations="{}"
|
||||||
|
group_by="[]"
|
||||||
|
@deleteItem="deleteType"
|
||||||
|
@createItem="createType"
|
||||||
|
@editItem="editType"
|
||||||
|
></ItemList>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
282
k356/items/templates/components/ItemView/vue.js
Normal file
282
k356/items/templates/components/ItemView/vue.js
Normal file
@ -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})
|
||||||
|
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
},
|
||||||
|
}
|
||||||
@ -1,5 +0,0 @@
|
|||||||
{% load i18n %}
|
|
||||||
|
|
||||||
<div>
|
|
||||||
<input class="form-control" type="text" v-model="item.name">
|
|
||||||
</div>
|
|
||||||
@ -1,4 +0,0 @@
|
|||||||
item = {
|
|
||||||
template: "#item",
|
|
||||||
props: ["crypto_key", "item"],
|
|
||||||
}
|
|
||||||
@ -1,88 +0,0 @@
|
|||||||
{% load i18n %}
|
|
||||||
|
|
||||||
<div>
|
|
||||||
<div class="card mt-4 pt-2 ps-lg-2">
|
|
||||||
<h5 class="card-header">{% trans "Your items" %}</h5>
|
|
||||||
<div class="card-body">
|
|
||||||
|
|
||||||
<v-data-table
|
|
||||||
:headers="data.items_headers"
|
|
||||||
:items="data.items"
|
|
||||||
:items-per-page="50"
|
|
||||||
:search="search"
|
|
||||||
group-by="type"
|
|
||||||
loading
|
|
||||||
dense>
|
|
||||||
<template v-slot:top>
|
|
||||||
<v-toolbar flat>
|
|
||||||
|
|
||||||
<v-text-field v-model="search" append-icon="mdi-magnify" label="Search" single-line hide-details></v-text-field>
|
|
||||||
<v-divider class="mx-4" insert vertical></v-divider>
|
|
||||||
|
|
||||||
<v-spacer></v-spacer>
|
|
||||||
|
|
||||||
<v-dialog v-model="dialog" max-width="500px">
|
|
||||||
<template v-slot:activator="{ on, attrs }">
|
|
||||||
<v-btn color="primary" dark class="mb-2" v-bind="attrs" v-on="on">{% trans "New item" %}</v-btn>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<v-card>
|
|
||||||
<v-card-text>
|
|
||||||
<v-container>
|
|
||||||
<v-row>
|
|
||||||
<v-text-field v-model="editedItem.name" label="Name"></v-text-field>
|
|
||||||
<v-select
|
|
||||||
v-model="editedItem.type"
|
|
||||||
:items="data.types"
|
|
||||||
label="Type"
|
|
||||||
item-text="name"
|
|
||||||
item-value="id"
|
|
||||||
persistent-hint
|
|
||||||
>
|
|
||||||
<template slot="item" slot-scope="data">
|
|
||||||
[[ data.item.name ]] - [[ data.item.custom_identifier ]]
|
|
||||||
</template>
|
|
||||||
</v-select>
|
|
||||||
<v-textarea v-model="editedItem.description" label="Description"></v-textarea>
|
|
||||||
<v-text-field v-model="editedItem.custom_identifier" label="Identifier"></v-text-field>
|
|
||||||
</v-row>
|
|
||||||
</v-container>
|
|
||||||
</v-card-text>
|
|
||||||
|
|
||||||
<v-card-actions>
|
|
||||||
<v-spacer></v-spacer>
|
|
||||||
<v-btn color="blue darken-1" text @click="close">{% trans "Cancel" %}</v-btn>
|
|
||||||
<v-btn color="blue darken-1" text @click="save">{% trans "Save" %}</v-btn>
|
|
||||||
</v-card-actions>
|
|
||||||
</v-card>
|
|
||||||
</v-dialog>
|
|
||||||
|
|
||||||
<v-dialog v-model="dialogDelete" max-width="500px">
|
|
||||||
<v-card>
|
|
||||||
<v-card-title class="text-h5">{% trans "Are you sure you want to delete this item?" %}</v-card-title>
|
|
||||||
<v-card-actions>
|
|
||||||
<v-spacer></v-spacer>
|
|
||||||
<v-btn color="blue darken-1" text @click="closeDelete">{% trans "Cancel" %}</v-btn>
|
|
||||||
<v-btn color="blue darken-1" text @click="deleteItemConfirm">{% trans "OK" %}</v-btn>
|
|
||||||
<v-spacer></v-spacer>
|
|
||||||
</v-card-actions>
|
|
||||||
</v-card>
|
|
||||||
</v-dialog>
|
|
||||||
</v-toolbar>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<template v-slot:item.actions="{ item }">
|
|
||||||
<v-icon small class="mr-2" @click="editItem(item)">mdi-pencil</v-icon>
|
|
||||||
<v-icon small @click="deleteItem(item)">mdi-delete</v-icon>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<template v-slot:no-data>
|
|
||||||
<v-btn color="primary" @click="initialize">{% trans "Reset" %}</v-btn>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
</v-data-table>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
@ -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()
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,5 +1,5 @@
|
|||||||
from django.urls import path
|
from django.urls import path
|
||||||
from items.views import item as item_view
|
from items.views import item_view, type_view
|
||||||
|
|
||||||
app_name = "items"
|
app_name = "items"
|
||||||
|
|
||||||
@ -7,4 +7,6 @@ urlpatterns = [
|
|||||||
path("", item_view.item_list, name="list"),
|
path("", item_view.item_list, name="list"),
|
||||||
path("<uuid:id>", item_view.item_edit, name="edit"),
|
path("<uuid:id>", item_view.item_edit, name="edit"),
|
||||||
path("create", item_view.item_edit, {"id": None}, name="create"),
|
path("create", item_view.item_edit, {"id": None}, name="create"),
|
||||||
|
path("type/<uuid:id>", type_view.type_edit, name="type.edit"),
|
||||||
|
path("type/create", type_view.type_edit, {"id": None}, name="type.create"),
|
||||||
]
|
]
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
import json
|
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.contrib.auth.decorators import login_required
|
||||||
from django.db import models
|
from django.db import models
|
||||||
from django.db.models.fields.related import RelatedField
|
from django.db.models.fields.related import RelatedField
|
||||||
@ -12,18 +12,15 @@ from items.models import Item, ItemType
|
|||||||
def item_list(request):
|
def item_list(request):
|
||||||
|
|
||||||
items = Item.objects.filter(author=request.user.setting)
|
items = Item.objects.filter(author=request.user.setting)
|
||||||
|
|
||||||
types = ItemType.objects.filter(author=request.user.setting)
|
types = ItemType.objects.filter(author=request.user.setting)
|
||||||
|
|
||||||
return JsonResponse(
|
return JsonResponse(
|
||||||
{
|
{
|
||||||
"result": {
|
"result": {
|
||||||
"items": list(items.serialize()),
|
"items": list(items.serialize()),
|
||||||
"types": list(types.serialize()),
|
|
||||||
"items_headers": header_for_table(Item),
|
"items_headers": header_for_table(Item),
|
||||||
|
"types": list(types.serialize()),
|
||||||
"types_headers": header_for_table(ItemType),
|
"types_headers": header_for_table(ItemType),
|
||||||
"items_encrypted": encrypted_fields(Item),
|
|
||||||
"types_encrypted": encrypted_fields(ItemType),
|
|
||||||
},
|
},
|
||||||
"count": items.count(),
|
"count": items.count(),
|
||||||
}
|
}
|
||||||
@ -85,6 +82,6 @@ def item_edit(request, id=None):
|
|||||||
|
|
||||||
return JsonResponse(
|
return JsonResponse(
|
||||||
{
|
{
|
||||||
"item": Item.objects.filter(id=item.id).serialize().first(),
|
"object": Item.objects.filter(id=item.id).serialize().first(),
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
67
k356/items/views/type_view.py
Normal file
67
k356/items/views/type_view.py
Normal file
@ -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(),
|
||||||
|
}
|
||||||
|
)
|
||||||
@ -1,5 +1,5 @@
|
|||||||
encryption_testing = {
|
EncryptionTesting = {
|
||||||
template: "#encryption-testing",
|
template: "#EncryptionTesting",
|
||||||
props: ["crypto_key"],
|
props: ["crypto_key"],
|
||||||
data: function() {
|
data: function() {
|
||||||
return {
|
return {
|
||||||
@ -1,7 +1,7 @@
|
|||||||
const rvalidate = Vue.resource(Urls["users:k356.validate"]);
|
const rvalidate = Vue.resource(Urls["users:k356.validate"]);
|
||||||
|
|
||||||
k356_loading = {
|
Loading = {
|
||||||
template: "#k356-loading",
|
template: "#Loading",
|
||||||
|
|
||||||
props: ["crypto_key"],
|
props: ["crypto_key"],
|
||||||
data: function() {
|
data: function() {
|
||||||
@ -1,10 +0,0 @@
|
|||||||
{% extends "base.html" %}
|
|
||||||
|
|
||||||
{% load i18n %}
|
|
||||||
|
|
||||||
{% block title %}{% trans "Home" %}{% endblock %}
|
|
||||||
{% block body %}
|
|
||||||
|
|
||||||
<k356-loading></k356-loading>
|
|
||||||
|
|
||||||
{% endblock %}
|
|
||||||
0
k356/main/templatetags/__init__.py
Normal file
0
k356/main/templatetags/__init__.py
Normal file
9
k356/main/templatetags/main.py
Normal file
9
k356/main/templatetags/main.py
Normal file
@ -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 ""
|
||||||
@ -11,9 +11,7 @@
|
|||||||
<script src="https://code.jquery.com/jquery-3.7.1.min.js"></script>
|
<script src="https://code.jquery.com/jquery-3.7.1.min.js"></script>
|
||||||
<script src="https://cdn.jsdelivr.net/npm/vue@2.7.16/dist/vue.js"></script>
|
<script src="https://cdn.jsdelivr.net/npm/vue@2.7.16/dist/vue.js"></script>
|
||||||
<script src="https://cdn.jsdelivr.net/npm/sweetalert2@11"></script>
|
<script src="https://cdn.jsdelivr.net/npm/sweetalert2@11"></script>
|
||||||
<!-- <script src="https://cdnjs.cloudflare.com/ajax/libs/vue-resource/0.6.1/vue-resource.min.js"></script> -->
|
|
||||||
<script src="https://cdn.jsdelivr.net/npm/vue-resource@1.5.3"></script>
|
<script src="https://cdn.jsdelivr.net/npm/vue-resource@1.5.3"></script>
|
||||||
<script src="https://unpkg.com/htmx.org@2.0.2"></script>
|
|
||||||
<script src="https://cdn.jsdelivr.net/npm/js-cookie@3.0.5/dist/js.cookie.min.js"></script>
|
<script src="https://cdn.jsdelivr.net/npm/js-cookie@3.0.5/dist/js.cookie.min.js"></script>
|
||||||
<script src="https://unpkg.com/vue-router@3/dist/vue-router.js"></script>
|
<script src="https://unpkg.com/vue-router@3/dist/vue-router.js"></script>
|
||||||
|
|
||||||
@ -28,14 +26,22 @@
|
|||||||
|
|
||||||
<link href="https://cdn.jsdelivr.net/npm/@mdi/font@7.4.47/css/materialdesignicons.min.css" rel="stylesheet">
|
<link href="https://cdn.jsdelivr.net/npm/@mdi/font@7.4.47/css/materialdesignicons.min.css" rel="stylesheet">
|
||||||
|
|
||||||
|
<link href="https://fonts.cdnfonts.com/css/jetbrains-mono" rel="stylesheet">
|
||||||
|
<style>
|
||||||
|
.font {
|
||||||
|
font-family: 'JetBrains Mono', sans-serif;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
</head>
|
</head>
|
||||||
<body hx-headers='{"X-CSRFToken": "{{ csrf_token }}"}'>
|
<body>
|
||||||
<v-app>
|
<v-app class="font">
|
||||||
<div id="main" data-app>
|
<div id="main" data-app>
|
||||||
|
|
||||||
<nav class="navbar navbar-expand-lg bg-primary" data-bs-theme="dark">
|
<nav class="navbar navbar-expand-lg bg-primary" data-bs-theme="dark">
|
||||||
<div class="container-fluid">
|
<div class="container-fluid">
|
||||||
<a href="#" @click="lock_me" class="navbar-brand">{% trans "Lock" %}</a>
|
<a href="#" @click="lock_me" class="navbar-brand">{% trans "Lock" %}</a>
|
||||||
|
|
||||||
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarColor01" aria-controls="navbarColor01" aria-expanded="false" aria-label="Toggle navigation">
|
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarColor01" aria-controls="navbarColor01" aria-expanded="false" aria-label="Toggle navigation">
|
||||||
<span class="navbar-toggler-icon"></span>
|
<span class="navbar-toggler-icon"></span>
|
||||||
</button>
|
</button>
|
||||||
@ -49,13 +55,13 @@
|
|||||||
</router-link>
|
</router-link>
|
||||||
</li>
|
</li>
|
||||||
<li class="nav-item">
|
<li class="nav-item">
|
||||||
<router-link class="nav-link" to="/item_list">{% trans "Items" %}</router-link>
|
<router-link class="nav-link" to="/ItemView">{% trans "Items" %}</router-link>
|
||||||
</li>
|
</li>
|
||||||
<li class="nav-item">
|
<li class="nav-item">
|
||||||
<a class="nav-link" href="#">{% trans "Properties" %}</a>
|
<a class="nav-link" href="#">{% trans "Properties" %}</a>
|
||||||
</li>
|
</li>
|
||||||
<li class="nav-item">
|
<li class="nav-item">
|
||||||
<router-link class="nav-link" to="/encryption-testing">{% trans "Encryption" %}</router-link>
|
<router-link class="nav-link" to="/EncryptionTesting">{% trans "Encryption" %}</router-link>
|
||||||
</li>
|
</li>
|
||||||
<li class="nav-item dropdown">
|
<li class="nav-item dropdown">
|
||||||
<a class="nav-link dropdown-toggle" data-bs-toggle="dropdown" href="#" role="button" aria-haspopup="true" aria-expanded="false">{% trans "More" %}</a>
|
<a class="nav-link dropdown-toggle" data-bs-toggle="dropdown" href="#" role="button" aria-haspopup="true" aria-expanded="false">{% trans "More" %}</a>
|
||||||
@ -77,7 +83,7 @@
|
|||||||
</nav>
|
</nav>
|
||||||
|
|
||||||
<div id="app" class="container">
|
<div id="app" class="container">
|
||||||
<k356-loading :crypto_key="key" @update_key="update_key" v-if="locked"></k356-loading>
|
<Loading :crypto_key="key" @update_key="update_key" v-if="locked"></Loading>
|
||||||
<template v-if="!locked">
|
<template v-if="!locked">
|
||||||
<router-view :crypto_key="key"></router-view>
|
<router-view :crypto_key="key"></router-view>
|
||||||
</template>
|
</template>
|
||||||
@ -101,17 +107,17 @@
|
|||||||
|
|
||||||
Vue.config.delimiters = ["[[", "]]"];
|
Vue.config.delimiters = ["[[", "]]"];
|
||||||
|
|
||||||
{% for name, value in components.items %}
|
{% for name, path in components.items %}
|
||||||
{% include value.path %}
|
{% include path %}
|
||||||
Vue.component("{{ name }}", {{ value.flat_name }});
|
Vue.component("{{ name }}", {{ name }});
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
|
||||||
const routes = [
|
const routes = [
|
||||||
{ path: '/', component: null },
|
{ path: '/', component: null },
|
||||||
{% for name, value in components.items %}
|
{% for name, path in components.items %}
|
||||||
{
|
{
|
||||||
path: "/{{ name }}",
|
path: "/{{ name }}",
|
||||||
component: {{ value.flat_name }},
|
component: {{ name }},
|
||||||
},
|
},
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
];
|
];
|
||||||
@ -131,7 +137,6 @@
|
|||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
update_key: function(key) {
|
update_key: function(key) {
|
||||||
console.log(key);
|
|
||||||
this.key.key = key;
|
this.key.key = key;
|
||||||
this.locked = key == null;
|
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.
|
// Prevent from routing if key is not present.
|
||||||
return approuter.key.key != null;
|
next(approuter.key.key != null);
|
||||||
});
|
});
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
@ -159,5 +164,16 @@
|
|||||||
refresh_csrftoken();
|
refresh_csrftoken();
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<script type="module">
|
||||||
|
import { useDate } from './vuetify.js'
|
||||||
|
|
||||||
|
const date = useDate()
|
||||||
|
|
||||||
|
const formatted = date.format('2010-04-13', 'fullDateWithWeekday')
|
||||||
|
|
||||||
|
console.log(formatted) // Tuesday, April 13, 2010
|
||||||
|
</script>
|
||||||
|
|
||||||
|
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
104
k356/templates/base_components/glist/template.html
Normal file
104
k356/templates/base_components/glist/template.html
Normal file
@ -0,0 +1,104 @@
|
|||||||
|
{% load i18n %}
|
||||||
|
|
||||||
|
{% block component %}
|
||||||
|
<v-data-table
|
||||||
|
:headers="items_headers"
|
||||||
|
:items="items"
|
||||||
|
:items-per-page="50"
|
||||||
|
:search="search"
|
||||||
|
:group-by="group_by"
|
||||||
|
loading
|
||||||
|
dense>
|
||||||
|
<template v-slot:top>
|
||||||
|
<v-toolbar flat>
|
||||||
|
|
||||||
|
<v-text-field v-model="search" append-icon="mdi-magnify" label="Search" single-line hide-details></v-text-field>
|
||||||
|
<v-divider class="mx-4" insert vertical></v-divider>
|
||||||
|
|
||||||
|
<v-spacer></v-spacer>
|
||||||
|
|
||||||
|
<v-dialog v-model="dialog" max-width="500px">
|
||||||
|
<template v-slot:activator="{ on, attrs }">
|
||||||
|
<v-btn color="primary" dark class="mb-2" v-bind="attrs" v-on="on">
|
||||||
|
<v-icon small class="mr-2">mdi-plus</v-icon>
|
||||||
|
{% trans "New" %}
|
||||||
|
</v-btn>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<v-card>
|
||||||
|
<v-card-title>
|
||||||
|
<span class="text-h5"> [[ editedItem.id ]]</span>
|
||||||
|
</v-card-title>
|
||||||
|
<v-card-text>
|
||||||
|
<v-container>
|
||||||
|
<v-row>
|
||||||
|
<template v-for="field in editable_fields">
|
||||||
|
<template v-if="field.field_widget == 'v-select'">
|
||||||
|
<v-select
|
||||||
|
v-model="editedItem[field.value]"
|
||||||
|
:items="items_relations[field.value]"
|
||||||
|
:label="field.text"
|
||||||
|
item-text="name"
|
||||||
|
item-value="id"
|
||||||
|
persistent-hint>
|
||||||
|
<template slot="item" slot-scope="data">
|
||||||
|
[[ data.item.name ]] - [[ data.item.custom_identifier ]]
|
||||||
|
</template>
|
||||||
|
</v-select>
|
||||||
|
</template>
|
||||||
|
<template v-else>
|
||||||
|
<component :is="field.field_widget" v-model="editedItem[field.value]" :label="field.text"></component>
|
||||||
|
</template>
|
||||||
|
</template>
|
||||||
|
</v-row>
|
||||||
|
</v-container>
|
||||||
|
</v-card-text>
|
||||||
|
|
||||||
|
<v-card-actions>
|
||||||
|
<v-spacer></v-spacer>
|
||||||
|
<v-btn color="blue darken-1" text @click="close">
|
||||||
|
<v-icon small class="mr-2">mdi-cancel</v-icon>
|
||||||
|
{% trans "Cancel" %}
|
||||||
|
</v-btn>
|
||||||
|
<v-btn color="blue darken-1" text @click="save">
|
||||||
|
<v-icon small class="mr-2">mdi-content-save-edit</v-icon>
|
||||||
|
{% trans "Save" %}
|
||||||
|
</v-btn>
|
||||||
|
</v-card-actions>
|
||||||
|
</v-card>
|
||||||
|
</v-dialog>
|
||||||
|
|
||||||
|
<v-dialog v-model="dialogDelete" max-width="500px">
|
||||||
|
<v-card>
|
||||||
|
<v-card-title class="text-h5">{% trans "Are you sure you want to delete this item?" %}</v-card-title>
|
||||||
|
<v-card-actions>
|
||||||
|
<v-spacer></v-spacer>
|
||||||
|
<v-btn color="blue darken-1" text @click="closeDelete">
|
||||||
|
<v-icon small class="mr-2">mdi-cancel</v-icon>
|
||||||
|
{% trans "Cancel" %}
|
||||||
|
</v-btn>
|
||||||
|
<v-btn color="blue darken-1" text @click="deleteItemConfirm">
|
||||||
|
<v-icon small class="mr-2">mdi-delete-forever</v-icon>
|
||||||
|
{% trans "OK" %}
|
||||||
|
</v-btn>
|
||||||
|
<v-spacer></v-spacer>
|
||||||
|
</v-card-actions>
|
||||||
|
</v-card>
|
||||||
|
</v-dialog>
|
||||||
|
</v-toolbar>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<template v-slot:item.id="{ item }">
|
||||||
|
[[ item.id.slice(0, 8) ]]...
|
||||||
|
</template>
|
||||||
|
<template v-slot:item.actions="{ item }">
|
||||||
|
<v-icon small class="mr-2" @click="editItem(item)">mdi-pencil</v-icon>
|
||||||
|
<v-icon small @click="deleteItem(item)">mdi-delete</v-icon>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<template v-slot:no-data>
|
||||||
|
<v-btn color="primary" @click="initialize">{% trans "Reset" %}</v-btn>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
</v-data-table>
|
||||||
|
{% endblock %}
|
||||||
87
k356/templates/base_components/glist/vue.js
Normal file
87
k356/templates/base_components/glist/vue.js
Normal file
@ -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 %}
|
||||||
Loading…
Reference in New Issue
Block a user