From 08ec7fc9d4a7adc040a7259387c7afe8485b3348 Mon Sep 17 00:00:00 2001 From: Ihor Sokhan Date: Thu, 16 Oct 2025 17:13:54 +0300 Subject: [PATCH 1/2] added systems tags management in admin for nodes and preprints --- admin/nodes/forms.py | 5 +++ admin/nodes/templatetags/node_extras.py | 5 +++ admin/nodes/urls.py | 1 + admin/nodes/views.py | 17 +++++++++ admin/preprints/urls.py | 1 + admin/preprints/views.py | 8 ++++- admin/templates/nodes/add_system_tags.html | 42 ++++++++++++++++++++++ admin/templates/nodes/node.html | 1 + admin/templates/preprints/preprint.html | 1 + admin/users/forms.py | 4 --- admin/users/views.py | 16 ++------- 11 files changed, 83 insertions(+), 18 deletions(-) create mode 100644 admin/nodes/forms.py create mode 100644 admin/templates/nodes/add_system_tags.html diff --git a/admin/nodes/forms.py b/admin/nodes/forms.py new file mode 100644 index 00000000000..1b980aae804 --- /dev/null +++ b/admin/nodes/forms.py @@ -0,0 +1,5 @@ +from django import forms + + +class AddSystemTagForm(forms.Form): + system_tag_to_add = forms.CharField(label='system_tag_to_add', min_length=1, max_length=1024, required=True) diff --git a/admin/nodes/templatetags/node_extras.py b/admin/nodes/templatetags/node_extras.py index 4fb9606f22e..be53d2f7506 100644 --- a/admin/nodes/templatetags/node_extras.py +++ b/admin/nodes/templatetags/node_extras.py @@ -88,3 +88,8 @@ def get_spam_status(resource): return mark_safe('Spam') elif resource.spam_status == SpamStatus.HAM: return mark_safe('Ham') + + +@register.filter +def get_class_name(resource): + return resource.__class__.__name__.lower() diff --git a/admin/nodes/urls.py b/admin/nodes/urls.py index 1d5f6e0bac9..c484ebff310 100644 --- a/admin/nodes/urls.py +++ b/admin/nodes/urls.py @@ -46,4 +46,5 @@ re_path(r'^(?P[a-z0-9]+)/update_moderation_state/$', views.NodeUpdateModerationStateView.as_view(), name='node-update-mod-state'), re_path(r'^(?P[a-z0-9]+)/resync_datacite/$', views.NodeResyncDataCiteView.as_view(), name='resync-datacite'), re_path(r'^(?P[a-z0-9]+)/revert/$', views.NodeRevertToDraft.as_view(), name='revert-to-draft'), + re_path(r'^(?P[a-z0-9]+)/system_tags/add/$', views.NodeAddSystemTag.as_view(), name='add-system-tag'), ] diff --git a/admin/nodes/views.py b/admin/nodes/views.py index 74321c8f908..ff64c95fd2c 100644 --- a/admin/nodes/views.py +++ b/admin/nodes/views.py @@ -22,6 +22,7 @@ from admin.base.views import GuidView from admin.base.forms import GuidForm from admin.notifications.views import detect_duplicate_notifications, delete_selected_notifications +from admin.nodes.forms import AddSystemTagForm from api.share.utils import update_share from api.caching.tasks import update_storage_usage_cache @@ -812,3 +813,19 @@ def post(self, request, *args, **kwargs): registration = self.get_object() registration.to_draft() return redirect(self.get_success_url()) + + +class NodeAddSystemTag(NodeMixin, FormView): + """ Allows authorized users to add system tags to a node. + """ + permission_required = 'osf.change_node' + raise_exception = True + form_class = AddSystemTagForm + + def form_valid(self, form): + resource = self.get_object() + system_tag_to_add = form.cleaned_data['system_tag_to_add'] + resource.add_system_tag(system_tag_to_add) + resource.save() + + return super().form_valid(form) diff --git a/admin/preprints/urls.py b/admin/preprints/urls.py index 4ab9bd33939..1e270fd8f3b 100644 --- a/admin/preprints/urls.py +++ b/admin/preprints/urls.py @@ -29,4 +29,5 @@ re_path(r'^(?P\w+)/resync_crossref/$', views.PreprintResyncCrossRefView.as_view(), name='resync-crossref'), re_path(r'^(?P\w+)/make_published/$', views.PreprintMakePublishedView.as_view(), name='make-published'), re_path(r'^(?P\w+)/unwithdraw/$', views.PreprintUnwithdrawView.as_view(), name='unwithdraw'), + re_path(r'^(?P\w+)/system_tags/add/$', views.PreprintAddSystemTag.as_view(), name='add-system-tag'), ] diff --git a/admin/preprints/views.py b/admin/preprints/views.py index ef7d1860e76..f497bc34c10 100644 --- a/admin/preprints/views.py +++ b/admin/preprints/views.py @@ -15,7 +15,7 @@ from admin.base.views import GuidView from admin.base.forms import GuidForm -from admin.nodes.views import NodeRemoveContributorView +from admin.nodes.views import NodeRemoveContributorView, NodeAddSystemTag from admin.preprints.forms import ChangeProviderForm, MachineStateForm from api.share.utils import update_share @@ -677,3 +677,9 @@ def post(self, request, *args, **kwargs): preprint.save() return redirect(self.get_success_url()) + + +class PreprintAddSystemTag(PreprintMixin, NodeAddSystemTag): + """ Allows authorized users to add system tags to a preprint. + """ + permission_required = 'osf.change_preprint' diff --git a/admin/templates/nodes/add_system_tags.html b/admin/templates/nodes/add_system_tags.html new file mode 100644 index 00000000000..255f206ba39 --- /dev/null +++ b/admin/templates/nodes/add_system_tags.html @@ -0,0 +1,42 @@ +{% load node_extras %} + + System tags + + {% for system_tag in resource.system_tags %} + {{ system_tag }}{% if not forloop.last %}, {% endif %} + {% endfor %} + + add system tag + + + + + diff --git a/admin/templates/nodes/node.html b/admin/templates/nodes/node.html index caf8bd5ebc6..27fc20a084b 100644 --- a/admin/templates/nodes/node.html +++ b/admin/templates/nodes/node.html @@ -97,6 +97,7 @@

{{ node.type|cut:'osf.'|title }}: {{ node.title }} Preprint: {{ preprint.title }} + {% include "nodes/add_system_tags.html" with resource=preprint %} {% include "preprints/contributors.html" with preprint=preprint %} {% include "nodes/spam_status.html" with resource=preprint %} {% include "preprints/withdraw_request.html" with preprint=preprint %} diff --git a/admin/users/forms.py b/admin/users/forms.py index e64a648ff77..01327a4eb73 100644 --- a/admin/users/forms.py +++ b/admin/users/forms.py @@ -19,7 +19,3 @@ class UserSearchForm(forms.Form): class MergeUserForm(forms.Form): user_guid_to_be_merged = forms.CharField(label='user_guid_to_be_merged', min_length=5, max_length=5, required=True) # TODO: Move max to 6 when needed - - -class AddSystemTagForm(forms.Form): - system_tag_to_add = forms.CharField(label='system_tag_to_add', min_length=1, max_length=1024, required=True) diff --git a/admin/users/views.py b/admin/users/views.py index 48c77a2bd26..49317871472 100644 --- a/admin/users/views.py +++ b/admin/users/views.py @@ -42,9 +42,9 @@ from admin.users.forms import ( EmailResetForm, UserSearchForm, - MergeUserForm, - AddSystemTagForm + MergeUserForm ) +from admin.nodes.views import NodeAddSystemTag from admin.base.views import GuidView from api.users.services import send_password_reset_email from website.settings import DOMAIN @@ -383,20 +383,10 @@ def post(self, request, *args, **kwargs): return redirect(self.get_success_url()) -class UserAddSystemTag(UserMixin, FormView): +class UserAddSystemTag(UserMixin, NodeAddSystemTag): """ Allows authorized users to add system tags to a user. """ permission_required = 'osf.change_osfuser' - raise_exception = True - form_class = AddSystemTagForm - - def form_valid(self, form): - user = self.get_object() - system_tag_to_add = form.cleaned_data['system_tag_to_add'] - user.add_system_tag(system_tag_to_add) - user.save() - - return super().form_valid(form) class UserMergeAccounts(UserMixin, FormView): From 458a5993d41088e646d0cf79d65556c9f0554198 Mon Sep 17 00:00:00 2001 From: Ihor Sokhan Date: Fri, 17 Oct 2025 19:44:01 +0300 Subject: [PATCH 2/2] added the ability to remove tags from nodes/registrations/preprints/users --- admin/nodes/urls.py | 1 + admin/nodes/views.py | 13 ++++++++ admin/preprints/urls.py | 1 + admin/preprints/views.py | 8 ++++- admin/templates/nodes/add_system_tags.html | 27 +++++++++++++---- admin/templates/users/add_system_tags.html | 35 ---------------------- admin/templates/users/user.html | 2 +- admin/users/urls.py | 1 + admin/users/views.py | 8 ++++- osf/models/node.py | 25 +++++++++++----- osf/models/preprint.py | 25 +++++++++++----- osf/models/registrations.py | 6 +++- osf/models/user.py | 33 ++++++++++++++++++-- 13 files changed, 125 insertions(+), 60 deletions(-) delete mode 100644 admin/templates/users/add_system_tags.html diff --git a/admin/nodes/urls.py b/admin/nodes/urls.py index c484ebff310..5b0b84aaf79 100644 --- a/admin/nodes/urls.py +++ b/admin/nodes/urls.py @@ -47,4 +47,5 @@ re_path(r'^(?P[a-z0-9]+)/resync_datacite/$', views.NodeResyncDataCiteView.as_view(), name='resync-datacite'), re_path(r'^(?P[a-z0-9]+)/revert/$', views.NodeRevertToDraft.as_view(), name='revert-to-draft'), re_path(r'^(?P[a-z0-9]+)/system_tags/add/$', views.NodeAddSystemTag.as_view(), name='add-system-tag'), + re_path(r'^(?P[a-z0-9]+)/system_tags/(?P[a-z0-9]+)/remove/$', views.NodeRemoveSystemTag.as_view(), name='remove-system-tag'), ] diff --git a/admin/nodes/views.py b/admin/nodes/views.py index ff64c95fd2c..887c7eeed40 100644 --- a/admin/nodes/views.py +++ b/admin/nodes/views.py @@ -829,3 +829,16 @@ def form_valid(self, form): resource.save() return super().form_valid(form) + + +class NodeRemoveSystemTag(NodeMixin, View): + """ Allows authorized users to remove system tags from a node. + """ + permission_required = 'osf.change_node' + raise_exception = True + + def post(self, request, *args, **kwargs): + resource = self.get_object() + tag = resource.system_tags_objects.get(id=kwargs['tag_id']) + resource.remove_tag(tag.name, auth=request.user) + return redirect(self.get_success_url()) diff --git a/admin/preprints/urls.py b/admin/preprints/urls.py index 1e270fd8f3b..a0d384e29e2 100644 --- a/admin/preprints/urls.py +++ b/admin/preprints/urls.py @@ -30,4 +30,5 @@ re_path(r'^(?P\w+)/make_published/$', views.PreprintMakePublishedView.as_view(), name='make-published'), re_path(r'^(?P\w+)/unwithdraw/$', views.PreprintUnwithdrawView.as_view(), name='unwithdraw'), re_path(r'^(?P\w+)/system_tags/add/$', views.PreprintAddSystemTag.as_view(), name='add-system-tag'), + re_path(r'^(?P\w+)/system_tags/(?P[a-z0-9]+)/remove/$', views.PreprintRemoveSystemTag.as_view(), name='remove-system-tag'), ] diff --git a/admin/preprints/views.py b/admin/preprints/views.py index f497bc34c10..2bacea5ac38 100644 --- a/admin/preprints/views.py +++ b/admin/preprints/views.py @@ -15,7 +15,7 @@ from admin.base.views import GuidView from admin.base.forms import GuidForm -from admin.nodes.views import NodeRemoveContributorView, NodeAddSystemTag +from admin.nodes.views import NodeRemoveContributorView, NodeAddSystemTag, NodeRemoveSystemTag from admin.preprints.forms import ChangeProviderForm, MachineStateForm from api.share.utils import update_share @@ -683,3 +683,9 @@ class PreprintAddSystemTag(PreprintMixin, NodeAddSystemTag): """ Allows authorized users to add system tags to a preprint. """ permission_required = 'osf.change_preprint' + + +class PreprintRemoveSystemTag(PreprintMixin, NodeRemoveSystemTag): + """ Allows authorized users to remove system tags from a preprint. + """ + permission_required = 'osf.change_preprint' diff --git a/admin/templates/nodes/add_system_tags.html b/admin/templates/nodes/add_system_tags.html index 255f206ba39..e8deb10a9c1 100644 --- a/admin/templates/nodes/add_system_tags.html +++ b/admin/templates/nodes/add_system_tags.html @@ -2,16 +2,33 @@ System tags - {% for system_tag in resource.system_tags %} - {{ system_tag }}{% if not forloop.last %}, {% endif %} - {% endfor %} +
+ {% for system_tag in resource.system_tags_objects %} + {% if resource|get_class_name == 'node' or resource|get_class_name == 'registration' %} +
+ {% elif resource|get_class_name == 'preprint' %} + + {% else %} + + {% endif %} +
+ {% csrf_token %} + +
+
+ {% endfor %} +
+
- add system tag + Add system tag