tina4-python 0.2.168__tar.gz → 0.2.170__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {tina4_python-0.2.168 → tina4_python-0.2.170}/PKG-INFO +1 -1
- {tina4_python-0.2.168 → tina4_python-0.2.170}/pyproject.toml +65 -65
- {tina4_python-0.2.168 → tina4_python-0.2.170}/tina4_python/CRUD.py +42 -9
- {tina4_python-0.2.168 → tina4_python-0.2.170}/tina4_python/FieldTypes.py +16 -9
- {tina4_python-0.2.168 → tina4_python-0.2.170}/tina4_python/ORM.py +5 -4
- {tina4_python-0.2.168 → tina4_python-0.2.170}/tina4_python/__init__.py +5 -1
- {tina4_python-0.2.168 → tina4_python-0.2.170}/tina4_python/templates/components/crud.twig +31 -4
- {tina4_python-0.2.168 → tina4_python-0.2.170}/.gitignore +0 -0
- {tina4_python-0.2.168 → tina4_python-0.2.170}/README.md +0 -0
- {tina4_python-0.2.168 → tina4_python-0.2.170}/tina4_python/Api.py +0 -0
- {tina4_python-0.2.168 → tina4_python-0.2.170}/tina4_python/Auth.py +0 -0
- {tina4_python-0.2.168 → tina4_python-0.2.170}/tina4_python/Constant.py +0 -0
- {tina4_python-0.2.168 → tina4_python-0.2.170}/tina4_python/Database.py +0 -0
- {tina4_python-0.2.168 → tina4_python-0.2.170}/tina4_python/DatabaseResult.py +0 -0
- {tina4_python-0.2.168 → tina4_python-0.2.170}/tina4_python/DatabaseTypes.py +0 -0
- {tina4_python-0.2.168 → tina4_python-0.2.170}/tina4_python/Debug.py +0 -0
- {tina4_python-0.2.168 → tina4_python-0.2.170}/tina4_python/Env.py +0 -0
- {tina4_python-0.2.168 → tina4_python-0.2.170}/tina4_python/HtmlElement.py +0 -0
- {tina4_python-0.2.168 → tina4_python-0.2.170}/tina4_python/Localization.py +0 -0
- {tina4_python-0.2.168 → tina4_python-0.2.170}/tina4_python/Messages.py +0 -0
- {tina4_python-0.2.168 → tina4_python-0.2.170}/tina4_python/MiddleWare.py +0 -0
- {tina4_python-0.2.168 → tina4_python-0.2.170}/tina4_python/Migration.py +0 -0
- {tina4_python-0.2.168 → tina4_python-0.2.170}/tina4_python/Queue.py +0 -0
- {tina4_python-0.2.168 → tina4_python-0.2.170}/tina4_python/Request.py +0 -0
- {tina4_python-0.2.168 → tina4_python-0.2.170}/tina4_python/Response.py +0 -0
- {tina4_python-0.2.168 → tina4_python-0.2.170}/tina4_python/Router.py +0 -0
- {tina4_python-0.2.168 → tina4_python-0.2.170}/tina4_python/Session.py +0 -0
- {tina4_python-0.2.168 → tina4_python-0.2.170}/tina4_python/ShellColors.py +0 -0
- {tina4_python-0.2.168 → tina4_python-0.2.170}/tina4_python/Swagger.py +0 -0
- {tina4_python-0.2.168 → tina4_python-0.2.170}/tina4_python/Template.py +0 -0
- {tina4_python-0.2.168 → tina4_python-0.2.170}/tina4_python/Testing.py +0 -0
- {tina4_python-0.2.168 → tina4_python-0.2.170}/tina4_python/WSDL.py +0 -0
- {tina4_python-0.2.168 → tina4_python-0.2.170}/tina4_python/Webserver.py +0 -0
- {tina4_python-0.2.168 → tina4_python-0.2.170}/tina4_python/Websocket.py +0 -0
- {tina4_python-0.2.168 → tina4_python-0.2.170}/tina4_python/cli.py +0 -0
- {tina4_python-0.2.168 → tina4_python-0.2.170}/tina4_python/messages.pot +0 -0
- {tina4_python-0.2.168 → tina4_python-0.2.170}/tina4_python/public/css/readme.md +0 -0
- {tina4_python-0.2.168 → tina4_python-0.2.170}/tina4_python/public/favicon.ico +0 -0
- {tina4_python-0.2.168 → tina4_python-0.2.170}/tina4_python/public/images/403.png +0 -0
- {tina4_python-0.2.168 → tina4_python-0.2.170}/tina4_python/public/images/404.png +0 -0
- {tina4_python-0.2.168 → tina4_python-0.2.170}/tina4_python/public/images/500.png +0 -0
- {tina4_python-0.2.168 → tina4_python-0.2.170}/tina4_python/public/images/logo.png +0 -0
- {tina4_python-0.2.168 → tina4_python-0.2.170}/tina4_python/public/images/readme.md +0 -0
- {tina4_python-0.2.168 → tina4_python-0.2.170}/tina4_python/public/js/readme.md +0 -0
- {tina4_python-0.2.168 → tina4_python-0.2.170}/tina4_python/public/js/reconnecting-websocket.js +0 -0
- {tina4_python-0.2.168 → tina4_python-0.2.170}/tina4_python/public/js/tina4helper.js +0 -0
- {tina4_python-0.2.168 → tina4_python-0.2.170}/tina4_python/public/swagger/index.html +0 -0
- {tina4_python-0.2.168 → tina4_python-0.2.170}/tina4_python/public/swagger/oauth2-redirect.html +0 -0
- {tina4_python-0.2.168 → tina4_python-0.2.170}/tina4_python/templates/errors/403.twig +0 -0
- {tina4_python-0.2.168 → tina4_python-0.2.170}/tina4_python/templates/errors/404.twig +0 -0
- {tina4_python-0.2.168 → tina4_python-0.2.170}/tina4_python/templates/errors/500.twig +0 -0
- {tina4_python-0.2.168 → tina4_python-0.2.170}/tina4_python/templates/readme.md +0 -0
- {tina4_python-0.2.168 → tina4_python-0.2.170}/tina4_python/translations/en/LC_MESSAGES/messages.mo +0 -0
- {tina4_python-0.2.168 → tina4_python-0.2.170}/tina4_python/translations/en/LC_MESSAGES/messages.po +0 -0
- {tina4_python-0.2.168 → tina4_python-0.2.170}/tina4_python/translations/fr/LC_MESSAGES/messages.mo +0 -0
- {tina4_python-0.2.168 → tina4_python-0.2.170}/tina4_python/translations/fr/LC_MESSAGES/messages.po +0 -0
|
@@ -1,65 +1,65 @@
|
|
|
1
|
-
[project]
|
|
2
|
-
name = "tina4-python"
|
|
3
|
-
version = "0.2.
|
|
4
|
-
description = "Tina4Python - This is not another framework for Python"
|
|
5
|
-
authors = [
|
|
6
|
-
{name = "Andre van Zuydam",email = "andrevanzuydam@gmail.com"}
|
|
7
|
-
]
|
|
8
|
-
readme = "README.md"
|
|
9
|
-
requires-python = ">=3.12,<4.0"
|
|
10
|
-
dependencies = [
|
|
11
|
-
"jinja2>=3.1.5,<4.0.0",
|
|
12
|
-
"libsass (>=0.23.0,<0.24.0)",
|
|
13
|
-
"python-dotenv (>=1.0.1,<2.0.0)",
|
|
14
|
-
"pyjwt (>=2.10.1,<3.0.0)",
|
|
15
|
-
"cryptography (>=44.0.0,<45.0.0)",
|
|
16
|
-
"watchdog (>=6.0.0,<7.0.0)",
|
|
17
|
-
"bcrypt (>=4.2.1,<5.0.0)",
|
|
18
|
-
"litequeue (>=0.9,<0.10)",
|
|
19
|
-
"simple-websocket (>=1.1.0,<2.0.0)",
|
|
20
|
-
"asyncer>=0.0.8",
|
|
21
|
-
"hypercorn>=0.18.0",
|
|
22
|
-
"requests>=2.32.5",
|
|
23
|
-
]
|
|
24
|
-
|
|
25
|
-
[dependency-groups]
|
|
26
|
-
dev = [
|
|
27
|
-
"flake8>=7.2.0",
|
|
28
|
-
"jurigged>=0.6.0",
|
|
29
|
-
"mkdocs>=1.6.1",
|
|
30
|
-
"mysql-connector-python>=9.3.0",
|
|
31
|
-
"psycopg2-binary>=2.9.10",
|
|
32
|
-
"pydoc-markdown>=4.8.2",
|
|
33
|
-
"pytest>=8.3.5",
|
|
34
|
-
"pytest-asyncio>=1.3.0",
|
|
35
|
-
"python-keycloak>=5.8.1",
|
|
36
|
-
"ruff>=0.11.9",
|
|
37
|
-
"safety>=3.5.0",
|
|
38
|
-
]
|
|
39
|
-
|
|
40
|
-
[build-system]
|
|
41
|
-
requires = ["hatchling"]
|
|
42
|
-
build-backend = "hatchling.build"
|
|
43
|
-
|
|
44
|
-
[tool.hatch.build]
|
|
45
|
-
include = [
|
|
46
|
-
"tina4_python/**/*"
|
|
47
|
-
]
|
|
48
|
-
|
|
49
|
-
[project.scripts]
|
|
50
|
-
tina4 = "tina4_python.cli:main"
|
|
51
|
-
|
|
52
|
-
[[tool.pydoc-markdown.loaders]]
|
|
53
|
-
type = "python"
|
|
54
|
-
search_path = [ "./tina4_python" ]
|
|
55
|
-
|
|
56
|
-
[tool.pydoc-markdown.renderer]
|
|
57
|
-
type = "mkdocs"
|
|
58
|
-
site_name= "Tina4Python"
|
|
59
|
-
|
|
60
|
-
[[tool.pydoc-markdown.renderer.pages]]
|
|
61
|
-
title = "API Documentation"
|
|
62
|
-
name = "index"
|
|
63
|
-
|
|
64
|
-
contents = [ "*" ]
|
|
65
|
-
|
|
1
|
+
[project]
|
|
2
|
+
name = "tina4-python"
|
|
3
|
+
version = "0.2.170"
|
|
4
|
+
description = "Tina4Python - This is not another framework for Python"
|
|
5
|
+
authors = [
|
|
6
|
+
{name = "Andre van Zuydam",email = "andrevanzuydam@gmail.com"}
|
|
7
|
+
]
|
|
8
|
+
readme = "README.md"
|
|
9
|
+
requires-python = ">=3.12,<4.0"
|
|
10
|
+
dependencies = [
|
|
11
|
+
"jinja2>=3.1.5,<4.0.0",
|
|
12
|
+
"libsass (>=0.23.0,<0.24.0)",
|
|
13
|
+
"python-dotenv (>=1.0.1,<2.0.0)",
|
|
14
|
+
"pyjwt (>=2.10.1,<3.0.0)",
|
|
15
|
+
"cryptography (>=44.0.0,<45.0.0)",
|
|
16
|
+
"watchdog (>=6.0.0,<7.0.0)",
|
|
17
|
+
"bcrypt (>=4.2.1,<5.0.0)",
|
|
18
|
+
"litequeue (>=0.9,<0.10)",
|
|
19
|
+
"simple-websocket (>=1.1.0,<2.0.0)",
|
|
20
|
+
"asyncer>=0.0.8",
|
|
21
|
+
"hypercorn>=0.18.0",
|
|
22
|
+
"requests>=2.32.5",
|
|
23
|
+
]
|
|
24
|
+
|
|
25
|
+
[dependency-groups]
|
|
26
|
+
dev = [
|
|
27
|
+
"flake8>=7.2.0",
|
|
28
|
+
"jurigged>=0.6.0",
|
|
29
|
+
"mkdocs>=1.6.1",
|
|
30
|
+
"mysql-connector-python>=9.3.0",
|
|
31
|
+
"psycopg2-binary>=2.9.10",
|
|
32
|
+
"pydoc-markdown>=4.8.2",
|
|
33
|
+
"pytest>=8.3.5",
|
|
34
|
+
"pytest-asyncio>=1.3.0",
|
|
35
|
+
"python-keycloak>=5.8.1",
|
|
36
|
+
"ruff>=0.11.9",
|
|
37
|
+
"safety>=3.5.0",
|
|
38
|
+
]
|
|
39
|
+
|
|
40
|
+
[build-system]
|
|
41
|
+
requires = ["hatchling"]
|
|
42
|
+
build-backend = "hatchling.build"
|
|
43
|
+
|
|
44
|
+
[tool.hatch.build]
|
|
45
|
+
include = [
|
|
46
|
+
"tina4_python/**/*"
|
|
47
|
+
]
|
|
48
|
+
|
|
49
|
+
[project.scripts]
|
|
50
|
+
tina4 = "tina4_python.cli:main"
|
|
51
|
+
|
|
52
|
+
[[tool.pydoc-markdown.loaders]]
|
|
53
|
+
type = "python"
|
|
54
|
+
search_path = [ "./tina4_python" ]
|
|
55
|
+
|
|
56
|
+
[tool.pydoc-markdown.renderer]
|
|
57
|
+
type = "mkdocs"
|
|
58
|
+
site_name= "Tina4Python"
|
|
59
|
+
|
|
60
|
+
[[tool.pydoc-markdown.renderer.pages]]
|
|
61
|
+
title = "API Documentation"
|
|
62
|
+
name = "index"
|
|
63
|
+
|
|
64
|
+
contents = [ "*" ]
|
|
65
|
+
|
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
import base64
|
|
9
9
|
import datetime
|
|
10
10
|
from decimal import Decimal
|
|
11
|
+
from shlex import join
|
|
11
12
|
|
|
12
13
|
from tina4_python.Debug import Debug
|
|
13
14
|
import re
|
|
@@ -252,6 +253,21 @@ class CRUD:
|
|
|
252
253
|
|
|
253
254
|
twig_file = self.ensure_crud_template(crud_name + ".twig")
|
|
254
255
|
|
|
256
|
+
def parse_request_for_images(request):
|
|
257
|
+
"""
|
|
258
|
+
Parses request for images and files
|
|
259
|
+
:param request:
|
|
260
|
+
:return:
|
|
261
|
+
"""
|
|
262
|
+
# if there are files in the request the then I assign the file value to the request body
|
|
263
|
+
if request.files:
|
|
264
|
+
for input_name in request.files:
|
|
265
|
+
prefix = ""
|
|
266
|
+
if "image" in request.files[input_name]["content_type"]:
|
|
267
|
+
prefix ="data:"+request.files[input_name]["content_type"]+";base64,"
|
|
268
|
+
request.body[input_name] = prefix+request.files[input_name]["content"]
|
|
269
|
+
return request
|
|
270
|
+
|
|
255
271
|
async def get_record(request, response):
|
|
256
272
|
limit = int(request.params.get("limit", options.get("limit", 10)))
|
|
257
273
|
offset = int(request.params.get("offset", options.get("offset", 0)))
|
|
@@ -285,12 +301,21 @@ class CRUD:
|
|
|
285
301
|
|
|
286
302
|
async def post_record(request, response):
|
|
287
303
|
Debug.info("CRUD CREATE", table_name, request.body)
|
|
304
|
+
|
|
305
|
+
request = parse_request_for_images(request)
|
|
306
|
+
|
|
288
307
|
self.dba.insert(table_name, request.body, primary_key=options["primary_key"])
|
|
289
308
|
self.dba.commit()
|
|
290
309
|
return response({"message": f"<script>showMessage('{table_nice_name} Record added');</script>"}, HTTP_OK, APPLICATION_JSON)
|
|
291
310
|
|
|
292
311
|
async def update_record(request, response):
|
|
293
312
|
Debug.info("CRUD UPDATE", table_name, request.params, request.body)
|
|
313
|
+
# Add the primary key if it is taken out of the form
|
|
314
|
+
if options["primary_key"] not in request.body:
|
|
315
|
+
request.body[options["primary_key"]] = request.params[options["primary_key"]]
|
|
316
|
+
|
|
317
|
+
request = parse_request_for_images(request)
|
|
318
|
+
|
|
294
319
|
self.dba.update(table_name, request.body, primary_key=options["primary_key"])
|
|
295
320
|
self.dba.commit()
|
|
296
321
|
return response({"message": f"<script>showMessage('{table_nice_name} Record updated');</script>", "post": request.body}, HTTP_OK, APPLICATION_JSON)
|
|
@@ -311,7 +336,7 @@ class CRUD:
|
|
|
311
336
|
fields.append({"name": column, "label": Template.get_nice_label(column)})
|
|
312
337
|
|
|
313
338
|
html = Template.render(twig_file.replace(os.path.join(tina4_python.root_path, "src", "templates"), "").replace("\\", "/"),
|
|
314
|
-
{"columns": fields, "records": self.to_array(),
|
|
339
|
+
{"columns": fields, "records": self.to_array(base64_encode=False),
|
|
315
340
|
"table_name": crud_name,
|
|
316
341
|
"total_records": self.total_count,
|
|
317
342
|
"options": options})
|
|
@@ -320,7 +345,7 @@ class CRUD:
|
|
|
320
345
|
except Exception as e:
|
|
321
346
|
return "Error rendering CRUD: "+str(e)
|
|
322
347
|
|
|
323
|
-
def to_array(self, _filter=None):
|
|
348
|
+
def to_array(self, _filter=None, base64_encode = True):
|
|
324
349
|
"""
|
|
325
350
|
Convert the internal records to a JSON-serializable list of dictionaries.
|
|
326
351
|
|
|
@@ -334,6 +359,8 @@ class CRUD:
|
|
|
334
359
|
|
|
335
360
|
Returns:
|
|
336
361
|
list[dict]: Serializable records.
|
|
362
|
+
:param _filter:
|
|
363
|
+
:param base64_encode: Override default base64 encoding
|
|
337
364
|
"""
|
|
338
365
|
if self.error is not None:
|
|
339
366
|
return {"error": self.error}
|
|
@@ -348,9 +375,15 @@ class CRUD:
|
|
|
348
375
|
elif isinstance(record[key], (datetime.date, datetime.datetime)):
|
|
349
376
|
json_record[key] = record[key].isoformat()
|
|
350
377
|
elif isinstance(record[key], memoryview):
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
378
|
+
if base64_encode:
|
|
379
|
+
json_record[key] = base64.b64encode(record[key].tobytes()).decode('utf-8')
|
|
380
|
+
else:
|
|
381
|
+
json_record[key] = record[key].tobytes().decode("utf-8")
|
|
382
|
+
elif isinstance(record[key], bytes) and base64_encode:
|
|
383
|
+
if base64_encode:
|
|
384
|
+
json_record[key] = base64.b64encode(record[key]).decode('utf-8')
|
|
385
|
+
else:
|
|
386
|
+
json_record[key] = record[key].decode("utf-8")
|
|
354
387
|
else:
|
|
355
388
|
json_record[key] = record[key]
|
|
356
389
|
|
|
@@ -363,13 +396,13 @@ class CRUD:
|
|
|
363
396
|
else:
|
|
364
397
|
return []
|
|
365
398
|
|
|
366
|
-
def to_list(self, _filter=None):
|
|
399
|
+
def to_list(self, _filter=None, base64_encode = True):
|
|
367
400
|
"""Alias of to_array() for readability."""
|
|
368
|
-
return self.to_array(_filter)
|
|
401
|
+
return self.to_array(_filter, base64_encode=base64_encode)
|
|
369
402
|
|
|
370
|
-
def to_json(self, _filter=None):
|
|
403
|
+
def to_json(self, _filter=None, base64_encode = True):
|
|
371
404
|
"""Return records as a JSON encoded string."""
|
|
372
|
-
return json.dumps(self.to_array(_filter))
|
|
405
|
+
return json.dumps(self.to_array(_filter, base64_encode=base64_encode))
|
|
373
406
|
|
|
374
407
|
def __iter__(self):
|
|
375
408
|
"""Allow iteration over records (yields results from to_array())."""
|
|
@@ -9,6 +9,7 @@ import ast
|
|
|
9
9
|
import inspect
|
|
10
10
|
from datetime import datetime
|
|
11
11
|
from tina4_python.DatabaseTypes import MSSQL, POSTGRES, FIREBIRD
|
|
12
|
+
from tina4_python import Debug
|
|
12
13
|
|
|
13
14
|
class BaseField:
|
|
14
15
|
primary_key = False
|
|
@@ -83,15 +84,21 @@ class BaseField:
|
|
|
83
84
|
self.decimal_places = decimal_places
|
|
84
85
|
|
|
85
86
|
if column_name is None:
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
87
|
+
try:
|
|
88
|
+
frame = inspect.stack()[1]
|
|
89
|
+
# Parse python syntax of the assignment line
|
|
90
|
+
st = ast.parse(frame.code_context[0].strip())
|
|
91
|
+
stmt = st.body[0]
|
|
92
|
+
# Assume class being instanced as simple assign statement
|
|
93
|
+
assert (isinstance(stmt, ast.Assign))
|
|
94
|
+
# Parse the target the class is assigned to
|
|
95
|
+
target = stmt.targets[0]
|
|
96
|
+
if hasattr(target, 'id'):
|
|
97
|
+
self.column_name = target.id
|
|
98
|
+
else:
|
|
99
|
+
raise Exception("Sorry we can't determine the column name for ORM ")
|
|
100
|
+
except Exception as e:
|
|
101
|
+
Debug.error("Error determining field column for ORM Object", str(e))
|
|
95
102
|
else:
|
|
96
103
|
self.column_name = column_name
|
|
97
104
|
|
|
@@ -5,12 +5,10 @@
|
|
|
5
5
|
#
|
|
6
6
|
# flake8: noqa: E501
|
|
7
7
|
import base64
|
|
8
|
-
from datetime import
|
|
9
|
-
import ast
|
|
8
|
+
from datetime import date
|
|
10
9
|
import json
|
|
11
10
|
import os
|
|
12
11
|
from tina4_python.Constant import TINA4_LOG_ERROR
|
|
13
|
-
from tina4_python.Debug import Debug
|
|
14
12
|
from tina4_python.FieldTypes import *
|
|
15
13
|
|
|
16
14
|
|
|
@@ -186,7 +184,10 @@ class ORM:
|
|
|
186
184
|
|
|
187
185
|
data[key] = current_value.value
|
|
188
186
|
elif isinstance(value, IntegerField):
|
|
189
|
-
|
|
187
|
+
try:
|
|
188
|
+
data[key] = int(current_value)
|
|
189
|
+
except Exception as e:
|
|
190
|
+
Debug.error("Could not save", current_value, "to", key)
|
|
190
191
|
else:
|
|
191
192
|
data[key] = str(current_value)
|
|
192
193
|
|
|
@@ -217,7 +217,7 @@ from .Router import get, post, put, patch, delete, middleware, cached, noauth, s
|
|
|
217
217
|
from .Testing import tests, assert_equal, assert_raises
|
|
218
218
|
from .Debug import Debug
|
|
219
219
|
from .Database import Database
|
|
220
|
-
from .ORM import ORM
|
|
220
|
+
from .ORM import ORM, orm
|
|
221
221
|
from .Api import Api
|
|
222
222
|
from .Template import template
|
|
223
223
|
from .Swagger import description, secure, summary, example, example_response, tags, params, describe
|
|
@@ -231,10 +231,14 @@ for deco in (get, post, put, patch, delete, middleware, cached, noauth, secured,
|
|
|
231
231
|
if deco.__name__ not in builtins.__dict__:
|
|
232
232
|
builtins.__dict__[deco.__name__] = deco
|
|
233
233
|
|
|
234
|
+
|
|
235
|
+
|
|
234
236
|
builtins.Debug = Debug
|
|
235
237
|
builtins.Api = Api
|
|
236
238
|
builtins.Database = Database
|
|
237
239
|
builtins.ORM = ORM
|
|
240
|
+
builtins.orm = orm
|
|
241
|
+
|
|
238
242
|
|
|
239
243
|
# Auto-import everything from src folders
|
|
240
244
|
if os.path.exists(root_path + os.sep + "src"):
|
|
@@ -59,7 +59,7 @@
|
|
|
59
59
|
{# --- Only show image if it's base64-like and long enough --- #}
|
|
60
60
|
{% if is_image and value|length > 50 and value is string %}
|
|
61
61
|
{% set src = value %}
|
|
62
|
-
{% if value[:
|
|
62
|
+
{% if value[:11] != 'data:image/' %}
|
|
63
63
|
{% set src = 'data:image/' ~ mime_type ~ ';base64,' ~ value %}
|
|
64
64
|
{% endif %}
|
|
65
65
|
|
|
@@ -131,7 +131,7 @@
|
|
|
131
131
|
{# --- Only show image if it's base64-like and long enough --- #}
|
|
132
132
|
{% if is_image %}
|
|
133
133
|
{% set src = value %}
|
|
134
|
-
{% if value[:
|
|
134
|
+
{% if value[:11] != 'data:image/' %}
|
|
135
135
|
{% set src = 'data:image/' ~ mime_type ~ ';base64,' ~ value %}
|
|
136
136
|
{% endif %}
|
|
137
137
|
|
|
@@ -253,7 +253,7 @@
|
|
|
253
253
|
|
|
254
254
|
html += `
|
|
255
255
|
<div class="mb-3">
|
|
256
|
-
<label class="form-label
|
|
256
|
+
<label class="form-label">${label}</label>
|
|
257
257
|
<div class="border rounded p-3 bg-light text-center">
|
|
258
258
|
<img src="${imageSrc}"
|
|
259
259
|
alt="Current ${label}"
|
|
@@ -317,7 +317,33 @@
|
|
|
317
317
|
value = '';
|
|
318
318
|
type = `{{ column.type|default('text') }}`;
|
|
319
319
|
|
|
320
|
-
|
|
320
|
+
if (field.indexOf('image') !== -1) {
|
|
321
|
+
let imageSrc = value;
|
|
322
|
+
if (value.length > 0 && !value.startsWith('data:image/')) {
|
|
323
|
+
imageSrc = `data:image/${mimeType};base64,${value}`;
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
html += `
|
|
327
|
+
<div class="mb-3">
|
|
328
|
+
<label class="form-label">${label}</label>
|
|
329
|
+
<div class="border rounded p-3 bg-light text-center">
|
|
330
|
+
<img src="${imageSrc}"
|
|
331
|
+
alt="Current ${label}"
|
|
332
|
+
class="img-thumbnail mb-3"
|
|
333
|
+
style="max-height: 180px; max-width: 100%;">
|
|
334
|
+
<div>
|
|
335
|
+
<input type="file"
|
|
336
|
+
accept="image/*"
|
|
337
|
+
class="form-control form-control-sm"
|
|
338
|
+
id="${field}"
|
|
339
|
+
name="${field}">
|
|
340
|
+
<small class="text-muted">Choose new image (optional)</small>
|
|
341
|
+
</div>
|
|
342
|
+
</div>
|
|
343
|
+
</div>`;
|
|
344
|
+
} else {
|
|
345
|
+
|
|
346
|
+
html += `<div class="mb-3">
|
|
321
347
|
<label for="${field}" class="form-label">${label}</label>
|
|
322
348
|
<input type="${type === 'password' ? 'password' : 'text'}"
|
|
323
349
|
class="form-control"
|
|
@@ -326,6 +352,7 @@
|
|
|
326
352
|
placeholder="${label}"
|
|
327
353
|
value='${value}'>
|
|
328
354
|
</div>`;
|
|
355
|
+
}
|
|
329
356
|
{% endfor %}
|
|
330
357
|
|
|
331
358
|
modalBody.innerHTML = html;
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{tina4_python-0.2.168 → tina4_python-0.2.170}/tina4_python/public/js/reconnecting-websocket.js
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{tina4_python-0.2.168 → tina4_python-0.2.170}/tina4_python/public/swagger/oauth2-redirect.html
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{tina4_python-0.2.168 → tina4_python-0.2.170}/tina4_python/translations/en/LC_MESSAGES/messages.mo
RENAMED
|
File without changes
|
{tina4_python-0.2.168 → tina4_python-0.2.170}/tina4_python/translations/en/LC_MESSAGES/messages.po
RENAMED
|
File without changes
|
{tina4_python-0.2.168 → tina4_python-0.2.170}/tina4_python/translations/fr/LC_MESSAGES/messages.mo
RENAMED
|
File without changes
|
{tina4_python-0.2.168 → tina4_python-0.2.170}/tina4_python/translations/fr/LC_MESSAGES/messages.po
RENAMED
|
File without changes
|