localcosmos-app-kit 0.9.15__py3-none-any.whl → 0.9.17__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 (32) hide show
  1. app_kit/admin_urls.py +5 -0
  2. app_kit/appbuilder/TaxonBuilder.py +31 -23
  3. app_kit/appbuilder/__pycache__/TaxonBuilder.cpython-313.pyc +0 -0
  4. app_kit/features/taxon_profiles/templates/taxon_profiles/ajax/delete_all_manually_added_images.html +37 -0
  5. app_kit/features/taxon_profiles/templates/taxon_profiles/manage_taxon_profiles.html +16 -0
  6. app_kit/features/taxon_profiles/tests/__pycache__/test_views.cpython-313.pyc +0 -0
  7. app_kit/features/taxon_profiles/tests/__pycache__/test_zip_import.cpython-313.pyc +0 -0
  8. app_kit/features/taxon_profiles/tests/test_views.py +100 -3
  9. app_kit/features/taxon_profiles/tests/test_zip_import.py +4 -0
  10. app_kit/features/taxon_profiles/urls.py +3 -0
  11. app_kit/features/taxon_profiles/views.py +38 -1
  12. app_kit/forms.py +8 -0
  13. app_kit/generic_content_zip_import.py +174 -0
  14. app_kit/locale/de/LC_MESSAGES/django.mo +0 -0
  15. app_kit/locale/de/LC_MESSAGES/django.po +247 -93
  16. app_kit/taxonomy/lazy.py +6 -6
  17. app_kit/taxonomy/sources/custom/forms.py +1 -0
  18. app_kit/taxonomy/views.py +1 -1
  19. app_kit/templates/app_kit/ajax/list_images_and_licences_content.html +44 -0
  20. app_kit/templates/app_kit/ajax/manage_content_licence.html +25 -0
  21. app_kit/templates/app_kit/list_images_and_licences.html +25 -0
  22. app_kit/templates/app_kit/manage_app.html +10 -1
  23. app_kit/templatetags/app_tags.py +23 -3
  24. app_kit/tests/TESTS_ROOT/media_for_tests/test/imagestore/31/a6a11b61d65ee19c4c22caa0682288ff.jpg +0 -0
  25. app_kit/tests/__pycache__/test_generic_content_zip_import.cpython-313.pyc +0 -0
  26. app_kit/tests/test_generic_content_zip_import.py +145 -2
  27. app_kit/views.py +61 -2
  28. {localcosmos_app_kit-0.9.15.dist-info → localcosmos_app_kit-0.9.17.dist-info}/METADATA +2 -2
  29. {localcosmos_app_kit-0.9.15.dist-info → localcosmos_app_kit-0.9.17.dist-info}/RECORD +32 -27
  30. {localcosmos_app_kit-0.9.15.dist-info → localcosmos_app_kit-0.9.17.dist-info}/WHEEL +1 -1
  31. {localcosmos_app_kit-0.9.15.dist-info → localcosmos_app_kit-0.9.17.dist-info}/licenses/LICENCE +0 -0
  32. {localcosmos_app_kit-0.9.15.dist-info → localcosmos_app_kit-0.9.17.dist-info}/top_level.txt +0 -0
app_kit/taxonomy/lazy.py CHANGED
@@ -115,14 +115,14 @@ class LazyTaxon(LazyTaxonBase):
115
115
 
116
116
  if db_lazy_taxon.taxon_nuid != self.taxon_nuid:
117
117
  self.changed_taxon_nuid_in_reference = True
118
- self.reference_errors.append(_('Taxon %s has changed its position in %s') % (self, verbose_taxon_source))
118
+ self.reference_errors.append(_('Taxon %(taxon)s has changed its position in %(tree)s') % {'taxon': self, 'tree': verbose_taxon_source})
119
119
 
120
120
  if str(taxon.name_uuid) != str(self.name_uuid):
121
121
  self.changed_name_uuid_in_reference = True
122
- self.reference_errors.append(_('Taxon %s has changed its identifier in %s') % (self, verbose_taxon_source))
122
+ self.reference_errors.append(_('Taxon %(taxon)s has changed its identifier in %(tree)s') % {'taxon': self, 'tree': verbose_taxon_source})
123
123
 
124
124
  if tree_query.count() > 1:
125
- self.reference_errors.append(_('Taxon %s found multiple times in %s') % (self, verbose_taxon_source))
125
+ self.reference_errors.append(_('Taxon %(taxon)s found multiple times in %(tree)s') % {'taxon': self, 'tree': verbose_taxon_source})
126
126
 
127
127
  else:
128
128
  synonyms_model = self.models.TaxonSynonymModel
@@ -135,11 +135,11 @@ class LazyTaxon(LazyTaxonBase):
135
135
  self.reference_synonym = synonym
136
136
  accepted_name = synonym.taxon
137
137
  self.reference_accepted_name = LazyTaxon(instance=accepted_name)
138
- self.reference_errors.append(_('Taxon %s not found as accepted name, but as synonym of %s') % (self,
139
- accepted_name))
138
+ self.reference_errors.append(_('Taxon %(taxon)s not found as accepted name, but as synonym of %(accepted_name)s') % {'taxon': self,
139
+ 'accepted_name': accepted_name})
140
140
 
141
141
  else:
142
- self.reference_errors.append(_('Taxon %s not found in %s') % (self, verbose_taxon_source))
142
+ self.reference_errors.append(_('Taxon %(taxon)s not found in %(tree)s') % {'taxon': self, 'tree': verbose_taxon_source})
143
143
 
144
144
 
145
145
  taxon_latnames_query = self.models.TaxonTreeModel.objects.filter(
@@ -13,6 +13,7 @@ TAXON_RANK_CHOICES = (
13
13
  ('', '-----'),
14
14
  ('kingdom', _('Kingdom')),
15
15
  ('phylum', _('Phylum')),
16
+ ('subphylum', _('Subphylum')),
16
17
  ('class', _('Class')),
17
18
  ('order', _('Order')),
18
19
  ('family', _('Family')),
app_kit/taxonomy/views.py CHANGED
@@ -79,7 +79,7 @@ class TaxonTreeView(TemplateView):
79
79
  if self.taxon:
80
80
  children_nuid_length = len(self.taxon.taxon_nuid) + 3
81
81
  taxa = self.models.TaxonTreeModel.objects.annotate(nuid_len=Length('taxon_nuid')).filter(
82
- taxon_nuid__startswith=self.taxon.taxon_nuid, nuid_len=children_nuid_length)
82
+ taxon_nuid__startswith=self.taxon.taxon_nuid, nuid_len=children_nuid_length).order_by('taxon_latname')
83
83
 
84
84
  else:
85
85
  taxa = self.get_root_taxa()
@@ -0,0 +1,44 @@
1
+ {% load i18n el_pagination_tags app_tags %}
2
+
3
+ {% paginate 28 registry_entries %}
4
+
5
+ {% show_pages %}
6
+
7
+ <div class="row">
8
+ {% for entry in registry_entries %}
9
+ <div class="col-12 col-md-6 col-lg-3">
10
+ {% get_object_from_licence entry as licenced_object %}
11
+ {% if licenced_object.is_imagestore %}
12
+ <div class="row mb-2">
13
+ <div class="col-6">
14
+ {% if licenced_object.object %}
15
+ <img src="{{ licenced_object.object.source_image.url }}" class="img-thumbnail img-fluid" alt="Content Image">
16
+ {% else %}
17
+ deleted image
18
+ {% endif %}
19
+ </div>
20
+ <div class="col-6 p-1">
21
+ <small>
22
+ {% trans 'Author' %}: {{ entry.creator_name}}<br>
23
+ {% trans 'Licence' %}: {{ entry.licence }} {{ entry.licence_version }}
24
+ {% if entry.creator_link %}
25
+ <br>(<a href="{{ entry.creator_link }}" target="_blank" rel="noopener noreferrer">{% trans 'Link to author' %}</a>)
26
+ {% endif %}
27
+ {% if entry.source_link %}
28
+ <br>(<a href="{{ entry.source_link }}" target="_blank" rel="noopener noreferrer">{% trans 'Link to source' %}</a>)
29
+ {% endif %}
30
+ <br>
31
+ {% if licenced_object.object %}
32
+ <a class="btn btn-xs btn-outline-primary mt-2 xhr" ajax-target="ModalContent" href="{% url 'manage_content_licence' meta_app.id entry.id %}">{% trans 'Edit licence' %}</a>
33
+ {% endif %}
34
+ </small>
35
+ </div>
36
+ </div>
37
+ {% else %}
38
+
39
+ {% endif %}
40
+ </div>
41
+ {% endfor %}
42
+ </div>
43
+
44
+ {% show_pages %}
@@ -0,0 +1,25 @@
1
+ {% extends 'localcosmos_server/modals/modal_form.html' %}
2
+ {% load i18n localcosmos_tags %}
3
+
4
+ {% block action %}
5
+ {% url 'manage_content_licence' meta_app.id registry_entry.id %}
6
+ {% endblock %}
7
+
8
+ {% block title %}
9
+ {% blocktrans %}Manage Licence{% endblocktrans %}
10
+ {% endblock %}
11
+
12
+
13
+ {% block footer %}
14
+ {% include 'localcosmos_server/modals/footers/save.html' %}
15
+ {% endblock %}
16
+
17
+ {% block script %}
18
+ {% if success %}
19
+
20
+ <script>
21
+ $("#LargeModal").modal("hide");
22
+ window.location.reload();
23
+ </script>
24
+ {% endif %}
25
+ {% endblock %}
@@ -0,0 +1,25 @@
1
+ {% extends 'app_kit/base.html' %}
2
+ {% load i18n static app_tags localcosmos_tags %}
3
+
4
+ {% block content %}
5
+ <br>
6
+ <div class="container">
7
+ <div class="row">
8
+ <div class="col-12">
9
+ <br>
10
+ <h3>
11
+ {% trans 'Images and Licences' %}
12
+ </h3>
13
+
14
+ <hr>
15
+ </div>
16
+ </div>
17
+ <div class="row">
18
+ <div class="col-12">
19
+ <div id="images-and-licences-content">
20
+ {% include 'app_kit/ajax/list_images_and_licences_content.html' %}
21
+ </div>
22
+ </div>
23
+ </div>
24
+ </div>
25
+ {% endblock %}
@@ -60,7 +60,16 @@
60
60
 
61
61
 
62
62
  {% block generic_content_specific %}
63
-
63
+ <div>
64
+ <div class="card">
65
+ <div class="card-body">
66
+ <h5 class="card-title">{% trans 'Images and Licences' %}</h5>
67
+ <div>
68
+ <a href="{% url 'list_images_and_licences' meta_app.id %}" class="btn btn-outline-primary">{% trans 'Manage Images and Licences' %}</a>
69
+ </div>
70
+ </div>
71
+ </div>
72
+ </div>
64
73
  {% endblock %}
65
74
 
66
75
  {% block extra_script %}
@@ -9,7 +9,7 @@ from django.contrib.staticfiles import finders
9
9
  from django.db.models import Q
10
10
 
11
11
  from app_kit.models import MetaApp, AppKitExternalMedia
12
- from localcosmos_server.models import EXTERNAL_MEDIA_TYPES
12
+ from localcosmos_server.models import EXTERNAL_MEDIA_TYPES, ServerImageStore
13
13
 
14
14
  from app_kit.features.backbonetaxonomy.models import TaxonRelationship, TaxonRelationshipType
15
15
 
@@ -28,7 +28,7 @@ def ranged(number):
28
28
  return range(1,int(number)+1)
29
29
 
30
30
 
31
- from app_kit.models import ContentImage
31
+ from app_kit.models import ContentImage, ImageStore
32
32
  @register.simple_tag
33
33
  def content_image(instance, image_type=None):
34
34
 
@@ -310,4 +310,24 @@ def render_taxon_relationships(context, meta_app, lazy_taxon):
310
310
  'taxon_relationship_types': relationship_types,
311
311
  }
312
312
 
313
- return tag_context
313
+ return tag_context
314
+
315
+
316
+ @register.simple_tag()
317
+ def get_object_from_licence(licence_registry_entry):
318
+
319
+ content = licence_registry_entry.content
320
+ content_type = licence_registry_entry.content_type
321
+
322
+ image_store_content_type = ContentType.objects.get_for_model(ImageStore)
323
+ server_image_store_content_type = ContentType.objects.get_for_model(ServerImageStore)
324
+
325
+ licenced_object = {
326
+ 'is_imagestore': False,
327
+ 'object': content,
328
+ }
329
+
330
+ if content_type == image_store_content_type or content_type == server_image_store_content_type:
331
+ licenced_object['is_imagestore'] = True
332
+
333
+ return licenced_object
@@ -320,7 +320,7 @@ class TestGenericContentZipImporter(WithUser, WithMetaApp, TenantTestCase):
320
320
  self.assertEqual(licence.licence, 'CC BY-NC')
321
321
  self.assertEqual(licence.licence_version, '4.0')
322
322
  self.assertEqual(licence.source_link, 'https://imageworld.com/lacerta-agilis.jpg')
323
- self.assertEqual(licence.creator_name, 'Art Vandeley')
323
+ self.assertEqual(licence.creator_name, 'New Author')
324
324
  self.assertEqual(content_image.alt_text, 'A new alt text')
325
325
  self.assertEqual(content_image.text, 'A new caption')
326
326
  self.assertEqual(content_image.title, 'Lizard')
@@ -421,6 +421,146 @@ class TestGenericContentZipImporter(WithUser, WithMetaApp, TenantTestCase):
421
421
  # No match for wrong author
422
422
  matches = importer.get_taxa_with_taxon_author_tolerance(taxon_source, taxon_latname, 'Smith')
423
423
  self.assertEqual(len(matches), 0)
424
+
425
+ # External Media URL validation
426
+ @test_settings
427
+ def test_validate_external_media_type_youtube_valid_urls(self):
428
+ importer = self.get_zip_importer()
429
+ importer.load_workbook()
430
+ importer.errors = []
431
+
432
+ valid_urls = [
433
+ 'https://youtu.be/dQw4w9WgXcQ',
434
+ 'http://youtu.be/dQw4w9WgXcQ',
435
+ 'https://www.youtube.com/watch?v=dQw4w9WgXcQ',
436
+ 'https://youtube.com/watch?v=dQw4w9WgXcQ',
437
+ 'https://www.youtube.com/embed/dQw4w9WgXcQ',
438
+ 'https://www.youtube.com/v/dQw4w9WgXcQ',
439
+ 'https://www.youtube.com/shorts/dQw4w9WgXcQ',
440
+ ]
441
+
442
+ for url in valid_urls:
443
+ importer.validate_external_media_type_youtube({'url': url}, importer.external_media_sheet_name, 2)
444
+ self.assertEqual(importer.errors, [])
445
+
446
+ @test_settings
447
+ def test_validate_external_media_type_youtube_invalid_urls(self):
448
+ importer = self.get_zip_importer()
449
+ importer.load_workbook()
450
+ importer.errors = []
451
+
452
+ # Empty URL
453
+ importer.validate_external_media_type_youtube({'url': ''}, importer.external_media_sheet_name, 2)
454
+ self.assertIn('[Generic Content.xlsx][Sheet:External Media][cell:A3] Invalid YouTube URL: empty value', importer.errors)
455
+
456
+ # Invalid scheme
457
+ bad_url = 'ftp://youtu.be/dQw4w9WgXcQ'
458
+ importer.validate_external_media_type_youtube({'url': bad_url}, importer.external_media_sheet_name, 2)
459
+ self.assertIn('[Generic Content.xlsx][Sheet:External Media][cell:A3] Invalid YouTube URL (scheme must be http/https): ftp://youtu.be/dQw4w9WgXcQ', importer.errors)
460
+
461
+ # No video id
462
+ bad_url = 'https://www.youtube.com/watch?v='
463
+ importer.validate_external_media_type_youtube({'url': bad_url}, importer.external_media_sheet_name, 2)
464
+ self.assertIn('[Generic Content.xlsx][Sheet:External Media][cell:A3] Invalid YouTube URL or video id not found: https://www.youtube.com/watch?v=', importer.errors)
465
+
466
+ @test_settings
467
+ def test_validate_external_media_type_vimeo_valid_urls(self):
468
+ importer = self.get_zip_importer()
469
+ importer.load_workbook()
470
+ importer.errors = []
471
+
472
+ valid_urls = [
473
+ 'https://vimeo.com/123456789',
474
+ 'http://vimeo.com/123456789',
475
+ 'https://player.vimeo.com/video/123456789',
476
+ 'https://www.vimeo.com/123456789',
477
+ ]
478
+
479
+ for url in valid_urls:
480
+ importer.validate_external_media_type_vimeo({'url': url}, importer.external_media_sheet_name, 2)
481
+ self.assertEqual(importer.errors, [])
482
+
483
+ @test_settings
484
+ def test_validate_external_media_type_vimeo_invalid_urls(self):
485
+ importer = self.get_zip_importer()
486
+ importer.load_workbook()
487
+ importer.errors = []
488
+
489
+ # Empty URL
490
+ importer.validate_external_media_type_vimeo({'url': ''}, importer.external_media_sheet_name, 2)
491
+ self.assertIn('[Generic Content.xlsx][Sheet:External Media][cell:A3] Invalid Vimeo URL: empty value', importer.errors)
492
+
493
+ # Invalid scheme
494
+ bad_url = 'ftp://vimeo.com/123456789'
495
+ importer.validate_external_media_type_vimeo({'url': bad_url}, importer.external_media_sheet_name, 2)
496
+ self.assertIn('[Generic Content.xlsx][Sheet:External Media][cell:A3] Invalid Vimeo URL (scheme must be http/https): ftp://vimeo.com/123456789', importer.errors)
497
+
498
+ # No video id
499
+ bad_url = 'https://vimeo.com/'
500
+ importer.validate_external_media_type_vimeo({'url': bad_url}, importer.external_media_sheet_name, 2)
501
+ self.assertIn('[Generic Content.xlsx][Sheet:External Media][cell:A3] Invalid Vimeo URL or video id not found: https://vimeo.com/', importer.errors)
502
+
503
+ @test_settings
504
+ def test_validate_external_media_type_image_url_extension(self):
505
+ importer = self.get_zip_importer()
506
+ importer.load_workbook()
507
+ importer.errors = []
508
+
509
+ # Valid image URL
510
+ importer.validate_external_media_type_image({'url': 'https://example.com/pic.jpg'}, importer.external_media_sheet_name, 2)
511
+ # Invalid image URL extension
512
+ importer.validate_external_media_type_image({'url': 'https://example.com/pic.bmp'}, importer.external_media_sheet_name, 2)
513
+
514
+ self.assertIn('[Generic Content.xlsx][Sheet:External Media][cell:A3] Invalid image format in URL: .bmp. Valid formats are: .jpg, .jpeg, .png, .webp, .gif', importer.errors)
515
+
516
+ @test_settings
517
+ def test_validate_external_media_type_audio_pdf(self):
518
+ importer = self.get_zip_importer()
519
+ importer.load_workbook()
520
+ importer.errors = []
521
+
522
+ # mp3
523
+ importer.validate_external_media_type_mp3({'url': 'https://example.com/audio.txt'}, importer.external_media_sheet_name, 2)
524
+ importer.validate_external_media_type_mp3({'url': 'https://example.com/audio.mp3'}, importer.external_media_sheet_name, 2)
525
+ self.assertIn('[Generic Content.xlsx][Sheet:External Media][cell:A3] Invalid mp3 format in URL: https://example.com/audio.txt. URL has to end with .mp3', importer.errors)
526
+
527
+ # wav
528
+ importer.validate_external_media_type_wav({'url': 'https://example.com/audio.txt'}, importer.external_media_sheet_name, 2)
529
+ importer.validate_external_media_type_wav({'url': 'https://example.com/audio.wav'}, importer.external_media_sheet_name, 2)
530
+ self.assertIn('[Generic Content.xlsx][Sheet:External Media][cell:A3] Invalid wav format in URL: https://example.com/audio.txt. URL has to end with .wav', importer.errors)
531
+
532
+ # pdf
533
+ importer.validate_external_media_type_pdf({'url': 'https://example.com/doc.txt'}, importer.external_media_sheet_name, 2)
534
+ importer.validate_external_media_type_pdf({'url': 'https://example.com/doc.pdf'}, importer.external_media_sheet_name, 2)
535
+ self.assertIn('[Generic Content.xlsx][Sheet:External Media][cell:A3] Invalid pdf format in URL: https://example.com/doc.txt. URL has to end with .pdf', importer.errors)
536
+
537
+ @test_settings
538
+ def test_validate_external_media_type_website(self):
539
+ importer = self.get_zip_importer()
540
+ importer.load_workbook()
541
+ importer.errors = []
542
+
543
+ # Invalid: ends with file extension
544
+ importer.validate_external_media_type_website({'url': 'https://example.com/image.jpg'}, importer.external_media_sheet_name, 2)
545
+ # Valid: no file extension
546
+ importer.validate_external_media_type_website({'url': 'https://example.com/some/path?query=x'}, importer.external_media_sheet_name, 2)
547
+ # Valid: domain-only with TLD
548
+ importer.validate_external_media_type_website({'url': 'https://example.com'}, importer.external_media_sheet_name, 2)
549
+
550
+ self.assertIn('[Generic Content.xlsx][Sheet:External Media][cell:A3] Invalid website format in URL: https://example.com/image.jpg. URL should not end with a file extension.', importer.errors)
551
+
552
+ @test_settings
553
+ def test_validate_external_media_type_file(self):
554
+ importer = self.get_zip_importer()
555
+ importer.load_workbook()
556
+ importer.errors = []
557
+
558
+ # Invalid: no file extension
559
+ importer.validate_external_media_type_file({'url': 'https://example.com/download/'}, importer.external_media_sheet_name, 2)
560
+ # Valid: has file extension
561
+ importer.validate_external_media_type_file({'url': 'https://example.com/download.zip'}, importer.external_media_sheet_name, 2)
562
+
563
+ self.assertIn('[Generic Content.xlsx][Sheet:External Media][cell:A3] Invalid file format in URL: https://example.com/download/. URL has to end with a file extension.', importer.errors)
424
564
 
425
565
  # real world test
426
566
  # algaebase entry is : Desmarestia viridis (O.F.Müller) J.V.Lamouroux 1813
@@ -587,4 +727,7 @@ class TestGenericContentZipImporterInvalidData(WithUser, WithMetaApp, TenantTest
587
727
  ignorin_importer.load_workbook()
588
728
  ignorin_importer.errors = []
589
729
  ignorin_importer.validate_listing_in_images_sheet('unlisted.jpg', 'A', 2)
590
- self.assertEqual(ignorin_importer.errors, [])
730
+ self.assertEqual(ignorin_importer.errors, [])
731
+
732
+
733
+
app_kit/views.py CHANGED
@@ -22,7 +22,7 @@ from .forms import (AddLanguageForm, MetaAppOptionsForm, TagAnyElementForm, Gene
22
22
  CreateGenericContentForm, AddExistingGenericContentForm, TranslateAppForm,
23
23
  EditGenericContentNameForm, ManageContentImageWithTextForm,
24
24
  ZipImportForm, BuildAppForm, CreateAppForm, ManageLocalizedContentImageForm,
25
- TranslateVernacularNamesForm)
25
+ TranslateVernacularNamesForm, ManageContentLicenceForm)
26
26
 
27
27
  from django_tenants.utils import get_tenant_domain_model
28
28
  Domain = get_tenant_domain_model()
@@ -53,6 +53,8 @@ from django.db import connection
53
53
  # activate permission rules
54
54
  from .permission_rules import *
55
55
 
56
+ from content_licencing.models import ContentLicenceRegistry
57
+
56
58
  LOCALCOSMOS_COMMERCIAL_BUILDER = getattr(settings, 'LOCALCOSMOS_COMMERCIAL_BUILDER', True)
57
59
 
58
60
 
@@ -1732,6 +1734,62 @@ class DeleteAppKitExternalMedia(MetaAppMixin, AjaxDeleteView):
1732
1734
  context = super().get_context_data(**kwargs)
1733
1735
  context['external_media_object'] = self.object.content_object
1734
1736
  return context
1737
+
1738
+
1739
+ class ListImagesAndLicences(MetaAppMixin, TemplateView):
1740
+
1741
+ template_name = 'app_kit/list_images_and_licences.html'
1742
+ ajax_template_name = 'app_kit/ajax/list_images_and_licences_content.html'
1743
+
1744
+ def get_context_data(self, **kwargs):
1745
+ context = super().get_context_data(**kwargs)
1746
+ registry_entries = ContentLicenceRegistry.objects.all().order_by('creator_name', 'pk')
1747
+ context['registry_entries'] = registry_entries
1748
+ return context
1749
+
1750
+
1751
+ class ManageContentLicence(MetaAppMixin, LicencingFormViewMixin, FormView):
1752
+
1753
+ form_class = ManageContentLicenceForm
1754
+
1755
+ template_name = 'app_kit/ajax/manage_content_licence.html'
1756
+
1757
+ @method_decorator(ajax_required)
1758
+ def dispatch(self, request, *args, **kwargs):
1759
+ self.set_instance(**kwargs)
1760
+ return super().dispatch(request, *args, **kwargs)
1761
+
1762
+ def set_instance(self, **kwargs):
1763
+ self.licence_registry_entry = ContentLicenceRegistry.objects.get(pk=kwargs['registry_entry_id'])
1764
+
1765
+ def get_form(self, form_class=None):
1766
+ if form_class is None:
1767
+ form_class = self.get_form_class()
1768
+ return form_class(self.licence_registry_entry.model_field, **self.get_form_kwargs())
1769
+
1770
+ def get_initial(self):
1771
+ initial = super().get_initial()
1772
+
1773
+ licencing_initial = self.get_licencing_initial()
1774
+ initial.update(licencing_initial)
1775
+
1776
+ return initial
1777
+
1778
+
1779
+ def get_context_data(self, **kwargs):
1780
+ context = super().get_context_data(**kwargs)
1781
+ context['registry_entry'] = self.licence_registry_entry
1782
+ return context
1783
+
1784
+ def form_valid(self, form):
1785
+ if self.licence_registry_entry.content:
1786
+ self.register_content_licence(form, self.licence_registry_entry.content, self.licence_registry_entry.model_field)
1787
+
1788
+ context = self.get_context_data(**self.kwargs)
1789
+ context['form'] = form
1790
+ context['success'] = True
1791
+ return self.render_to_response(context)
1792
+
1735
1793
 
1736
1794
  # LEGAL
1737
1795
  class IdentityMixin:
@@ -1759,4 +1817,5 @@ class PrivacyStatement(IdentityMixin, TemplateView):
1759
1817
  @method_decorator(csrf_exempt)
1760
1818
  @method_decorator(requires_csrf_token)
1761
1819
  def dispatch(self, request, *args, **kwargs):
1762
- return super().dispatch(request, *args, **kwargs)
1820
+ return super().dispatch(request, *args, **kwargs)
1821
+
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: localcosmos_app_kit
3
- Version: 0.9.15
3
+ Version: 0.9.17
4
4
  Summary: LocalCosmos App Kit. Web Portal to build Android and iOS apps
5
5
  Home-page: https://github.com/localcosmos/app-kit
6
6
  Author: Thomas Uher
@@ -14,7 +14,7 @@ Classifier: Operating System :: OS Independent
14
14
  Requires-Python: >=3.8
15
15
  Description-Content-Type: text/markdown
16
16
  License-File: LICENCE
17
- Requires-Dist: localcosmos-server==0.24.10
17
+ Requires-Dist: localcosmos-server==0.24.11
18
18
  Requires-Dist: localcosmos-cordova-builder==0.9.6
19
19
  Requires-Dist: django-tenants==3.7.0
20
20
  Requires-Dist: django-cleanup==9.0.0