diff --git a/src/app/submission/sections/cc-license/submission-section-cc-licenses.component.html b/src/app/submission/sections/cc-license/submission-section-cc-licenses.component.html index 0f79fa6a1dd..b59fddc846e 100644 --- a/src/app/submission/sections/cc-license/submission-section-cc-licenses.component.html +++ b/src/app/submission/sections/cc-license/submission-section-cc-licenses.component.html @@ -43,7 +43,7 @@
- {{ field.label }} + {{ field.label | translate }}
@@ -86,7 +86,7 @@
- {{ option.label }} + {{ option.label | translate }} {{ 'submission.sections.ccLicense.option.select' | translate }} @@ -97,7 +97,7 @@
@@ -108,10 +108,10 @@
class="d-flex flex-row m-1">
- {{ option.label }} + {{ option.label | translate }}
diff --git a/src/app/submission/sections/cc-license/submission-section-cc-licenses.component.scss b/src/app/submission/sections/cc-license/submission-section-cc-licenses.component.scss index 142cd82822a..eef804bac93 100644 --- a/src/app/submission/sections/cc-license/submission-section-cc-licenses.component.scss +++ b/src/app/submission/sections/cc-license/submission-section-cc-licenses.component.scss @@ -3,7 +3,12 @@ } .ccLicense-select { - width: fit-content; + width: 400px; + max-width: 100%; + + ngb-dropdown, .input-group, input.form-control { + width: 100%; + } } .scrollable-menu { diff --git a/src/app/submission/sections/cc-license/submission-section-cc-licenses.component.spec.ts b/src/app/submission/sections/cc-license/submission-section-cc-licenses.component.spec.ts index e59cf69ca48..268f08aaf4e 100644 --- a/src/app/submission/sections/cc-license/submission-section-cc-licenses.component.spec.ts +++ b/src/app/submission/sections/cc-license/submission-section-cc-licenses.component.spec.ts @@ -1,7 +1,7 @@ import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; import { SubmissionSectionCcLicensesComponent } from './submission-section-cc-licenses.component'; import { SUBMISSION_CC_LICENSE } from '../../../core/submission/models/submission-cc-licence.resource-type'; -import { of as observableOf } from 'rxjs'; +import { of as observableOf, Subject } from 'rxjs'; import { SubmissionCcLicenseDataService } from '../../../core/submission/submission-cc-license-data.service'; import { DebugElement } from '@angular/core'; import { By } from '@angular/platform-browser'; @@ -139,9 +139,11 @@ describe('SubmissionSectionCcLicensesComponent', () => { getCcLicenseLink: observableOf('test cc license link'), }); + let sectionState$: Subject; + const sectionService = { getSectionState: () => { - return observableOf({}); + return sectionState$; }, setSectionStatus: () => undefined, updateSectionData: (submissionId, sectionId, updatedData) => { @@ -186,6 +188,7 @@ describe('SubmissionSectionCcLicensesComponent', () => { })); beforeEach(() => { + sectionState$ = new Subject(); fixture = TestBed.createComponent(SubmissionSectionCcLicensesComponent); component = fixture.componentInstance; de = fixture.debugElement; @@ -281,4 +284,168 @@ describe('SubmissionSectionCcLicensesComponent', () => { }); }); }); + + describe('when a license uri is stored', () => { + + const enumOption = (id: string) => ({ id, label: id, description: id }); + const _links = { self: { href: 'test link' } }; + const cc30: SubmissionCcLicence = { + id: 'cc30', + type: SUBMISSION_CC_LICENSE, + name: 'Creative Commons License (3.0)', + fields: [ + { id: 'commercial', label: 'commercial', description: '', enums: [enumOption('y'), enumOption('n')] }, + { id: 'derivatives', label: 'derivatives', description: '', enums: [enumOption('y'), enumOption('sa'), enumOption('n')] }, + { id: 'jurisdiction', label: 'jurisdiction', description: '', enums: [enumOption(''), enumOption('us')] }, + ], + _links, + }; + const cc40: SubmissionCcLicence = { + id: 'cc40', + type: SUBMISSION_CC_LICENSE, + name: 'Creative Commons License (4.0)', + fields: [ + { id: 'commercial', label: 'commercial', description: '', enums: [enumOption('y'), enumOption('n')] }, + { id: 'derivatives', label: 'derivatives', description: '', enums: [enumOption('y'), enumOption('sa'), enumOption('n')] }, + ], + _links, + }; + const cc0: SubmissionCcLicence = { id: 'cc0', type: SUBMISSION_CC_LICENSE, name: 'CC0 1.0 Universal', fields: [], _links }; + const mark: SubmissionCcLicence = { id: 'mark', type: SUBMISSION_CC_LICENSE, name: 'Public Domain Mark 1.0 Universal', fields: [], _links }; + const sampling: SubmissionCcLicence = { + id: 'sampling', + type: SUBMISSION_CC_LICENSE, + name: 'Creative Commons Sampling License (1.0)', + fields: [ + { id: 'sampling', label: 'sampling', description: '', enums: [enumOption('sampling'), enumOption('samplingplus'), enumOption('ncsamplingplus')] }, + ], + _links, + }; + + const restore = (uri: string) => { + component.submissionCcLicenses = [cc30, cc40, cc0, mark, sampling]; + component.sectionData.data = { uri }; + (component as any).restoreSelectionFromStoredUri(); + }; + + it('should restore a CC 4.0 license with its options', () => { + restore('https://creativecommons.org/licenses/by-nc-sa/4.0/'); + expect(component.data.ccLicense.id).toEqual('cc40'); + expect(component.data.ccLicense.fields.commercial.id).toEqual('n'); + expect(component.data.ccLicense.fields.derivatives.id).toEqual('sa'); + expect(component.data.uri).toEqual('https://creativecommons.org/licenses/by-nc-sa/4.0/'); + }); + + it('should restore a CC 3.0 license with its jurisdiction', () => { + restore('https://creativecommons.org/licenses/by-nd/3.0/us/'); + expect(component.data.ccLicense.id).toEqual('cc30'); + expect(component.data.ccLicense.fields.commercial.id).toEqual('y'); + expect(component.data.ccLicense.fields.derivatives.id).toEqual('n'); + expect(component.data.ccLicense.fields.jurisdiction.id).toEqual('us'); + }); + + it('should restore a Sampling license with its option', () => { + restore('https://creativecommons.org/licenses/nc-sampling+/1.0/'); + expect(component.data.ccLicense.id).toEqual('sampling'); + expect(component.data.ccLicense.fields.sampling.id).toEqual('ncsamplingplus'); + }); + + it('should distinguish public domain licenses with the same version', () => { + restore('https://creativecommons.org/publicdomain/mark/1.0/'); + expect(component.data.ccLicense.id).toEqual('mark'); + restore('https://creativecommons.org/publicdomain/zero/1.0/'); + expect(component.data.ccLicense.id).toEqual('cc0'); + }); + + it('should leave an unknown uri untouched', () => { + restore('https://example.org/my-license'); + expect(component.data.ccLicense).toBeUndefined(); + expect(component.accepted).toBeTrue(); + }); + + it('should not override an existing selection', () => { + component.submissionCcLicenses = [cc30, cc40, cc0, mark]; + component.sectionData.data = { uri: 'https://creativecommons.org/licenses/by/4.0/', ccLicense: { id: 'cc0', fields: {} } }; + (component as any).restoreSelectionFromStoredUri(); + expect(component.data.ccLicense.id).toEqual('cc0'); + }); + }); + + describe('when the section state changes', () => { + const uri = 'https://creativecommons.org/licenses/by/4.0/'; + + beforeEach(() => { + operationsBuilder.add.calls.reset(); + operationsBuilder.remove.calls.reset(); + }); + + it('should queue a remove when the stored license is no longer accepted', () => { + component.sectionData.data = { uri }; + sectionState$.next({ data: { uri, accepted: false } }); + expect(operationsBuilder.remove).toHaveBeenCalledTimes(1); + }); + + it('should queue the stored license again when it is unticked and ticked again before saving', () => { + component.sectionData.data = { uri }; + sectionState$.next({ data: { uri, accepted: false } }); + sectionState$.next({ data: { uri, accepted: true } }); + expect(operationsBuilder.remove).toHaveBeenCalledTimes(1); + expect(operationsBuilder.add).toHaveBeenCalledTimes(1); + }); + + describe('when the stored license is selected', () => { + const cc0Uri = 'https://creativecommons.org/publicdomain/zero/1.0/'; + const cc0License: SubmissionCcLicence = { + id: 'cc0', type: SUBMISSION_CC_LICENSE, name: 'CC0 1.0 Universal', fields: [], _links: submissionCcLicenses[0]._links, + }; + + beforeEach(() => { + component.submissionCcLicenses = [cc0License]; + submissionCcLicenseUrlDataService.getCcLicenseLink.calls.reset(); + }); + + it('should queue the stored uri right away when it is ticked again before saving', () => { + component.sectionData.data = { uri: cc0Uri, ccLicense: { id: 'cc0', fields: {} } }; + sectionState$.next({ data: { uri: cc0Uri, ccLicense: { id: 'cc0', fields: {} }, accepted: false } }); + sectionState$.next({ data: { uri: cc0Uri, ccLicense: { id: 'cc0', fields: {} }, accepted: true } }); + expect(operationsBuilder.add).toHaveBeenCalledOnceWith(jasmine.anything(), cc0Uri, false, true); + expect(submissionCcLicenseUrlDataService.getCcLicenseLink).not.toHaveBeenCalled(); + }); + + it('should look up the link when the selection differs from the stored uri', () => { + component.sectionData.data = { uri, ccLicense: { id: 'cc0', fields: {} } }; + sectionState$.next({ data: { uri, ccLicense: { id: 'cc0', fields: {} }, accepted: false } }); + sectionState$.next({ data: { uri, ccLicense: { id: 'cc0', fields: {} }, accepted: true } }); + expect(operationsBuilder.add).toHaveBeenCalledOnceWith(jasmine.anything(), 'test cc license link', false, true); + }); + }); + + it('should not queue a duplicate add for a license the server already stores', () => { + component.sectionData.data = { uri }; + sectionState$.next({ data: { uri, accepted: false } }); + // Section data from the server, without an accepted state, ends the pending remove + sectionState$.next({ data: { uri } }); + sectionState$.next({ data: { uri, accepted: true } }); + expect(operationsBuilder.add).not.toHaveBeenCalled(); + }); + + it('should not queue a remove for a save response of an already removed license', () => { + component.sectionData.data = { uri, accepted: false }; + // The REST API returns the cleared section data without an accepted state + sectionState$.next({ data: { uri: null, rights: null, file: null } }); + expect(operationsBuilder.remove).not.toHaveBeenCalled(); + expect(component.accepted).toBeFalse(); + }); + + it('should queue a single remove when another license is selected', () => { + // Like the store, only apply the update once it is emitted by the section state + let updatedData; + spyOn(sectionService, 'updateSectionData').and.callFake((submissionId, sectionId, data) => updatedData = data); + component.submissionCcLicenses = submissionCcLicenses; + component.sectionData.data = { uri }; + component.selectCcLicense(submissionCcLicenses[0]); + sectionState$.next({ data: updatedData }); + expect(operationsBuilder.remove).toHaveBeenCalledTimes(1); + }); + }); }); diff --git a/src/app/submission/sections/cc-license/submission-section-cc-licenses.component.ts b/src/app/submission/sections/cc-license/submission-section-cc-licenses.component.ts index b999db5d339..7fecf2f16f0 100644 --- a/src/app/submission/sections/cc-license/submission-section-cc-licenses.component.ts +++ b/src/app/submission/sections/cc-license/submission-section-cc-licenses.component.ts @@ -94,6 +94,12 @@ export class SubmissionSectionCcLicensesComponent extends SectionModelComponent */ private ccLicenseLinkTrigger$ = new Subject(); + /** + * Whether a remove of the stored uri is queued but not yet saved, so accepting the same license again + * has to queue an add instead of being skipped as unchanged. + */ + private uriRemoveQueued = false; + /** * The Creative Commons link saved in the workspace item. */ @@ -167,14 +173,16 @@ export class SubmissionSectionCcLicensesComponent extends SectionModelComponent return; } this.selectedCcLicense = ccLicense; - this.setAccepted(false); + // A single update, as separate updates would each be compared against stale section data this.updateSectionData({ ccLicense: { id: ccLicense.id, fields: {}, }, uri: undefined, + accepted: false, }); + this.updateSectionStatus(); // Trigger the debounced license link update this.ccLicenseLinkTrigger$.next(); } @@ -238,13 +246,10 @@ export class SubmissionSectionCcLicensesComponent extends SectionModelComponent * Get the link to the Creative Commons license corresponding with the selected options. */ getCcLicenseLink$(): Observable { - - if (!!this.storedCcLicenseLink) { - return observableOf(this.storedCcLicenseLink); - } if (!this.getSelectedCcLicense() || this.getSelectedCcLicense().fields.some( (field) => !this.getSelectedOption(this.getSelectedCcLicense(), field))) { - return undefined; + // Fall back to the stored link when the selection could not be (fully) restored from it + return this.storedCcLicenseLink ? observableOf(this.storedCcLicenseLink) : undefined; } const selectedCcLicense = this.getSelectedCcLicense(); return this.submissionCcLicenseUrlDataService.getCcLicenseLink( @@ -309,29 +314,49 @@ export class SubmissionSectionCcLicensesComponent extends SectionModelComponent }), map((sectionState) => sectionState.data as WorkspaceitemSectionCcLicenseObject), ).subscribe((data) => { - const wasAccepted = this.data.accepted; const wasUri = this.data.uri; + if (!hasValue(data.accepted)) { + // Section data from the REST API, so any queued patch has been saved + this.uriRemoveQueued = false; + } - // Only process if acceptance state actually changed - if (wasAccepted !== data.accepted && data.accepted !== undefined) { + // The REST API never returns the accepted state, so only changes made in this section (which always + // set it) should queue a patch. Otherwise a save response would queue a remove of an already removed uri. + if (hasValue(data.accepted) && this.data.accepted !== data.accepted) { const path = this.pathCombiner.getPath('uri'); - if (data.accepted && !wasAccepted) { + if (data.accepted && this.uriRemoveQueued && wasUri && this.isSelectionForUri(wasUri)) { + // The stored license is accepted again before its remove was saved. Queue it right away, as an add + // from the asynchronous link lookup below could miss a save that is already on its way. + this.operationsBuilder.add(path, wasUri, false, true); + this.uriRemoveQueued = false; + } else if (data.accepted) { // Only add URI if we're switching from not accepted to accepted const licenseLink$ = this.getCcLicenseLink$(); if (licenseLink$) { licenseLink$.pipe( take(1), - filter(link => !!link && link !== wasUri) // Only proceed if link exists and is different + // Only proceed if link exists and is different, or the stored one is about to be removed + filter(link => !!link && (this.uriRemoveQueued || link !== wasUri)) ).subscribe((link) => { this.operationsBuilder.add(path, link.toString(), false, true); + this.uriRemoveQueued = false; }); } - } else if (!data.accepted && wasAccepted && !!this.data.uri) { - // Only remove URI if we're switching from accepted to not accepted + } else if (this.data.uri) { this.operationsBuilder.remove(path); + this.uriRemoveQueued = true; } } - this.sectionData.data = data; + const previousCcLicense = this.data?.ccLicense; + this.sectionData.data = { + ...data, + // The REST API only returns the stored uri, so keep the (restored) selection when it is missing + ccLicense: data.ccLicense ?? previousCcLicense, + }; + // The selection is updated asynchronously, so recompute the link once it has landed in the store + if (JSON.stringify(previousCcLicense) !== JSON.stringify(this.data.ccLicense)) { + this.ccLicenseLinkTrigger$.next(); + } }), this.configService.findByPropertyName('cc.license.jurisdiction').pipe( getFirstCompletedRemoteData(), @@ -387,9 +412,120 @@ export class SubmissionSectionCcLicensesComponent extends SectionModelComponent (licenses) => { this.submissionCcLicenses = [...this.submissionCcLicenses, ...licenses]; this.isLoading = false; + this.restoreSelectionFromStoredUri(); this.ref.detectChanges(); }, ), ); } + + /** + * Rebuild the license selection (license + field answers) from the stored license uri. + * The REST API only returns the uri of a saved license, so without this the previously chosen + * license and options can't be shown or changed after the submission has been saved and reopened. + * Does not touch the uri or accepted state, as the stored license remains valid. + */ + private restoreSelectionFromStoredUri(): void { + if (hasValue(this.data?.ccLicense) || !this.storedCcLicenseLink) { + return; + } + const selection = this.getSelectionForUri(this.storedCcLicenseLink); + if (!selection) { + // Unknown uri, or the license might be on a page that hasn't been loaded yet + return; + } + this.selectedCcLicense = selection.license; + this.updateSectionData({ + ccLicense: { + id: selection.license.id, + fields: selection.fields, + }, + }); + } + + /** + * Whether the currently selected license and options are the ones the given license uri was issued for. + * @param uri the license uri + */ + private isSelectionForUri(uri: string): boolean { + const selection = this.getSelectionForUri(uri); + const selectedCcLicense = this.getSelectedCcLicense(); + if (!selection || !selectedCcLicense || selectedCcLicense.id !== selection.license.id) { + return false; + } + return (selection.license.fields ?? []).every((field) => { + const expected = selection.fields[field.id]; + const actual = this.getSelectedOption(selectedCcLicense, field); + return !!expected && !!actual && expected.id === actual.id; + }); + } + + /** + * Get the license and field answers that a given license uri was issued for. + * Returns null when the uri is not a known Creative Commons license or the license isn't loaded. + * @param uri the license uri + */ + private getSelectionForUri(uri: string): { license: SubmissionCcLicence, fields: { [fieldId: string]: Option } } { + const parsed = this.parseCcLicenseUri(uri); + if (!parsed) { + return null; + } + const license = this.submissionCcLicenses.find((ccLicense) => parsed.matches(ccLicense.name ?? '')); + if (!license) { + return null; + } + + const fields: { [fieldId: string]: Option } = {}; + (license.fields ?? []).forEach((field) => { + let optionId: string; + switch (field.id) { + case 'commercial': + optionId = parsed.unit.includes('nc') ? 'n' : 'y'; + break; + case 'derivatives': + optionId = parsed.unit.includes('nd') ? 'n' : parsed.unit.includes('sa') ? 'sa' : 'y'; + break; + case 'jurisdiction': + optionId = parsed.jurisdiction ?? ''; + break; + case 'sampling': + // e.g. 'nc-sampling+' -> 'ncsamplingplus' + optionId = parsed.unit.replace('-', '').replace('+', 'plus'); + break; + } + const option = field.enums?.find((o) => o.id === optionId); + if (option) { + fields[field.id] = option; + } + }); + return { license, fields }; + } + + /** + * Parse a creativecommons.org license uri into its unit (e.g. 'by-nc-nd', 'sampling+', 'zero'), version and + * optional jurisdiction, together with a matcher for the name of the corresponding license. + * Returns null for uris that don't match a known Creative Commons pattern. + * @param uri the stored license uri + */ + private parseCcLicenseUri(uri: string): { unit: string, jurisdiction?: string, matches: (name: string) => boolean } { + const standard = /creativecommons\.org\/licenses\/([a-z+-]+)\/(\d+\.\d+)(?:\/([a-z_-]+))?/i.exec(uri); + if (standard) { + const version = standard[2]; + return { + unit: standard[1].toLowerCase(), + jurisdiction: standard[3]?.toLowerCase(), + matches: (name) => name.includes(`(${version})`), + }; + } + const publicDomain = /creativecommons\.org\/publicdomain\/(zero|mark|certification)\//i.exec(uri); + if (publicDomain) { + const unit = publicDomain[1].toLowerCase(); + const namePart = { zero: 'cc0', mark: 'mark', certification: 'certification' }[unit]; + return { + unit, + matches: (name) => name.toLowerCase().includes(namePart), + }; + } + return null; + } } diff --git a/src/assets/i18n/cs.json5 b/src/assets/i18n/cs.json5 index b9b7081b238..83e51685f5b 100644 --- a/src/assets/i18n/cs.json5 +++ b/src/assets/i18n/cs.json5 @@ -6948,6 +6948,81 @@ // "submission.sections.ccLicense.confirmation": "I grant the license above", "submission.sections.ccLicense.confirmation": "Uděluji výše uvedenou licenci", + // "cc-license.commercial.label": "Allow commercial uses of your work?", + "cc-license.commercial.label": "Povolit komerční užití vašeho díla?", + + // "cc-license.commercial.hint": "Choose whether others may use your work for commercial purposes.", + "cc-license.commercial.hint": "Zvolte, zda mohou ostatní užívat vaše dílo pro komerční účely.", + + // "cc-license.commercial.option.yes.label": "Yes", + "cc-license.commercial.option.yes.label": "Ano", + + // "cc-license.commercial.option.yes.hint": "Visitors may copy, distribute, display and perform your Submission for any purpose, including commercial purposes.", + "cc-license.commercial.option.yes.hint": "Návštěvníci mohou váš příspěvek kopírovat, šířit, vystavovat a předvádět za jakýmkoli účelem, včetně komerčního.", + + // "cc-license.commercial.option.no.label": "No", + "cc-license.commercial.option.no.label": "Ne", + + // "cc-license.commercial.option.no.hint": "Visitors may copy, distribute, display and perform your Submission, but only for non-commercial purposes.", + "cc-license.commercial.option.no.hint": "Návštěvníci mohou váš příspěvek kopírovat, šířit, vystavovat a předvádět, ale pouze pro nekomerční účely.", + + // "cc-license.derivatives.label": "Allow modifications of your work?", + "cc-license.derivatives.label": "Povolit úpravy vašeho díla?", + + // "cc-license.derivatives.hint": "Choose whether others may create derivative works based on your Submission.", + "cc-license.derivatives.hint": "Zvolte, zda mohou ostatní vytvářet odvozená díla na základě vašeho příspěvku.", + + // "cc-license.derivatives.option.yes.label": "Yes", + "cc-license.derivatives.option.yes.label": "Ano", + + // "cc-license.derivatives.option.yes.hint": "Visitors may copy, distribute, display, perform and modify your Submission in any way.", + "cc-license.derivatives.option.yes.hint": "Návštěvníci mohou váš příspěvek kopírovat, šířit, vystavovat, předvádět a jakkoli upravovat.", + + // "cc-license.derivatives.option.sharealike.label": "ShareAlike", + "cc-license.derivatives.option.sharealike.label": "Ano, se zachováním licence (ShareAlike)", + + // "cc-license.derivatives.option.sharealike.hint": "Visitors are permitted to copy, display, perform and modify your Submission, as long as they distribute the modified version on similar use terms.", + "cc-license.derivatives.option.sharealike.hint": "Návštěvníci mohou váš příspěvek kopírovat, vystavovat, předvádět a upravovat, pokud upravenou verzi šíří za stejných licenčních podmínek.", + + // "cc-license.derivatives.option.no.label": "No", + "cc-license.derivatives.option.no.label": "Ne", + + // "cc-license.derivatives.option.no.hint": "Visitors are only permitted to copy and distribute unaltered versions of your Submission.", + "cc-license.derivatives.option.no.hint": "Návštěvníci mohou váš příspěvek pouze kopírovat a šířit v nezměněné podobě.", + + // "cc-license.jurisdiction.label": "Jurisdiction", + "cc-license.jurisdiction.label": "Jurisdikce", + + // "cc-license.jurisdiction.hint": "Select the legal jurisdiction for your Creative Commons license, or leave as the default international license.", + "cc-license.jurisdiction.hint": "Vyberte právní jurisdikci licence Creative Commons, nebo ponechte výchozí mezinárodní licenci.", + + // "cc-license.jurisdiction.option.unported.label": "Generic (Unported)", + "cc-license.jurisdiction.option.unported.label": "Obecná (mezinárodní)", + + // "cc-license.sampling.label": "Which Sampling license would you like to use?", + "cc-license.sampling.label": "Kterou licenci Sampling chcete použít?", + + // "cc-license.sampling.hint": "Choose which kind of sampling, remixing or mash-up of your work you allow.", + "cc-license.sampling.hint": "Zvolte, jaký druh samplování, remixování nebo mash-upu vašeho díla povolujete.", + + // "cc-license.sampling.option.sampling.label": "Sampling", + "cc-license.sampling.option.sampling.label": "Sampling", + + // "cc-license.sampling.option.sampling.hint": "Visitors may sample, remix or transform parts of your work for any purpose other than advertising, but may not copy or distribute the entire work.", + "cc-license.sampling.option.sampling.hint": "Návštěvníci mohou části vašeho díla samplovat, remixovat nebo upravovat za jakýmkoli účelem kromě reklamy, ale nesmí kopírovat ani šířit celé dílo.", + + // "cc-license.sampling.option.samplingplus.label": "Sampling Plus", + "cc-license.sampling.option.samplingplus.label": "Sampling Plus", + + // "cc-license.sampling.option.samplingplus.hint": "Visitors may sample, remix or transform parts of your work for any purpose other than advertising, and may also copy and distribute the entire work for non-commercial purposes.", + "cc-license.sampling.option.samplingplus.hint": "Návštěvníci mohou části vašeho díla samplovat, remixovat nebo upravovat za jakýmkoli účelem kromě reklamy a celé dílo mohou kopírovat a šířit pro nekomerční účely.", + + // "cc-license.sampling.option.ncsamplingplus.label": "NonCommercial Sampling Plus", + "cc-license.sampling.option.ncsamplingplus.label": "Nekomerční Sampling Plus", + + // "cc-license.sampling.option.ncsamplingplus.hint": "Visitors may sample, remix or transform parts of your work, and copy and distribute the entire work, but only for non-commercial purposes.", + "cc-license.sampling.option.ncsamplingplus.hint": "Návštěvníci mohou části vašeho díla samplovat, remixovat nebo upravovat a celé dílo kopírovat a šířit, ale pouze pro nekomerční účely.", + // "submission.sections.general.add-more": "Add more", "submission.sections.general.add-more": "Přidat další", diff --git a/src/assets/i18n/en.json5 b/src/assets/i18n/en.json5 index e24bf4378ca..a38f69f43c2 100644 --- a/src/assets/i18n/en.json5 +++ b/src/assets/i18n/en.json5 @@ -6437,4 +6437,42 @@ "clarin-license-agreement-page.download-error": "An error occurred while downloading the file. Please try again later.", "footer.link.university": "University Library", + + "cc-license.commercial.label": "Allow commercial uses of your work?", + "cc-license.commercial.hint": "Choose whether others may use your work for commercial purposes.", + + "cc-license.commercial.option.yes.label": "Yes", + "cc-license.commercial.option.yes.hint": "Visitors may copy, distribute, display and perform your Submission for any purpose, including commercial purposes.", + + "cc-license.commercial.option.no.label": "No", + "cc-license.commercial.option.no.hint": "Visitors may copy, distribute, display and perform your Submission, but only for non-commercial purposes.", + + "cc-license.derivatives.label": "Allow modifications of your work?", + "cc-license.derivatives.hint": "Choose whether others may create derivative works based on your Submission.", + + "cc-license.derivatives.option.yes.label": "Yes", + "cc-license.derivatives.option.yes.hint": "Visitors may copy, distribute, display, perform and modify your Submission in any way.", + + "cc-license.derivatives.option.sharealike.label": "ShareAlike", + "cc-license.derivatives.option.sharealike.hint": "Visitors are permitted to copy, display, perform and modify your Submission, as long as they distribute the modified version on similar use terms.", + + "cc-license.derivatives.option.no.label": "No", + "cc-license.derivatives.option.no.hint": "Visitors are only permitted to copy and distribute unaltered versions of your Submission.", + + "cc-license.jurisdiction.label": "Jurisdiction", + "cc-license.jurisdiction.hint": "Select the legal jurisdiction for your Creative Commons license, or leave as the default international license.", + + "cc-license.jurisdiction.option.unported.label": "Generic (Unported)", + + "cc-license.sampling.label": "Which Sampling license would you like to use?", + "cc-license.sampling.hint": "Choose which kind of sampling, remixing or mash-up of your work you allow.", + + "cc-license.sampling.option.sampling.label": "Sampling", + "cc-license.sampling.option.sampling.hint": "Visitors may sample, remix or transform parts of your work for any purpose other than advertising, but may not copy or distribute the entire work.", + + "cc-license.sampling.option.samplingplus.label": "Sampling Plus", + "cc-license.sampling.option.samplingplus.hint": "Visitors may sample, remix or transform parts of your work for any purpose other than advertising, and may also copy and distribute the entire work for non-commercial purposes.", + + "cc-license.sampling.option.ncsamplingplus.label": "NonCommercial Sampling Plus", + "cc-license.sampling.option.ncsamplingplus.hint": "Visitors may sample, remix or transform parts of your work, and copy and distribute the entire work, but only for non-commercial purposes.", }