From bb25c37f8f8f195e5db94ab7e3c6401ba8ba5262 Mon Sep 17 00:00:00 2001 From: Anton Suharev Date: Thu, 30 May 2024 17:35:09 -0700 Subject: [PATCH] fix: disable editing Contributor attributes from UI Contributors can be created from: - existing Contributor - existing User - manually Contributor attributes cannot be edited from UI. Only ReleaseContributor attributes (index, is_citable and roles) can be modified from UI. --- django/library/serializers.py | 32 ++++--------------- .../releaseEditor/ContributorAddModal.vue | 3 ++ .../releaseEditor/ContributorEditForm.vue | 23 ++++++------- frontend/src/types.ts | 1 - 4 files changed, 20 insertions(+), 39 deletions(-) diff --git a/django/library/serializers.py b/django/library/serializers.py index a70fb751c..c844a3a1a 100644 --- a/django/library/serializers.py +++ b/django/library/serializers.py @@ -69,19 +69,16 @@ class ContributorSerializer(serializers.ModelSerializer): json_affiliations = models.JSONField( default=list, help_text=_("JSON-LD list of affiliated institutions") ) - mutable = serializers.SerializerMethodField(method_name="is_mutable") profile_url = serializers.SerializerMethodField() - def is_mutable(self, obj): - # FIXME: determine mutability based on incoming data without need for db queries - return obj.user_id or obj.id is not None - # return self._is_exclusive_to_one_codebase(obj) - def _trying_to_modify_attributes(self, instance, validated_data): try: + if "user_id" in validated_data: + # is user_id is present, then proxy user has been changed, skip the attribute check + return False + for key in [ - "user_id", "given_name", "middle_name", "family_name", @@ -96,7 +93,7 @@ def _trying_to_modify_attributes(self, instance, validated_data): logger.debug( "[key %s] has different incoming value %s != %s - Contributors can only be updated when exclusive to one codebase", key, - instance_dict[key], + getattr(instance, key), validated_data[key], ) return True @@ -106,14 +103,6 @@ def _trying_to_modify_attributes(self, instance, validated_data): logger.exception(e) return True - def _is_exclusive_to_one_codebase(self, obj): - return ( - Codebase.objects.filter( - releases__codebase_contributors__contributor=obj - ).count() - <= 1 - ) - def get_existing_contributor(self, validated_data): user = validated_data.get("user") username = user.get("username") if user else None @@ -161,14 +150,8 @@ def get_profile_url(self, instance): ) def update(self, instance, validated_data): - if not self.is_mutable(instance) and self._trying_to_modify_attributes( - instance, validated_data - ): - raise ValidationError( - _( - "Contributors can only be updated when they are exclusive to one codebase" - ) - ) + if self._trying_to_modify_attributes(instance, validated_data): + raise ValidationError("Updating contributors is not allowed.") # Server side validation: # if a contributor proxies a User, do not allow modification of its data. # POP following: @@ -211,7 +194,6 @@ class Meta: "json_affiliations", "primary_json_affiliation_name", "profile_url", - "mutable", ) diff --git a/frontend/src/components/releaseEditor/ContributorAddModal.vue b/frontend/src/components/releaseEditor/ContributorAddModal.vue index a73797b4a..f7d88e901 100644 --- a/frontend/src/components/releaseEditor/ContributorAddModal.vue +++ b/frontend/src/components/releaseEditor/ContributorAddModal.vue @@ -55,6 +55,7 @@ @@ -83,6 +84,7 @@ const editFormRef = ref | null>(null); const contributorSearchRef = ref | null>(null); const showCustomInput = ref(false); +const disableEditForm = ref(true); function populateFromContributor(contributor: Contributor) { if (editFormRef.value) { @@ -93,6 +95,7 @@ function populateFromContributor(contributor: Contributor) { function createNewContributor() { resetForm(); if (editFormRef.value) { + disableEditForm.value = false; showCustomInput.value = true; } } diff --git a/frontend/src/components/releaseEditor/ContributorEditForm.vue b/frontend/src/components/releaseEditor/ContributorEditForm.vue index d849348ed..ec61767d6 100644 --- a/frontend/src/components/releaseEditor/ContributorEditForm.vue +++ b/frontend/src/components/releaseEditor/ContributorEditForm.vue @@ -25,7 +25,7 @@
@@ -44,7 +44,7 @@ name="givenName" label="First Name" required - :disabled="fromUser" + :disabled="disableEditForm" />
@@ -52,7 +52,7 @@ class="mb-3" name="middleName" label="Middle Name" - :disabled="fromUser" + :disabled="disableEditForm" />
@@ -61,7 +61,7 @@ name="familyName" label="Last Name" required - :disabled="fromUser" + :disabled="disableEditForm" />
@@ -71,13 +71,13 @@ name="email" label="Email" required - :disabled="fromUser" + :disabled="disableEditForm" /> @@ -93,7 +93,7 @@ placeholder="Add contributor's role(s)" multiple required - :disabled="fromUser" + :disabled="disableEditForm" /> @@ -139,11 +139,13 @@ const props = withDefaults( defineProps<{ id: string; showCustomInput?: boolean; + disableEditForm?: boolean; contributor?: ReleaseContributor; onSuccess?: () => void; }>(), { showCustomInput: false, + disableEditForm: true, } ); @@ -152,7 +154,7 @@ const emit = defineEmits(["success"]); const { serverErrors: profileErrors, search } = useProfileAPI(); const store = useReleaseEditorStore(); - +const disableEditForm = computed(() => !!values.user || props.disableEditForm); const roleLookup = { author: "Author", publisher: "Publisher", @@ -203,7 +205,6 @@ const schema = yup.object().shape({ type: yup.string().oneOf(["person", "organization"]).default("person"), roles: yup.array().of(yup.string()).min(1).label("Roles"), includeInCitation: yup.bool().required().label("Include in citation"), - mutable: yup.bool().label("Mutable"), }); type ContributorFields = yup.InferType; @@ -215,7 +216,6 @@ const initialValues: ContributorFields = { user: null, affiliations: [], jsonAffiliations: [], - mutable: true, roles: [], includeInCitation: true, }; @@ -266,8 +266,6 @@ const { errors, handleSubmit, handleReset, values, setValues } = useForm values.type === "person"); const hasName = computed(() => values.user || values.givenName); -// used to disable UI inputs if prefilled from existing user or contributor is not mutable (some other release uses the contributor) -const fromUser = computed(() => !!values.user || !values.mutable); function populateFromReleaseContributor(releaseContributor: ReleaseContributor) { const user = releaseContributor.contributor.user; @@ -288,7 +286,6 @@ function populateFromReleaseContributor(releaseContributor: ReleaseContributor) middleName: releaseContributor.contributor.middleName, // affiliations: releaseContributor.contributor.affiliations, jsonAffiliations: releaseContributor.contributor.jsonAffiliations, - mutable: releaseContributor.contributor.mutable, type: releaseContributor.contributor.type, roles: releaseContributor.roles, includeInCitation: releaseContributor.includeInCitation, diff --git a/frontend/src/types.ts b/frontend/src/types.ts index 642241c21..2f7b2bc24 100644 --- a/frontend/src/types.ts +++ b/frontend/src/types.ts @@ -139,7 +139,6 @@ export interface ReleaseContributor { export interface Contributor { affiliations: any[]; jsonAffiliations: any[]; - mutable: boolean; primaryAffiliationName: string; primaryJsonAffiliationName: string; email: string;