localcosmos-app-kit 0.9.14__py3-none-any.whl → 0.9.16__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.
- app_kit/admin_urls.py +5 -0
- app_kit/appbuilder/AppReleaseBuilder.py +2 -2
- app_kit/appbuilder/TaxonBuilder.py +31 -23
- app_kit/appbuilder/__pycache__/AppReleaseBuilder.cpython-313.pyc +0 -0
- app_kit/appbuilder/__pycache__/TaxonBuilder.cpython-313.pyc +0 -0
- app_kit/appbuilder/app/frontends/Multiverse/settings.json +2 -2
- app_kit/features/nature_guides/matrix_filter_forms.py +17 -1
- app_kit/features/nature_guides/models.py +5 -2
- app_kit/features/nature_guides/templates/nature_guides/ajax/manage_matrix_filter.html +15 -8
- app_kit/features/nature_guides/views.py +8 -0
- app_kit/features/taxon_profiles/templates/taxon_profiles/ajax/delete_all_manually_added_images.html +37 -0
- app_kit/features/taxon_profiles/templates/taxon_profiles/ajax/manage_taxon_profile_form.html +1 -1
- app_kit/features/taxon_profiles/templates/taxon_profiles/manage_taxon_profiles.html +16 -0
- app_kit/features/taxon_profiles/tests/__pycache__/test_views.cpython-313.pyc +0 -0
- app_kit/features/taxon_profiles/tests/test_views.py +100 -3
- app_kit/features/taxon_profiles/urls.py +3 -0
- app_kit/features/taxon_profiles/views.py +38 -1
- app_kit/features/taxon_profiles/zip_import.py +4 -0
- app_kit/forms.py +8 -0
- app_kit/locale/de/LC_MESSAGES/django.mo +0 -0
- app_kit/locale/de/LC_MESSAGES/django.po +254 -100
- app_kit/taxonomy/lazy.py +6 -6
- app_kit/templates/app_kit/ajax/list_images_and_licences_content.html +44 -0
- app_kit/templates/app_kit/ajax/manage_content_licence.html +25 -0
- app_kit/templates/app_kit/list_images_and_licences.html +25 -0
- app_kit/templates/app_kit/manage_app.html +10 -1
- app_kit/templatetags/app_tags.py +23 -3
- app_kit/views.py +61 -2
- {localcosmos_app_kit-0.9.14.dist-info → localcosmos_app_kit-0.9.16.dist-info}/METADATA +1 -1
- {localcosmos_app_kit-0.9.14.dist-info → localcosmos_app_kit-0.9.16.dist-info}/RECORD +33 -29
- {localcosmos_app_kit-0.9.14.dist-info → localcosmos_app_kit-0.9.16.dist-info}/WHEEL +1 -1
- {localcosmos_app_kit-0.9.14.dist-info → localcosmos_app_kit-0.9.16.dist-info}/licenses/LICENCE +0 -0
- {localcosmos_app_kit-0.9.14.dist-info → localcosmos_app_kit-0.9.16.dist-info}/top_level.txt +0 -0
app_kit/admin_urls.py
CHANGED
|
@@ -136,4 +136,9 @@ urlpatterns = [
|
|
|
136
136
|
views.ManageAppKitExternalMedia.as_view(), name='update_app_kit_external_media'),
|
|
137
137
|
path('delete-external-media/<int:meta_app_id>/<int:pk>/',
|
|
138
138
|
views.DeleteAppKitExternalMedia.as_view(), name='delete_app_kit_external_media'),
|
|
139
|
+
# licences
|
|
140
|
+
path('list-images-and-licences/<int:meta_app_id>/',
|
|
141
|
+
views.ListImagesAndLicences.as_view(), name='list_images_and_licences'),
|
|
142
|
+
path('manage-content-licence/<int:meta_app_id>/<int:registry_entry_id>/',
|
|
143
|
+
views.ManageContentLicence.as_view(), name='manage_content_licence'),
|
|
139
144
|
]
|
|
@@ -916,10 +916,10 @@ class AppReleaseBuilder(AppBuilderBase):
|
|
|
916
916
|
|
|
917
917
|
# detect duplicates by name
|
|
918
918
|
all_taxon_profiles = TaxonProfile.objects.filter(taxon_profiles=taxon_profiles)
|
|
919
|
-
for taxon_profile in all_taxon_profiles:
|
|
919
|
+
for taxon_profile in all_taxon_profiles:
|
|
920
920
|
duplicates = TaxonProfile.objects.filter(taxon_profiles=taxon_profiles,
|
|
921
921
|
taxon_source=taxon_profile.taxon_source, taxon_latname=taxon_profile.taxon_latname,
|
|
922
|
-
taxon_author=taxon_profile.taxon_author).exclude(pk=taxon_profile.pk)
|
|
922
|
+
taxon_author=taxon_profile.taxon_author, morphotype=taxon_profile.morphotype).exclude(pk=taxon_profile.pk)
|
|
923
923
|
if duplicates.exists():
|
|
924
924
|
warning_message = _('The taxon profile of %(taxon_latname)s has duplicates.') % {
|
|
925
925
|
'taxon_latname':taxon_profile.taxon_latname}
|
|
@@ -332,33 +332,41 @@ class TaxonSerializer:
|
|
|
332
332
|
if name_type not in self.taxa_builder.cache['search']:
|
|
333
333
|
self.taxa_builder.cache['search'][name_type] = {}
|
|
334
334
|
|
|
335
|
-
if name in self.taxa_builder.cache['search'][name_type]:
|
|
336
|
-
|
|
335
|
+
if name not in self.taxa_builder.cache['search'][name_type]:
|
|
336
|
+
# the same name might occur in different taxonomies
|
|
337
|
+
self.taxa_builder.cache['search'][name_type][name] = {}
|
|
337
338
|
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
accepted_name_uuid = self.lazy_taxon.name_uuid
|
|
342
|
-
|
|
343
|
-
if accepted_name_uuid:
|
|
344
|
-
accepted_name_uuid = str(accepted_name_uuid)
|
|
345
|
-
|
|
346
|
-
has_taxon_profile = False
|
|
347
|
-
taxon_profile = self.get_taxon_profile()
|
|
348
|
-
if taxon_profile:
|
|
349
|
-
has_taxon_profile = True
|
|
339
|
+
if name in self.taxa_builder.cache['search'][name_type]:
|
|
340
|
+
# the same name might occur in different taxonomies
|
|
341
|
+
taxon_source = self.lazy_taxon.taxon_source
|
|
350
342
|
|
|
351
|
-
|
|
343
|
+
if taxon_source in self.taxa_builder.cache['search'][name_type][name]:
|
|
344
|
+
search_taxon_json = self.taxa_builder.cache['search'][name_type][name][taxon_source]
|
|
352
345
|
|
|
353
|
-
|
|
354
|
-
'nameType': name_type,
|
|
355
|
-
'name': name,
|
|
356
|
-
'isPreferredName': is_preferred_name,
|
|
357
|
-
'acceptedNameUuid': accepted_name_uuid,
|
|
358
|
-
'hasTaxonProfile': has_taxon_profile,
|
|
359
|
-
})
|
|
346
|
+
else:
|
|
360
347
|
|
|
361
|
-
|
|
348
|
+
if not accepted_name_uuid:
|
|
349
|
+
accepted_name_uuid = self.lazy_taxon.name_uuid
|
|
350
|
+
|
|
351
|
+
if accepted_name_uuid:
|
|
352
|
+
accepted_name_uuid = str(accepted_name_uuid)
|
|
353
|
+
|
|
354
|
+
has_taxon_profile = False
|
|
355
|
+
taxon_profile = self.get_taxon_profile()
|
|
356
|
+
if taxon_profile:
|
|
357
|
+
has_taxon_profile = True
|
|
358
|
+
|
|
359
|
+
search_taxon_json = self.serialize_extended()
|
|
360
|
+
|
|
361
|
+
search_taxon_json.update({
|
|
362
|
+
'nameType': name_type,
|
|
363
|
+
'name': name,
|
|
364
|
+
'isPreferredName': is_preferred_name,
|
|
365
|
+
'acceptedNameUuid': accepted_name_uuid,
|
|
366
|
+
'hasTaxonProfile': has_taxon_profile,
|
|
367
|
+
})
|
|
368
|
+
|
|
369
|
+
self.taxa_builder.cache['search'][name_type][name][taxon_source] = search_taxon_json
|
|
362
370
|
|
|
363
371
|
search_taxon_json_copy = copy.deepcopy(search_taxon_json)
|
|
364
372
|
|
|
Binary file
|
|
Binary file
|
|
@@ -5,7 +5,7 @@ from django import forms
|
|
|
5
5
|
|
|
6
6
|
from localcosmos_server.forms import LocalizeableForm
|
|
7
7
|
|
|
8
|
-
from .models import MatrixFilter
|
|
8
|
+
from .models import MatrixFilter, IDENTIFICATION_MODE_POLYTOMOUS
|
|
9
9
|
from .matrix_filters import MATRIX_FILTER_TYPES
|
|
10
10
|
|
|
11
11
|
from .forms import is_active_field
|
|
@@ -63,6 +63,22 @@ class MatrixFilterManagementForm(LocalizeableForm):
|
|
|
63
63
|
raise forms.ValidationError(_('A matrix filter with this name already exists.'))
|
|
64
64
|
|
|
65
65
|
return name
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
def clean(self):
|
|
69
|
+
cleaned_data = super().clean()
|
|
70
|
+
|
|
71
|
+
identification_mode = self.meta_node.identification_mode
|
|
72
|
+
|
|
73
|
+
if identification_mode == IDENTIFICATION_MODE_POLYTOMOUS and not self.matrix_filter:
|
|
74
|
+
|
|
75
|
+
filter_exists = MatrixFilter.objects.filter(meta_node=self.meta_node).exists()
|
|
76
|
+
|
|
77
|
+
if filter_exists:
|
|
78
|
+
raise forms.ValidationError(_('In polytomous identification mode, only one matrix filter is allowed.'))
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
return cleaned_data
|
|
66
82
|
|
|
67
83
|
|
|
68
84
|
class MatrixFilterManagementFormWithUnit(MatrixFilterManagementForm):
|
|
@@ -1130,6 +1130,7 @@ class CrosslinkManager:
|
|
|
1130
1130
|
|
|
1131
1131
|
is_circular = False
|
|
1132
1132
|
|
|
1133
|
+
# you may not link a node to one of its parents
|
|
1133
1134
|
if crosslink[0].startswith(crosslink[1]):
|
|
1134
1135
|
is_circular = True
|
|
1135
1136
|
|
|
@@ -1156,17 +1157,17 @@ class CrosslinkManager:
|
|
|
1156
1157
|
|
|
1157
1158
|
if is_circular == False:
|
|
1158
1159
|
|
|
1159
|
-
found_connection = True
|
|
1160
|
-
|
|
1161
1160
|
for crosslink in crosslinks:
|
|
1162
1161
|
|
|
1163
1162
|
# start a new chain
|
|
1164
1163
|
chain = [crosslink]
|
|
1164
|
+
found_connection = True
|
|
1165
1165
|
|
|
1166
1166
|
while found_connection == True and is_circular == False:
|
|
1167
1167
|
|
|
1168
1168
|
for crosslink_2 in crosslinks:
|
|
1169
1169
|
|
|
1170
|
+
# get the last nuid in the chain, which is a list of 2-tuples
|
|
1170
1171
|
chain_end = chain[-1][1]
|
|
1171
1172
|
|
|
1172
1173
|
found_connection = False
|
|
@@ -1212,6 +1213,8 @@ class NatureGuideCrosslinks(models.Model):
|
|
|
1212
1213
|
|
|
1213
1214
|
all_crosslinks.append(crosslink_tuple)
|
|
1214
1215
|
|
|
1216
|
+
print('Checking crosslinks for circularity:')
|
|
1217
|
+
print(all_crosslinks)
|
|
1215
1218
|
crosslink_manager = CrosslinkManager()
|
|
1216
1219
|
is_circular = crosslink_manager.check_circularity(all_crosslinks)
|
|
1217
1220
|
|
|
@@ -10,15 +10,22 @@
|
|
|
10
10
|
{% endblock %}
|
|
11
11
|
|
|
12
12
|
{% block body %}
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
</p>
|
|
18
|
-
{% if success is True %}
|
|
19
|
-
<div class="alert alert-success">
|
|
20
|
-
{% trans 'Successfully saved matrix filter.' %}
|
|
13
|
+
|
|
14
|
+
{% if adding_allowed == False and not matrix_filter %}
|
|
15
|
+
<div class="alert alert-warning">
|
|
16
|
+
{% trans "In dichotomous or polytomous identification mode, only one matrix filter is allowed." %}
|
|
21
17
|
</div>
|
|
18
|
+
{% else %}
|
|
19
|
+
<p>
|
|
20
|
+
<div>
|
|
21
|
+
{% render_bootstrap_form form %}
|
|
22
|
+
</div>
|
|
23
|
+
</p>
|
|
24
|
+
{% if success is True %}
|
|
25
|
+
<div class="alert alert-success">
|
|
26
|
+
{% trans 'Successfully saved matrix filter.' %}
|
|
27
|
+
</div>
|
|
28
|
+
{% endif %}
|
|
22
29
|
{% endif %}
|
|
23
30
|
{% endblock %}
|
|
24
31
|
|
|
@@ -819,6 +819,14 @@ class ManageMatrixFilter(FormLanguageMixin, MetaAppMixin, FormView):
|
|
|
819
819
|
context['meta_node'] = self.meta_node
|
|
820
820
|
context['filter_type'] = self.filter_type
|
|
821
821
|
context['matrix_filter'] = self.matrix_filter
|
|
822
|
+
|
|
823
|
+
adding_allowed = True
|
|
824
|
+
if self.meta_node.identification_mode == IDENTIFICATION_MODE_POLYTOMOUS:
|
|
825
|
+
existing_filters_count = MatrixFilter.objects.filter(meta_node=self.meta_node).count()
|
|
826
|
+
if existing_filters_count >= 1:
|
|
827
|
+
adding_allowed = False
|
|
828
|
+
|
|
829
|
+
context['adding_allowed'] = adding_allowed
|
|
822
830
|
|
|
823
831
|
# fallback
|
|
824
832
|
verbose_filter_name = self.filter_type
|
app_kit/features/taxon_profiles/templates/taxon_profiles/ajax/delete_all_manually_added_images.html
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
{% extends "localcosmos_server/modals/modal_form.html" %}
|
|
2
|
+
{% load i18n %}
|
|
3
|
+
|
|
4
|
+
{% block title %}
|
|
5
|
+
{% trans 'Delete All Manually Added Images' %}
|
|
6
|
+
{% endblock %}
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
{% block action %}
|
|
10
|
+
{% url 'delete_all_manually_added_taxon_profile_images' meta_app.id taxon_profiles.id %}
|
|
11
|
+
{% endblock %}
|
|
12
|
+
|
|
13
|
+
{% block body %}
|
|
14
|
+
<p>
|
|
15
|
+
{% if success %}
|
|
16
|
+
<div class="alert alert-success">
|
|
17
|
+
{% trans 'All manually added images have been successfully deleted.' %}
|
|
18
|
+
</div>
|
|
19
|
+
{% else %}
|
|
20
|
+
<div class="alert alert-danger">
|
|
21
|
+
{% trans 'Are you sure you want to delete all manually added images across all taxon profiles? This action cannot be undone.' %}
|
|
22
|
+
</div>
|
|
23
|
+
{% endif %}
|
|
24
|
+
</p>
|
|
25
|
+
{% endblock %}
|
|
26
|
+
|
|
27
|
+
{% block footer %}
|
|
28
|
+
{% if success %}
|
|
29
|
+
{% include 'localcosmos_server/modals/footers/close.html' %}
|
|
30
|
+
{% else %}
|
|
31
|
+
{% include 'localcosmos_server/modals/footers/delete.html' %}
|
|
32
|
+
{% endif %}
|
|
33
|
+
{% endblock %}
|
|
34
|
+
|
|
35
|
+
{% block script %}
|
|
36
|
+
|
|
37
|
+
{% endblock %}
|
|
@@ -168,6 +168,22 @@
|
|
|
168
168
|
</div>
|
|
169
169
|
</div>
|
|
170
170
|
</div>
|
|
171
|
+
|
|
172
|
+
<div class="mt-5">
|
|
173
|
+
<h3 class="text-danger">
|
|
174
|
+
{% trans 'Danger Zone' %}
|
|
175
|
+
</h3>
|
|
176
|
+
</div>
|
|
177
|
+
<div class="card mt-3">
|
|
178
|
+
<div class="card-body">
|
|
179
|
+
<div>
|
|
180
|
+
<button type="button" class="btn btn-outline-danger xhr" ajax-target="ModalContent" data-url="{% url 'delete_all_manually_added_taxon_profile_images' meta_app.id generic_content.id %}">{% trans 'Delete all Taxon Profile images that have been added manually' %}</button>
|
|
181
|
+
</div>
|
|
182
|
+
<div class="mt-3">
|
|
183
|
+
{% trans 'This will delete all manually added images across all taxon profiles.' %}
|
|
184
|
+
</div>
|
|
185
|
+
</div>
|
|
186
|
+
</div>
|
|
171
187
|
{% endblock %}
|
|
172
188
|
|
|
173
189
|
{% block extra_script %}
|
|
Binary file
|
|
@@ -5,7 +5,7 @@ from django.urls import reverse
|
|
|
5
5
|
|
|
6
6
|
from app_kit.tests.common import test_settings
|
|
7
7
|
|
|
8
|
-
from app_kit.models import MetaAppGenericContent, ContentImage
|
|
8
|
+
from app_kit.models import MetaAppGenericContent, ContentImage, MetaApp
|
|
9
9
|
|
|
10
10
|
from app_kit.tests.mixins import (WithMetaApp, WithTenantClient, WithUser, WithLoggedInUser, WithAjaxAdminOnly,
|
|
11
11
|
WithAdminOnly, ViewTestMixin, WithImageStore, WithMedia, WithFormTest)
|
|
@@ -20,7 +20,8 @@ from app_kit.features.taxon_profiles.views import (ManageTaxonProfiles, ManageTa
|
|
|
20
20
|
DeleteTaxonProfilesNavigationEntry, GetTaxonProfilesNavigation, ManageNavigationImage,
|
|
21
21
|
DeleteNavigationImage, DeleteTaxonProfilesNavigationEntryTaxon, DeleteTaxonTextTypeCategory,
|
|
22
22
|
ChangeNavigationEntryPublicationStatus, ManageTaxonTextTypeCategory, ManageTaxonTextSet,
|
|
23
|
-
DeleteTaxonTextSet, GetTaxonTextsManagement, SetTaxonTextSetForTaxonProfile
|
|
23
|
+
DeleteTaxonTextSet, GetTaxonTextsManagement, SetTaxonTextSetForTaxonProfile,
|
|
24
|
+
DeleteAllManuallyAddedTaxonProfileImages)
|
|
24
25
|
|
|
25
26
|
from app_kit.features.taxon_profiles.models import (TaxonProfiles, TaxonProfile, TaxonTextType,
|
|
26
27
|
TaxonText, TaxonProfilesNavigation, TaxonProfilesNavigationEntry,
|
|
@@ -2590,4 +2591,100 @@ class TestSetTaxonTextSetForTaxonProfile(WithTaxonProfile, WithTaxonProfiles, Vi
|
|
|
2590
2591
|
|
|
2591
2592
|
self.taxon_profile.refresh_from_db()
|
|
2592
2593
|
|
|
2593
|
-
self.assertEqual(self.taxon_profile.taxon_text_set, text_set)
|
|
2594
|
+
self.assertEqual(self.taxon_profile.taxon_text_set, text_set)
|
|
2595
|
+
|
|
2596
|
+
|
|
2597
|
+
class TestDeleteAllManuallyAddedTaxonProfileImages(WithTaxonProfile, WithTaxonProfiles, ViewTestMixin,
|
|
2598
|
+
WithImageStore, WithMedia, WithAjaxAdminOnly, WithUser, WithLoggedInUser, WithMetaApp, WithTenantClient, TenantTestCase):
|
|
2599
|
+
|
|
2600
|
+
url_name = 'delete_all_manually_added_taxon_profile_images'
|
|
2601
|
+
view_class = DeleteAllManuallyAddedTaxonProfileImages
|
|
2602
|
+
|
|
2603
|
+
def get_url_kwargs(self):
|
|
2604
|
+
url_kwargs = {
|
|
2605
|
+
'meta_app_id': self.meta_app.id,
|
|
2606
|
+
'taxon_profiles_id': self.generic_content.id,
|
|
2607
|
+
}
|
|
2608
|
+
return url_kwargs
|
|
2609
|
+
|
|
2610
|
+
def create_content_images(self):
|
|
2611
|
+
|
|
2612
|
+
# taxon image
|
|
2613
|
+
self.taxon_image_store = self.create_image_store()
|
|
2614
|
+
|
|
2615
|
+
# add image to nature guide meta node
|
|
2616
|
+
self.meta_node_image = self.create_content_image(self.meta_app, self.user)
|
|
2617
|
+
|
|
2618
|
+
# add image to taxon profile
|
|
2619
|
+
self.taxon_profile_image = self.create_content_image(self.taxon_profile, self.user)
|
|
2620
|
+
|
|
2621
|
+
|
|
2622
|
+
|
|
2623
|
+
@test_settings
|
|
2624
|
+
def test_set_taxon_profiles(self):
|
|
2625
|
+
|
|
2626
|
+
view = self.get_view()
|
|
2627
|
+
view.meta_app = self.meta_app
|
|
2628
|
+
view.set_taxon_profiles(**view.kwargs)
|
|
2629
|
+
|
|
2630
|
+
self.assertEqual(view.taxon_profiles, self.generic_content)
|
|
2631
|
+
|
|
2632
|
+
@test_settings
|
|
2633
|
+
def test_get_context_data(self):
|
|
2634
|
+
|
|
2635
|
+
view = self.get_view()
|
|
2636
|
+
view.meta_app = self.meta_app
|
|
2637
|
+
view.set_taxon_profiles(**view.kwargs)
|
|
2638
|
+
|
|
2639
|
+
context = view.get_context_data(**view.kwargs)
|
|
2640
|
+
|
|
2641
|
+
self.assertEqual(context['taxon_profiles'], self.generic_content)
|
|
2642
|
+
self.assertFalse(context['success'])
|
|
2643
|
+
|
|
2644
|
+
@test_settings
|
|
2645
|
+
def test_post(self):
|
|
2646
|
+
|
|
2647
|
+
view = self.get_view()
|
|
2648
|
+
view.meta_app = self.meta_app
|
|
2649
|
+
view.set_taxon_profiles(**view.kwargs)
|
|
2650
|
+
|
|
2651
|
+
self.create_content_images()
|
|
2652
|
+
|
|
2653
|
+
taxon_profile_ctype = ContentType.objects.get_for_model(TaxonProfile)
|
|
2654
|
+
meta_app_ctype = ContentType.objects.get_for_model(MetaApp)
|
|
2655
|
+
|
|
2656
|
+
# verify image exists
|
|
2657
|
+
images_qry = ContentImage.objects.filter(
|
|
2658
|
+
content_type=taxon_profile_ctype,
|
|
2659
|
+
object_id=self.taxon_profile.id,
|
|
2660
|
+
)
|
|
2661
|
+
self.assertTrue(images_qry.exists())
|
|
2662
|
+
|
|
2663
|
+
meta_app_images_qry = ContentImage.objects.filter(
|
|
2664
|
+
content_type=meta_app_ctype,
|
|
2665
|
+
object_id=self.meta_app.id,
|
|
2666
|
+
)
|
|
2667
|
+
|
|
2668
|
+
self.assertTrue(meta_app_images_qry.exists())
|
|
2669
|
+
|
|
2670
|
+
# perform post to delete images
|
|
2671
|
+
response = view.post(view.request, **view.kwargs)
|
|
2672
|
+
|
|
2673
|
+
self.assertEqual(response.status_code, 200)
|
|
2674
|
+
self.assertTrue(response.context_data['success'])
|
|
2675
|
+
|
|
2676
|
+
# verify images deleted
|
|
2677
|
+
images_qry = ContentImage.objects.filter(
|
|
2678
|
+
content_type=taxon_profile_ctype,
|
|
2679
|
+
object_id=self.taxon_profile.id,
|
|
2680
|
+
)
|
|
2681
|
+
self.assertFalse(images_qry.exists())
|
|
2682
|
+
|
|
2683
|
+
# verify meta app image still exists
|
|
2684
|
+
meta_app_images_qry = ContentImage.objects.filter(
|
|
2685
|
+
content_type=meta_app_ctype,
|
|
2686
|
+
object_id=self.meta_app.id,
|
|
2687
|
+
)
|
|
2688
|
+
self.assertTrue(meta_app_images_qry.exists())
|
|
2689
|
+
|
|
2690
|
+
|
|
@@ -119,4 +119,7 @@ urlpatterns = [
|
|
|
119
119
|
# move images
|
|
120
120
|
path('move-taxon-profile-image-to-section/<int:meta_app_id>/<int:taxon_profile_id>/<int:content_image_id>/',
|
|
121
121
|
views.MoveImageToSection.as_view(), name='move_taxon_profile_image_to_section'),
|
|
122
|
+
# delete all manually added images
|
|
123
|
+
path('delete-all-manually-added-taxon-profile-images/<int:meta_app_id>/<int:taxon_profiles_id>/',
|
|
124
|
+
views.DeleteAllManuallyAddedTaxonProfileImages.as_view(), name='delete_all_manually_added_taxon_profile_images'),
|
|
122
125
|
]
|
|
@@ -1580,4 +1580,41 @@ class MoveImageToSection(MetaAppMixin, FormView):
|
|
|
1580
1580
|
context['success'] = True
|
|
1581
1581
|
|
|
1582
1582
|
return self.render_to_response(context)
|
|
1583
|
-
|
|
1583
|
+
|
|
1584
|
+
|
|
1585
|
+
class DeleteAllManuallyAddedTaxonProfileImages(MetaAppMixin, TemplateView):
|
|
1586
|
+
|
|
1587
|
+
template_name = 'taxon_profiles/ajax/delete_all_manually_added_images.html'
|
|
1588
|
+
|
|
1589
|
+
@method_decorator(ajax_required)
|
|
1590
|
+
def dispatch(self, request, *args, **kwargs):
|
|
1591
|
+
self.set_taxon_profiles(**kwargs)
|
|
1592
|
+
return super().dispatch(request, *args, **kwargs)
|
|
1593
|
+
|
|
1594
|
+
def set_taxon_profiles(self, **kwargs):
|
|
1595
|
+
self.taxon_profiles = TaxonProfiles.objects.get(pk=kwargs['taxon_profiles_id'])
|
|
1596
|
+
|
|
1597
|
+
def get_context_data(self, **kwargs):
|
|
1598
|
+
context = super().get_context_data(**kwargs)
|
|
1599
|
+
context['taxon_profiles'] = self.taxon_profiles
|
|
1600
|
+
context['success'] = False
|
|
1601
|
+
return context
|
|
1602
|
+
|
|
1603
|
+
def post(self, request, *args, **kwargs):
|
|
1604
|
+
|
|
1605
|
+
taxon_profile_ctype = ContentType.objects.get_for_model(TaxonProfile)
|
|
1606
|
+
all_taxon_profiles = TaxonProfile.objects.filter(taxon_profiles=self.taxon_profiles)
|
|
1607
|
+
|
|
1608
|
+
images_to_delete = ContentImage.objects.filter(
|
|
1609
|
+
content_type=taxon_profile_ctype,
|
|
1610
|
+
object_id__in=all_taxon_profiles.values_list('id', flat=True)
|
|
1611
|
+
)
|
|
1612
|
+
|
|
1613
|
+
deleted_count = images_to_delete.count()
|
|
1614
|
+
images_to_delete.delete()
|
|
1615
|
+
|
|
1616
|
+
context = self.get_context_data(**self.kwargs)
|
|
1617
|
+
context['deleted_count'] = deleted_count
|
|
1618
|
+
context['success'] = True
|
|
1619
|
+
|
|
1620
|
+
return self.render_to_response(context)
|
|
@@ -260,6 +260,7 @@ class TaxonProfilesZipImporter(GenericContentZipImporter):
|
|
|
260
260
|
|
|
261
261
|
|
|
262
262
|
def validate_external_media(self, url, col_letter, row_index):
|
|
263
|
+
|
|
263
264
|
validator = URLValidator()
|
|
264
265
|
|
|
265
266
|
is_valid = True
|
|
@@ -566,6 +567,9 @@ class TaxonProfilesZipImporter(GenericContentZipImporter):
|
|
|
566
567
|
elif column_type == ColumnType.EXTERNAL_MEDIA.value:
|
|
567
568
|
|
|
568
569
|
external_media_url = cell_value
|
|
570
|
+
|
|
571
|
+
if not external_media_url:
|
|
572
|
+
continue
|
|
569
573
|
|
|
570
574
|
external_media_data = self.get_external_media_data_from_external_media_sheet(external_media_url)
|
|
571
575
|
|
app_kit/forms.py
CHANGED
|
@@ -18,6 +18,8 @@ from app_kit.appbuilder.AppBuilderBase import AppBuilderBase
|
|
|
18
18
|
from taxonomy.models import MetaVernacularNames
|
|
19
19
|
from taxonomy.lazy import LazyTaxon
|
|
20
20
|
|
|
21
|
+
from content_licencing.mixins import LicencingFormMixin
|
|
22
|
+
|
|
21
23
|
import base64, math, uuid
|
|
22
24
|
|
|
23
25
|
from .definitions import TEXT_LENGTH_RESTRICTIONS
|
|
@@ -130,7 +132,13 @@ class AddLanguageForm(forms.Form):
|
|
|
130
132
|
'''
|
|
131
133
|
from localcosmos_server.forms import (ManageContentImageForm, ManageContentImageWithTextForm,
|
|
132
134
|
ManageLocalizedContentImageForm, OptionalContentImageForm)
|
|
135
|
+
|
|
136
|
+
class ManageContentLicenceForm(LicencingFormMixin):
|
|
137
|
+
content_field = None
|
|
133
138
|
|
|
139
|
+
def __init__(self, content_field, *args, **kwargs):
|
|
140
|
+
self.content_field = content_field
|
|
141
|
+
super().__init__(*args, **kwargs)
|
|
134
142
|
|
|
135
143
|
class GenericContentOptionsForm(forms.Form):
|
|
136
144
|
|
|
Binary file
|