Skip to content

Commit

Permalink
fix: disable editing Contributor attributes from UI
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
asuworks authored and alee committed Jun 10, 2024
1 parent 52b8e58 commit bb25c37
Show file tree
Hide file tree
Showing 4 changed files with 20 additions and 39 deletions.
32 changes: 7 additions & 25 deletions django/library/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand All @@ -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
Expand All @@ -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
Expand Down Expand Up @@ -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:
Expand Down Expand Up @@ -211,7 +194,6 @@ class Meta:
"json_affiliations",
"primary_json_affiliation_name",
"profile_url",
"mutable",
)


Expand Down
3 changes: 3 additions & 0 deletions frontend/src/components/releaseEditor/ContributorAddModal.vue
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@
<ContributorEditForm
id="add-contributor-form"
:show-custom-input="showCustomInput"
:disable-edit-form="disableEditForm"
ref="editFormRef"
@success="() => editContributorModal?.hide()"
/>
Expand Down Expand Up @@ -83,6 +84,7 @@ const editFormRef = ref<InstanceType<typeof ContributorEditForm> | null>(null);
const contributorSearchRef = ref<InstanceType<typeof ContributorSearch> | null>(null);
const showCustomInput = ref(false);
const disableEditForm = ref(true);
function populateFromContributor(contributor: Contributor) {
if (editFormRef.value) {
Expand All @@ -93,6 +95,7 @@ function populateFromContributor(contributor: Contributor) {
function createNewContributor() {
resetForm();
if (editFormRef.value) {
disableEditForm.value = false;
showCustomInput.value = true;
}
}
Expand Down
23 changes: 10 additions & 13 deletions frontend/src/components/releaseEditor/ContributorEditForm.vue
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
<div style="position: relative">
<!-- Container element with relative positioning -->
<div
v-if="fromUser"
v-if="disableEditForm"
class="position-absolute top-0 start-0 w-100 h-100 bg-light opacity-50"
style="z-index: 3"
></div>
Expand All @@ -44,15 +44,15 @@
name="givenName"
label="First Name"
required
:disabled="fromUser"
:disabled="disableEditForm"
/>
</div>
<div class="col-3 pe-0">
<TextField
class="mb-3"
name="middleName"
label="Middle Name"
:disabled="fromUser"
:disabled="disableEditForm"
/>
</div>
<div class="col-5">
Expand All @@ -61,7 +61,7 @@
name="familyName"
label="Last Name"
required
:disabled="fromUser"
:disabled="disableEditForm"
/>
</div>
</div>
Expand All @@ -71,13 +71,13 @@
name="email"
label="Email"
required
:disabled="fromUser"
:disabled="disableEditForm"
/>
<ResearchOrgListField
name="jsonAffiliations"
placeholder="Type to find organizations"
label="Affiliations"
:disabled="fromUser"
:disabled="disableEditForm"
/>
</div>
</div>
Expand All @@ -93,7 +93,7 @@
placeholder="Add contributor's role(s)"
multiple
required
:disabled="fromUser"
:disabled="disableEditForm"
/>
<CheckboxField name="includeInCitation" label="Include in Citation?" />
<FormAlert :validation-errors="Object.values(errors)" :server-errors="serverErrors" />
Expand Down Expand Up @@ -139,11 +139,13 @@ const props = withDefaults(
defineProps<{
id: string;
showCustomInput?: boolean;
disableEditForm?: boolean;
contributor?: ReleaseContributor;
onSuccess?: () => void;
}>(),
{
showCustomInput: false,
disableEditForm: true,
}
);
Expand All @@ -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",
Expand Down Expand Up @@ -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<typeof schema>;
Expand All @@ -215,7 +216,6 @@ const initialValues: ContributorFields = {
user: null,
affiliations: [],
jsonAffiliations: [],
mutable: true,
roles: [],
includeInCitation: true,
};
Expand Down Expand Up @@ -266,8 +266,6 @@ const { errors, handleSubmit, handleReset, values, setValues } = useForm<Contrib
const isLoading = ref(false);
const isPerson = computed(() => 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;
Expand All @@ -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,
Expand Down
1 change: 0 additions & 1 deletion frontend/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,6 @@ export interface ReleaseContributor {
export interface Contributor {
affiliations: any[];
jsonAffiliations: any[];
mutable: boolean;
primaryAffiliationName: string;
primaryJsonAffiliationName: string;
email: string;
Expand Down

0 comments on commit bb25c37

Please sign in to comment.