djgentelella 0.4.30__py3-none-any.whl → 0.5.1__py3-none-any.whl

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.
Files changed (66) hide show
  1. djgentelella/__init__.py +4 -1
  2. djgentelella/admin.py +41 -2
  3. djgentelella/firmador_digital/consumers/sign.py +24 -3
  4. djgentelella/firmador_digital/forms.py +140 -0
  5. djgentelella/firmador_digital/models.py +19 -24
  6. djgentelella/firmador_digital/utils.py +11 -0
  7. djgentelella/firmador_digital/views.py +31 -0
  8. djgentelella/firmador_digital/viewsets.py +15 -4
  9. djgentelella/forms/forms.py +10 -2
  10. djgentelella/groute.py +4 -1
  11. djgentelella/history/api.py +145 -0
  12. djgentelella/history/filterset.py +23 -0
  13. djgentelella/history/serializers.py +50 -0
  14. djgentelella/history/utils.py +70 -0
  15. djgentelella/locale/es/LC_MESSAGES/django.mo +0 -0
  16. djgentelella/locale/es/LC_MESSAGES/django.po +105 -0
  17. djgentelella/locale/es/LC_MESSAGES/djangojs.mo +0 -0
  18. djgentelella/locale/es/LC_MESSAGES/djangojs.po +46 -0
  19. djgentelella/migrations/0014_trash.py +31 -0
  20. djgentelella/migrations/0015_trash_deleted_by.py +21 -0
  21. djgentelella/migrations/0016_trash_created_at.py +20 -0
  22. djgentelella/migrations/0017_alter_chunkedupload_status.py +18 -0
  23. djgentelella/migrations/0018_alter_chunkedupload_status.py +18 -0
  24. djgentelella/models.py +97 -0
  25. djgentelella/models_manager.py +40 -0
  26. djgentelella/serializers/firmador_digital.py +9 -1
  27. djgentelella/serializers/selects.py +14 -0
  28. djgentelella/static/djgentelella.readonly.vendors.min.css +9 -5
  29. djgentelella/static/djgentelella.readonly.vendors.min.js +1 -1
  30. djgentelella/static/gentelella/css/custom.css +23 -2
  31. djgentelella/static/gentelella/css/modern.css +15 -0
  32. djgentelella/static/gentelella/css/modern_black_white.css +15 -0
  33. djgentelella/static/gentelella/css/pdfviewer.css +43 -1
  34. djgentelella/static/gentelella/images/default.png +0 -0
  35. djgentelella/static/gentelella/js/base/digital_signature.js +318 -79
  36. djgentelella/static/gentelella/js/base/form.common.js +9 -4
  37. djgentelella/static/gentelella/js/base/select2_wrap.js +15 -1
  38. djgentelella/static/gentelella/js/base/select2related.js +5 -0
  39. djgentelella/static/gentelella/js/base.js +347 -83
  40. djgentelella/static/gentelella/js/custom.js +6 -1
  41. djgentelella/static/gentelella/js/datatables.js +15 -0
  42. djgentelella/static/gentelella/js/digital_signature_update.js +29 -0
  43. djgentelella/static/gentelella/js/obj_api_management.js +403 -344
  44. djgentelella/static/gentelella/js/widgets.js +10 -1
  45. djgentelella/static/vendors/timeline/css/timeline.css +9 -5
  46. djgentelella/static/vendors/timeline/js/timeline.js +1 -1
  47. djgentelella/templates/forms/as_grid.html +2 -2
  48. djgentelella/templates/forms/as_inline.html +3 -5
  49. djgentelella/templates/gentelella/app/sidebar.html +16 -13
  50. djgentelella/templates/gentelella/digital_signature/update_signature_settings.html +45 -0
  51. djgentelella/templates/gentelella/registration/reset_done.html +24 -0
  52. djgentelella/templates/gentelella/widgets/digital_signature.html +47 -11
  53. djgentelella/trash/api.py +77 -0
  54. djgentelella/trash/filterset.py +20 -0
  55. djgentelella/trash/serializer.py +45 -0
  56. djgentelella/urls.py +13 -2
  57. djgentelella/views/select2autocomplete.py +40 -0
  58. djgentelella/widgets/core.py +6 -5
  59. djgentelella/widgets/digital_signature.py +5 -21
  60. djgentelella/widgets/selects.py +47 -0
  61. {djgentelella-0.4.30.dist-info → djgentelella-0.5.1.dist-info}/METADATA +2 -2
  62. {djgentelella-0.4.30.dist-info → djgentelella-0.5.1.dist-info}/RECORD +66 -48
  63. {djgentelella-0.4.30.dist-info → djgentelella-0.5.1.dist-info}/AUTHORS +0 -0
  64. {djgentelella-0.4.30.dist-info → djgentelella-0.5.1.dist-info}/LICENSE.txt +0 -0
  65. {djgentelella-0.4.30.dist-info → djgentelella-0.5.1.dist-info}/WHEEL +0 -0
  66. {djgentelella-0.4.30.dist-info → djgentelella-0.5.1.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,50 @@
1
+ from django.contrib.admin.models import LogEntry
2
+ from django.contrib.contenttypes.models import ContentType
3
+ from django.utils.translation import gettext_lazy as _
4
+ from rest_framework import serializers
5
+ from djgentelella.serializers import GTDateField, GTDateTimeField
6
+
7
+
8
+
9
+ class HistorySerializer(serializers.ModelSerializer):
10
+ user = serializers.SerializerMethodField()
11
+ action_flag = serializers.SerializerMethodField()
12
+ action_time = GTDateTimeField()
13
+ change_message = serializers.SerializerMethodField()
14
+ actions = serializers.SerializerMethodField()
15
+
16
+ def get_user(self, obj):
17
+ if not obj or not obj.user:
18
+ return _("No user found")
19
+ name = obj.user.get_full_name()
20
+ return name or obj.user.username
21
+
22
+ def get_action_flag(self, obj):
23
+ if obj.action_flag == 4:
24
+ return _("Hard deleted")
25
+ elif obj.action_flag == 5:
26
+ return _("Restored")
27
+
28
+ return obj.get_action_flag_display()
29
+
30
+ def get_change_message(self, obj):
31
+ return obj.change_message
32
+
33
+ def get_actions(self, obj):
34
+ return {
35
+ "create": False,
36
+ "update": False,
37
+ "destroy": False,
38
+ }
39
+
40
+ class Meta:
41
+ model = LogEntry
42
+ fields = "__all__"
43
+
44
+
45
+ class HistoryDataTableSerializer(serializers.Serializer):
46
+ data = serializers.ListField(child=HistorySerializer(), required=True)
47
+ draw = serializers.IntegerField(required=True)
48
+ recordsFiltered = serializers.IntegerField(required=True)
49
+ recordsTotal = serializers.IntegerField(required=True)
50
+
@@ -0,0 +1,70 @@
1
+ from django.utils.translation import gettext_lazy as _
2
+ from django.contrib.admin.models import LogEntry, ADDITION, CHANGE, DELETION
3
+ from django.contrib.contenttypes.models import ContentType
4
+
5
+ HARD_DELETION = 4
6
+ RESTORE = 5
7
+
8
+ ACTIONS = {
9
+ ADDITION: _("created"),
10
+ CHANGE: _("updated"),
11
+ DELETION: _("deleted"),
12
+ HARD_DELETION: _("hard deleted"),
13
+ RESTORE: _("restored"),
14
+ }
15
+
16
+
17
+ def add_log(
18
+ user,
19
+ object,
20
+ action_flag,
21
+ model_name=None,
22
+ changed_data=None,
23
+ object_repr="",
24
+ change_message="",
25
+ content_type=None,
26
+ ):
27
+ if content_type is None:
28
+ content_type = ContentType.objects.get_for_model(object)
29
+
30
+ if model_name is None:
31
+ model_name = object._meta.verbose_name
32
+
33
+ if not isinstance(action_flag, int):
34
+ raise ValueError("action_flag must be an integer")
35
+
36
+ action_label = ACTIONS.get(action_flag, str(action_flag))
37
+
38
+ if not object_repr:
39
+ object_repr = _("An object of model %(model)s has been %(action)s") % {
40
+ "model": _(str(model_name).capitalize()),
41
+ "action": action_label,
42
+ }
43
+
44
+ changed_data = changed_data or []
45
+
46
+ if change_message:
47
+ if action_flag != DELETION and changed_data:
48
+ verbose_changes = [object._meta.get_field(f).verbose_name for f in
49
+ changed_data]
50
+
51
+ change_message = _("%(msg)s. Fields: %(fields)s") % {
52
+ "msg": change_message,
53
+ "fields": ", ".join(verbose_changes),
54
+ }
55
+ else: # delete, restore, hard delete
56
+ change_message = _("The record %(obj)s of model %(model)s has been %(action)s") % {
57
+ "obj": str(object),
58
+ "model": _(model_name),
59
+ "action": action_label,
60
+ }
61
+
62
+ LogEntry.objects.log_action(
63
+ user_id=user.id,
64
+ content_type_id=content_type.id,
65
+ object_id=object.pk,
66
+ object_repr=object_repr,
67
+ action_flag=action_flag,
68
+ change_message=change_message,
69
+ )
70
+
@@ -616,3 +616,108 @@ msgstr "Iniciar"
616
616
 
617
617
  msgid "Unknown"
618
618
  msgstr "Desconocido"
619
+
620
+ msgid "LEFT"
621
+ msgstr "IZQUIERDA"
622
+
623
+ msgid "CENTER"
624
+ msgstr "CENTRO"
625
+
626
+ msgid "RIGHT"
627
+ msgstr "DERECHA"
628
+
629
+ msgid "Signature settings"
630
+ msgstr "Configuración de firma"
631
+
632
+ msgid "Updated signature settings successfully."
633
+ msgstr "Configuración de firma actualizada correctamente."
634
+
635
+ msgid "Background color"
636
+ msgstr "Color de fondo"
637
+
638
+ msgid "Contact"
639
+ msgstr "Contacto"
640
+
641
+ msgid "Date format"
642
+ msgstr "Formato de fecha"
643
+
644
+ msgid "Signature message"
645
+ msgstr "Mensaje de firma"
646
+
647
+ msgid "Font"
648
+ msgstr "Fuente"
649
+
650
+ msgid "Font alignment"
651
+ msgstr "Alineación de fuente"
652
+
653
+ msgid "Font color"
654
+ msgstr "Color de fuente"
655
+
656
+ msgid "Font size"
657
+ msgstr "Tamaño de fuente"
658
+
659
+ msgid "Place"
660
+ msgstr "Lugar"
661
+
662
+ msgid "Reason"
663
+ msgstr "Razón"
664
+
665
+ msgid "Visible signature"
666
+ msgstr "Firma visible"
667
+
668
+ msgid "This registry of trash does not exist."
669
+ msgstr "Este registro de papelera no existe."
670
+
671
+ msgid "The registry was successfully restored."
672
+ msgstr "El registro fue restaurado con éxito."
673
+
674
+ msgid "The registry could not be restored."
675
+ msgstr "No se pudo restaurar el registro."
676
+
677
+ msgid "Signature image"
678
+ msgstr "Imagen de firma"
679
+
680
+ msgid "Image preview"
681
+ msgstr "Vista previa de imagen"
682
+
683
+ msgid "Image signature"
684
+ msgstr "Imagen de firma"
685
+ msgid "Expand"
686
+ msgstr "Expandir"
687
+
688
+ # history
689
+ msgid "Restoration"
690
+ msgstr "Restaurado"
691
+
692
+ msgid "deleted"
693
+ msgstr "eliminado"
694
+
695
+ msgid "Hard deleted"
696
+ msgstr "Eliminación permanente"
697
+
698
+ msgid "History"
699
+ msgstr "Historial"
700
+
701
+ msgid "General"
702
+ msgstr "General"
703
+
704
+ msgid "restored"
705
+ msgstr "restaurado"
706
+
707
+ msgid "Restored"
708
+ msgstr "Restaurado"
709
+
710
+ msgid "hard deleted"
711
+ msgstr "eliminado permanentemente"
712
+
713
+ msgid "%(msg)s. Fields: %(fields)s"
714
+ msgstr "%(msg)s. Campos: %(fields)s"
715
+
716
+ msgid "The record %(obj)s of model %(model)s has been %(action)s"
717
+ msgstr "El registro %(obj)s del modelo %(model)s ha sido %(action)s"
718
+
719
+ msgid "An object of model %(model)s has been %(action)s"
720
+ msgstr "Un objeto del modelo %(model)s ha sido %(action)s"
721
+
722
+ msgid "%(obj)s in trash"
723
+ msgstr "%(obj)s en papelera"
@@ -213,3 +213,49 @@ msgstr ""
213
213
 
214
214
  msgid "loading"
215
215
  msgstr "Cargando"
216
+
217
+ msgid "Please, sign the document before saving"
218
+ msgstr "Por favor, firme el documento antes de guardar"
219
+
220
+ msgid "The document is not digitally signed. Please sign the document before saving."
221
+ msgstr "El documento no está firmado digitalmente. Por favor, firme el documento antes de guardar."
222
+
223
+ msgid "The document was saved"
224
+ msgstr "El documento se ha guardado"
225
+
226
+ # history
227
+ msgid "Restoration"
228
+ msgstr "Restaurado"
229
+
230
+ msgid "deleted"
231
+ msgstr "eliminado"
232
+
233
+ msgid "Hard deleted"
234
+ msgstr "Eliminación permanente"
235
+
236
+ msgid "History"
237
+ msgstr "Historial"
238
+
239
+ msgid "General"
240
+ msgstr "General"
241
+
242
+ msgid "restored"
243
+ msgstr "restaurado"
244
+
245
+ msgid "Restored"
246
+ msgstr "Restaurado"
247
+
248
+ msgid "hard deleted"
249
+ msgstr "eliminado permanentemente"
250
+
251
+ msgid "%(msg)s. Fields: %(fields)s"
252
+ msgstr "%(msg)s. Campos: %(fields)s"
253
+
254
+ msgid "The record %(obj)s of model %(model)s has been %(action)s"
255
+ msgstr "El registro %(obj)s del modelo %(model)s ha sido %(action)s"
256
+
257
+ msgid "An object of model %(model)s has been %(action)s"
258
+ msgstr "Un objeto del modelo %(model)s ha sido %(action)s"
259
+
260
+ msgid "%(obj)s in trash"
261
+ msgstr "%(obj)s en papelera"
@@ -0,0 +1,31 @@
1
+ # Generated by Django 5.1.6 on 2025-08-03 17:13
2
+
3
+ import django.db.models.deletion
4
+ from django.db import migrations, models
5
+
6
+
7
+ class Migration(migrations.Migration):
8
+
9
+ dependencies = [
10
+ ('contenttypes', '0002_remove_content_type_name'),
11
+ ('djgentelella', '0013_usersignatureconfig'),
12
+ ]
13
+
14
+ operations = [
15
+ migrations.CreateModel(
16
+ name='Trash',
17
+ fields=[
18
+ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
19
+ ('object_id', models.PositiveIntegerField(verbose_name='Object ID')),
20
+ ('object_repr', models.CharField(help_text='Value of str(instance) at deletion time', max_length=200, verbose_name='Object repr')),
21
+ ('content_type', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='contenttypes.contenttype', verbose_name='Content type')),
22
+ ],
23
+ options={
24
+ 'verbose_name': 'Trash',
25
+ 'verbose_name_plural': 'Trash',
26
+ 'ordering': ('id',),
27
+ 'indexes': [models.Index(fields=['content_type', 'object_id'], name='djgentelell_content_9fa474_idx')],
28
+ 'unique_together': {('content_type', 'object_id')},
29
+ },
30
+ ),
31
+ ]
@@ -0,0 +1,21 @@
1
+ # Generated by Django 5.1.6 on 2025-08-03 17:29
2
+
3
+ import django.db.models.deletion
4
+ from django.conf import settings
5
+ from django.db import migrations, models
6
+
7
+
8
+ class Migration(migrations.Migration):
9
+
10
+ dependencies = [
11
+ ('djgentelella', '0014_trash'),
12
+ migrations.swappable_dependency(settings.AUTH_USER_MODEL),
13
+ ]
14
+
15
+ operations = [
16
+ migrations.AddField(
17
+ model_name='trash',
18
+ name='deleted_by',
19
+ field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to=settings.AUTH_USER_MODEL, verbose_name='Deleted by'),
20
+ ),
21
+ ]
@@ -0,0 +1,20 @@
1
+ # Generated by Django 5.1.6 on 2025-08-03 17:54
2
+
3
+ import django.utils.timezone
4
+ from django.db import migrations, models
5
+
6
+
7
+ class Migration(migrations.Migration):
8
+
9
+ dependencies = [
10
+ ('djgentelella', '0015_trash_deleted_by'),
11
+ ]
12
+
13
+ operations = [
14
+ migrations.AddField(
15
+ model_name='trash',
16
+ name='created_at',
17
+ field=models.DateTimeField(auto_now_add=True, default=django.utils.timezone.now),
18
+ preserve_default=False,
19
+ ),
20
+ ]
@@ -0,0 +1,18 @@
1
+ # Generated by Django 5.2.5 on 2025-08-29 23:46
2
+
3
+ from django.db import migrations, models
4
+
5
+
6
+ class Migration(migrations.Migration):
7
+
8
+ dependencies = [
9
+ ('djgentelella', '0016_trash_created_at'),
10
+ ]
11
+
12
+ operations = [
13
+ migrations.AlterField(
14
+ model_name='chunkedupload',
15
+ name='status',
16
+ field=models.PositiveSmallIntegerField(choices=[(1, 'Uploading'), (2, 'Complete')], default=1),
17
+ ),
18
+ ]
@@ -0,0 +1,18 @@
1
+ # Generated by Django 5.2.5 on 2025-08-29 23:55
2
+
3
+ from django.db import migrations, models
4
+
5
+
6
+ class Migration(migrations.Migration):
7
+
8
+ dependencies = [
9
+ ('djgentelella', '0017_alter_chunkedupload_status'),
10
+ ]
11
+
12
+ operations = [
13
+ migrations.AlterField(
14
+ model_name='chunkedupload',
15
+ name='status',
16
+ field=models.PositiveSmallIntegerField(choices=[(1, 'Subiendo'), (2, 'Completo')], default=1),
17
+ ),
18
+ ]
djgentelella/models.py CHANGED
@@ -5,7 +5,12 @@ from django.utils.translation import gettext_lazy as _
5
5
  from tree_queries.models import TreeNode
6
6
 
7
7
  from djgentelella.chunked_upload.models import AbstractChunkedUpload
8
+ from django.contrib.contenttypes.models import ContentType
9
+ from django.contrib.contenttypes.fields import GenericForeignKey
10
+ from .models_manager import ObjectManager, AllObjectsManager, \
11
+ DeletedObjectsManager
8
12
 
13
+ from djgentelella.history.utils import add_log, ADDITION
9
14
 
10
15
  class GentelellaSettings(models.Model):
11
16
  """
@@ -123,3 +128,95 @@ class ChunkedUpload(AbstractChunkedUpload):
123
128
  null=DEFAULT_MODEL_USER_FIELD_NULL,
124
129
  blank=DEFAULT_MODEL_USER_FIELD_BLANK
125
130
  )
131
+
132
+
133
+ # Trash
134
+ class Trash(models.Model):
135
+ """
136
+ Trash generic. Each row represents an instance deleted.
137
+ """
138
+ content_type = models.ForeignKey(ContentType, on_delete=models.PROTECT,
139
+ verbose_name=_("Content type"))
140
+ object_id = models.PositiveIntegerField(verbose_name=_("Object ID"))
141
+ content_object = GenericForeignKey("content_type", "object_id")
142
+ object_repr = models.CharField(
143
+ _("Object repr"), max_length=200,
144
+ help_text=_("Value of str(instance) at deletion time"),
145
+ )
146
+ deleted_by = models.ForeignKey(
147
+ User,
148
+ on_delete=models.SET_NULL,
149
+ null=True,
150
+ blank=True,
151
+ verbose_name=_("Deleted by")
152
+ )
153
+ created_at = models.DateTimeField(auto_now_add=True)
154
+
155
+ class Meta:
156
+ ordering = ("id",)
157
+ unique_together = ("content_type", "object_id")
158
+ indexes = [
159
+ models.Index(fields=["content_type", "object_id"]),
160
+ ]
161
+ verbose_name = _("Trash")
162
+ verbose_name_plural = _("Trash")
163
+
164
+ def __str__(self):
165
+ return _("%(obj)s in trash") % {"obj": self.object_repr}
166
+
167
+ def restore(self, user=None):
168
+ obj = self.content_object
169
+
170
+ # if `is_deleted` is in the model, unmark it
171
+ if hasattr(obj, "restore"):
172
+ obj.restore()
173
+
174
+ self.delete() # delete the instance of trash
175
+
176
+
177
+ def hard_delete(self):
178
+ """
179
+ Permanent deletion of the original object and then the trash entry.
180
+ """
181
+ obj = self.content_object
182
+
183
+ if obj is None:
184
+ return
185
+
186
+ obj.delete(hard=True)
187
+
188
+ super().delete()
189
+
190
+
191
+ class DeletedWithTrash(models.Model):
192
+ is_deleted = models.BooleanField(default=False, db_index=True)
193
+
194
+ objects = ObjectManager()
195
+ objects_with_deleted = AllObjectsManager()
196
+ objects_deleted_only = DeletedObjectsManager()
197
+
198
+ class Meta:
199
+ abstract = True
200
+
201
+ def delete(self, using=None, keep_parents=False, *, hard=False, user=None):
202
+ if hard:
203
+ # Permanent deletion
204
+ return super().delete(using=using, keep_parents=keep_parents)
205
+
206
+ self.is_deleted = True
207
+ self.save(update_fields=["is_deleted"])
208
+
209
+ # create trash instance
210
+ trash = Trash.objects.get_or_create(
211
+ content_type=ContentType.objects.get_for_model(self.__class__),
212
+ object_id=self.pk,
213
+ defaults={
214
+ "object_repr": str(self)[:200],
215
+ "deleted_by": user,
216
+ },
217
+ )
218
+
219
+ # Restore an object
220
+ def restore(self):
221
+ self.is_deleted = False
222
+ self.save(update_fields=["is_deleted"])
@@ -0,0 +1,40 @@
1
+ from django.db import models
2
+
3
+
4
+ class ObjectQuerySet(models.QuerySet):
5
+ # Non-deleted records
6
+ def alive(self):
7
+ return self.filter(is_deleted=False)
8
+
9
+ # Only deleted records
10
+ def dead(self):
11
+ return self.filter(is_deleted=True)
12
+
13
+ # Overridden delete (soft delete)
14
+ def delete(self):
15
+ return super().update(is_deleted=True)
16
+
17
+ # Permanent deletion
18
+ def hard_delete(self):
19
+ return super().delete()
20
+
21
+ # Bulk restore
22
+ def restore(self):
23
+ return self.update(is_deleted=False)
24
+
25
+
26
+ class ObjectManager(models.Manager):
27
+ # Only alive objects by default
28
+ def get_queryset(self):
29
+ return ObjectQuerySet(self.model, using=self._db).alive()
30
+
31
+ class AllObjectsManager(models.Manager):
32
+ # Explicit access to all objects
33
+ def get_queryset(self):
34
+ return ObjectQuerySet(self.model, using=self._db)
35
+
36
+ class DeletedObjectsManager(models.Manager):
37
+ # Only deleted objects
38
+ def get_queryset(self):
39
+ return ObjectQuerySet(self.model, using=self._db).dead()
40
+
@@ -69,7 +69,8 @@ class RequestInitialDoc(serializers.Serializer):
69
69
 
70
70
 
71
71
  class WSRequest(serializers.Serializer):
72
- action = serializers.CharField()
72
+ action = serializers.CharField(required=True)
73
+ socket_id = serializers.CharField(required=True)
73
74
 
74
75
 
75
76
  class CardSerializer(serializers.Serializer):
@@ -88,6 +89,7 @@ class SignatureSerializer(serializers.Serializer):
88
89
 
89
90
  class InitialSignatureSerializer(serializers.Serializer):
90
91
  action = serializers.CharField()
92
+ socket_id = serializers.CharField(required=True)
91
93
  docsettings = DocumentSettingsSerializer()
92
94
  card = CardSerializer()
93
95
  logo_url = serializers.CharField(required=False, allow_null=True, allow_blank=True)
@@ -96,8 +98,14 @@ class InitialSignatureSerializer(serializers.Serializer):
96
98
 
97
99
  class CompleteSignatureSerializer(serializers.Serializer):
98
100
  action = serializers.CharField()
101
+ socket_id = serializers.CharField(required=True)
99
102
  documentid = serializers.CharField()
100
103
  certificate = serializers.CharField()
101
104
  signature = SignatureSerializer()
102
105
  logo_url = serializers.CharField(required=False, allow_null=True, allow_blank=True)
103
106
  instance = InstanceSerializer()
107
+
108
+ class ValidateDocumentSerializer(serializers.Serializer):
109
+ action = serializers.CharField()
110
+ socket_id = serializers.CharField(required=True)
111
+ instance = InstanceSerializer()
@@ -30,3 +30,17 @@ class GTS2SerializerBase(serializers.Serializer):
30
30
  text = serializers.SerializerMethodField()
31
31
  disabled = serializers.SerializerMethodField()
32
32
  selected = serializers.SerializerMethodField()
33
+
34
+
35
+ class ChoicesGTS2Serializer(GTS2SerializerBase):
36
+ def __init__(self, *args, choices=None, **kwargs):
37
+ self.choices = dict(choices)
38
+ super().__init__(*args, **kwargs)
39
+
40
+ def get_id(self, obj):
41
+ return obj
42
+
43
+ def get_text(self, obj):
44
+ if obj in self.choices:
45
+ return self.choices[obj]
46
+ return obj
@@ -808,6 +808,7 @@
808
808
  position: relative;
809
809
  overflow: hidden;
810
810
  border-top: 1px solid #e5e5e5;
811
+ direction: ltr;
811
812
  }
812
813
  .tl-timenav .tl-timenav-line {
813
814
  position: absolute;
@@ -2018,7 +2019,6 @@
2018
2019
  .tl-slidenav-previous .tl-slidenav-content-container,
2019
2020
  .tl-slidenav-next .tl-slidenav-content-container {
2020
2021
  width: 100px;
2021
- position: absolute;
2022
2022
  }
2023
2023
  .tl-slidenav-previous .tl-slidenav-title,
2024
2024
  .tl-slidenav-next .tl-slidenav-title,
@@ -2104,7 +2104,7 @@
2104
2104
  .tl-slidenav-next {
2105
2105
  text-align: right;
2106
2106
  margin-right: 10px;
2107
- right: 100px;
2107
+ right: 0;
2108
2108
  }
2109
2109
  .tl-slidenav-next .tl-slidenav-title,
2110
2110
  .tl-slidenav-next .tl-slidenav-description {
@@ -2118,6 +2118,7 @@
2118
2118
  }
2119
2119
  .tl-slidenav-previous {
2120
2120
  text-align: left;
2121
+ left: 0;
2121
2122
  margin-left: 10px;
2122
2123
  }
2123
2124
  .tl-slidenav-previous .tl-slidenav-icon {
@@ -2126,6 +2127,12 @@
2126
2127
  .tl-slidenav-previous .tl-slidenav-icon:before {
2127
2128
  content: "\e650";
2128
2129
  }
2130
+ [dir="rtl"] .tl-slidenav-previous {
2131
+ direction: ltr;
2132
+ }
2133
+ [dir="rtl"] .tl-slidenav-next:hover .tl-slidenav-icon {
2134
+ margin-right: -4px;
2135
+ }
2129
2136
  /* NAVIGATION HOVER
2130
2137
  ================================================== */
2131
2138
  .tl-slidenav-previous:hover .tl-slidenav-title,
@@ -2154,9 +2161,6 @@
2154
2161
  .tl-slidenav-previous:focus-visible .tl-slidenav-icon {
2155
2162
  margin-left: -4px;
2156
2163
  }
2157
- .tl-skinny .tl-slidenav-next {
2158
- right: 32px;
2159
- }
2160
2164
  .tl-skinny .tl-slidenav-next .tl-slidenav-icon {
2161
2165
  margin-left: 8px;
2162
2166
  }