diff --git a/src/components/OcCatalogForm/OcCatalogForm.vue b/src/components/OcCatalogForm/OcCatalogForm.vue index 059854689520b7ea28a29d4e05005a44f8a55e44..3752c53dd79bbe39f3f771a47db0bffedc06deb8 100644 --- a/src/components/OcCatalogForm/OcCatalogForm.vue +++ b/src/components/OcCatalogForm/OcCatalogForm.vue @@ -10,7 +10,7 @@ <OcCatalogSelect inputId="parentCatalog" :model-value="value" - @update:model-value="handleChange" + @update:model-value="updateParentCatalogValue($event, handleChange, resetField)" :community="community" :auth="auth" :invalid="!!errorMessage" @@ -21,6 +21,40 @@ </OcField> <Message v-if="errorMessage" severity="error">{{ errorMessage }}</Message> </Field> + <Field v-if="!edit" name="visibility" v-slot="{ value, errorMessage, handleChange }"> + <div class="flex gap-1 mb-2 items-center"> + <label for="visibility" class="required"> + {{ t('catalog.new.access') }} + </label> + <OcFairBadge :letter="false" size="small" :badges="['f', 'r']" :popover="true" /> + </div> + <Message severity="secondary" icon="fa fa-info" class="text-sm"> + <p class="italic mb-2">{{ t('catalog.new.accessHelp.1') }}</p> + <p class="mb-2">{{ t('catalog.new.accessHelp.2') }}</p> + <p>{{ t('catalog.new.accessHelp.3') }}</p> + <p class="mb-2">{{ t('catalog.new.accessHelp.4') }}</p> + <p class="mb-2">{{ t('catalog.new.accessHelp.5') }}</p> + <p class="mb-2">{{ t('catalog.new.accessHelp.6') }}</p> + </Message> + <SelectButton + inputId="visibility" + :model-value="value" + :options="visibilityOptions" + optionLabel="key" + optionValue="graph" + optionDisabled="disabled" + @update:model-value="handleChange" + :invalid="!!errorMessage" + required + > + <template #option="slotProps"> + <OcVisibilityIcon :visibility="slotProps.option.key" class="text-[0.6rem] -mr-1" /> + {{ t('resourceVisibility.' + slotProps.option.key) }} + </template> + </SelectButton> + <Message v-if="errorMessage" severity="error">{{ errorMessage }}</Message> + </Field> + <hr v-if="!edit" class="my-4"/> <Field name="title" v-slot="{ value, handleChange, errorMessage }"> <OcField for="title" :metadata="catalogMetadata.title"> <OcMultilingualField @@ -175,6 +209,7 @@ import DatePicker from 'primevue/datepicker' import InputText from 'primevue/inputtext' import Textarea from 'primevue/textarea' import Message from 'primevue/message' +import SelectButton from 'primevue/selectbutton' import OcField from '@/components/FormInputs/OcField/OcField.vue' import OcAgentAutocomplete from '@/components/FormInputs/OcAgentAutocomplete/OcAgentAutocomplete.vue' import OcConceptAutocomplete from '@/components/FormInputs/OcConceptAutocomplete/OcConceptAutocomplete.vue' @@ -185,9 +220,13 @@ import { toTypedSchema } from '@vee-validate/yup' import OcMultilingualField from '../FormInputs/OcMultilingualField/OcMultilingualField.vue' import OcMultipleField from '../FormInputs/OcMultipleField/OcMultipleField.vue' import type { PropType } from 'vue' +import { ref } from 'vue' +import { getVisibilityOptionsFor } from '@/helpers/resourceVisibility' +import { useAbility } from '@casl/vue' const { t } = useI18n() const { translateValue } = useTranslateValue() +const { can } = useAbility() const props = defineProps({ community: { @@ -227,17 +266,36 @@ const emit = defineEmits<{ cancel: [] }>() +const visibilityOptions = ref( + getVisibilityOptionsFor(can, props.community, undefined, props.userPrivateGraph) +) + let parentCatalog = yup .object<OcCatalogSummary>() .label(translateValue(catalogMetadata.parentCatalog.label)) +let visibility = yup + .string() + .matches( + new RegExp( + visibilityOptions.value + .filter((item) => !item.disabled) + .map((item) => item.key) + .join('|') + ), + 'Choosen option is disabled' + ) + .label(t('catalog.new.access')) + if (!props.edit) { parentCatalog = parentCatalog.required() + visibility = visibility.required() } const validationSchema = toTypedSchema( yup.object({ parentCatalog, + visibility, title: yup .mixed<LocalizedProperty<string>>() .required() @@ -274,7 +332,6 @@ const validationSchema = toTypedSchema( const submit = async (values: GenericObject) => { // Mandatory values - model.value.parentCatalog = values.parentCatalog model.value.title = values.title model.value.description = values.description model.value.contactPoint = values.contactPoint @@ -285,6 +342,26 @@ const submit = async (values: GenericObject) => { model.value.issued = values.issued model.value.temporal = values.temporal model.value.spatial = values.spatial + + if (!props.edit){ + model.value.parentCatalog = values.parentCatalog + model.value.graph = [values.visibility] + } emit('save') } + +const updateParentCatalogValue = ( + value: OcCatalogSummary | undefined, + handleChange: Function, + resetField: Function +) => { + handleChange(value) + visibilityOptions.value = getVisibilityOptionsFor( + can, + props.community, + value, + props.userPrivateGraph + ) + resetField('visibility', undefined) +} </script> diff --git a/src/locales/en.ts b/src/locales/en.ts index f985907f7ba4e3f7df498a796f312965989dab14..0c371964ea597e4acba0a8c2d71977c770aa09ca 100644 --- a/src/locales/en.ts +++ b/src/locales/en.ts @@ -267,7 +267,7 @@ export default { title: 'Quality information', provenance: 'Provenance', input: 'Input data', - other: 'Others ressources' + other: 'Others resources' }, theme: 'Theme(s)', distribution: { @@ -334,6 +334,15 @@ Greetings, errorMessage: 'An error has occurred while creating your catalogue. Please try again later or contact the site administrator.', seeCatalogPage: 'See the catalogue page', + access: 'Access rights to the resource', + accessHelp: { + 1: "Access rights determine who can view the resource.", + 2: "Depending on your role within the community and the catalog in which you wish to insert the resource, the available options may vary.", + 3: "If you want your resource to be public but the option is not available, it's because you don't have sufficient rights to do so.", + 4: "In this case, create your resource as a community resource. Once created, you'll be able to ask a moderator to publish it.", + 5: "A community resource is only visible to members of the community.", + 6: "You can choose to keep your resource private, in which case it will only be visible to you. It will then be possible to modify this access from the dashboard." + } }, edit: { edit: 'Edit', @@ -341,32 +350,32 @@ Greetings, confirmMessage: 'Your catalog has been correctly updated!', errorMessage: 'An error has occurred while updating your catalog. Please try again later or contact the site administrator.', - seeCatalogPage: 'Go back to the catalog page', + seeCatalogPage: 'Go back to the catalog page' } }, search: { - title: "Search", - resourceType: "Resource type", - results: "Results", - launchSearch: "Launch the search", - searchBarParamButtonLabel: "Parameters", + title: 'Search', + resourceType: 'Resource type', + results: 'Results', + launchSearch: 'Launch the search', + searchBarParamButtonLabel: 'Parameters', searchResult: { - description: "Description", - datasets: "Datasets", - distributions: "Distributions", - parentCatalog: "Parent catalogue", - parentCatalogs: "Parent catalogues", - id: "Id", - version: "Version" + description: 'Description', + datasets: 'Datasets', + distributions: 'Distributions', + parentCatalog: 'Parent catalogue', + parentCatalogs: 'Parent catalogues', + id: 'Id', + version: 'Version' } }, resourceType: { - 'catalog': 'catalogue', - 'dataset': 'dataset', - 'distribution': 'distribution', - 'service': 'service', - 'concept': 'concept', - 'unknown': 'unknown', + catalog: 'catalogue', + dataset: 'dataset', + distribution: 'distribution', + service: 'service', + concept: 'concept', + unknown: 'unknown' }, and: 'and' } diff --git a/src/locales/fr.ts b/src/locales/fr.ts index 22e8be9e22b4f93e3a2b395567cd1a43f0139f30..de4d2fe297c43f4640d6f670d21736751870c501 100644 --- a/src/locales/fr.ts +++ b/src/locales/fr.ts @@ -181,9 +181,9 @@ export default { accessHelp: { 1: "Les droits d'accès déterminent qui aura le droit de voir la ressource.", 2: 'En fonction de votre rôle au sein de la communauté et du catalogue dans lequel vous souhaitez insérer la ressource, les options disponibles peuvent varier.', - 3: "Si vous souhaitez qui votre resource soit publique mais que l'option n'est pas disponible, c'est que vous n'avez pas les droits suffisants pour le faire.", - 4: 'Dans ce cas, créez votre jeu de données en tant que ressource communautaire et une fois votre resource créée, vous aurez la possibilité de demander sa publication à un modérateur.', - 5: "Une resource communautaire n'est visible que des membre de la communauté.", + 3: "Si vous souhaitez qui votre ressource soit publique mais que l'option n'est pas disponible, c'est que vous n'avez pas les droits suffisants pour le faire.", + 4: 'Dans ce cas, créez votre jeu de données en tant que ressource communautaire et une fois votre ressource créée, vous aurez la possibilité de demander sa publication à un modérateur.', + 5: "Une ressource communautaire n'est visible que des membre de la communauté.", 6: 'Vous pouvez choisir de garder votre ressource privée, dans ce cas, elle ne sera visible que par vous. Il sera ensuite possible de modifier accès à partir du tableau de bord.' } }, @@ -347,42 +347,51 @@ Cordialement, sendingMessage: 'Catalogue en cours de création...', confirmMessage: 'Votre catalogue a été créé correctement !', errorMessage: - 'Une erreur est survenue lors de la création de votre catalogue. Merci de réessayer plus tard ou bien de contacter l\'administrateur du site.', + "Une erreur est survenue lors de la création de votre catalogue. Merci de réessayer plus tard ou bien de contacter l'administrateur du site.", seeCatalogPage: 'Voir la page du catalogue', + access: "Droit d'accès de la ressource", + accessHelp: { + 1: "Les droits d'accès déterminent qui aura le droit de voir la ressource.", + 2: "En fonction de votre rôle au sein de la communauté et du catalogue dans lequel vous souhaitez insérer la ressource, les options disponibles peuvent varier.", + 3: "Si vous souhaitez qui votre ressource soit publique mais que l'option n'est pas disponible, c'est que vous n'avez pas les droits suffisants pour le faire.", + 4: "Dans ce cas, créez votre jeu de données en tant que ressource communautaire et une fois votre ressource créée, vous aurez la possibilité de demander sa publication à un modérateur.", + 5: "Une ressource communautaire n'est visible que des membres de la communauté.", + 6: "Vous pouvez choisir de garder votre ressource privée, dans ce cas, elle ne sera visible que par vous. Il sera ensuite possible de modifier l'accès à partir du tableau de bord." + } }, edit: { edit: 'Modifier', savingMessage: 'Mise à jour du catalogue en cours...', confirmMessage: 'Votre catalogue a été correctement mis à jour !', errorMessage: - 'Une erreur est survenue lors de la mise à jour de votre catalogue. Merci de réessayer plus tard ou bien de contacter l\'administrateur du site.', - seeCatalogPage: 'Retourner à la page du catalogue', - }, + "Une erreur est survenue lors de la mise à jour de votre catalogue. Merci de réessayer plus tard ou bien de contacter l'administrateur du site.", + seeCatalogPage: 'Retourner à la page du catalogue' + } }, search: { - title: "Rechercher", - resourceType: "Type de ressource", - results: "Résultats", - launchSearch: "Lancer la recherche", - searchBarParamButtonLabel: "Paramètres", + title: 'Rechercher', + resourceType: 'Type de ressource', + results: 'Résultats', + launchSearch: 'Lancer la recherche', + searchBarParamButtonLabel: 'Paramètres', searchResult: { - description: "Description", - catalogues: "Sous-catalogues", - datasets: "Jeux de données", - distributions: "Distributions", - parentCatalog: "Catalogue parent", - parentCatalogs: "Catalogues parent", - id: "Id", - version: "Version" + description: 'Description', + catalogues: 'Sous-catalogues', + datasets: 'Jeux de données', + distributions: 'Distributions', + parentCatalog: 'Catalogue parent', + parentCatalogs: 'Catalogues parent', + id: 'Id', + version: 'Version' } }, resourceType: { - 'catalog': 'catalogue', - 'dataset': 'jeu de données', - 'distribution': 'distribution', - 'service': 'service', - 'concept': 'concept', - 'unknown': 'inconnu', + catalog: 'catalogue', + dataset: 'jeu de données', + distribution: 'distribution', + service: 'service', + concept: 'concept', + unknown: 'inconnu' }, and: 'et' } diff --git a/src/pages/community/[community]/catalog/[[identifier]].new.vue b/src/pages/community/[community]/catalog/[[identifier]].new.vue index d67ebef26bbb3dd16eb0e29a4f87f92defcffd01..35ef3a4bb9d3f98ea97bd15e3791d70d88716d2d 100644 --- a/src/pages/community/[community]/catalog/[[identifier]].new.vue +++ b/src/pages/community/[community]/catalog/[[identifier]].new.vue @@ -119,8 +119,6 @@ const send = async () => { // Insert catalog catalog.value = await insertCatalog( catalog.value, - // TODO Is it really here we want to insert ? (#67) - accountStore.infos.hasPrivateGraph, catalog.value.parentCatalog as OcCatalog, accountStore.profile, accountStore.auth diff --git a/src/sparql/catalog.ts b/src/sparql/catalog.ts index ff5af15782155b8d5177e47214fc49713873a6a7..7ecb484082ce09a849fbeea2b14b6f394bfdff0b 100644 --- a/src/sparql/catalog.ts +++ b/src/sparql/catalog.ts @@ -185,7 +185,6 @@ export const getCatalogSummaryFromChildUri = async (uri: string, auth?: Credenti export async function insertCatalog( catalog: OcCatalog, - graph: string, parentCatalog: OcCatalog, profile: OcPerson, auth?: Credentials @@ -193,8 +192,15 @@ export async function insertCatalog( catalog.identifier ||= crypto.randomUUID() catalog['@id'] ||= `${import.meta.env.VITE_OC_URI_BASE_URL}/catalogs/${catalog.identifier}` + if (!catalog.graph) { + throw Error('Catalog has no graph!') + } + if (!catalog.parentCatalog) { + throw Error('Catalog has no parent catalog!') + } + let insertQuery = ` - INSERT INTO <${graph}> { + INSERT INTO <${catalog.graph}> { <${parentCatalog['@id']}> dcat:catalog <${catalog['@id']}>. ` insertQuery += buildCatalogTriples(catalog, profile) @@ -240,7 +246,7 @@ export async function updateCatalog(catalog: OcCatalog, profile: OcPerson, auth? OPTIONAL { <${catalog['@id']}> dct:temporal ?temporal. ?temporal ?p2 ?o2. - filter isblank(?temporal) + FILTER isblank(?temporal) } <${catalog['@id']}> ?p ?o. }; @@ -382,8 +388,8 @@ export async function getCatalog(identifier: string, auth?: Credentials): Promis } WHERE { <${catalogUri}> dct:creator ?creator. + VALUES ?p1 { rdf:type foaf:familyName foaf:name foaf:givenName foaf:firstName foaf:lastName foaf:mbox } ?creator ?p1 ?o1. - FILTER (?p1 IN (rdf:type, foaf:familyName, foaf:name, foaf:givenName, foaf:firstName, foaf:lastName, foaf:mbox)) } `, { @@ -401,8 +407,8 @@ export async function getCatalog(identifier: string, auth?: Credentials): Promis } WHERE { <${catalogUri}> dcat:contactPoint ?contactPoint. + VALUES ?p1 { rdf:type foaf:familyName foaf:name foaf:givenName foaf:firstName foaf:lastName foaf:mbox } ?contactPoint ?p1 ?o1. - FILTER (?p1 IN (rdf:type, foaf:familyName, foaf:name, foaf:givenName, foaf:firstName, foaf:lastName, foaf:mbox)) } `, { @@ -545,6 +551,26 @@ export async function getCatalog(identifier: string, auth?: Credentials): Promis } ) + /** + * Publisher + */ + const catalogPublisherPromise = executeSparqlConstruct<OcOrganization>( + ` + CONSTRUCT { + ?publisher ?p ?o. + } + WHERE { + <${catalogUri}> dct:publisher ?publisher. + VALUES ?p { dct:identifier rdf:type dct:title dct:description foaf:name } + ?publisher ?p ?o. + } + `, + { + auth, + context: resourceContext + } + ) + const catalogResponse = await Promise.all([ catalogTemporalPromise, // 0 catalogStatusPromise, // 1 @@ -555,7 +581,8 @@ export async function getCatalog(identifier: string, auth?: Credentials): Promis catalogLanguagePromise, // 6 catalogTypePromise, // 7 catalogSubsPromise, // 8 - catalogDatasetsPromise // 9 + catalogDatasetsPromise, // 9 + catalogPublisherPromise // 10 ]) catalog.temporal = catalogResponse[0].map<[Date, Date]>((temporal) => [ @@ -571,6 +598,7 @@ export async function getCatalog(identifier: string, auth?: Credentials): Promis catalog.type = catalogResponse[7][0] catalog.catalogs = catalogResponse[8] catalog.datasets = catalogResponse[9] + catalog.publisher = catalogResponse[10][0] return catalog }