diff --git a/api/funkwhale_api/common/mutations.py b/api/funkwhale_api/common/mutations.py index 25abaede2e979bffd24e24565e2ed2009d678274..c3e92c15b1579675a4ce243029ffe8a93614ab63 100644 --- a/api/funkwhale_api/common/mutations.py +++ b/api/funkwhale_api/common/mutations.py @@ -115,9 +115,13 @@ class UpdateMutationSerializer(serializers.ModelSerializer, MutationSerializer): # payload for field, attr in self.serialized_relations.items(): try: - data[field] = getattr(data[field], attr) + obj = data[field] except KeyError: continue + if obj is None: + data[field] = None + else: + data[field] = getattr(obj, attr) return data def create(self, validated_data): diff --git a/api/tests/music/test_mutations.py b/api/tests/music/test_mutations.py index d6b8223d4efe9397710227028703ca6573155983..062d00f98c6ce6739a5f8b0a2e98ab8f6017e8ad 100644 --- a/api/tests/music/test_mutations.py +++ b/api/tests/music/test_mutations.py @@ -13,6 +13,18 @@ def test_track_license_mutation(factories, now): assert track.license.code == "cc-by-sa-4.0" +def test_track_null_license_mutation(factories, now): + track = factories["music.Track"](license="cc-by-sa-4.0") + mutation = factories["common.Mutation"]( + type="update", target=track, payload={"license": None} + ) + licenses.load(licenses.LICENSES) + mutation.apply() + track.refresh_from_db() + + assert track.license is None + + def test_track_title_mutation(factories, now): track = factories["music.Track"](title="foo") mutation = factories["common.Mutation"]( diff --git a/front/src/components/library/EditForm.vue b/front/src/components/library/EditForm.vue index 0001081192048274ac2c8d3d55f5f809aa29093c..a2df96c0018dcf0d2f9bf5dc461be7cf872632c1 100644 --- a/front/src/components/library/EditForm.vue +++ b/front/src/components/library/EditForm.vue @@ -59,10 +59,28 @@ <label :for="fieldConfig.id">{{ fieldConfig.label }}</label> <input :type="fieldConfig.inputType || 'text'" v-model="values[fieldConfig.id]" :required="fieldConfig.required" :name="fieldConfig.id" :id="fieldConfig.id"> </template> + <template v-else-if="fieldConfig.type === 'license'"> + <label :for="fieldConfig.id">{{ fieldConfig.label }}</label> + + <select + ref="license" + v-model="values[fieldConfig.id]" + :required="fieldConfig.required" + :id="fieldConfig.id" + class="ui fluid search dropdown"> + <option :value="null"><translate translate-context="*/*/*">N/A</translate></option> + <option v-for="license in licenses" :key="license.code" :value="license.code">{{ license.name}}</option> + </select> + <button class="ui tiny basic left floated button" form="noop" @click.prevent="values[fieldConfig.id] = null"> + <i class="x icon"></i> + <translate translate-context="Content/Library/Button.Label">Clear</translate> + </button> + + </template> <div v-if="values[fieldConfig.id] != initialValues[fieldConfig.id]"> <button class="ui tiny basic right floated reset button" form="noop" @click.prevent="values[fieldConfig.id] = initialValues[fieldConfig.id]"> <i class="undo icon"></i> - <translate translate-context="Content/Library/Button.Label" :translate-params="{value: initialValues[fieldConfig.id]}">Reset to initial value: %{ value }</translate> + <translate translate-context="Content/Library/Button.Label" :translate-params="{value: initialValues[fieldConfig.id] || ''}">Reset to initial value: %{ value }</translate> </button> </div> </div> @@ -87,6 +105,7 @@ </template> <script> +import $ from 'jquery' import _ from '@/lodash' import axios from "axios" import EditList from '@/components/library/EditList' @@ -94,7 +113,7 @@ import EditCard from '@/components/library/EditCard' import edits from '@/edits' export default { - props: ["objectType", "object"], + props: ["objectType", "object", "licenses"], components: { EditList, EditCard @@ -113,6 +132,9 @@ export default { created () { this.setValues() }, + mounted() { + $(".ui.dropdown").dropdown({fullTextSearch: true}) + }, computed: { configs: edits.getConfigs, config: edits.getConfig, @@ -182,6 +204,15 @@ export default { } ) } + }, + watch: { + 'values.license' (newValue) { + if (newValue === null) { + $(this.$refs.license).dropdown('clear') + } else { + $(this.$refs.license).dropdown('set selected', newValue) + } + } } } </script> diff --git a/front/src/components/library/TrackEdit.vue b/front/src/components/library/TrackEdit.vue index 7e26d1df10595cbb229f68b58350c715431c6c27..32b3a627ac6960903cf2a4e2acac0f6f463413d8 100644 --- a/front/src/components/library/TrackEdit.vue +++ b/front/src/components/library/TrackEdit.vue @@ -6,7 +6,12 @@ <translate v-if="canEdit" key="1" translate-context="Content/*/Title">Edit this track</translate> <translate v-else key="2" translate-context="Content/*/Title">Suggest an edit on this track</translate> </h2> - <edit-form :object-type="objectType" :object="object" :can-edit="canEdit"></edit-form> + <edit-form + v-if="!isLoadingLicenses" + :object-type="objectType" + :object="object" + :can-edit="canEdit" + :licenses="licenses"></edit-form> </div> </section> </template> @@ -19,12 +24,27 @@ export default { props: ["objectType", "object", "libraries"], data() { return { - id: this.object.id + id: this.object.id, + isLoadingLicenses: false, + licenses: [] } }, components: { EditForm }, + created () { + this.fetchLicenses() + }, + methods: { + fetchLicenses () { + let self = this + self.isLoadingLicenses = true + axios.get('licenses/').then((response) => { + self.isLoadingLicenses = false + self.licenses = response.data.results + }) + } + }, computed: { canEdit () { return true diff --git a/front/src/edits.js b/front/src/edits.js index ccc9c16b39ee27a357ba5f6545228fc1be900697..4e6a85c737f21a599f54112e17c8805509f5aa64 100644 --- a/front/src/edits.js +++ b/front/src/edits.js @@ -12,10 +12,10 @@ export default { }, { id: 'license', - type: 'text', + type: 'license', required: false, label: this.$pgettext('Content/*/*/Noun', 'License'), - getValue: (obj) => { return obj.license } + getValue: (obj) => { return obj.license }, }, { id: 'position',