diff --git a/package-lock.json b/package-lock.json index 53ef6545c6a4af7690456fe427581fabbc86502b..9c7af015632b1b077417c91ba155cfbc58e1a62c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -12073,4 +12073,4 @@ } } } -} \ No newline at end of file +} diff --git a/package.json b/package.json index 31c65f330e25e38109eefbad6ebc78f3f0ac8b23..00c12e533679ed282ea319541d35e84aaa34bdce 100644 --- a/package.json +++ b/package.json @@ -75,4 +75,4 @@ "type": "git", "url": "gitlab.phaidra.org:fairness/FAIR-v1.git" } -} \ No newline at end of file +} diff --git a/src/components/browse/PCollectionGallery.vue b/src/components/browse/PCollectionGallery.vue new file mode 100644 index 0000000000000000000000000000000000000000..4e8a9925a2d5883ba429ad3f839d271880ba5910 --- /dev/null +++ b/src/components/browse/PCollectionGallery.vue @@ -0,0 +1,219 @@ +<template> + <v-container> + <v-row> + <v-toolbar> + <v-toolbar-title>Toolbar</v-toolbar-title> + <v-spacer></v-spacer> + <v-btn icon @click="mode = 'single'"> + <v-icon>mdi-image-outline</v-icon> + </v-btn> + <v-btn icon @click="mode = 'gallery'"> + <v-icon>mdi-grid</v-icon> + </v-btn> + </v-toolbar> + </v-row> + <v-row> + <v-col md="auto"> + <v-treeview + :items="collections" + :load-children="getChildren" + :active.sync="active" + :open.sync="open" + activatable + transition + color="primary" + ></v-treeview> + </v-col> + <v-divider vertical></v-divider> + <v-col v-if="mode === 'single'"> + <v-carousel hide-delimiters height="100%"> + <v-carousel-item :src="'https://phaidra.univie.ac.at/preview/o:1039867/ImageManipulator/boxImage/800/jpg'"></v-carousel-item> + <v-carousel-item :src="'https://phaidra.univie.ac.at/preview/o:1039866/ImageManipulator/boxImage/800/jpg'"></v-carousel-item> + <v-carousel-item :src="'https://phaidra.univie.ac.at/preview/o:1039865/ImageManipulator/boxImage/800/jpg'"></v-carousel-item> + <v-carousel-item :src="'https://phaidra.univie.ac.at/preview/o:1039864/ImageManipulator/boxImage/800/jpg'"></v-carousel-item> + <v-carousel-item :src="'https://phaidra.univie.ac.at/preview/o:1039863/ImageManipulator/boxImage/800/jpg'"></v-carousel-item> + <v-carousel-item :src="'https://phaidra.univie.ac.at/preview/o:1039862/ImageManipulator/boxImage/800/jpg'"></v-carousel-item> + <v-carousel-item :src="'https://phaidra.univie.ac.at/preview/o:1039861/ImageManipulator/boxImage/800/jpg'"></v-carousel-item> + <v-carousel-item :src="'https://phaidra.univie.ac.at/preview/o:1039860/ImageManipulator/boxImage/800/jpg'"></v-carousel-item> + <v-carousel-item :src="'https://phaidra.univie.ac.at/preview/o:1039859/ImageManipulator/boxImage/800/jpg'"></v-carousel-item> + </v-carousel> + </v-col> + <v-col v-if="mode === 'gallery'"> + <v-container> + <v-row> + <v-col class="d-flex child-flex" cols="4"> + <v-card flat tile class="d-flex"> + <v-img class="grey lighten-2" aspect-ratio="1" :src="'https://phaidra.univie.ac.at/preview/o:1039867/ImageManipulator/boxImage/800/jpg'"> + <template v-slot:placeholder> + <v-row class="fill-height ma-0" align="center" justify="center"> + <v-progress-circular indeterminate color="grey lighten-5"></v-progress-circular> + </v-row> + </template> + </v-img> + </v-card> + </v-col> + <v-col class="d-flex child-flex" cols="4"> + <v-card flat tile class="d-flex"> + <v-img class="grey lighten-2" aspect-ratio="1" :src="'https://phaidra.univie.ac.at/preview/o:1039866/ImageManipulator/boxImage/800/jpg'"> + <template v-slot:placeholder> + <v-row class="fill-height ma-0" align="center" justify="center"> + <v-progress-circular indeterminate color="grey lighten-5"></v-progress-circular> + </v-row> + </template> + </v-img> + </v-card> + </v-col> + <v-col class="d-flex child-flex" cols="4"> + <v-card flat tile class="d-flex"> + <v-img class="grey lighten-2" aspect-ratio="1" :src="'https://phaidra.univie.ac.at/preview/o:1039865/ImageManipulator/boxImage/800/jpg'"> + <template v-slot:placeholder> + <v-row class="fill-height ma-0" align="center" justify="center"> + <v-progress-circular indeterminate color="grey lighten-5"></v-progress-circular> + </v-row> + </template> + </v-img> + </v-card> + </v-col> + <v-col class="d-flex child-flex" cols="4"> + <v-card flat tile class="d-flex"> + <v-img class="grey lighten-2" aspect-ratio="1" :src="'https://phaidra.univie.ac.at/preview/o:1039864/ImageManipulator/boxImage/800/jpg'"> + <template v-slot:placeholder> + <v-row class="fill-height ma-0" align="center" justify="center"> + <v-progress-circular indeterminate color="grey lighten-5"></v-progress-circular> + </v-row> + </template> + </v-img> + </v-card> + </v-col> + <v-col class="d-flex child-flex" cols="4"> + <v-card flat tile class="d-flex"> + <v-img class="grey lighten-2" aspect-ratio="1" :src="'https://phaidra.univie.ac.at/preview/o:1039863/ImageManipulator/boxImage/800/jpg'"> + <template v-slot:placeholder> + <v-row class="fill-height ma-0" align="center" justify="center"> + <v-progress-circular indeterminate color="grey lighten-5"></v-progress-circular> + </v-row> + </template> + </v-img> + </v-card> + </v-col> + <v-col class="d-flex child-flex" cols="4"> + <v-card flat tile class="d-flex"> + <v-img class="grey lighten-2" aspect-ratio="1" :src="'https://phaidra.univie.ac.at/preview/o:1039862/ImageManipulator/boxImage/800/jpg'"> + <template v-slot:placeholder> + <v-row class="fill-height ma-0" align="center" justify="center"> + <v-progress-circular indeterminate color="grey lighten-5"></v-progress-circular> + </v-row> + </template> + </v-img> + </v-card> + </v-col> + <v-col class="d-flex child-flex" cols="4"> + <v-card flat tile class="d-flex"> + <v-img class="grey lighten-2" aspect-ratio="1" :src="'https://phaidra.univie.ac.at/preview/o:1039861/ImageManipulator/boxImage/800/jpg'"> + <template v-slot:placeholder> + <v-row class="fill-height ma-0" align="center" justify="center"> + <v-progress-circular indeterminate color="grey lighten-5"></v-progress-circular> + </v-row> + </template> + </v-img> + </v-card> + </v-col> + <v-col class="d-flex child-flex" cols="4"> + <v-card flat tile class="d-flex"> + <v-img class="grey lighten-2" aspect-ratio="1" :src="'https://phaidra.univie.ac.at/preview/o:1039860/ImageManipulator/boxImage/800/jpg'"> + <template v-slot:placeholder> + <v-row class="fill-height ma-0" align="center" justify="center"> + <v-progress-circular indeterminate color="grey lighten-5"></v-progress-circular> + </v-row> + </template> + </v-img> + </v-card> + </v-col> + <v-col class="d-flex child-flex" cols="4"> + <v-card flat tile class="d-flex"> + <v-img class="grey lighten-2" aspect-ratio="1" :src="'https://phaidra.univie.ac.at/preview/o:1039859/ImageManipulator/boxImage/800/jpg'"> + <template v-slot:placeholder> + <v-row class="fill-height ma-0" align="center" justify="center"> + <v-progress-circular indeterminate color="grey lighten-5"></v-progress-circular> + </v-row> + </template> + </v-img> + </v-card> + </v-col> + </v-row> + </v-container> + </v-col> + </v-row> + </v-container> +</template> + +<script> +import qs from 'qs' + +export default { + name: 'p-collection-gallery', + props: { + collection: { + type: String, + required: true + } + }, + data () { + return { + active: [], + open: [this.collection], + collections: [ + { + id: this.collection, + name: 'Root', + children: [] + } + ], + mode: 'gallery', + selectedImage: { + src: '' + } + } + }, + methods: { + getChildren: async function (item) { + try { + let params = { + q: '*:*', + defType: 'edismax', + wt: 'json', + fq: 'ispartof:"' + item.id + '"', + start: 0, + rows: 5000 + } + let response = await this.$http.request({ + method: 'POST', + url: this.$store.state.instanceconfig.solr + '/select', + data: qs.stringify(params, { arrayFormat: 'repeat' }), + headers: { + 'content-type': 'application/x-www-form-urlencoded' + } + }) + let docs = response.data.response.docs + let total = response.data.response.numFound + if (total < 1) { + console.log(item.id + ' has ' + total + ' members') + return + } + for (let member of docs) { + let node = { + id: member.pid, + name: member.dc_title, + children: [] + } + item.children.push(node) + } + } catch (error) { + console.log(error) + this.$store.commit('setAlerts', [{ type: 'danger', msg: error }]) + } + } + } + +} +</script> diff --git a/src/components/display/phaidra_display/PDAdaptation.vue b/src/components/display/phaidra_display/PDAdaptation.vue new file mode 100644 index 0000000000000000000000000000000000000000..0b95244c218e5fda447ae2904d1731e9b9fc2db3 --- /dev/null +++ b/src/components/display/phaidra_display/PDAdaptation.vue @@ -0,0 +1,80 @@ +<template> + <v-layout row> + <v-flex xs10> + <v-card> + <v-card-title class="subheading grey white--text"> + <span>{{ $t(p) }}</span> + </v-card-title> + <v-divider></v-divider> + <v-card-text class="mt-4"> + <v-layout row wrap> + <template v-for="(title, j) in o['dce:title']"> + <template v-for="(mt, i) in title['bf:mainTitle']"> + <v-flex md4 xs12 class="pdlabel primary--text" :key="'mt'+j+i"> + {{ $t(title['@type']) }} + <template v-if="mt['@language']">({{ mt['@language'] }})</template> + </v-flex> + <v-flex md8 xs12 :key="'mtv'+i"> + <v-layout column> + <v-flex class="valuefield">{{ mt['@value'] }}</v-flex> + <template v-for="(st, i) in title['bf:subtitle']"> + <v-flex class="valuefield" :key="'stv'+i">{{ st['@value'] }}</v-flex> + </template> + </v-layout> + </v-flex> + </template> + </template> + </v-layout> + <v-layout v-for="(obj, pred, i) in o" :key="'role' + i" row wrap> + <template v-if="pred.startsWith('role')"> + <v-flex + md4 + xs12 + class="pdlabel primary--text" + >{{ getLocalizedTermLabel('rolepredicate', pred) }}</v-flex> + <v-flex md8 xs12 v-for="(n, i) in obj" :key="'adpname' + i"> + <v-layout column> + <v-flex> + <template + class="valuefield" + v-for="(gn) in n['schema:givenName']" + >{{ gn['@value'] }}</template> + <template + class="valuefield" + v-for="(fn) in n['schema:familyName']" + >{{ fn['@value'] }}</template> + <template class="valuefield" v-for="(fn) in n['schema:name']">{{ fn['@value'] }}</template> + <template v-if="n['schema:affiliation']" class="grey--text"> + <template v-for="(af) in n['schema:affiliation']"> + <template class="valuefield" v-for="(afn) in af">{{ afn['@value'] }}</template> + </template> + </template> + </v-flex> + </v-layout> + </v-flex> + </template> + </v-layout> + </v-card-text> + </v-card> + </v-flex> + </v-layout> +</template> + +<script> +import { vocabulary } from "../../../mixins/vocabulary"; + +export default { + name: "p-d-series", + mixins: [vocabulary], + props: { + o: { + type: Object, + required: true + }, + p: { + type: String + } + } +}; +</script> + diff --git a/src/components/display/phaidra_display/PDBfPublication.vue b/src/components/display/phaidra_display/PDBfPublication.vue new file mode 100644 index 0000000000000000000000000000000000000000..d942c2745cf1eb0d74fb1a83bd5431ec19d6a67f --- /dev/null +++ b/src/components/display/phaidra_display/PDBfPublication.vue @@ -0,0 +1,64 @@ +<template> + <v-layout row> + <v-flex xs10> + <v-card> + <v-card-title class="subheading grey white--text"> + <span>{{ $t(p) }}</span> + </v-card-title> + <v-divider></v-divider> + <v-card-text class="mt-4"> + <v-layout row wrap> + <template v-for="(publisher, j) in o['bf:agent']"> + <template v-for="(publishername, i) in publisher['schema:name']"> + <v-flex md4 xs12 class="pdlabel primary--text" :key="'publnamel'+j+i">{{ $t('ORG_PUBLISHER') }}</v-flex> + <v-flex md8 xs12 :key="'publname'+j+i"> + <v-layout column> + <v-flex class="valuefield">{{ publishername['@value'] }}</v-flex> + </v-layout> + </v-flex> + </template> + </template> + </v-layout> + <v-layout row wrap> + <template v-for="(publishingplace, j) in o['bf:place']"> + <template v-for="(place, i) in publishingplace['skos:prefLabel']"> + <v-flex md4 xs12 class="pdlabel primary--text" :key="'publplacel'+j+i">{{ $t('Place') }}</v-flex> + <v-flex md8 xs12 :key="'publplace'+j+i"> + <v-layout column> + <v-flex class="valuefield">{{ place['@value'] }}</v-flex> + </v-layout> + </v-flex> + </template> + </template> + </v-layout> + <v-layout row wrap> + <template v-for="(publishingdate, j) in o['bf:date']"> + <v-flex md4 xs12 class="pdlabel primary--text" :key="'publdatel'+j">{{ $t('Date') }}</v-flex> + <v-flex md8 xs12 :key="'publdate'+j"> + <v-layout column> + <v-flex>{{ publishingdate }}</v-flex> + </v-layout> + </v-flex> + </template> + </v-layout> + </v-card-text> + </v-card> + </v-flex> + </v-layout> +</template> + +<script> +export default { + name: 'p-d-bf-publication', + props: { + o: { + type: Object, + required: true + }, + p: { + type: String + } + } +} +</script> + diff --git a/src/components/display/phaidra_display/PDCitation.vue b/src/components/display/phaidra_display/PDCitation.vue new file mode 100644 index 0000000000000000000000000000000000000000..5be1cda210ca4f8ff2fcfa8274c7c0eb71af69d2 --- /dev/null +++ b/src/components/display/phaidra_display/PDCitation.vue @@ -0,0 +1,34 @@ +<template> + <v-flex> + <v-layout column > + <v-flex> + <template v-for="(pl, i) in o['skos:prefLabel']"> + <v-layout :key="'row'+i" row wrap> + <v-flex md4 xs12 class="pdlabel primary--text" :key="'mt'+i">{{ $t(p) }}<template v-if="pl['@language']"> ({{ pl['@language'] }})</template></v-flex> + <v-flex md8 xs12> + <v-layout row :key="'mtv'+i"> + <v-flex class="valuefield">{{ pl['@value'] }}<template v-for="(identifier) in o['skos:exactMatch']"> ({{ identifier }})</template></v-flex> + </v-layout> + </v-flex> + </v-layout> + </template> + </v-flex> + </v-layout> + </v-flex> +</template> + +<script> +export default { + name: 'p-d-citation', + props: { + o: { + type: Object, + required: true + }, + p: { + type: String + } + } +} +</script> + diff --git a/src/components/display/phaidra_display/PDDimension.vue b/src/components/display/phaidra_display/PDDimension.vue new file mode 100644 index 0000000000000000000000000000000000000000..d029ecd4a3e6bd68ab6845599b650bf83d70895f --- /dev/null +++ b/src/components/display/phaidra_display/PDDimension.vue @@ -0,0 +1,33 @@ +<template> + <v-flex> + <v-layout row wrap> + <v-flex md4 xs12 class="pdlabel primary--text" xs3>{{ $t(p) }}</v-flex> + <v-flex md8 xs12> + <span v-for="(v, i) in o['schema:value']" :key="'v'+i">{{ v }}</span> + <span + v-for="(v, i) in o['schema:unitCode']" + :key="'u'+i" + >{{ getLocalizedTermLabel('uncefact', v) }}</span> + </v-flex> + </v-layout> + </v-flex> +</template> + +<script> +import { vocabulary } from "../../../mixins/vocabulary"; + +export default { + name: "p-d-dimension", + mixins: [vocabulary], + props: { + o: { + type: Object, + required: true + }, + p: { + type: String + } + } +}; +</script> + diff --git a/src/components/display/phaidra_display/PDDuration.vue b/src/components/display/phaidra_display/PDDuration.vue new file mode 100644 index 0000000000000000000000000000000000000000..3f89989243995e3e7d609822f01a1f02eba038df --- /dev/null +++ b/src/components/display/phaidra_display/PDDuration.vue @@ -0,0 +1,43 @@ +<template> + <v-flex> + <v-layout row wrap> + <v-flex md4 xs12 class="pdlabel primary--text" xs3>{{ $t(p) }}</v-flex> + <v-flex md8 xs12> + <template v-if="duration.hours > 0">{{ duration.hours }} {{$t('hours') + ' '}}</template> + <template v-if="duration.minutes > 0">{{ duration.minutes }} {{$t('minutes') + ' '}}</template> + <template v-if="duration.seconds > 0">{{ duration.seconds }} {{$t('seconds')}}</template> + </v-flex> + </v-layout> + </v-flex> +</template> + +<script> +import { vocabulary } from "../../../mixins/vocabulary"; + +export default { + name: "p-d-duration", + mixins: [vocabulary], + props: { + o: { + type: String, + required: true + }, + p: { + type: String + } + }, + computed: { + duration: function() { + let m = this.o.match(/PT(\d+)H(\d+)M(\d+)S/); + if (m) { + return { + hours: m[1], + minutes: m[2], + seconds: m[3] + }; + } + } + } +}; +</script> + diff --git a/src/components/display/phaidra_display/PDEntity.vue b/src/components/display/phaidra_display/PDEntity.vue new file mode 100644 index 0000000000000000000000000000000000000000..7c75ed000bdd2e0c5cc3d08c0b38fee28057a802 --- /dev/null +++ b/src/components/display/phaidra_display/PDEntity.vue @@ -0,0 +1,56 @@ +<template> + <v-flex> + <v-layout v-if="entity" row wrap> + <v-flex md4 xs12 class="pdlabel primary--text">{{ getLocalizedTermLabel(this.role) }}</v-flex> + <v-flex md8 xs12> + <v-layout column> + <v-flex> + <template + class="valuefield" + v-for="(gn) in entity['schema:givenName']" + >{{ gn['@value'] }}</template> + <template + class="valuefield" + v-for="(fn) in entity['schema:familyName']" + >{{ fn['@value'] }}</template> + <template class="valuefield" v-for="(fn) in entity['schema:name']">{{ fn['@value'] }}</template> + <template v-if="entity['schema:affiliation']" class="grey--text"> + <template v-for="(af) in entity['schema:affiliation']"> + <template class="valuefield" v-for="(afn) in af">{{ afn['@value'] }}</template> + </template> + </template> + </v-flex> + </v-layout> + </v-flex> + </v-layout> + </v-flex> +</template> + +<script> +import { vocabulary } from "../../../mixins/vocabulary"; + +export default { + name: "p-d-entity", + mixins: [vocabulary], + props: { + entity: { + type: Object, + required: true + }, + role: { + type: String, + required: true + } + }, + methods: { + getLocalizedTermLabel: function(role) { + return this.$store.getters.getLocalizedTermLabel( + "rolepredicate", + role, + this.$i18n.locale + ); + } + } +}; +</script> + diff --git a/src/components/display/phaidra_display/PDExactMatch.vue b/src/components/display/phaidra_display/PDExactMatch.vue new file mode 100644 index 0000000000000000000000000000000000000000..3e4daf424b57c1cb73cbd7abe03c199b2e431e73 --- /dev/null +++ b/src/components/display/phaidra_display/PDExactMatch.vue @@ -0,0 +1,55 @@ +<template> + <v-flex> + <v-layout row wrap> + <v-flex md4 xs12 class="pdlabel primary--text" xs3>{{ $t(p) }}</v-flex> + <v-flex md8 xs12>{{ resolve(p, o['skos:exactMatch']) }}</v-flex> + </v-layout> + </v-flex> +</template> + +<script> +import { vocabulary } from "../../../mixins/vocabulary"; + +export default { + name: "p-d-exact-match", + mixins: [vocabulary], + props: { + o: { + type: Object, + required: true + }, + p: { + type: String + } + }, + data() { + return { + langCode2to3: { + en: "eng", + de: "deu", + it: "ita" + } + }; + }, + methods: { + resolve: function(p, v) { + var vocabulary = ""; + switch (p) { + case "vra:hasInscription": + vocabulary = "https://phaidra.org/vocabulary/stamp"; + break; + + default: + console.error( + "p-d-exact-match resolve: unrecognized predicate ", + p, + v + ); + } + + return this.getLocalizedTermLabel(vocabulary, v); + } + } +}; +</script> + diff --git a/src/components/display/phaidra_display/PDFunder.vue b/src/components/display/phaidra_display/PDFunder.vue new file mode 100644 index 0000000000000000000000000000000000000000..09722aacb742f96107097aeee7c3ba789a96fb39 --- /dev/null +++ b/src/components/display/phaidra_display/PDFunder.vue @@ -0,0 +1,29 @@ +<template> + <v-flex> + <v-layout row wrap> + <template v-for="(l, i) in o['skos:prefLabel']"> + <v-flex class="pdlabel primary--text" md4 xs12 :key="'fl'+i">{{ $t('Funder') }} ({{ l['@language'] }})</v-flex> + <v-flex class="valuefield" md8 xs12 :key="'fv'+i">{{ l['@value'] }}</v-flex> + </template> + </v-layout> + <v-layout row wrap> + <template v-for="(id, i) in o['skos:exactMatch']"> + <v-flex class="pdlabel primary--text" md4 xs12 :key="'idl'+i">{{ $t('Funder Id') }}</v-flex> + <v-flex md8 xs12 :key="'idv'+i">{{ id }}</v-flex> + </template> + </v-layout> + </v-flex> +</template> + +<script> +export default { + name: 'p-d-funder', + props: { + o: { + type: Object, + required: true + } + } +} +</script> + diff --git a/src/components/display/phaidra_display/PDGeoreference.vue b/src/components/display/phaidra_display/PDGeoreference.vue new file mode 100644 index 0000000000000000000000000000000000000000..c63c8124ed85b830cdb1417e932c8903dba870f6 --- /dev/null +++ b/src/components/display/phaidra_display/PDGeoreference.vue @@ -0,0 +1,33 @@ +<template> + <v-flex> + <template v-if="o['skos:prefLabel']"> + <v-layout row wrap> + <v-flex class="pdlabel primary--text" md4 xs12>{{ $t(p) }}<template v-for="(l) in o['skos:prefLabel']"><template v-if="l['@language']"> ({{ l['@language'] }})</template></template></v-flex> + <v-flex md8 xs12> + <v-layout column> + <v-flex class="valuefield" v-for="(l, i) in o['skos:prefLabel']" :key="'gplv'+i">{{ l['@value'] }}</v-flex> + <template v-if="o['rdfs:label']"> + <v-flex class="grey--text valuefield" v-for="(l, i) in o['rdfs:label']" :key="'gl'+i">[{{ l['@value'] }}]</v-flex> + </template> + </v-layout> + </v-flex> + </v-layout> + </template> + </v-flex> +</template> + +<script> +export default { + name: 'p-d-georeference', + props: { + o: { + type: Object, + required: true + }, + p: { + type: String + } + } +} +</script> + diff --git a/src/components/display/phaidra_display/PDJsonld.vue b/src/components/display/phaidra_display/PDJsonld.vue new file mode 100644 index 0000000000000000000000000000000000000000..bd08475ee33d0e670a92b713e7f59165e74fe615 --- /dev/null +++ b/src/components/display/phaidra_display/PDJsonld.vue @@ -0,0 +1,399 @@ +<template> + <p-d-jsonld-layout v-if="jsonld"> + <template v-if="pid" slot="pid"> + <v-flex> + <v-layout row wrap> + <v-flex md4 xs12 class="pdlabel primary--text">{{ $t('Persistent identifier') }}</v-flex> + <v-flex md8 xs12>https://{{ instance.baseurl }}/{{ pid }}</v-flex> + </v-layout> + </v-flex> + </template> + + <template v-for="(o, p) in jsonld"> + <template v-if="p==='dcterms:type'" slot="dcterms:type"> + <p-d-skos-preflabel :p="p" :o="item" v-for="(item, j) in o" :key="componentid+'type'+j"></p-d-skos-preflabel> + </template> + + <template v-else-if="p==='edm:hasType'" slot="edm:hasType"> + <p-d-skos-preflabel :p="p" :o="item" v-for="(item, j) in o" :key="componentid+'objtyp'+j"></p-d-skos-preflabel> + </template> + + <template v-else-if="p==='schema:genre'" slot="schema:genre"> + <p-d-skos-preflabel :p="p" :o="item" v-for="(item, j) in o" :key="componentid+'genre'+j"></p-d-skos-preflabel> + </template> + + <template v-else-if="p==='rdau:P60059'" slot="rdau:P60059"> + <p-d-skos-preflabel :p="p" :o="item" v-for="(item, j) in o" :key="componentid+'P60059'+j"></p-d-skos-preflabel> + </template> + + <template v-else-if="p==='dce:title'" slot="dce:title"> + <p-d-title :o="t" v-for="(t, j) in o" :key="componentid+'title'+j"></p-d-title> + </template> + + <template v-else-if="p.startsWith('role:')" slot="role"> + <p-d-entity :role="p" :entity="e" v-for="(e, j) in o" :key="componentid+'entity'+p+j"></p-d-entity> + </template> + + <template v-else-if="p==='bf:note'" slot="bf:note"> + <p-d-skos-preflabel :p="p" :o="item" v-for="(item, j) in o" :key="componentid+'text'+j"></p-d-skos-preflabel> + </template> + + <template v-else-if="p==='bf:tableOfContents'" slot="bf:tableOfContents"> + <p-d-skos-preflabel :p="p" :o="item" v-for="(item, j) in o" :key="componentid+'toc'+j"></p-d-skos-preflabel> + </template> + + <template v-else-if="p==='dce:subject'" slot="dce:subject"> + <p-d-keyword :p="p" :o="item" v-for="(item, j) in o" :key="componentid+'keyword'+j"></p-d-keyword> + </template> + + <template v-else-if="p==='dcterms:language'" slot="dcterms:language"> + <p-d-labeled-value :p="p" :o="item" v-for="(item, j) in o" :key="componentid+'lan'+j"></p-d-labeled-value> + </template> + + <template v-else-if="p==='schema:subtitleLanguage'" slot="schema:subtitleLanguage"> + <p-d-labeled-value :p="p" :o="item" v-for="(item, j) in o" :key="componentid+'sublan'+j"></p-d-labeled-value> + </template> + + <template v-else-if="p==='dcterms:date'" slot="dcterms:date"> + <p-d-value :p="p" :o="item" v-for="(item, j) in o" :key="componentid+'date'+j"></p-d-value> + </template> + + <template v-else-if="p==='dcterms:created'" slot="dcterms:created"> + <p-d-value :p="p" :o="item" v-for="(item, j) in o" :key="componentid+'created'+j"></p-d-value> + </template> + + <template v-else-if="p==='dcterms:modified'" slot="dcterms:modified"> + <p-d-value :p="p" :o="item" v-for="(item, j) in o" :key="componentid+'modified'+j"></p-d-value> + </template> + + <template v-else-if="p==='dcterms:available'" slot="dcterms:available"> + <p-d-value :p="p" :o="item" v-for="(item, j) in o" :key="componentid+'available'+j"></p-d-value> + </template> + + <template v-else-if="p==='dcterms:issued'" slot="dcterms:issued"> + <p-d-value :p="p" :o="item" v-for="(item, j) in o" :key="componentid+'issued'+j"></p-d-value> + </template> + + <template v-else-if="p==='dcterms:valid'" slot="dcterms:valid"> + <p-d-value :p="p" :o="item" v-for="(item, j) in o" :key="componentid+'valid'+j"></p-d-value> + </template> + + <template v-else-if="p==='dcterms:dateAccepted'" slot="dcterms:dateAccepted"> + <p-d-value :p="p" :o="item" v-for="(item, j) in o" :key="componentid+'dateAccepted'+j"></p-d-value> + </template> + + <template v-else-if="p==='dcterms:dateCopyrighted'" slot="dcterms:dateCopyrighted"> + <p-d-value :p="p" :o="item" v-for="(item, j) in o" :key="componentid+'dateCopyrighted'+j"></p-d-value> + </template> + + <template v-else-if="p==='dcterms:dateSubmitted'" slot="dcterms:dateSubmitted"> + <p-d-value :p="p" :o="item" v-for="(item, j) in o" :key="componentid+'dateSubmitted'+j"></p-d-value> + </template> + + <template v-else-if="p==='rdau:P60071'" slot="rdau:P60071"> + <p-d-value :p="p" :o="item" v-for="(item, j) in o" :key="componentid+'dateOfProduction'+j"></p-d-value> + </template> + + <template v-else-if="p==='phaidra:dateAccessioned'" slot="phaidra:dateAccessioned"> + <p-d-value + :p="p" + :o="item" + v-for="(item, j) in o" + :key="componentid+'phaidra:dateAccessioned'+j" + ></p-d-value> + </template> + + <template v-else-if="p==='dcterms:temporal'" slot="dcterms:temporal"> + <p-d-lang-value :p="p" :o="item" v-for="(item, j) in o" :key="componentid+'temporal'+j"></p-d-lang-value> + </template> + + <template v-else-if="p==='rdau:P60193'" slot="rdau:P60193"> + <p-d-series :p="p" :o="item" v-for="(item, j) in o" :key="componentid+'series'+j"></p-d-series> + </template> + + <template v-else-if="p==='bf:provisionActivity'" slot="bf:provisionActivity"> + <p-d-bf-publication :p="p" :o="item" v-for="(item, j) in o" :key="componentid+'bfpubl'+j"></p-d-bf-publication> + </template> + + <template v-else-if="p==='cito:cites'" slot="cito:cites"> + <p-d-citation :p="p" :o="item" v-for="(item, j) in o" :key="componentid+'cites'+j"></p-d-citation> + </template> + + <template v-else-if="p==='cito:isCitedBy'" slot="cito:isCitedBy"> + <p-d-citation :p="p" :o="item" v-for="(item, j) in o" :key="componentid+'citedby'+j"></p-d-citation> + </template> + + <template v-else-if="p==='rdau:P60227'" slot="rdau:P60227"> + <p-d-adaptation :p="p" :o="item" v-for="(item, j) in o" :key="componentid+'adaptation'+j"></p-d-adaptation> + </template> + + <template v-else-if="p==='frapo:isOutputOf'" slot="frapo:isOutputOf"> + <template v-for="(item, j) in o"> + <template v-if="item['@type']==='aaiso:Programme'"> + <p-d-study-plan :p="p" :o="item" :key="componentid+'study-plan'+j"></p-d-study-plan> + </template> + <template v-else-if="item['@type']==='foaf:Project'"> + <p-d-project :p="p" :o="item" :key="componentid+'project'+j"></p-d-project> + </template> + </template> + </template> + + <template v-else-if="p==='frapo:hasFundingAgency'" slot="frapo:hasFundingAgency"> + <p-d-funder :p="p" :o="item" v-for="(item, j) in o" :key="componentid+'funder'+j"></p-d-funder> + </template> + + <template v-else-if="p==='rdax:P00009'" slot="rdax:P00009"> + <p-d-skos-preflabel + :p="p" + :o="item" + v-for="(item, j) in o" + :key="componentid+'association'+j" + ></p-d-skos-preflabel> + </template> + + <template v-else-if="p==='bf:physicalLocation'" slot="bf:physicalLocation"> + <p-d-lang-value :p="p" :o="item" v-for="(item, j) in o" :key="componentid+'physloc'+j"></p-d-lang-value> + </template> + + <template v-else-if="p==='bf:shelfMark'" slot="bf:shelfMark"> + <p-d-value :p="p" :o="item" v-for="(item, j) in o" :key="componentid+'callnr'+j"></p-d-value> + </template> + + <template v-else-if="p==='dcterms:provenance'" slot="dcterms:provenance"> + <p-d-skos-preflabel :p="p" :o="item" v-for="(item, j) in o" :key="componentid+'prov'+j"></p-d-skos-preflabel> + </template> + + <template v-else-if="p==='dcterms:spatial'" slot="dcterms:spatial"> + <p-d-georeference :p="p" :o="item" v-for="(item, j) in o" :key="componentid+'spatial'+j"></p-d-georeference> + </template> + + <template v-else-if="p==='vra:placeOfCreation'" slot="vra:placeOfCreation"> + <p-d-georeference + :p="p" + :o="item" + v-for="(item, j) in o" + :key="componentid+'placeOfCreation'+j" + ></p-d-georeference> + </template> + + <template v-else-if="p==='vra:placeOfRepository'" slot="vra:placeOfRepository"> + <p-d-georeference :p="p" :o="item" v-for="(item, j) in o" :key="'placeOfRepository'+j"></p-d-georeference> + </template> + + <template v-else-if="p==='vra:placeOfSite'" slot="vra:placeOfSite"> + <p-d-georeference + :p="p" + :o="item" + v-for="(item, j) in o" + :key="componentid+'placeOfSite'+j" + ></p-d-georeference> + </template> + + <template v-else-if="p==='ebucore:filename'" slot="ebucore:filename"> + <p-d-value :p="p" :o="item" v-for="(item, j) in o" :key="componentid+'filename'+j"></p-d-value> + </template> + + <template v-else-if="p==='ebucore:hasMimeType'" slot="ebucore:hasMimeType"> + <p-d-labeled-value :p="p" :o="item" v-for="(item, j) in o" :key="componentid+'mime'+j"></p-d-labeled-value> + </template> + + <template v-else-if="p==='opaque:cco_accessionNumber'" slot="opaque:cco_accessionNumber"> + <p-d-value :p="p" :o="item" v-for="(item, j) in o" :key="componentid+'accnr'+j"></p-d-value> + </template> + + <template v-else-if="p==='vra:hasInscription'" slot="vra:hasInscription"> + <p-d-skos-preflabel :p="p" :o="item" v-for="(item, j) in o" :key="componentid+'inscr'+j"></p-d-skos-preflabel> + </template> + + <template v-else-if="p==='vra:material'" slot="vra:material"> + <p-d-skos-preflabel :p="p" :o="item" v-for="(item, j) in o" :key="componentid+'material'+j"></p-d-skos-preflabel> + </template> + + <template v-else-if="p==='vra:hasTechnique'" slot="vra:hasTechnique"> + <p-d-skos-preflabel :p="p" :o="item" v-for="(item, j) in o" :key="componentid+'techn'+j"></p-d-skos-preflabel> + </template> + + <template v-else-if="p==='dce:format'" slot="dce:format"> + <p-d-skos-preflabel :p="p" :o="item" v-for="(item, j) in o" :key="componentid+'format'+j"></p-d-skos-preflabel> + </template> + + <template v-else-if="p==='rdau:P60048'" slot="rdau:P60048"> + <p-d-skos-preflabel + :p="p" + :o="item" + v-for="(item, j) in o" + :key="componentid+'carriertype'+j" + ></p-d-skos-preflabel> + </template> + + <template v-else-if="p==='rdau:P60059'" slot="rdau:P60059"> + <p-d-skos-preflabel :p="p" :o="item" v-for="(item, j) in o" :key="componentid+'regenc'+j"></p-d-skos-preflabel> + </template> + + <template v-else-if="p==='schema:width'" slot="schema:width"> + <p-d-dimension :p="p" :o="item" v-for="(item, j) in o" :key="componentid+'width'+j"></p-d-dimension> + </template> + + <template v-else-if="p==='schema:height'" slot="schema:height"> + <p-d-dimension :p="p" :o="item" v-for="(item, j) in o" :key="componentid+'height'+j"></p-d-dimension> + </template> + + <template v-else-if="p==='schema:depth'" slot="schema:depth"> + <p-d-dimension :p="p" :o="item" v-for="(item, j) in o" :key="componentid+'depth'+j"></p-d-dimension> + </template> + + <template v-else-if="p==='schema:weight'" slot="schema:weight"> + <p-d-dimension :p="p" :o="item" v-for="(item, j) in o" :key="componentid+'weight'+j"></p-d-dimension> + </template> + + <template v-else-if="p==='schema:duration'" slot="schema:duration"> + <p-d-duration :p="p" :o="item" v-for="(item, j) in o" :key="componentid+'duration'+j"></p-d-duration> + </template> + + <template v-else-if="p==='schema:numberOfPages'" slot="schema:numberOfPages"> + <p-d-value :p="p" :o="item" v-for="(item, j) in o" :key="componentid+'numberOfPages'+j"></p-d-value> + </template> + + <template v-else-if="p==='bf:soundCharacteristic'" slot="bf:soundCharacteristic"> + <p-d-value + :p="p" + :o="item" + v-for="(item, j) in o" + :key="componentid+'soundCharacteristic'+j" + ></p-d-value> + </template> + + <template v-else-if="p==='bf:supplementaryContent'" slot="bf:supplementaryContent"> + <p-d-skos-preflabel + :p="p" + :o="item" + v-for="(item, j) in o" + :key="componentid+'supplementaryContent'+j" + ></p-d-skos-preflabel> + </template> + + <template v-else-if="p==='dcterms:audience'" slot="dcterms:audience"> + <p-d-skos-preflabel :p="p" :o="item" v-for="(item, j) in o" :key="componentid+'audience'+j"></p-d-skos-preflabel> + </template> + + <template v-else-if="p==='bf:awards'" slot="bf:awards"> + <p-d-skos-preflabel :p="p" :o="item" v-for="(item, j) in o" :key="componentid+'awards'+j"></p-d-skos-preflabel> + </template> + + <template v-else-if="p==='edm:rights'" slot="edm:rights"> + <p-d-license :p="p" :o="item" v-for="(item, j) in o" :key="componentid+'license'+j"></p-d-license> + </template> + + <template v-else-if="p==='dce:rights'" slot="dce:rights"> + <p-d-lang-value :p="p" :o="item" v-for="(item, j) in o" :key="componentid+'rights'+j"></p-d-lang-value> + </template> + + <template v-else-if="p==='dcterms:subject'" slot="dcterms:subject"> + <template v-for="(subject, j) in o"> + <v-card + class="mt-3" + v-if="subject['@type']==='phaidra:Subject'" + :key="componentid+'psubject'+j" + > + <v-toolbar dense flat> + <v-layout> + <v-toolbar-title class="font-weight-light">Subject</v-toolbar-title> + </v-layout> + </v-toolbar> + <v-card-text class="ma-2"> + <p-d-jsonld :jsonld="subject"></p-d-jsonld> + </v-card-text> + </v-card> + <p-d-skos-preflabel v-else :p="p" :o="subject" :key="componentid+'subject'+j"></p-d-skos-preflabel> + </template> + </template> + + <template v-else-if="p==='@type'"></template> + + <template v-else slot="unknown-predicate"> + <v-container :key="p"> + <v-alert :type="'error'" :value="true" transition="fade-transition"> + Unknown predicate + <b>{{p}}</b> + </v-alert> + <p-i-unknown-readonly :jsonld="o" :label="p"></p-i-unknown-readonly> + </v-container> + </template> + </template> + </p-d-jsonld-layout> +</template> + +<script> +import PDLicense from "../phaidra_display/PDLicense"; +import PDTitle from "../phaidra_display/PDTitle"; +import PDSkosPreflabel from "../phaidra_display/PDSkosPreflabel"; +import PDKeyword from "../phaidra_display/PDKeyword"; +import PDLangValue from "../phaidra_display/PDLangValue"; +import PDValue from "../phaidra_display/PDValue"; +import PDDimension from "../phaidra_display/PDDimension"; +import PDDuration from "../phaidra_display/PDDuration"; +import PDGeoreference from "../phaidra_display/PDGeoreference"; +import PDEntity from "../phaidra_display/PDEntity"; +import PDLabeledValue from "../phaidra_display/PDLabeledValue"; +import PDFunder from "../phaidra_display/PDFunder"; +import PDProject from "../phaidra_display/PDProject"; +import PDBfPublication from "../phaidra_display/PDBfPublication"; +import PDStudyPlan from "../phaidra_display/PDStudyPlan"; +import PDSeries from "../phaidra_display/PDSeries"; +import PDAdaptation from "../phaidra_display/PDAdaptation"; +import PDCitation from "../phaidra_display/PDCitation"; +import PDJsonldLayout from "../phaidra_display/PDJsonldLayout"; +import PIUnknownReadonly from "../../input/phaidra_inputs/PIUnknownReadonly"; +import { vocabulary } from "../../../mixins/vocabulary"; + +export default { + name: "p-d-jsonld", + mixins: [vocabulary], + props: { + jsonld: { + type: Object, + default: null + }, + pid: String + }, + components: { + PDTitle, + PDEntity, + PDJsonldLayout, + PDSkosPreflabel, + PDKeyword, + PDLangValue, + PDLicense, + PDValue, + PDDimension, + PDDuration, + PDGeoreference, + PDLabeledValue, + PDFunder, + PDProject, + PDBfPublication, + PDStudyPlan, + PDSeries, + PDAdaptation, + PDCitation, + PIUnknownReadonly + }, + computed: { + instance: function() { + return this.$store.state.settings.instance; + }, + componentid: function() { + return Math.floor(Math.random() * 10000000); + } + }, + mounted: function() { + this.$store.dispatch("loadLanguages"); + } +}; +</script> + +<style> +.valuefield { + white-space: pre-wrap; +} +</style> \ No newline at end of file diff --git a/src/components/display/phaidra_display/PDJsonldLayout.vue b/src/components/display/phaidra_display/PDJsonldLayout.vue new file mode 100644 index 0000000000000000000000000000000000000000..d9eeb652061639aa5f29cfeaae376d3d021d87bf --- /dev/null +++ b/src/components/display/phaidra_display/PDJsonldLayout.vue @@ -0,0 +1,86 @@ +<template> + <v-container> + <v-layout column> + <slot name="dce:title"></slot> + <slot name="role"></slot> + <slot name="bf:note"></slot> + <slot name="bf:tableOfContents"></slot> + + <slot name="edm:hasType"></slot> + <slot name="schema:genre"></slot> + + <slot name="dcterms:language"></slot> + <slot name="schema:subtitleLanguage"></slot> + <slot name="dce:subject"></slot> + + <slot name="dcterms:date"></slot> + <slot name="dcterms:created"></slot> + <slot name="dcterms:modified"></slot> + <slot name="dcterms:available"></slot> + <slot name="dcterms:issued"></slot> + <slot name="dcterms:valid"></slot> + <slot name="dcterms:dateAccepted"></slot> + <slot name="dcterms:dateCopyrighted"></slot> + <slot name="dcterms:dateSubmitted"></slot> + <slot name="rdau:P60071"></slot> + <slot name="phaidra:dateAccessioned"></slot> + <slot name="dcterms:temporal"></slot> + + <slot name="rdau:P60193"></slot> + <slot name="cito:cites"></slot> + <slot name="cito:isCitedBy"></slot> + <slot name="bf:provisionActivity"></slot> + <slot name="rdau:P60227"></slot> + + <slot name="frapo:hasFundingAgency"></slot> + <slot name="frapo:isOutputOf"></slot> + <slot name="rdax:P00009"></slot> + <slot name="dcterms:provenance"></slot> + + <slot name="dcterms:spatial"></slot> + <slot name="vra:placeOfCreation"></slot> + <slot name="vra:placeOfRepository"></slot> + <slot name="vra:placeOfSite"></slot> + + <slot name="schema:numberOfPages"></slot> + <slot name="bf:soundCharacteristic"></slot> + <slot name="bf:supplementaryContent"></slot> + <slot name="bf:awards"></slot> + <slot name="dcterms:audience"></slot> + <slot name="rdau:P60059"></slot> + + <slot name="ebucore:filename"></slot> + <slot name="ebucore:hasMimeType"></slot> + + <slot name="opaque:cco_accessionNumber"></slot> + <slot name="bf:shelfMark"></slot> + <slot name="bf:physicalLocation"></slot> + + <slot name="vra:hasInscription"></slot> + <slot name="vra:material"></slot> + <slot name="vra:hasTechnique"></slot> + <slot name="dce:format"></slot> + <slot name="rdau:P60048"></slot> + <slot name="schema:width"></slot> + <slot name="schema:height"></slot> + <slot name="schema:depth"></slot> + <slot name="schema:weight"></slot> + <slot name="schema:duration"></slot> + + <slot name="dcterms:type"></slot> + <slot name="pid"></slot> + + <slot name="edm:rights"></slot> + <slot name="dce:rights"></slot> + + <slot name="dcterms:subject"></slot> + + <slot name="unknown-predicate"></slot> + </v-layout> + </v-container> +</template> +<script> +export default { + name: 'p-d-jsonld-layout' +} +</script> \ No newline at end of file diff --git a/src/components/display/phaidra_display/PDKeyword.vue b/src/components/display/phaidra_display/PDKeyword.vue new file mode 100644 index 0000000000000000000000000000000000000000..f2560c4425b62a159120b7a4ab70947d7c79cbda --- /dev/null +++ b/src/components/display/phaidra_display/PDKeyword.vue @@ -0,0 +1,41 @@ +<template> + <v-flex> + <v-layout column > + <v-flex> + <template v-for="(l, i) in o['skos:prefLabel']"> + <v-layout row wrap :key="'row'+i"> + <v-flex class="pdlabel primary--text" md4 xs12 :key="'l'+i">{{ $t(p) }}<template v-if="l['@language']"> ({{ l['@language'] }})</template></v-flex> + <v-flex md8 xs12 :key="'t'+i"> + <v-chip v-for="(keyword, j) in getKeywords(l)" :key="'kw'+j">{{keyword}}</v-chip> + </v-flex> + </v-layout> + </template> + </v-flex> + </v-layout> + </v-flex> +</template> + +<script> +export default { + name: 'p-d-keyword', + props: { + o: { + type: Object, + required: true + }, + p: { + type: String + } + }, + methods: { + getKeywords: function (l) { + if (l['@value']) { + return l['@value'].split(',') + } else { + return [] + } + } + } +} +</script> + diff --git a/src/components/display/phaidra_display/PDLabeledValue.vue b/src/components/display/phaidra_display/PDLabeledValue.vue new file mode 100644 index 0000000000000000000000000000000000000000..59ee0c3b5f5fa2d6968bed91af6429f74f05eaab --- /dev/null +++ b/src/components/display/phaidra_display/PDLabeledValue.vue @@ -0,0 +1,50 @@ +<template> + <v-flex> + <v-layout row wrap> + <v-flex md4 xs12 class="pdlabel primary--text">{{ $t(p) }}</v-flex> + <v-flex md8 xs12> + {{ resolve(p, o) }} + <span class="grey--text">[{{o}}]</span> + </v-flex> + </v-layout> + </v-flex> +</template> + +<script> +import { vocabulary } from "../../../mixins/vocabulary"; + +export default { + name: "p-d-labeled-value", + mixins: [vocabulary], + props: { + o: { + type: String, + required: true + }, + p: { + type: String + } + }, + methods: { + resolve: function(p, v) { + var vocabulary = ""; + switch (p) { + case "dcterms:language": + case "schema:subtitleLanguage": + vocabulary = "lang"; + break; + + case "ebucore:hasMimeType": + vocabulary = "mimetypes"; + break; + + default: + //console.error('p-d-uri resolve: unrecognized predicate ', p, v) + } + + return this.getLocalizedTermLabel(vocabulary, v); + } + } +}; +</script> + diff --git a/src/components/display/phaidra_display/PDLangValue.vue b/src/components/display/phaidra_display/PDLangValue.vue new file mode 100644 index 0000000000000000000000000000000000000000..e75a707ee8429f29ce010767e6f895a3e7926eb3 --- /dev/null +++ b/src/components/display/phaidra_display/PDLangValue.vue @@ -0,0 +1,24 @@ +<template> + <v-flex> + <v-layout row wrap> + <v-flex md4 xs12 class="pdlabel primary--text" xs3>{{ $t(p) }}<template v-if="o['@language']"> ({{ o['@language'] }})</template></v-flex> + <v-flex class="valuefield" md8 xs12>{{ o['@value'] }}</v-flex> + </v-layout> + </v-flex> +</template> + +<script> +export default { + name: 'p-d-lang-value', + props: { + o: { + type: Object, + required: true + }, + p: { + type: String + } + } +} +</script> + diff --git a/src/components/display/phaidra_display/PDLicense.vue b/src/components/display/phaidra_display/PDLicense.vue new file mode 100644 index 0000000000000000000000000000000000000000..78b26296d1d7c23a87098806e5177d8909e2ec29 --- /dev/null +++ b/src/components/display/phaidra_display/PDLicense.vue @@ -0,0 +1,44 @@ +<template> + <v-flex> + <v-layout row wrap> + <v-flex md4 xs12 class="pdlabel primary--text">{{ $t(p) }}</v-flex> + <v-flex md8 xs12> + <a :href="o" target="_blank">{{ getLocalizedTermLabel('licenses', o) }}</a> + <!-- + <v-flex> + <v-img :src="'../../assets/' + getTermProperty('licenses', o, 'img')" :alt="o" class="license-icon"/> + </v-flex> + --> + </v-flex> + </v-layout> + </v-flex> +</template> + +<script> +import { vocabulary } from "../../../mixins/vocabulary"; + +export default { + name: "p-d-license", + mixins: [vocabulary], + props: { + o: { + type: String, + required: true + }, + p: { + type: String + } + } +}; +</script> + +<style scoped> +.license-icon { + height: 1.3em; + vertical-align: text-bottom; +} + +.license-label { + vertical-align: middle; +} +</style> diff --git a/src/components/display/phaidra_display/PDProject.vue b/src/components/display/phaidra_display/PDProject.vue new file mode 100644 index 0000000000000000000000000000000000000000..f9d9465b7f05d0d2365aa1a1b1e103f175c4fb7c --- /dev/null +++ b/src/components/display/phaidra_display/PDProject.vue @@ -0,0 +1,44 @@ +<template> + <v-flex> + <v-layout row wrap> + <template v-for="(l, i) in o['skos:prefLabel']"> + <v-flex md4 xs12 class="pdlabel primary--text" :key="'pl'+i">{{ $t('Project') }} ({{ l['@language'] }})</v-flex> + <v-flex class="valuefield" md8 xs12 :key="'pv'+i" >{{ l['@value'] }}</v-flex> + </template> + </v-layout> + <v-layout row wrap> + <template v-for="(id, i) in o['skos:exactMatch']"> + <v-flex md4 xs12 class="pdlabel primary--text" :key="'idl'+i">{{ $t('Project Id') }}</v-flex> + <v-flex md8 xs12 :key="'idv'+i">{{ id }}</v-flex> + </template> + </v-layout> + <v-layout row wrap> + <template v-for="(d, i) in o['rdfs:comment']"> + <v-flex md4 xs12 class="pdlabel primary--text" :key="'dl'+i">{{ $t('Project Description') }} ({{ d['@language'] }})</v-flex> + <v-flex class="valuefield" md8 xs12 :key="'dv'+i">{{ d['@value'] }}</v-flex> + </template> + </v-layout> + <v-layout row wrap> + <template v-for="(hp, i) in o['foaf:homepage']"> + <v-flex md4 xs12 class="pdlabel primary--text" :key="'hpl'+i">{{ $t('Project Homepage') }}</v-flex> + <v-flex md8 xs12 :key="'hpv'+i">{{ hp }}</v-flex> + </template> + </v-layout> + </v-flex> +</template> + +<script> +export default { + name: 'p-d-project', + props: { + o: { + type: Object, + required: true + }, + p: { + type: String + } + } +} +</script> + diff --git a/src/components/display/phaidra_display/PDSeries.vue b/src/components/display/phaidra_display/PDSeries.vue new file mode 100644 index 0000000000000000000000000000000000000000..2775efcff6deddb86a0b89d2ba5caea5382a2179 --- /dev/null +++ b/src/components/display/phaidra_display/PDSeries.vue @@ -0,0 +1,72 @@ +<template> + <v-layout row wrap> + <v-flex xs10> + <v-card > + <v-card-title class="subheading grey white--text"> + <span>{{ $t(p) }}</span> + </v-card-title> + <v-divider></v-divider> + <v-card-text class="mt-4"> + <v-layout row wrap> + <template v-for="(title, j) in o['dce:title']"> + <template v-for="(mt, i) in title['bf:mainTitle']"> + <v-flex md4 xs12 class="pdlabel primary--text" :key="'mt'+j+i">{{ $t(title['@type']) }}<template v-if="mt['@language']"> ({{ mt['@language'] }})</template></v-flex> + <v-flex md8 xs12 :key="'mtv'+j+i"> + <v-layout column> + <v-flex class="valuefield">{{ mt['@value'] }}</v-flex> + </v-layout> + </v-flex> + </template> + </template> + </v-layout> + <v-layout row wrap> + <template v-for="(volume, i) in o['bibo:volume']"> + <v-flex md4 xs12 class="pdlabel primary--text" :key="'vl'+i">{{ $t('Volume') }}</v-flex> + <v-flex md8 xs12 :key="'v'+i">{{ volume }}</v-flex> + </template> + </v-layout> + <v-layout row wrap> + <template v-for="(issue, i) in o['bibo:issue']"> + <v-flex md4 xs12 class="pdlabel primary--text" :key="'il'+i">{{ $t('Issue') }}</v-flex> + <v-flex md8 xs12 :key="'i'+i">{{ issue }}</v-flex> + </template> + </v-layout> + <v-layout row wrap> + <template v-for="(issn, i) in o['identifiers:issn']"> + <v-flex md4 xs12 class="pdlabel primary--text" :key="'isl'+i">{{ $t('ISSN') }}</v-flex> + <v-flex md8 xs12 :key="'is'+i">{{ issn }}</v-flex> + </template> + </v-layout> + <v-layout row wrap> + <template v-for="(issued, i) in o['dcterms:issued']"> + <v-flex md4 xs12 class="pdlabel primary--text" :key="'idatel'+i">{{ $t('Issued') }}</v-flex> + <v-flex md8 xs12 :key="'idate'+i">{{ issued }}</v-flex> + </template> + </v-layout> + <v-layout row wrap> + <template v-for="(id, i) in o['skos:exactMatch']"> + <v-flex md4 xs12 class="pdlabel primary--text" :key="'idatel'+i">{{ $t('Identifier') }}</v-flex> + <v-flex md8 xs12 :key="'id'+i">{{ id }}</v-flex> + </template> + </v-layout> + </v-card-text> + </v-card> + </v-flex> + </v-layout> +</template> + +<script> +export default { + name: 'p-d-series', + props: { + o: { + type: Object, + required: true + }, + p: { + type: String + } + } +} +</script> + diff --git a/src/components/display/phaidra_display/PDSkosPreflabel.vue b/src/components/display/phaidra_display/PDSkosPreflabel.vue new file mode 100644 index 0000000000000000000000000000000000000000..ba3f12cc83210d75085f7f45510291f4844d4c1c --- /dev/null +++ b/src/components/display/phaidra_display/PDSkosPreflabel.vue @@ -0,0 +1,44 @@ +<template> + <v-flex> + <v-layout column > + <v-flex> + <template v-for="(l, i) in o['skos:prefLabel']"> + <v-layout :key="'lay'+i" row wrap v-if="l['@language'] === displaylang"> + <v-flex md4 xs12 v-if="p==='bf:note'" class="pdlabel primary--text" :key="'l'+i">{{ $t(o['@type']) }}<template v-if="l['@language']"> ({{ l['@language'] }})</template></v-flex> + <v-flex md4 xs12 v-else class="pdlabel primary--text" :key="'l'+i">{{ $t(p) }}<template v-if="l['@language']"> ({{ l['@language'] }})</template></v-flex> + <v-flex md8 xs12 v-if="o['skos:exactMatch']" :key="'t-id'+i"><a class="valuefield" :href="o['skos:exactMatch'][0]" target="_blank">{{ l['@value'] }}</a></v-flex> + <v-flex class="valuefield" md8 xs12 v-else :key="'t'+i">{{ l['@value'] }}</v-flex> + </v-layout> + </template> + </v-flex> + </v-layout> + </v-flex> +</template> + +<script> +export default { + name: 'p-d-skos-preflabel', + props: { + o: { + type: Object, + required: true + }, + p: { + type: String + } + }, + computed: { + displaylang: function() { + let lang + let somelang + for (let label of this.o['skos:prefLabel']) { + somelang = label['@language'] + if (label['@language'] === this.$i18n.locale) { + lang = this.$i18n.locale + } + } + return lang ? lang : somelang + } + } +} +</script> diff --git a/src/components/display/phaidra_display/PDStudyPlan.vue b/src/components/display/phaidra_display/PDStudyPlan.vue new file mode 100644 index 0000000000000000000000000000000000000000..f278c574300ba1a43e0c30884057d050c1deaa29 --- /dev/null +++ b/src/components/display/phaidra_display/PDStudyPlan.vue @@ -0,0 +1,32 @@ +<template> + <v-flex> + <v-layout row wrap> + <template v-for="(l, i) in o['skos:prefLabel']"> + <v-flex md4 xs12 class="pdlabel primary--text" :key="'pl'+i">{{ $t('Study plan') }}</v-flex> + <v-flex md8 xs12 :key="'spl'+i"> + <v-layout column> + <v-flex class="valuefield" >{{ l['@value'] }}</v-flex> + <template v-for="(id, i) in o['skos:notation']"> + <v-flex class="grey--text" xs4 :key="'notation'+i">[{{ id }}]</v-flex> + </template> + </v-layout> + </v-flex> + + </template> + </v-layout> + + </v-flex> +</template> + +<script> +export default { + name: 'p-d-study-plan', + props: { + o: { + type: Object, + required: true + } + } +} +</script> + diff --git a/src/components/display/phaidra_display/PDTitle.vue b/src/components/display/phaidra_display/PDTitle.vue new file mode 100644 index 0000000000000000000000000000000000000000..a8d3911b09819fa5e7e1839109d0610a005a80b2 --- /dev/null +++ b/src/components/display/phaidra_display/PDTitle.vue @@ -0,0 +1,34 @@ +<template> + <v-flex> + <v-layout column > + <v-flex> + <template v-for="(mt, i) in o['bf:mainTitle']"> + <v-layout :key="'row'+i" row wrap> + <v-flex md4 xs12 class="pdlabel primary--text" :key="'mt'+i">{{ $t(o['@type']) }}<template v-if="mt['@language']"> ({{ mt['@language'] }})</template></v-flex> + <v-flex md8 xs12> + <v-layout column :key="'mtv'+i"> + <v-flex class="valuefield">{{ mt['@value'] }}</v-flex> + <template v-for="(st, i) in o['bf:subtitle']"> + <v-flex class="valuefield" :key="'stv'+i">{{ st['@value'] }}</v-flex> + </template> + </v-layout> + </v-flex> + </v-layout> + </template> + </v-flex> + </v-layout> + </v-flex> +</template> + +<script> +export default { + name: 'p-d-title', + props: { + o: { + type: Object, + required: true + } + } +} +</script> + diff --git a/src/components/display/phaidra_display/PDUwmetadata.vue b/src/components/display/phaidra_display/PDUwmetadata.vue new file mode 100644 index 0000000000000000000000000000000000000000..e7ce685cb6bbbbc5845126c3faf7278db2809945 --- /dev/null +++ b/src/components/display/phaidra_display/PDUwmetadata.vue @@ -0,0 +1,212 @@ +<template> + <v-flex v-if="this.indexdata"> + <v-flex v-for="(title,i) in getTitles()" :key="'title'+i" class="mt-3"> + <v-container fluid> + <v-layout row> + <v-flex class="caption grey--text" xs2>{{ $t('Title') }} ({{ title.lang }})</v-flex> + <v-flex xs9>{{ title.value }}</v-flex> + </v-layout> + </v-container> + </v-flex> + + <v-flex v-for="(role,i) in parsedRolesUwm()" :key="'role'+i" class="mt-3"> + <v-container fluid> + <v-layout row> + <v-flex class="caption grey--text" xs2>{{ role.label }}</v-flex> + <v-flex xs9> + <v-layout column> + <v-flex v-for="(entity,j) in role.entities" :key="j"> + {{ entity.firstname }} {{ entity.lastname }} + <span class="grey--text">{{ entity.institution }}</span> + </v-flex> + </v-layout> + </v-flex> + </v-layout> + </v-container> + </v-flex> + + <v-flex v-if="indexdata.bib_journal" class="mt-3"> + <v-container fluid> + <v-layout row> + <v-flex class="caption grey--text" xs2>{{ $t('Journal') }}</v-flex> + <v-flex xs9> + <v-layout column> + <v-flex v-for="(v,i) in indexdata.bib_journal" :key="i">{{v}}</v-flex> + </v-layout> + </v-flex> + </v-layout> + </v-container> + </v-flex> + + <v-flex v-if="indexdata.bib_volume" class="mt-3"> + <v-container fluid> + <v-layout row> + <v-flex class="caption grey--text" xs2>{{ $t('Volume') }}</v-flex> + <v-flex xs9> + <v-layout column> + <v-flex v-for="(v,i) in indexdata.bib_volume" :key="i">{{v}}</v-flex> + </v-layout> + </v-flex> + </v-layout> + </v-container> + </v-flex> + + <v-flex v-if="indexdata.bib_publisher" class="mt-3"> + <v-container fluid> + <v-layout row> + <v-flex class="caption grey--text" xs2>{{ $t('Publisher') }}</v-flex> + <v-flex xs9> + <v-layout column> + <v-flex v-for="(v,i) in indexdata.bib_publisher" :key="i">{{v}}</v-flex> + </v-layout> + </v-flex> + </v-layout> + </v-container> + </v-flex> + + <v-flex v-if="indexdata.bib_published" class="mt-3"> + <v-container fluid> + <v-layout row> + <v-flex class="caption grey--text" xs2>{{ $t('Published') }}</v-flex> + <v-flex xs9> + <v-layout column> + <v-flex v-for="(v,i) in indexdata.bib_published" :key="i">{{v}}</v-flex> + </v-layout> + </v-flex> + </v-layout> + </v-container> + </v-flex> + + <v-flex v-if="indexdata.bib_publisherlocation" class="mt-3"> + <v-container fluid> + <v-layout row> + <v-flex class="caption grey--text" xs2>{{ $t('Publisher location') }}</v-flex> + <v-flex xs9> + <v-layout column> + <v-flex v-for="(v,i) in indexdata.bib_publisherlocation" :key="i">{{v}}</v-flex> + </v-layout> + </v-flex> + </v-layout> + </v-container> + </v-flex> + + <v-flex v-for="(desc,i) in getDescriptions()" :key="'desc'+i" class="mt-3"> + <v-container fluid> + <v-layout row> + <v-flex class="caption grey--text" xs2>{{ $t('Description') }} ({{ desc.lang }})</v-flex> + <v-flex xs9>{{ desc.value }}</v-flex> + </v-layout> + </v-container> + </v-flex> + + <v-flex class="mt-3" v-if="indexdata.dc_license"> + <v-container fluid> + <v-layout row> + <v-flex class="caption grey--text" xs2>{{ $t('License') }}</v-flex> + <v-flex xs9> + <p-d-license v-if="indexdata.dc_license" :dclicense="indexdata.dc_license[0]"></p-d-license> + </v-flex> + </v-layout> + </v-container> + </v-flex> + </v-flex> +</template> + +<script> +import PDLicense from "./PDLicense"; +import { vocabulary } from "../../../mixins/vocabulary"; + +export default { + name: "p-d-uwmetadata", + mixins: [vocabulary], + props: { + pid: { + type: String + }, + indexdata: { + type: Object, + default: null + } + }, + components: { + PDLicense + }, + methods: { + getRoleLabel: function(role) { + var id = role.substring(role.indexOf(":") + 1); + var roleTerms = this.vocabularies["rolepredicate"].terms; + for (var i = 0; i < roleTerms.length; i++) { + if (roleTerms[i]["@id"] === id) { + return roleTerms[i]["skos:prefLabel"][0]["@value"]; + } + } + }, + getTitles: function() { + var titles = []; + var doc = this.indexdata; + Object.keys(doc).forEach(function(field) { + if (field.startsWith("dc_title_")) { + for (var i = 0; i < doc[field].length; i++) { + titles.push({ + value: doc[field][i], + lang: field.substr(field.length - 3) + }); + } + } + }); + return titles; + }, + getDescriptions: function() { + var descriptions = []; + var doc = this.indexdata; + Object.keys(doc).forEach(function(field) { + if (field.startsWith("dc_description_")) { + for (var i = 0; i < doc[field].length; i++) { + descriptions.push({ + value: doc[field][i], + lang: field.substr(field.length - 3) + }); + } + } + }); + return descriptions; + }, + parsedRolesUwm: function() { + var rolesHash = {}; + if (this.indexdata.uwm_roles_json) { + var sortedContr = JSON.parse(this.indexdata.uwm_roles_json).sort( + function(a, b) { + return a.data_order - b.data_order; + } + ); + for (var i = 0; i < sortedContr.length; i++) { + sortedContr[i].entities = sortedContr[i].entities.sort(function( + a, + b + ) { + return a.data_order - b.data_order; + }); + // merge multiple entities and multiple contributions if they have the same role + if (!rolesHash[sortedContr[i].role]) { + rolesHash[sortedContr[i].role] = { + role: sortedContr[i].role, + label: this.getRoleLabel(sortedContr[i].role), + entities: [] + }; + } + for (var j = 0; j < sortedContr[i].entities.length; j++) { + rolesHash[sortedContr[i].role]["entities"].push( + sortedContr[i].entities[j] + ); + } + } + } + var roles = []; + Object.keys(rolesHash).forEach(function(r) { + roles.push(rolesHash[r]); + }); + return roles; + } + } +}; +</script> diff --git a/src/components/display/phaidra_display/PDValue.vue b/src/components/display/phaidra_display/PDValue.vue new file mode 100644 index 0000000000000000000000000000000000000000..49c1e49fdca02065aeccbd5d0fad7ebbe866e9b7 --- /dev/null +++ b/src/components/display/phaidra_display/PDValue.vue @@ -0,0 +1,24 @@ +<template> + <v-flex> + <v-layout row wrap> + <v-flex md4 xs12 class="pdlabel primary--text">{{ $t(p) }}</v-flex> + <v-flex md8 xs12>{{ o }}</v-flex> + </v-layout> + </v-flex> +</template> + +<script> +export default { + name: 'p-d-value', + props: { + o: { + type: String, + required: true + }, + p: { + type: String + } + } +} +</script> + diff --git a/src/components/input/phaidra_inputs/PIAdaptation.vue b/src/components/input/phaidra_inputs/PIAdaptation.vue new file mode 100644 index 0000000000000000000000000000000000000000..ed5572afd95b71fd5e228f7d9b6544e72ef3e426 --- /dev/null +++ b/src/components/input/phaidra_inputs/PIAdaptation.vue @@ -0,0 +1,192 @@ +<template> + <v-layout row> + <v-flex xs12> + <v-card> + <v-card-title class="subheading grey white--text"> + <span>{{ $t(label) }}</span> + <v-spacer></v-spacer> + <v-menu open-on-hover bottom offset-y v-if="actions.length"> + <v-btn slot="activator" icon dark> + <v-icon dark>more_vert</v-icon> + </v-btn> + <v-list> + <v-list-tile + v-for="(action, i) in actions" + :key="i" + @click="$emit(action.event, $event)" + > + <v-list-tile-title>{{ action.title }}</v-list-tile-title> + </v-list-tile> + </v-list> + </v-menu> + </v-card-title> + <v-divider></v-divider> + <v-card-text class="mt-4"> + <v-layout column> + <v-flex> + <v-layout row> + <v-flex xs4> + <v-text-field + :value="title" + :label="$t('Title')" + v-on:blur="$emit('input-title',$event.target.value)" + box + ></v-text-field> + </v-flex> + <v-flex xs4> + <v-text-field + :value="subtitle" + :label="$t('Subtitle')" + v-on:blur="$emit('input-subtitle',$event.target.value)" + box + ></v-text-field> + </v-flex> + <v-flex xs4> + <v-autocomplete + :value="getTerm('lang', titleLanguage)" + v-on:input="$emit('input-title-language', $event)" + :items="vocabularies['lang'].terms" + :filter="autocompleteFilter" + hide-no-data + :label="$t('Language')" + box + return-object + clearable + > + <template slot="item" slot-scope="{ item }"> + <v-list-tile-content two-line> + <v-list-tile-title v-html="`${getLocalizedTermLabel('lang', item['@id'])}`"></v-list-tile-title> + <v-list-tile-sub-title v-html="`${item['@id']}`"></v-list-tile-sub-title> + </v-list-tile-content> + </template> + <template slot="selection" slot-scope="{ item }"> + <v-list-tile-content> + <v-list-tile-title v-html="`${getLocalizedTermLabel('lang', item['@id'])}`"></v-list-tile-title> + </v-list-tile-content> + </template> + </v-autocomplete> + </v-flex> + </v-layout> + + <v-layout row> + <template v-if="showname"> + <v-flex xs4> + <v-text-field + :value="name" + :label="$t('Name')" + v-on:blur="$emit('input-name',$event.target.value)" + box + ></v-text-field> + </v-flex> + </template> + <template v-else> + <v-flex xs4> + <v-text-field + :value="firstname" + :label="$t('Firstname')" + v-on:blur="$emit('input-firstname',$event.target.value)" + box + ></v-text-field> + </v-flex> + <v-flex xs4> + <v-text-field + :value="lastname" + :label="$t('Lastname')" + v-on:blur="$emit('input-lastname',$event.target.value)" + box + ></v-text-field> + </v-flex> + </template> + <v-flex xs4> + <v-autocomplete + :disabled="disablerole" + v-on:input="$emit('input-role', $event)" + :label="$t('Role')" + :items="vocabularies['rolepredicate'].terms" + :value="getTerm('rolepredicate', role)" + :filter="autocompleteFilter" + box + return-object + clearable + > + <template slot="item" slot-scope="{ item }"> + <v-list-tile-content two-line> + <v-list-tile-title + v-html="`${getLocalizedTermLabel('rolepredicate', item['@id'])}`" + ></v-list-tile-title> + <v-list-tile-sub-title v-html="`${item['@id']}`"></v-list-tile-sub-title> + </v-list-tile-content> + </template> + <template slot="selection" slot-scope="{ item }"> + <v-list-tile-content> + <v-list-tile-title + v-html="`${getLocalizedTermLabel('rolepredicate', item['@id'])}`" + ></v-list-tile-title> + </v-list-tile-content> + </template> + </v-autocomplete> + </v-flex> + </v-layout> + </v-flex> + </v-layout> + </v-card-text> + </v-card> + </v-flex> + </v-layout> +</template> + +<script> +import { vocabulary } from "../../../mixins/vocabulary"; +import { fieldproperties } from "../../../mixins/fieldproperties"; + +export default { + name: "p-i-adaptation", + mixins: [vocabulary, fieldproperties], + props: { + type: { + type: String + }, + label: { + type: String + }, + title: { + type: String + }, + subtitle: { + type: String + }, + titleLanguage: { + type: String + }, + firstname: { + type: String + }, + lastname: { + type: String + }, + name: { + type: String + }, + role: { + type: String + }, + disablerole: { + type: Boolean, + default: false + }, + showname: { + type: Boolean, + default: false + } + } +}; +</script> + +<style scoped> +.v-btn { + margin: 0; +} +.vertical-center { + align-items: center; +} +</style> diff --git a/src/components/input/phaidra_inputs/PIAssociation.vue b/src/components/input/phaidra_inputs/PIAssociation.vue new file mode 100644 index 0000000000000000000000000000000000000000..f5361b73d6d8fe8ebfb910bab77fccdd24da70c7 --- /dev/null +++ b/src/components/input/phaidra_inputs/PIAssociation.vue @@ -0,0 +1,172 @@ +<template> + <v-layout row> + <v-flex xs8> + <v-autocomplete + :value="getTerm(value)" + :required="required" + v-on:input="handleInput($event)" + :rules="required ? [ v => !!v || 'Required'] : []" + :items="orgunits" + :loading="loading" + :filter="autocompleteFilter" + hide-no-data + :label="$t(label)" + box + return-object + clearable + :disabled="disabled" + :messages="path" + > + <template slot="item" slot-scope="{ item }"> + <v-list-tile-content two-line> + <v-list-tile-title v-html="`${getLocalizedTermLabel(item)}`"></v-list-tile-title> + <v-list-tile-sub-title v-html="`${item['@id']}`"></v-list-tile-sub-title> + </v-list-tile-content> + </template> + <template slot="selection" slot-scope="{ item }"> + <v-list-tile-content> + <v-list-tile-title v-html="`${getLocalizedTermLabel(item)}`"></v-list-tile-title> + </v-list-tile-content> + </template> + </v-autocomplete> + </v-flex> + <v-flex xs1 v-if="actions.length"> + <v-menu open-on-hover bottom offset-y> + <v-btn slot="activator" icon> + <v-icon>more_vert</v-icon> + </v-btn> + <v-list> + <v-list-tile v-for="(action, i) in actions" :key="i" @click="$emit(action.event, $event)"> + <v-list-tile-title>{{ action.title }}</v-list-tile-title> + </v-list-tile> + </v-list> + </v-menu> + </v-flex> + </v-layout> +</template> + +<script> +import { fieldproperties } from "../../../mixins/fieldproperties"; + +export default { + name: "p-i-association", + mixins: [fieldproperties], + methods: { + autocompleteFilter: function(item, queryText) { + const lab = item["skos:prefLabel"][this.$i18n.locale] + ? item["skos:prefLabel"][this.$i18n.locale].toLowerCase() + : item["skos:prefLabel"]["eng"].toLowerCase(); + const query = queryText.toLowerCase(); + return lab.indexOf(query) > -1; + }, + getTerm: function(v) { + for (let u of this.orgunits) { + if (u["@id"] === v) { + return u; + } + } + }, + getLocalizedTermLabel: function(item) { + return item["skos:prefLabel"][this.$i18n.locale] + ? item["skos:prefLabel"][this.$i18n.locale] + : item["skos:prefLabel"]["eng"]; + }, + addToOrgunits: function(units, parent) { + for (let u of units) { + this.orgunits.push(u); + u.parent = parent; + if (u["subunits"]) { + if (u.subunits.length > 0) { + this.addToOrgunits(u.subunits, u); + } + } + } + }, + handleInput: function(unit) { + this.path = ""; + let parentpath = []; + this.getParentPath(unit, parentpath); + for (let u of parentpath.reverse()) { + this.path = this.path + u["skos:prefLabel"][this.$i18n.locale] + " > "; + } + this.path = this.path + unit["skos:prefLabel"][this.$i18n.locale]; + this.$emit("input", unit); + }, + getParentPath: function(unit, parentpath) { + if (unit["parent"]) { + parentpath.push(unit.parent); + this.getParentPath(unit.parent, parentpath); + } + }, + loadOrgUnits: function() { + var self = this; + this.loading = true; + var url = + self.$store.state.settings.instance.api + "/directory/org_get_units"; + var promise = fetch(url, { + method: "GET", + mode: "cors", + headers: { + "X-XSRF-TOKEN": this.$store.state.user.token + } + }) + .then(function(response) { + return response.json(); + }) + .then(function(json) { + if (json.alerts && json.alerts.length > 0) { + self.$store.commit("setAlerts", json.alerts); + } + self.loading = false; + self.templatedialog = false; + self.addToOrgunits(json.units, null); + }) + .catch(function(error) { + console.log(error); + }); + return promise; + } + }, + props: { + value: { + type: String + }, + label: { + type: String, + required: true + }, + required: { + type: Boolean + }, + disabled: { + type: Boolean, + default: false + } + }, + data() { + return { + loading: false, + orgunits: [], + path: "" + }; + }, + mounted: function() { + this.$nextTick(function() { + let self = this; + this.loadOrgUnits().then(function() { + if (self.value) { + let term = self.getTerm(self.value); + // emit input to set skos:prefLabel in parent + self.handleInput(term); + } + }); + }); + } +}; +</script> + +<style scoped> +.v-btn { + margin: 0; +} +</style> diff --git a/src/components/input/phaidra_inputs/PIBfPublication.vue b/src/components/input/phaidra_inputs/PIBfPublication.vue new file mode 100644 index 0000000000000000000000000000000000000000..bfc0d40aa36cb1d11ef99798eafee2556fb60952 --- /dev/null +++ b/src/components/input/phaidra_inputs/PIBfPublication.vue @@ -0,0 +1,105 @@ +<template> +<v-layout row> + <v-flex xs12> + + <v-card > + <v-card-title class="subheading grey white--text"> + <span>{{ $t(label) }}</span> + <v-spacer></v-spacer> + <v-menu open-on-hover bottom offset-y v-if="actions.length"> + <v-btn slot="activator" icon dark> + <v-icon dark>more_vert</v-icon> + </v-btn> + <v-list> + <v-list-tile v-for="(action, i) in actions" :key="i" @click="$emit(action.event, $event)"> + <v-list-tile-title>{{ action.title }}</v-list-tile-title> + </v-list-tile> + </v-list> + </v-menu> + </v-card-title> + <v-divider></v-divider> + <v-card-text class="mt-4"> + + <v-layout row> + <v-flex xs4> + <v-text-field + :value="publisherName" + v-on:blur="$emit('input-publisher-name',$event.target.value)" + :label="$t(publisherNameLabel ? publisherNameLabel : '')" + :required="required" + :rules="required ? [ v => !!v || 'Required'] : []" + box + ></v-text-field> + </v-flex> + <v-flex xs4> + <v-text-field + :value="publishingPlace" + v-on:blur="$emit('input-publishing-place',$event.target.value)" + :label="$t(publishingPlaceLabel ? publishingPlaceLabel : '')" + :required="required" + :rules="required ? [ v => !!v || 'Required'] : []" + box + ></v-text-field> + </v-flex> + <v-flex xs4> + <v-text-field + :value="publishingDate" + v-on:blur="$emit('input-publishing-date',$event.target.value)" + :label="$t(publishingDateLabel ? publishingDateLabel : '')" + :required="required" + :hint="'Format YYYY-MM-DD'" + :rules="[validationrules.date]" + box + ></v-text-field> + </v-flex> + </v-layout> + + </v-card-text> + </v-card> + </v-flex> + </v-layout> +</template> + +<script> +import qs from "qs"; +import { fieldproperties } from "../../../mixins/fieldproperties"; +import { validationrules } from "../../../mixins/validationrules"; + +export default { + name: "p-i-bf-publication", + mixins: [validationrules, fieldproperties], + props: { + publisherName: { + type: String, + required: true + }, + publishingDate: { + type: String + }, + publishingPlace: { + type: String + }, + label: { + type: String + }, + publisherNameLabel: { + type: String + }, + publishingDateLabel: { + type: String + }, + publishingPlaceLabel: { + type: String + }, + required: { + type: Boolean + } + } +}; +</script> + +<style scoped> +.v-btn { + margin: 0; +} +</style> diff --git a/src/components/input/phaidra_inputs/PICitation.vue b/src/components/input/phaidra_inputs/PICitation.vue new file mode 100644 index 0000000000000000000000000000000000000000..eba1b3d6a75339f6e89ab7ccaa4f50d64f6953cc --- /dev/null +++ b/src/components/input/phaidra_inputs/PICitation.vue @@ -0,0 +1,146 @@ +<template> + <v-layout row> + <v-flex xs2> + <v-autocomplete + v-on:input="$emit('input-citation-type', $event)" + :label="$t('Citation type')" + :items="vocabularies['citationpredicate'].terms" + :value="getTerm('citationpredicate', type)" + :filter="autocompleteFilter" + :disabled="disabletype" + box + return-object + clearable + > + <template slot="item" slot-scope="{ item }"> + <v-list-tile-content two-line> + <v-list-tile-title + v-html="`${getLocalizedTermLabel('citationpredicate', item['@id'])}`" + ></v-list-tile-title> + <v-list-tile-sub-title v-html="`${item['@id']}`"></v-list-tile-sub-title> + </v-list-tile-content> + </template> + <template slot="selection" slot-scope="{ item }"> + <v-list-tile-content> + <v-list-tile-title + v-html="`${getLocalizedTermLabel('citationpredicate', item['@id'])}`" + ></v-list-tile-title> + </v-list-tile-content> + </template> + </v-autocomplete> + </v-flex> + <v-flex xs4> + <v-text-field + :value="citation" + v-on:input="$emit('input-citation', $event)" + :label="$t(citationLabel)" + :required="required" + :rules="required ? [ v => !!v || 'Required'] : []" + box + ></v-text-field> + </v-flex> + <v-flex xs2> + <v-autocomplete + :value="getTerm('lang', citationLanguage)" + v-on:input="$emit('input-citation-language', $event)" + :items="vocabularies['lang'].terms" + :filter="autocompleteFilter" + hide-no-data + :label="$t('Language')" + box + return-object + clearable + > + <template slot="item" slot-scope="{ item }"> + <v-list-tile-content two-line> + <v-list-tile-title v-html="`${getLocalizedTermLabel('lang', item['@id'])}`"></v-list-tile-title> + <v-list-tile-sub-title v-html="`${item['@id']}`"></v-list-tile-sub-title> + </v-list-tile-content> + </template> + <template slot="selection" slot-scope="{ item }"> + <v-list-tile-content> + <v-list-tile-title v-html="`${getLocalizedTermLabel('lang', item['@id'])}`"></v-list-tile-title> + </v-list-tile-content> + </template> + </v-autocomplete> + </v-flex> + <v-flex xs4> + <v-text-field + :value="identifier" + v-on:input="$emit('input-identifier', $event)" + :label="$t(identifierLabel)" + :required="required" + :rules="required ? [ v => !!v || 'Required'] : []" + box + ></v-text-field> + </v-flex> + <v-flex xs1 v-if="actions.length"> + <v-menu open-on-hover bottom offset-y> + <v-btn slot="activator" icon> + <v-icon>more_vert</v-icon> + </v-btn> + <v-list> + <v-list-tile v-for="(action, i) in actions" :key="i" @click="$emit(action.event, $event)"> + <v-list-tile-title>{{ action.title }}</v-list-tile-title> + </v-list-tile> + </v-list> + </v-menu> + </v-flex> + </v-layout> +</template> + +<script> +import { vocabulary } from "../../../mixins/vocabulary"; +import { fieldproperties } from "../../../mixins/fieldproperties"; + +export default { + name: "p-i-citation", + mixins: [vocabulary, fieldproperties], + props: { + citation: { + type: String + }, + citationLanguage: { + type: String + }, + identifier: { + type: String + }, + type: { + type: String + }, + citationLabel: { + type: String, + required: true + }, + identifierLabel: { + type: String, + required: true + }, + required: { + type: Boolean + }, + disabletype: { + type: Boolean + } + }, + mounted: function() { + this.$nextTick(function() { + this.loading = !this.vocabularies["citationpredicate"].loaded; + // emit input to set skos:prefLabel in parent + if (this.type) { + this.$emit( + "input-citation-type", + this.getTerm("citationpredicate", this.type) + ); + } + }); + } +}; +</script> + +<style scoped> +.v-btn { + margin: 0; +} +</style> diff --git a/src/components/input/phaidra_inputs/PIContainedIn.vue b/src/components/input/phaidra_inputs/PIContainedIn.vue new file mode 100644 index 0000000000000000000000000000000000000000..937ebf41a72ce445670573cb329f4ac1e838c4a3 --- /dev/null +++ b/src/components/input/phaidra_inputs/PIContainedIn.vue @@ -0,0 +1,408 @@ +<template> + + <v-row> + <v-col cols="12"> + <v-card > + <v-card-title class="title font-weight-light grey white--text"> + <span>{{ $t(label) }}</span> + </v-card-title> + <v-divider></v-divider> + <v-card-text class="mt-4"> + <v-row> + <v-col> + <v-row > + <v-col cols="12" :md="multilingual ? 4 : 6"> + <v-text-field + :value="title" + :label="$t('Title')" + v-on:blur="$emit('input-title',$event.target.value)" + :filled="inputStyle==='filled'" + :outlined="inputStyle==='outlined'" + ></v-text-field> + </v-col> + <v-col cols="12" :md="multilingual ? 4 : 6"> + <v-text-field + :value="subtitle" + :label="$t('Subtitle')" + v-on:blur="$emit('input-subtitle',$event.target.value)" + :filled="inputStyle==='filled'" + :outlined="inputStyle==='outlined'" + ></v-text-field> + </v-col> + <v-col cols="4" v-if="multilingual"> + <v-autocomplete + :value="getTerm('lang', titleLanguage)" + v-on:input="$emit('input-title-language', $event)" + :items="vocabularies['lang'].terms" + :filter="autocompleteFilter" + hide-no-data + :label="$t('Language')" + :filled="inputStyle==='filled'" + :outlined="inputStyle==='outlined'" + return-object + clearable + :item-value="'@id'" + > + <template slot="item" slot-scope="{ item }"> + <v-list-item-content two-line> + <v-list-item-title v-html="`${getLocalizedTermLabel('lang', item['@id'])}`"></v-list-item-title> + <v-list-item-subtitle v-if="showIds" v-html="`${item['@id']}`"></v-list-item-subtitle> + </v-list-item-content> + </template> + <template slot="selection" slot-scope="{ item }"> + <v-list-item-content> + <v-list-item-title v-html="`${getLocalizedTermLabel('lang', item['@id'])}`"></v-list-item-title> + </v-list-item-content> + </template> + </v-autocomplete> + </v-col> + </v-row> + <v-row v-for="(role, i) in roles" :key="'role'+i"> + <v-col cols="4"> + <v-autocomplete + :disabled="disablerole" + v-on:input="$emit('input-role', { role: role, roleTerm: $event })" + :label="$t('Role')" + :items="vocabularies['rolepredicate'].terms" + :value="getTerm('rolepredicate', role.role)" + :filter="autocompleteFilter" + :filled="inputStyle==='filled'" + :outlined="inputStyle==='outlined'" + return-object + clearable + :item-value="'@id'" + > + <template slot="item" slot-scope="{ item }"> + <v-list-item-content two-line> + <v-list-item-title v-html="`${getLocalizedTermLabel('rolepredicate', item['@id'])}`"></v-list-item-title> + <v-list-item-subtitle v-if="showIds" v-html="`${item['@id']}`"></v-list-item-subtitle> + </v-list-item-content> + </template> + <template slot="selection" slot-scope="{ item }"> + <v-list-item-content> + <v-list-item-title v-html="`${getLocalizedTermLabel('rolepredicate', item['@id'])}`"></v-list-item-title> + </v-list-item-content> + </template> + </v-autocomplete> + </v-col> + <template v-if="showname"> + <v-col cols="4" > + <v-text-field + :value="role.name" + :label="$t('Name')" + v-on:blur="$emit('input-role',{ role: role, name: $event.target.value })" + :filled="inputStyle==='filled'" + :outlined="inputStyle==='outlined'" + ></v-text-field> + </v-col> + </template> + <template v-else> + <v-col cols="3"> + <v-text-field + :value="role.firstname" + :label="$t('Firstname')" + v-on:blur="$emit('input-role',{ role: role, firstname: $event.target.value })" + :filled="inputStyle==='filled'" + :outlined="inputStyle==='outlined'" + ></v-text-field> + </v-col> + <v-col cols="3"> + <v-text-field + :value="role.lastname" + :label="$t('Lastname')" + v-on:blur="$emit('input-role',{ role: role, lastname: $event.target.value })" + :filled="inputStyle==='filled'" + :outlined="inputStyle==='outlined'" + ></v-text-field> + </v-col> + </template> + <v-col cols="1" v-if="roleActions.length"> + <v-menu open-on-hover bottom offset-y> + <template v-slot:activator="{ on }"> + <v-btn v-on="on" icon> + <v-icon>mdi-dots-vertical</v-icon> + </v-btn> + </template> + <v-list> + <v-list-item v-for="(action, i) in roleActions" :key="i" @click="$emit(action.event, role)"> + <v-list-item-title>{{ action.title }}</v-list-item-title> + </v-list-item> + </v-list> + </v-menu> + </v-col> + </v-row> + <v-row> + <v-col cols="12" md="6"> + <v-text-field + :value="pageStart" + :label="$t(pageStartLabel)" + v-on:blur="$emit('input-page-start',$event.target.value)" + :filled="inputStyle==='filled'" + :outlined="inputStyle==='outlined'" + ></v-text-field> + </v-col> + <v-col cols="12" md="6"> + <v-text-field + :value="pageEnd" + :label="$t(pageEndLabel)" + v-on:blur="$emit('input-page-end',$event.target.value)" + :filled="inputStyle==='filled'" + :outlined="inputStyle==='outlined'" + ></v-text-field> + </v-col> + </v-row> + </v-col> + </v-row> + <v-row no-gutters> + <v-col cols="12"> + <v-card> + <v-card-title class="title font-weight-light grey white--text"> + <span>{{ $t(seriesLabel) }}</span> + <v-spacer></v-spacer> + <span> + <v-icon dark v-show="collapseSeriesModel" @click="collapseSeriesModel=!collapseSeriesModel">mdi-arrow-right-drop-circle</v-icon> + <v-icon dark v-show="!collapseSeriesModel" @click="collapseSeriesModel=!collapseSeriesModel">mdi-arrow-down-drop-circle</v-icon> + </span> + </v-card-title> + <v-card-text class="mt-4" v-show="!collapseSeriesModel"> + <v-container> + <v-row > + <v-col cols="12" :md="multilingual ? 10 : 12"> + <v-text-field + :value="seriesTitle" + :label="$t('Title')" + v-on:blur="$emit('input-series-title',$event.target.value)" + :filled="inputStyle==='filled'" + :outlined="inputStyle==='outlined'" + ></v-text-field> + </v-col> + <v-col cols="12" md="2" v-if="multilingual"> + <v-autocomplete + :value="getTerm('lang', seriesTitleLanguage)" + v-on:input="$emit('input-series-title-language', $event )" + :items="vocabularies['lang'].terms" + :item-value="'@id'" + :filter="autocompleteFilter" + hide-no-data + :label="$t('Language')" + :filled="inputStyle==='filled'" + :outlined="inputStyle==='outlined'" + return-object + clearable + > + <template slot="item" slot-scope="{ item }"> + <v-list-item-content two-line> + <v-list-item-title v-html="`${getLocalizedTermLabel('lang', item['@id'])}`"></v-list-item-title> + <v-list-item-subtitle v-if="showIds" v-html="`${item['@id']}`"></v-list-item-subtitle> + </v-list-item-content> + </template> + <template slot="selection" slot-scope="{ item }"> + <v-list-item-content> + <v-list-item-title v-html="`${getLocalizedTermLabel('lang', item['@id'])}`"></v-list-item-title> + </v-list-item-content> + </template> + </v-autocomplete> + </v-col> + + </v-row> + + <v-row > + + <v-col cols="4" v-if="!hideSeriesVolume"> + <v-text-field + :value="seriesVolume" + :label="$t('Volume')" + v-on:blur="$emit('input-series-volume',$event.target.value)" + :filled="inputStyle==='filled'" + :outlined="inputStyle==='outlined'" + ></v-text-field> + </v-col> + + <v-col cols="4" v-if="!hideSeriesIssue"> + <v-text-field + :value="seriesIssue" + :label="$t('Issue')" + v-on:blur="$emit('input-series-issue',$event.target.value)" + :filled="inputStyle==='filled'" + :outlined="inputStyle==='outlined'" + ></v-text-field> + </v-col> + + <v-col cols="4" v-if="!hideSeriesIssued"> + + <v-text-field + :value="seriesIssued" + v-on:blur="$emit('input-series-issued',$event.target.value)" + :label="$t(seriesIssuedDateLabel ? seriesIssuedDateLabel : 'Issued')" + :hint="'Format YYYY-MM-DD'" + :rules="[validationrules.date]" + :filled="inputStyle==='filled'" + :outlined="inputStyle==='outlined'" + ></v-text-field> + + </v-col> + + </v-row> + + <v-row > + + <v-col cols="4" v-if="!hideSeriesIssn"> + <v-text-field + :value="seriesIssn" + :label="$t('ISSN')" + v-on:blur="$emit('input-series-issn',$event.target.value)" + :filled="inputStyle==='filled'" + :outlined="inputStyle==='outlined'" + ></v-text-field> + </v-col> + + <v-col cols="4" v-if="!hideSeriesIdentifier"> + <v-text-field + :value="seriesIdentifier" + :label="$t('Identifier')" + v-on:blur="$emit('input-series-identifier',$event.target.value)" + :filled="inputStyle==='filled'" + :outlined="inputStyle==='outlined'" + ></v-text-field> + </v-col> + + </v-row> + </v-container> + </v-card-text> + </v-card> + </v-col> + </v-row> + </v-card-text> + </v-card> + </v-col> + </v-row> +</template> + +<script> +import { fieldproperties } from "../../../mixins/fieldproperties"; +import { vocabulary } from "../../../mixins/vocabulary"; +import { validationrules } from "../../../mixins/validationrules"; + +export default { + name: "p-i-contained-in", + mixins: [fieldproperties, vocabulary, validationrules], + props: { + type: { + type: String + }, + multilingual: { + type: Boolean + }, + label: { + type: String + }, + title: { + type: String + }, + subtitle: { + type: String + }, + titleLanguage: { + type: String + }, + roles: { + type: Array + }, + disablerole: { + type: Boolean, + default: false + }, + showname: { + type: Boolean, + default: false + }, + showIds: { + type: Boolean, + default: false + }, + pageStartLabel: { + type: String + }, + pageEndLabel: { + type: String + }, + pageStart: { + type: String + }, + pageEnd: { + type: String + }, + seriesLabel: { + type: String + }, + seriesTitle: { + type: String + }, + seriesTitleLanguage: { + type: String + }, + hideSeriesVolume: { + type: Boolean + }, + seriesVolume: { + type: String + }, + hideSeriesIssue: { + type: Boolean + }, + seriesIssue: { + type: String + }, + hideSeriesIssued: { + type: Boolean + }, + seriesIssued: { + type: String + }, + seriesIssuedDateLabel: { + type: String + }, + hideSeriesIssn: { + type: Boolean + }, + seriesIssn: { + type: String + }, + hideSeriesIdentifier: { + type: Boolean, + default: true + }, + seriesIdentifier: { + type: String + }, + collapseSeries: { + type: Boolean, + default: false + } + }, + computed: { + roleActions: function() { + var arr = []; + arr.push({ title: this.$t("Remove"), event: "remove-role" }); + arr.push({ title: this.$t("Duplicate"), event: "add-role" }); + arr.push({ title: this.$t("Move up"), event: "up-role" }); + arr.push({ title: this.$t("Move down"), event: "down-role" }); + return arr; + } + }, + data() { + return { + collapseSeriesModel: this.collapseSeries + }; + } +}; +</script> + +<style scoped> +.v-btn { + margin: 0; +} +.vertical-center { + align-items: center; +} +</style> diff --git a/src/components/input/phaidra_inputs/PIDateEdtf.vue b/src/components/input/phaidra_inputs/PIDateEdtf.vue new file mode 100644 index 0000000000000000000000000000000000000000..00f189a859cfddfdea08665d4f9e625335354daf --- /dev/null +++ b/src/components/input/phaidra_inputs/PIDateEdtf.vue @@ -0,0 +1,94 @@ +<template> + <v-layout row> + <v-flex xs4 v-if="!hideType"> + <v-autocomplete + v-on:input="$emit('input-date-type', $event)" + :label="$t('Type of date')" + :items="vocabularies['datepredicate'].terms" + :value="getTerm('datepredicate', type)" + :filter="autocompleteFilter" + box + return-object + clearable + > + <template slot="item" slot-scope="{ item }"> + <v-list-tile-content two-line> + <v-list-tile-title v-html="`${getLocalizedTermLabel('datepredicate', item['@id'])}`"></v-list-tile-title> + <v-list-tile-sub-title v-html="`${item['@id']}`"></v-list-tile-sub-title> + </v-list-tile-content> + </template> + <template slot="selection" slot-scope="{ item }"> + <v-list-tile-content> + <v-list-tile-title v-html="`${getLocalizedTermLabel('datepredicate', item['@id'])}`"></v-list-tile-title> + </v-list-tile-content> + </template> + </v-autocomplete> + </v-flex> + <v-flex xs4> + <v-text-field + :value="value" + v-on:blur="$emit('input-date',$event.target.value)" + :label="$t(dateLabel ? dateLabel : '')" + :required="required" + :hint="'Format YYYY-MM-DD'" + :rules="[validationrules.date]" + box + ></v-text-field> + </v-flex> + <v-flex xs1 v-if="actions.length"> + <v-menu open-on-hover bottom offset-y> + <v-btn slot="activator" icon> + <v-icon>more_vert</v-icon> + </v-btn> + <v-list> + <v-list-tile v-for="(action, i) in actions" :key="i" @click="$emit(action.event, $event)"> + <v-list-tile-title>{{ action.title }}</v-list-tile-title> + </v-list-tile> + </v-list> + </v-menu> + </v-flex> + </v-layout> +</template> + +<script> +import { vocabulary } from "../../../mixins/vocabulary"; +import { fieldproperties } from "../../../mixins/fieldproperties"; +import { validationrules } from "../../../mixins/validationrules"; + +export default { + name: "p-i-date-edtf", + mixins: [vocabulary, fieldproperties, validationrules], + props: { + value: { + type: String + }, + dateLabel: { + type: String + }, + type: { + type: String + }, + hideType: { + type: Boolean + }, + required: { + type: Boolean + } + }, + mounted: function() { + this.$nextTick(function() { + this.loading = !this.vocabularies["datepredicate"].loaded; + // emit input to set skos:prefLabel in parent + if (this.type) { + this.$emit("input-date-type", this.getTerm("datepredicate", this.type)); + } + }); + } +}; +</script> + +<style scoped> +.v-btn { + margin: 0; +} +</style> diff --git a/src/components/input/phaidra_inputs/PIDimension.vue b/src/components/input/phaidra_inputs/PIDimension.vue new file mode 100644 index 0000000000000000000000000000000000000000..0f93fe357e79154ba7379b8d767515e54322b987 --- /dev/null +++ b/src/components/input/phaidra_inputs/PIDimension.vue @@ -0,0 +1,73 @@ +<template> + <v-layout row> + <v-flex xs2> + <v-text-field + :value="value" + v-on:blur="$emit('input-value',$event.target.value)" + :label="$t(label)" + box + ></v-text-field> + </v-flex> + <v-flex xs2> + <v-select + v-on:blur="$emit('input-unit',$event.target.value)" + :label="$t('Unit')" + :items="vocabularies['uncefact'].terms" + :value="getTerm('uncefact', unit)" + box + > + <template slot="item" slot-scope="{ item }"> + <v-list-tile-content two-line> + <v-list-tile-title v-html="`${getLocalizedTermLabel('uncefact', item['@id'])}`"></v-list-tile-title> + <v-list-tile-sub-title v-html="`${item['@id']}`"></v-list-tile-sub-title> + </v-list-tile-content> + </template> + <template slot="selection" slot-scope="{ item }"> + <v-list-tile-content> + <v-list-tile-title v-html="`${getLocalizedTermLabel('uncefact', item['@id'])}`"></v-list-tile-title> + </v-list-tile-content> + </template> + </v-select> + </v-flex> + <v-flex xs1 v-if="actions.length"> + <v-menu open-on-hover bottom offset-y> + <v-btn slot="activator" icon> + <v-icon>more_vert</v-icon> + </v-btn> + <v-list> + <v-list-tile v-for="(action, i) in actions" :key="i" @click="$emit(action.event, $event)"> + <v-list-tile-title>{{ action.title }}</v-list-tile-title> + </v-list-tile> + </v-list> + </v-menu> + </v-flex> + </v-layout> +</template> + +<script> +import { vocabulary } from "../../../mixins/vocabulary"; +import { fieldproperties } from "../../../mixins/fieldproperties"; + +export default { + name: "p-i-dimension", + mixins: [vocabulary, fieldproperties], + props: { + unit: { + type: String + }, + value: { + type: String + }, + label: { + type: String, + required: true + } + } +}; +</script> + +<style scoped> +.v-btn { + margin: 0; +} +</style> \ No newline at end of file diff --git a/src/components/input/phaidra_inputs/PIDuration.vue b/src/components/input/phaidra_inputs/PIDuration.vue new file mode 100644 index 0000000000000000000000000000000000000000..229c28bcbc26a96e3665ea7f62d61e9f6aefe533 --- /dev/null +++ b/src/components/input/phaidra_inputs/PIDuration.vue @@ -0,0 +1,128 @@ +<template> + <v-layout row> + <v-flex xs3 v-if="!hideHours"> + <v-text-field + v-model="hours" + :rules="[v => (!v || (parseInt(v, 10) >= 0)) || 'Must be a non negative integer']" + type="number" + :label="$t('Duration')" + :suffix="$t('hours')" + box + ></v-text-field> + </v-flex> + <v-flex xs3 v-if="!hideMinutes"> + <v-text-field + v-model="minutes" + :rules="[v => (!v || (parseInt(v, 10) >= 0)) || 'Must be a non negative integer']" + type="number" + :label="$t('Duration')" + :suffix="$t('minutes')" + box + ></v-text-field> + </v-flex> + <v-flex xs3 v-if="!hideSeconds"> + <v-text-field + v-model="seconds" + :rules="[v => (!v || (parseInt(v, 10) >= 0)) || 'Must be a non negative integer']" + type="number" + :label="$t('Duration')" + :suffix="$t('seconds')" + box + ></v-text-field> + </v-flex> + <v-flex xs1 v-if="actions.length"> + <v-menu open-on-hover bottom offset-y> + <v-btn slot="activator" icon> + <v-icon>more_vert</v-icon> + </v-btn> + <v-list> + <v-list-tile v-for="(action, i) in actions" :key="i" @click="$emit(action.event, $event)"> + <v-list-tile-title>{{ action.title }}</v-list-tile-title> + </v-list-tile> + </v-list> + </v-menu> + </v-flex> + </v-layout> +</template> + +<script> +import { fieldproperties } from "../../../mixins/fieldproperties"; + +export default { + name: "p-i-duration", + mixins: [fieldproperties], + props: { + /* + hoursValue: { + type: Number + }, + minutesValue: { + type: Number + }, + secondsValue: { + type: Number + }, + */ + value: { + type: String + }, + hideHours: { + type: Boolean + }, + hideMinutes: { + type: Boolean + }, + hideSeconds: { + type: Boolean + }, + label: { + type: String, + required: true + } + }, + watch: { + hours: function(val) { + this.$emit("input", this.duration); + }, + minutes: function(val) { + this.$emit("input", this.duration); + }, + seconds: function(val) { + this.$emit("input", this.duration); + } + }, + computed: { + duration: { + get: function() { + return ( + "PT" + this.hours + "H" + this.minutes + "M" + this.seconds + "S" + ); + }, + set: function(v) { + let m = this.value.match(/PT(\d+)H(\d+)M(\d+)S/); + if (m) { + this.hours = m[1]; + this.minutes = m[2]; + this.seconds = m[3]; + } + } + } + }, + data() { + return { + hours: 0, + minutes: 0, + seconds: 0 + }; + }, + mounted: function() { + this.duration = this.value; + } +}; +</script> + +<style scoped> +.v-btn { + margin: 0; +} +</style> \ No newline at end of file diff --git a/src/components/input/phaidra_inputs/PIEntity.vue b/src/components/input/phaidra_inputs/PIEntity.vue new file mode 100644 index 0000000000000000000000000000000000000000..b6965fec91f8331eabcb9401775704ad7196d4a0 --- /dev/null +++ b/src/components/input/phaidra_inputs/PIEntity.vue @@ -0,0 +1,161 @@ +<template> + <v-layout row> + <v-flex xs4 v-if="!hideRole"> + <v-autocomplete + :disabled="disablerole" + v-on:input="$emit('input-role', $event)" + :label="$t('Role')" + :items="vocabularies['rolepredicate'].terms" + :value="getTerm('rolepredicate', role)" + :filter="autocompleteFilter" + box + return-object + clearable + > + <template slot="item" slot-scope="{ item }"> + <v-list-tile-content two-line> + <v-list-tile-title v-html="`${getLocalizedTermLabel('rolepredicate', item['@id'])}`"></v-list-tile-title> + <v-list-tile-sub-title v-html="`${item['@id']}`"></v-list-tile-sub-title> + </v-list-tile-content> + </template> + <template slot="selection" slot-scope="{ item }"> + <v-list-tile-content> + <v-list-tile-title v-html="`${getLocalizedTermLabel('rolepredicate', item['@id'])}`"></v-list-tile-title> + </v-list-tile-content> + </template> + </v-autocomplete> + </v-flex> + <template v-if="type === 'schema:Person'"> + <template v-if="showname"> + <v-flex xs4> + <v-text-field + :value="name" + :label="$t('Name')" + v-on:blur="$emit('input-name',$event.target.value)" + box + ></v-text-field> + </v-flex> + </template> + <template v-else> + <v-flex xs2> + <v-text-field + :value="firstname" + :label="$t('Firstname')" + v-on:blur="$emit('input-firstname',$event.target.value)" + box + ></v-text-field> + </v-flex> + <v-flex xs2> + <v-text-field + :value="lastname" + :label="$t('Lastname')" + v-on:blur="$emit('input-lastname',$event.target.value)" + box + ></v-text-field> + </v-flex> + </template> + </template> + <v-flex xs4 v-if="type === 'schema:Organisation'"> + <v-text-field + :value="institution" + :label="$t( institutionLabel ? institutionLabel : 'Institution' )" + v-on:blur="$emit('input-institution',$event.target.value)" + box + ></v-text-field> + </v-flex> + <v-flex xs2 v-if="showidentifier"> + <v-text-field + :value="identifier" + :label="$t('Identifier')" + v-on:blur="$emit('input-identifier',$event.target.value)" + box + ></v-text-field> + </v-flex> + <v-flex xs1 v-if="actions.length"> + <v-menu open-on-hover bottom offset-y> + <v-btn slot="activator" icon> + <v-icon>more_vert</v-icon> + </v-btn> + <v-list> + <v-list-tile v-for="(action, i) in actions" :key="i" @click="$emit(action.event, $event)"> + <v-list-tile-title>{{ action.title }}</v-list-tile-title> + </v-list-tile> + </v-list> + </v-menu> + </v-flex> + </v-layout> +</template> + +<script> +import { vocabulary } from "../../../mixins/vocabulary"; +import { fieldproperties } from "../../../mixins/fieldproperties"; + +export default { + name: "p-i-entity", + mixins: [vocabulary, fieldproperties], + props: { + firstname: { + type: String + }, + lastname: { + type: String + }, + name: { + type: String + }, + institution: { + type: String + }, + institutionLabel: { + type: String + }, + identifier: { + type: String + }, + role: { + type: String + }, + hideRole: { + type: Boolean + }, + type: { + type: String + }, + required: { + type: Boolean + }, + disablerole: { + type: Boolean, + default: false + }, + showidentifier: { + type: Boolean, + default: true + }, + showname: { + type: Boolean, + default: false + } + }, + data() { + return { + vocabulary: "rolepredicate" + }; + }, + mounted: function() { + this.$nextTick(function() { + this.loading = !this.vocabularies[this.vocabulary].loaded; + // emit input to set skos:prefLabel in parent + if (this.role) { + this.$emit("input", this.getTerm("rolepredicate", this.role)); + } + }); + } +}; +</script> + +<style scoped> +.v-btn { + margin: 0; +} +</style> diff --git a/src/components/input/phaidra_inputs/PIEntityExtended.vue b/src/components/input/phaidra_inputs/PIEntityExtended.vue new file mode 100644 index 0000000000000000000000000000000000000000..c00152297180be1b198bd35005eca7ace319fcce --- /dev/null +++ b/src/components/input/phaidra_inputs/PIEntityExtended.vue @@ -0,0 +1,514 @@ +<template> + <v-row> + <v-col cols="12"> + <v-card width="100%"> + <v-card-title class="title font-weight-light grey white--text"> + <span>{{ $t(label) }}</span> + <v-spacer></v-spacer> + <v-btn icon dark @click="$emit('add', $event)"> + <v-icon>mdi-content-duplicate</v-icon> + </v-btn> + <v-btn icon dark @click="$emit('add-clear', $event)"> + <v-icon>mdi-plus</v-icon> + </v-btn> + <v-btn icon dark @click="$emit('remove', $event)"> + <v-icon>mdi-minus</v-icon> + </v-btn> + <v-btn icon dark @click="$emit('up', $event)"> + <v-icon>mdi-chevron-up</v-icon> + </v-btn> + <v-btn icon dark @click="$emit('down', $event)"> + <v-icon>mdi-chevron-down</v-icon> + </v-btn> + </v-card-title> + <v-divider></v-divider> + <v-card-text class="mt-4"> + <v-container> + <v-row> + <v-col cols="8" v-if="!hideRole"> + <v-autocomplete + :disabled="disablerole" + v-on:input="$emit('input-role', $event)" + :label="$t('Role')" + :items="vocabularies[roleVocabulary].terms" + :item-value="'@id'" + :value="getTerm(roleVocabulary, role)" + :filter="autocompleteFilter" + :filled="inputStyle==='filled'" + :outlined="inputStyle==='outlined'" + return-object + clearable + :error-messages="roleErrorMessages" + > + <template slot="item" slot-scope="{ item }"> + <v-list-item-content two-line> + <v-list-item-title v-html="`${getLocalizedTermLabel(roleVocabulary, item['@id'])}`"></v-list-item-title> + <v-list-item-subtitle v-if="showIds" v-html="`${item['@id']}`"></v-list-item-subtitle> + </v-list-item-content> + </template> + <template slot="selection" slot-scope="{ item }"> + <v-list-item-content> + <v-list-item-title v-html="`${getLocalizedTermLabel(roleVocabulary, item['@id'])}`"></v-list-item-title> + </v-list-item-content> + </template> + </v-autocomplete> + </v-col> + <v-col v-if="enableTypeSelect" cols="2"> + <v-radio-group v-model="typeModel" class="mt-0" @change="$emit('change-type', $event)"> + <v-radio color="primary" :label="$t('Personal')" :value="'schema:Person'"></v-radio> + <v-radio color="primary" :label="$t('Corporate')" :value="'schema:Organization'"></v-radio> + </v-radio-group> + </v-col> + </v-row> + <template v-if="typeModel === 'schema:Person'"> + <v-row> + <template v-if="showname"> + <v-col cols="12" :md="(showIdentifier && !showIdentifierType) ? 8 : 12"> + <v-text-field + :value="name" + :label="$t('Name')" + v-on:blur="$emit('input-name',$event.target.value)" + :filled="inputStyle==='filled'" + :outlined="inputStyle==='outlined'" + :error-messages="nameErrorMessages" + ></v-text-field> + </v-col> + </template> + <template v-else> + <v-col cols="12" :md="(showIdentifier && !showIdentifierType) ? 4 : 6"> + <v-text-field + :value="firstname" + :label="$t('Firstname')" + v-on:blur="$emit('input-firstname',$event.target.value)" + :filled="inputStyle==='filled'" + :outlined="inputStyle==='outlined'" + :error-messages="firstnameErrorMessages" + ></v-text-field> + </v-col> + <v-col cols="12" :md="(showIdentifier && !showIdentifierType) ? 4 : 6"> + <v-text-field + :value="lastname" + :label="$t('Lastname')" + v-on:blur="$emit('input-lastname',$event.target.value)" + :filled="inputStyle==='filled'" + :outlined="inputStyle==='outlined'" + :error-messages="lastnameErrorMessages" + ></v-text-field> + </v-col> + </template> + <template v-if="showIdentifier && !showIdentifierType"> + <v-col cols="12" md="4"> + <v-text-field + v-show="identifierType === 'ids:orcid'" + v-mask="'####-####-####-####'" + :value="identifierText" + :label="identifierLabel ? identifierLabel : $t('Identifier')" + v-on:blur="$emit('input-identifier', $event.target.value)" + :placeholder="identifierTypePlaceholder" + :rules="identifierType ? [validationrules['orcid']] : [validationrules['noop']]" + :filled="inputStyle==='filled'" + :outlined="inputStyle==='outlined'" + ></v-text-field> + <v-text-field + v-show="identifierType !== 'ids:orcid'" + :value="identifierText" + :label="identifierLabel ? identifierLabel : $t('Identifier')" + v-on:blur="$emit('input-identifier', $event.target.value)" + :placeholder="identifierTypePlaceholder" + :rules="identifierType ? [validationrules[getIdentifierRuleName(identifierType)]] : [validationrules['noop']]" + :filled="inputStyle==='filled'" + :outlined="inputStyle==='outlined'" + ></v-text-field> + </v-col> + </template> + </v-row> + <v-row v-if="showIdentifier && showIdentifierType"> + <v-col cols="12" md="6"> + <v-autocomplete + v-on:input="$emit('input-identifier-type', $event)" + :label="$t('Type of identifier')" + :items="vocabularies[identifierVocabulary].terms" + :item-value="'@id'" + :value="getTerm(identifierVocabulary, identifierType)" + :filter="autocompleteFilter" + :disabled="disableIdentifierType" + :filled="inputStyle==='filled'" + :outlined="inputStyle==='outlined'" + return-object + clearable + > + <template slot="item" slot-scope="{ item }"> + <v-list-item-content two-line> + <v-list-item-title v-html="`${getLocalizedTermLabel(identifierVocabulary, item['@id'])}`"></v-list-item-title> + </v-list-item-content> + </template> + <template slot="selection" slot-scope="{ item }"> + <v-list-item-content> + <v-list-item-title v-html="`${getLocalizedTermLabel(identifierVocabulary, item['@id'])}`"></v-list-item-title> + </v-list-item-content> + </template> + </v-autocomplete> + </v-col> + <v-col cols="12" md="6" > + <v-text-field + v-show="identifierType === 'ids:orcid'" + v-mask="'####-####-####-####'" + :value="identifierText" + :label="identifierLabel ? identifierLabel : $t('Identifier')" + v-on:blur="$emit('input-identifier', $event.target.value)" + :placeholder="identifierTypePlaceholder" + :rules="identifierType ? [validationrules['orcid']] : [validationrules['noop']]" + :filled="inputStyle==='filled'" + :outlined="inputStyle==='outlined'" + ></v-text-field> + <v-text-field + v-show="identifierType !== 'ids:orcid'" + :value="identifierText" + :label="identifierLabel ? identifierLabel : $t('Identifier')" + v-on:blur="$emit('input-identifier', $event.target.value)" + :placeholder="identifierTypePlaceholder" + :rules="identifierType ? [validationrules[getIdentifierRuleName(identifierType)]] : [validationrules['noop']]" + :filled="inputStyle==='filled'" + :outlined="inputStyle==='outlined'" + ></v-text-field> + </v-col> + </v-row> + </template> + <template v-if="typeModel === 'schema:Organization'"> + <v-row> + <v-col cols="2"> + <v-radio-group v-model="organizationRadio" class="mt-0" @change="$emit('change-organization-type', $event)"> + <v-radio color="primary" :label="$t(instanceconfig.institution)" :value="'select'"></v-radio> + <v-radio color="primary" :label="$t('OTHER_FEMININE')" :value="'other'"></v-radio> + </v-radio-group> + </v-col> + <v-col cols="12" md="10" v-if="organizationRadio === 'select'"> + <v-autocomplete + :value="getTerm('orgunits', organization)" + :required="required" + v-on:input="handleInput($event, 'organizationPath', 'input-organization-select')" + :rules="required ? [ v => !!v || 'Required'] : []" + :items="vocabularies['orgunits'].terms" + :item-value="'@id'" + :loading="loading" + :filter="autocompleteFilter" + hide-no-data + :label="$t(organizationSelectLabel)" + :filled="inputStyle==='filled'" + :outlined="inputStyle==='outlined'" + return-object + clearable + :disabled="disabled" + :messages="organizationPath" + :error-messages="organizationErrorMessages" + > + <template slot="item" slot-scope="{ item }"> + <v-list-item-content two-line> + <v-list-item-title v-html="`${getLocalizedTermLabel('orgunits', item['@id'])}`"></v-list-item-title> + <v-list-item-subtitle v-if="showIds" v-html="`${item['@id']}`"></v-list-item-subtitle> + </v-list-item-content> + </template> + <template slot="selection" slot-scope="{ item }"> + <v-list-item-content> + <v-list-item-title v-html="`${getLocalizedTermLabel('orgunits', item['@id'])}`"></v-list-item-title> + </v-list-item-content> + </template> + <template v-slot:append-outer> + <v-icon @click="$refs.organizationstreedialog.open()">mdi-file-tree</v-icon> + </template> + </v-autocomplete> + </v-col> + <v-col cols="12" md="10" v-else> + <v-text-field + :value="organizationText" + :label="$t('Organization')" + v-on:blur="$emit('input-organization-other', $event.target.value)" + :filled="inputStyle==='filled'" + :outlined="inputStyle==='outlined'" + :error-messages="organizationTextErrorMessages" + ></v-text-field> + </v-col> + </v-row> + </template> + <v-row v-if="typeModel === 'schema:Person'"> + <v-col cols="2"> + <v-radio-group v-model="affiliationRadio" class="mt-0" @change="$emit('change-affiliation-type', $event)"> + <v-radio color="primary" :label="$t(instanceconfig.institution)" :value="'select'"></v-radio> + <v-radio color="primary" :label="$t('OTHER_FEMININE')" :value="'other'"></v-radio> + </v-radio-group> + </v-col> + <v-col cols="12" md="10" v-if="affiliationRadio === 'select'"> + <v-autocomplete + :value="getTerm('orgunits', affiliation)" + :required="required" + v-on:input="handleInput($event, 'affiliationPath', 'input-affiliation-select')" + :rules="required ? [ v => !!v || 'Required'] : []" + :items="vocabularies['orgunits'].terms" + :item-value="'@id'" + :loading="loading" + :filter="autocompleteFilter" + hide-no-data + :label="$t(affiliationSelectLabel)" + :filled="inputStyle==='filled'" + :outlined="inputStyle==='outlined'" + return-object + clearable + :disabled="disabled" + :messages="affiliationPath" + :error-messages="affiliationErrorMessages" + > + <template slot="item" slot-scope="{ item }"> + <v-list-item-content two-line> + <v-list-item-title v-html="`${getLocalizedTermLabel('orgunits', item['@id'])}`"></v-list-item-title> + <v-list-item-subtitle v-if="showIds" v-html="`${item['@id']}`"></v-list-item-subtitle> + </v-list-item-content> + </template> + <template slot="selection" slot-scope="{ item }"> + <v-list-item-content> + <v-list-item-title v-html="`${getLocalizedTermLabel('orgunits', item['@id'])}`"></v-list-item-title> + </v-list-item-content> + </template> + <template v-slot:append-outer> + <v-icon @click="$refs.affiliationstreedialog.open()">mdi-file-tree</v-icon> + </template> + </v-autocomplete> + </v-col> + <v-col cols="12" md="10" v-else> + <v-text-field + :value="affiliationText" + :label="$t('Affiliation')" + v-on:blur="$emit('input-affiliation-other',$event.target.value)" + :filled="inputStyle==='filled'" + :outlined="inputStyle==='outlined'" + :error-messages="affiliationTextErrorMessages" + ></v-text-field> + </v-col> + </v-row> + </v-container> + </v-card-text> + </v-card> + </v-col> + <org-units-tree-dialog ref="organizationstreedialog" @unit-selected="handleInput(getTerm('orgunits', $event), 'organizationPath', 'input-organization-select')"></org-units-tree-dialog> + <org-units-tree-dialog ref="affiliationstreedialog" @unit-selected="handleInput(getTerm('orgunits', $event), 'affiliationPath', 'input-affiliation-select')"></org-units-tree-dialog> + </v-row> +</template> + +<script> +import { mask } from "vue-the-mask"; +import { vocabulary } from "../../../mixins/vocabulary"; +import { fieldproperties } from "../../../mixins/fieldproperties"; +import { validationrules } from "../../../mixins/validationrules"; +import OrgUnitsTreeDialog from "../../select/OrgUnitsTreeDialog"; + +export default { + name: "p-i-entity-extended", + mixins: [vocabulary, fieldproperties, validationrules], + components: { + OrgUnitsTreeDialog + }, + directives: { + mask + }, + props: { + label: { + type: String + }, + firstname: { + type: String + }, + lastname: { + type: String + }, + name: { + type: String + }, + affiliation: { + type: String + }, + affiliationText: { + type: String + }, + affiliationType: { + type: String + }, + organization: { + type: String + }, + organizationText: { + type: String + }, + organizationType: { + type: String + }, + identifierText: { + type: String + }, + identifierType: { + type: String + }, + identifierLabel: { + type: String + }, + showIdentifier: { + type: Boolean + }, + showIdentifierType: { + type: Boolean + }, + disableIdentifierType: { + type: Boolean + }, + role: { + type: String + }, + hideRole: { + type: Boolean + }, + type: { + type: String + }, + required: { + type: Boolean + }, + disablerole: { + type: Boolean, + default: false + }, + nameErrorMessages: { + type: Array + }, + firstnameErrorMessages: { + type: Array + }, + lastnameErrorMessages: { + type: Array + }, + roleErrorMessages: { + type: Array + }, + affiliationErrorMessages: { + type: Array + }, + affiliationTextErrorMessages: { + type: Array + }, + organizationErrorMessages: { + type: Array + }, + organizationTextErrorMessages: { + type: Array + }, + showname: { + type: Boolean, + default: false + }, + roleVocabulary: { + type: String, + default: "rolepredicate" + }, + identifierVocabulary: { + type: String, + default: "entityidentifiertype" + }, + showIds: { + type: Boolean, + default: false + }, + enableTypeSelect: { + type: Boolean, + default: true + }, + organizationSelectLabel: { + type: String, + default: "Please choose" + }, + affiliationSelectLabel: { + type: String, + default: "Please choose" + } + }, + computed: { + instanceconfig: function() { + return this.$root.$store.state.instanceconfig; + }, + appconfig: function() { + return this.$root.$store.state.appconfig; + }, + identifierTypePlaceholder: function() { + for (let i of this.vocabularies[this.identifierVocabulary].terms) { + if (i["@id"] === this.identifierType) { + return i["skos:example"]; + } + } + return ""; + } + }, + data() { + return { + loading: false, + disabled: false, + typeModel: this.type, + affiliationRadio: this.affiliationType, + organizationRadio: this.organizationType, + affiliationPath: "", + organizationPath: "" + }; + }, + methods: { + getParentPath: function(unit, parentpath) { + if (unit) { + if (unit["parent"]) { + parentpath.push(unit.parent); + this.getParentPath(unit.parent, parentpath); + } + } + }, + handleInput: function(unit, propName, eventName) { + this[propName] = ""; + let parentpath = []; + if (unit) { + this.getParentPath(unit, parentpath); + for (let u of parentpath.reverse()) { + this[propName] = + this[propName] + u["skos:prefLabel"][this.$i18n.locale] + " > "; + } + this[propName] = + this[propName] + unit["skos:prefLabel"][this.$i18n.locale]; + } + this.$emit(eventName, unit); + } + }, + mounted: function() { + this.$nextTick(function() { + if (!this.vocabularies["orgunits"].loaded) { + this.$store.dispatch("loadOrgUnits", this.$i18n.locale); + } + this.loading = !this.vocabularies[this.roleVocabulary].loaded; + // emit input to set skos:prefLabel in parent + if (this.role) { + this.$emit("input", this.getTerm(this.roleVocabulary, this.role)); + } + if (this.organization) { + this.handleInput( + this.getTerm("orgunits", this.organization), + "organizationPath", + "input-organization-select" + ); + } + if (this.affiliation) { + this.handleInput( + this.getTerm("orgunits", this.affiliation), + "affiliationPath", + "input-affiliation-select" + ); + } + }); + } +}; +</script> + +<style scoped> +.v-btn { + margin: 0; +} +</style> diff --git a/src/components/input/phaidra_inputs/PIFile.vue b/src/components/input/phaidra_inputs/PIFile.vue new file mode 100644 index 0000000000000000000000000000000000000000..6cd417e248455bf739663ba0a9f9facd7b3c3c69 --- /dev/null +++ b/src/components/input/phaidra_inputs/PIFile.vue @@ -0,0 +1,50 @@ +<template> + <v-layout row> + <v-flex xs10> + <v-card class="mb-4"> + <v-card-title class="headline grey white--text"> + <span>{{ $t(label) }}</span> + <v-spacer></v-spacer> + <v-menu v-if="actions.length" open-on-hover bottom offset-y> + <v-btn slot="activator" icon dark> + <v-icon dark>more_vert</v-icon> + </v-btn> + <v-list> + <v-list-tile + v-for="(action, i) in actions" + :key="i" + @click="$emit(action.event, $event)" + > + <v-list-tile-title>{{ action.title }}</v-list-tile-title> + </v-list-tile> + </v-list> + </v-menu> + </v-card-title> + <v-card-text class="mt-4"> + <input type="file" v-on:input="$emit('input-file', $event)" /> + </v-card-text> + </v-card> + </v-flex> + </v-layout> +</template> + +<script> +import { fieldproperties } from "../../../mixins/fieldproperties"; + +export default { + name: "p-i-file", + mixins: [fieldproperties], + props: { + label: { + type: String, + required: true + } + } +}; +</script> + +<style scoped> +.v-btn { + margin: 0; +} +</style> \ No newline at end of file diff --git a/src/components/input/phaidra_inputs/PIFilename.vue b/src/components/input/phaidra_inputs/PIFilename.vue new file mode 100644 index 0000000000000000000000000000000000000000..421aba3c143c86a6bf6f1b8e6e8e534d88ae9c5f --- /dev/null +++ b/src/components/input/phaidra_inputs/PIFilename.vue @@ -0,0 +1,30 @@ +<template> + <v-row > + <v-col cols="8"> + <v-text-field + :value="value" + :label="$t('Filename')" + v-on:blur="$emit('input-value',$event.target.value)" + :filled="inputStyle==='filled'" + :outlined="inputStyle==='outlined'" + ></v-text-field> + </v-col> + </v-row> +</template> +<script> +import { fieldproperties } from "../../../mixins/fieldproperties"; + +export default { + name: "p-i-filename", + mixins: [fieldproperties], + props: { + value: { + type: String + }, + label: { + type: String, + required: true + } + } +}; +</script> diff --git a/src/components/input/phaidra_inputs/PIFilenameReadonly.vue b/src/components/input/phaidra_inputs/PIFilenameReadonly.vue new file mode 100644 index 0000000000000000000000000000000000000000..abc93518c015cd0b28239cf621c0fdca9293fc8b --- /dev/null +++ b/src/components/input/phaidra_inputs/PIFilenameReadonly.vue @@ -0,0 +1,27 @@ +<template> + <v-layout row> + <v-flex xs8> + <v-text-field + :value="value" + :label="$t('Filename')" + readonly + box + ></v-text-field> + </v-flex> + </v-layout> +</template> +<script> + +export default { + name: 'p-i-filename-readonly', + props: { + value: { + type: String + }, + label: { + type: String, + required: true + } + } +} +</script> \ No newline at end of file diff --git a/src/components/input/phaidra_inputs/PIForm.vue b/src/components/input/phaidra_inputs/PIForm.vue new file mode 100644 index 0000000000000000000000000000000000000000..0c598bef214b38bc1a3f31b0cef328a34ff64896 --- /dev/null +++ b/src/components/input/phaidra_inputs/PIForm.vue @@ -0,0 +1,1128 @@ +<template> + <v-container fluid v-if="form && form.sections"> + <v-tabs v-model="activetab" align-with-title> + <v-tab class="title font-weight-light text-capitalize">{{ $t('Metadata') }}<template v-if="targetpid"> - <span class="text-lowercase">{{ targetpid }}</span></template></v-tab> + <v-tab @click="metadatapreview = getMetadata()" class="title font-weight-light text-capitalize">{{ $t('JSON-LD') }}</v-tab> + <v-tab v-if="templating" @click="loadTemplates()" class="title font-weight-light text-capitalize">{{ $t('Templates') }}</v-tab> + </v-tabs> + + <v-tabs-items v-model="activetab"> + <v-tab-item class="pa-3" v-if="form"> + + <v-row v-for="(s) in this.form.sections" :key="s.id" class="ma-3"> + + <v-card v-if="s.type === 'resourcelink'" width="100%"> + <v-card-title class="title font-weight-light grey white--text"> + <span>{{s.title}}</span> + <v-spacer></v-spacer> + </v-card-title> + <v-card-text class="mt-4"> + <v-text-field v-model="s.resourcelink" + :label="$t('Resource link')" + :required="true" + :placeholder="$t('e.g.: https://phaidra.org')" + :rules="[ v => !!v || 'Required']" + filled + ></v-text-field> + </v-card-text> + </v-card> + + <v-card v-else-if="(s.type !== 'accessrights')" width="100%"> + <v-card-title class="title font-weight-light grey white--text"> + <span v-t="s.title"></span> + <v-spacer></v-spacer> + <v-checkbox dark color="white" v-if="s.type === 'member'" v-model="previewMember" :label="$t('Container thumbnail')" :value="s.id"></v-checkbox> + <v-spacer></v-spacer> + <v-menu open-on-hover bottom offset-y v-if="!s.disablemenu"> + <template v-slot:activator="{ on }"> + <v-btn v-on="on" icon dark> + <v-icon dark>mdi-dots-vertical</v-icon> + </v-btn> + </template> + <v-list> + <v-list-item v-if="s.multiplicable && (s.type === 'member') || (s.type === 'phaidra:Subject')" @click="addSection(s)"> + <v-list-item-title><span v-t="'Duplicate'"></span></v-list-item-title> + </v-list-item> + <v-list-item v-if="s.removable && (s.type != 'digitalobject')" @click="removeSection(s)"> + <v-list-item-title><span v-t="'Remove'"></span></v-list-item-title> + </v-list-item> + <v-list-item v-if="s.type === 'member'" @click="sortMemberUp(s)"> + <v-list-item-title><span v-t="'Move up'"></span></v-list-item-title> + </v-list-item> + <v-list-item v-if="s.type === 'member'" @click="sortMemberDown(s)"> + <v-list-item-title><span v-t="'Move down'"></span></v-list-item-title> + </v-list-item> + <v-list-item v-if="s.type === 'digitalobject'" @click="$emit('add-phaidrasubject-section', s)"> + <v-list-item-title><span v-t="'Add subject metadata section'"></span></v-list-item-title> + </v-list-item> + </v-list> + </v-menu> + </v-card-title> + <v-card-text class="mt-4"> + + <template v-for="(f) in s.fields"> + + <v-row no-gutters :key="f.id"> + + <template v-if="f.component === 'p-text-field'"> + <p-i-text-field + v-bind.sync="f" + v-on:input="f.value=$event" + v-on:input-language="setSelected(f, 'language', $event)" + v-on:add="addField(s.fields, f)" + v-on:remove="removeField(s.fields, f)" + ></p-i-text-field> + </template> + + <template v-else-if="f.component === 'p-text-field-suggest'"> + <p-i-text-field-suggest + v-bind.sync="f" + v-on:input="f.value=$event" + v-on:input-language="setSelected(f, 'language', $event)" + v-on:add="addField(s.fields, f)" + v-on:remove="removeField(s.fields, f)" + ></p-i-text-field-suggest> + </template> + + <template v-else-if="f.component === 'p-keyword'"> + <p-i-keyword + v-bind.sync="f" + v-on:input="f.value=$event" + v-on:input-language="setSelected(f, 'language', $event)" + v-on:add="addField(s.fields, f)" + v-on:remove="removeField(s.fields, f)" + ></p-i-keyword> + </template> + + <template v-if="f.component === 'p-title'"> + <p-i-title + v-bind.sync="f" + v-on:input-title="f.title=$event" + v-on:input-subtitle="f.subtitle=$event" + v-on:input-language="setSelected(f, 'language', $event)" + v-on:add="addField(s.fields, f)" + v-on:remove="removeField(s.fields, f)" + v-on:up="sortFieldUp(s.fields, f)" + v-on:down="sortFieldDown(s.fields, f)" + ></p-i-title> + </template> + + <template v-else-if="f.component === 'p-select'"> + <p-i-select + v-bind.sync="f" + v-on:input="selectInput(f, $event)" + v-on:add="addField(s.fields, f)" + v-on:remove="removeField(s.fields, f)" + ></p-i-select> + </template> + + <template v-else-if="f.component === 'p-select-text'"> + <p-i-select-text + v-bind.sync="f" + v-on:input="f.value=$event" + v-on:input-select="f.selectvalue=$event" + v-on:input-text="f.textvalue=$event" + v-on:input-language="setSelected(f, 'language', $event)" + v-on:add="addField(s.fields, f)" + v-on:remove="removeField(s.fields, f)" + ></p-i-select-text> + </template> + + <template v-else-if="f.component === 'p-date-edtf'"> + <p-i-date-edtf + v-bind.sync="f" + v-on:input-date="f.value=$event" + v-on:input-date-type="setSelected(f, 'type', $event)" + v-on:add="addField(s.fields, f)" + v-on:remove="removeField(s.fields, f)" + ></p-i-date-edtf> + </template> + + <template v-else-if="f.component === 'p-duration'"> + <p-i-duration + v-bind.sync="f" + v-on:input="f.value=$event" + v-on:add="addField(s.fields, f)" + v-on:remove="removeField(s.fields, f)" + ></p-i-duration> + </template> + + <template v-else-if="f.component === 'p-series'"> + <p-i-series + v-bind.sync="f" + v-on:input-select-journal="selectJournal(f, $event)" + v-on:input-title="f.title=$event" + v-on:input-title-language="setSelected(f, 'titleLanguage', $event)" + v-on:input-volume="f.volume=$event" + v-on:input-issue="f.issue=$event" + v-on:input-issued="f.issued=$event" + v-on:input-issn="f.issn=$event" + v-on:input-identifier="f.identifier=$event" + v-on:input-page-start="f.pageStart=$event" + v-on:input-page-end="f.pageEnd=$event" + v-on:add="addField(s.fields, f)" + v-on:remove="removeField(s.fields, f)" + ></p-i-series> + </template> + + <template v-else-if="f.component === 'p-citation'"> + <p-i-citation + v-bind.sync="f" + v-on:input-citation-type="setSelected(f, 'type', $event)" + v-on:input-citation="f.citation=$event" + v-on:input-citation-language="setSelected(f, 'citationLanguage', $event)" + v-on:input-identifier="f.identifier=$event" + v-on:add="addField(s.fields, f)" + v-on:remove="removeField(s.fields, f)" + ></p-i-citation> + </template> + + <template v-else-if="f.component === 'p-bf-publication'"> + <p-i-bf-publication + v-bind.sync="f" + v-on:input-suggest-publisher="publisherSuggestInput(f, $event)" + v-on:input-publisher-name="f.publisherName=$event" + v-on:change-type="f.publisherType = $event" + v-on:input-publisher-select="publisherSelectInput(f, $event)" + v-on:input-publishing-place="f.publishingPlace=$event" + v-on:input-publishing-date="f.publishingDate=$event" + v-on:add="addField(s.fields, f)" + v-on:remove="removeField(s.fields, f)" + ></p-i-bf-publication> + </template> + + <template v-else-if="f.component === 'p-adaptation'"> + <p-i-adaptation + v-bind.sync="f" + v-on:input-title="f.title=$event" + v-on:input-subtitle="f.subtitle=$event" + v-on:input-title-language="setSelected(f, 'titleLanguage', $event)" + v-on:input-firstname="f.firstname=$event" + v-on:input-lastname="f.lastname=$event" + v-on:input-name="f.name=$event" + v-on:input-role="roleInput(f, $event)" + v-on:add="addField(s.fields, f)" + v-on:remove="removeField(s.fields, f)" + ></p-i-adaptation> + </template> + + <template v-else-if="f.component === 'p-contained-in'"> + <p-i-contained-in + v-bind.sync="f" + v-on:input-title="f.title=$event" + v-on:input-subtitle="f.subtitle=$event" + v-on:input-title-language="setSelected(f, 'titleLanguage', $event)" + v-on:input-role="containedInRoleInput(f, $event)" + v-on:input-series-title="f.seriesTitle=$event" + v-on:input-series-title-language="setSelected(f, 'seriesTitleLanguage', $event)" + v-on:input-series-volume="f.seriesVolume=$event" + v-on:input-series-issue="f.seriesIssue=$event" + v-on:input-series-issued="f.seriesIssued=$event" + v-on:input-series-issn="f.seriesIssn=$event" + v-on:input-series-identifier="f.seriesIdentifier=$event" + v-on:add-role="addContainedInRole(f.roles, $event)" + v-on:remove-role="removeContainedInRole(f.roles, $event)" + v-on:up-role="sortContainedInRoleUp(f.roles, $event)" + v-on:down-role="sortContainedInRoleDown(f.roles, $event)" + ></p-i-contained-in> + </template> + + <template v-else-if="f.component === 'p-entity'"> + <p-i-entity + v-bind.sync="f" + v-on:input-firstname="f.firstname=$event" + v-on:input-lastname="f.lastname=$event" + v-on:input-name="f.name=$event" + v-on:input-organization="f.organization=$event" + v-on:input-role="roleInput(f, $event)" + v-on:add="addField(s.fields, f)" + v-on:remove="removeField(s.fields, f)" + v-on:up="sortFieldUp(s.fields, f)" + v-on:down="sortFieldDown(s.fields, f)" + ></p-i-entity> + </template> + + <template v-else-if="f.component === 'p-entity-extended'"> + <p-i-entity-extended + v-bind.sync="f" + v-on:change-type="f.type = $event" + v-on:input-firstname="f.firstname = $event" + v-on:input-lastname="f.lastname = $event" + v-on:input-name="f.name = $event" + v-on:input-identifier-type="setSelected(f, 'identifierType', $event)" + v-on:input-identifier="f.identifierText = $event" + v-on:change-affiliation-type="f.affiliationType = $event" + v-on:input-affiliation-select="affiliationSelectInput(f, $event)" + v-on:input-affiliation-other="f.affiliationText = $event" + v-on:change-organization-type="f.organizationType = $event" + v-on:input-organization-select="organizationSelectInput(f, $event)" + v-on:input-organization-other="f.organizationText = $event" + v-on:input-role="roleInput(f, $event)" + v-on:add="addField(s.fields, f)" + v-on:add-clear="addEntityClear(s.fields, f)" + v-on:remove="removeField(s.fields, f)" + v-on:up="sortFieldUp(s.fields, f)" + v-on:down="sortFieldDown(s.fields, f)" + ></p-i-entity-extended> + </template> + + <template v-else-if="f.component === 'p-subject-gnd'"> + <p-i-subject-gnd + v-bind.sync="f" + v-on:input="f.value=$event" + v-on:resolve="updateSubject(f, $event)" + v-on:add="addField(s.fields, f)" + v-on:remove="removeField(s.fields, f)" + ></p-i-subject-gnd> + </template> + + <template v-else-if="f.component === 'p-spatial-getty'"> + <p-i-spatial-getty + v-bind.sync="f" + v-on:input="f.value=$event" + v-on:input-place-type="setSelected(f, 'type', $event)" + v-on:resolve="updatePlace(f, $event)" + v-on:add="addField(s.fields, f)" + v-on:remove="removeField(s.fields, f)" + ></p-i-spatial-getty> + </template> + + <template v-else-if="f.component === 'p-spatial-text'"> + <p-i-spatial-text + v-bind.sync="f" + v-on:input="f.value=$event" + v-on:input-place-type="setSelected(f, 'type', $event)" + v-on:input-language="setSelected(f, 'language', $event)" + v-on:add="addField(s.fields, f)" + v-on:remove="removeField(s.fields, f)" + ></p-i-spatial-text> + </template> + + <template v-else-if="f.component === 'p-dimension'"> + <p-i-dimension + v-bind.sync="f" + v-on:input-value="f.value=$event" + v-on:input-unit="setSelected(f, 'unitCode', $event)" + v-on:add="addField(s.fields, f)" + v-on:remove="removeField(s.fields, f)" + ></p-i-dimension> + </template> + + <template v-else-if="(f.component === 'p-literal') && (f.predicate !== 'schema:pageStart') && (f.predicate !== 'schema:pageEnd')"> + <p-i-literal + v-bind.sync="f" + v-on:input-value="f.value=$event" + v-on:add="addField(s.fields, f)" + v-on:remove="removeField(s.fields, f)" + ></p-i-literal> + </template> + + <template v-else-if="f.component === 'p-alternate-identifier'"> + <p-i-alternate-identifier + v-bind.sync="f" + v-on:input-identifier="f.value=$event" + v-on:input-identifier-type="setSelected(f, 'identifierType', $event)" + v-on:add="addField(s.fields, f)" + v-on:remove="removeField(s.fields, f)" + class="my-2" + ></p-i-alternate-identifier> + </template> + + <template v-else-if="f.component === 'p-study-plan'"> + <p-i-study-plan + v-bind.sync="f" + v-on:input-name="f.name=$event" + v-on:input-name-language="setSelected(f, 'nameLanguage', $event)" + v-on:input-notation="f.notation=$event" + v-on:add="addField(s.fields, f)" + v-on:remove="removeField(s.fields, f)" + ></p-i-study-plan> + </template> + + <template v-else-if="f.component === 'p-project'"> + <p-i-project + v-bind.sync="f" + v-on:input-name="f.name=$event" + v-on:input-name-language="setSelected(f, 'nameLanguage', $event)" + v-on:input-funder-name="f.funderName=$event" + v-on:input-funder-name-language="setSelected(f, 'funderNameLanguage', $event)" + v-on:input-description="f.description=$event" + v-on:input-description-language="setSelected(f, 'descriptionLanguage', $event)" + v-on:input-identifier="f.identifier=$event" + v-on:input-funder-identifier="f.funderIdentifier=$event" + v-on:input-homepage="f.homepage=$event" + v-on:add="addField(s.fields, f)" + v-on:remove="removeField(s.fields, f)" + ></p-i-project> + </template> + + <template v-else-if="f.component === 'p-funder'"> + <p-i-funder + v-bind.sync="f" + v-on:input-name="f.name=$event" + v-on:input-name-language="setSelected(f, 'nameLanguage', $event)" + v-on:input-identifier="f.identifier=$event" + v-on:add="addField(s.fields, f)" + v-on:remove="removeField(s.fields, f)" + ></p-i-funder> + </template> + + <template v-else-if="f.component === 'p-association'"> + <p-i-association + v-bind.sync="f" + v-on:input="selectInput(f, $event)" + v-on:add="addField(s.fields, f)" + v-on:remove="removeField(s.fields, f)" + ></p-i-association> + </template> + + <template v-else-if="f.component === 'p-filename'"> + <p-i-filename + v-bind.sync="f" + v-on:input-value="f.value=$event" + ></p-i-filename> + </template> + + <template v-else-if="f.component === 'p-filename-readonly'"> + <p-i-filename-readonly v-bind.sync="f"></p-i-filename-readonly> + </template> + + <template v-else-if="f.component === 'p-unknown-readonly'"> + <p-i-unknown-readonly v-bind.sync="f"></p-i-unknown-readonly> + </template> + + <template v-else-if="f.component === 'p-vocab-ext-readonly'"> + <p-i-vocab-ext-readonly + v-bind.sync="f" + v-on:remove="removeField(s.fields, f)" + ></p-i-vocab-ext-readonly> + </template> + + <template v-else-if="f.component === 'p-spatial-getty-readonly'"> + <p-i-spatial-getty-readonly + v-bind.sync="f" + v-on:remove="removeField(s.fields, f)" + ></p-i-spatial-getty-readonly> + </template> + + <template v-else-if="f.component === 'p-file'"> + <p-i-file + v-bind.sync="f" + v-on:input-file="setFilename(f, $event)" + v-on:input-mimetype="setSelected(f, 'mimetype', $event)" + v-on:add="addField(s.fields, f)" + v-on:remove="removeField(s.fields, f)" + ></p-i-file> + </template> + + </v-row> + </template> + + <v-row> + <v-col> + <v-dialog v-if="addbutton" class="pb-4" v-model="s['adddialogue']" scrollable width="700px"> + <template v-slot:activator="{ on }"> + <v-btn v-on="on" fab depressed small color="grey lighten-3"> + <v-icon color="grey darken-1">mdi-plus</v-icon> + </v-btn> + </template> + <v-card> + <v-card-title class="grey white--text"><span v-t="'Add metadata fields'"></span></v-card-title> + <v-card-text> + <v-list three-line > + <v-text-field clearable label="Search..." append-icon="mdi-magnify" v-model="searchfieldsinput"></v-text-field> + <template v-for="field in filteredMetadatafields"> + <v-list-item :key="field.id" @click="addfieldselection.push(field)"> + <v-list-item-content> + <v-list-item-title>{{field.fieldname}}</v-list-item-title> + <v-list-item-subtitle>{{field.definition}}</v-list-item-subtitle> + </v-list-item-content> + </v-list-item> + <v-divider :key="'divi'+field.id"></v-divider> + </template> + </v-list> + </v-card-text> + <v-divider :key="'divi'+s.id"></v-divider> + <v-card-actions> + <v-container> + <v-row> + <v-col v-if="addfieldselection.length > 0"> + <span v-t="'Selected fields:'" class="mr-2"></span> <v-chip :key="index" v-for="(ch, index) in addfieldselection" close @click:close="removeField(addfieldselection, ch)">{{ ch.fieldname }}</v-chip> + </v-col> + <v-col v-else><span v-t="'Please select metadata fields from the list'"></span></v-col> + </v-row> + <v-row justify="end"> + <v-btn color="grey" dark @click="addfieldselection = []; s['adddialogue'] = false"><span v-t="'Cancel'"></span></v-btn> + <v-btn color="primary" @click="addFields(s)"><span v-t="'Add'"></span></v-btn> + </v-row> + </v-container> + </v-card-actions> + </v-card> + + </v-dialog> + </v-col> + </v-row> + </v-card-text> + + </v-card> + </v-row> + + <v-row align="center" justify="end" class="ma-3"> + <v-dialog v-if="templating" v-model="templatedialog" width="500"> + <template v-slot:activator="{ on }"> + <v-btn class="mr-3" v-on="on" dark raised :loading="loading" :disabled="loading" color="grey"><span v-t="'Save as template'"></span></v-btn> + </template> + <v-card> + <v-card-title class="title font-weight-light grey lighten-2" primary-title><span v-t="'Save as template'"></span></v-card-title> + <v-card-text> + <v-text-field class="mt-4" hide-details filled single-line v-model="templatename" :label="$t('Template name')" ></v-text-field> + </v-card-text> + <v-card-actions> + <v-spacer></v-spacer> + <v-btn :loading="loading" :disabled="loading" color="grey" dark @click="templatedialog= false"><span v-t="'Cancel'"></span></v-btn> + <v-btn :loading="loading" :disabled="loading" color="primary" @click="saveAsTemplate()"><span v-t="'Save'"></span></v-btn> + </v-card-actions> + </v-card> + </v-dialog> + <v-btn fixed bottom right v-if="targetpid && floatingsavebutton" raised :loading="loading" :disabled="loading" color="primary" @click="save()"><span v-t="'Save'"></span></v-btn> + <v-btn v-else-if="targetpid && !floatingsavebutton" raised :loading="loading" :disabled="loading" color="primary" @click="save()"><span v-t="'Save'"></span></v-btn> + <v-btn v-else raised :loading="loading" :disabled="loading" color="primary" @click="submit()"><span v-t="'Submit'"></span></v-btn> + </v-row> + + </v-tab-item> + <v-tab-item class="pa-3"> + <code>{{ metadatapreview }}</code> + </v-tab-item> + <v-tab-item class="ma-4"> + <p-templates ref="templates" v-on:load-template="loadTemplate($event)"></p-templates> + </v-tab-item> + </v-tabs-items> + + </v-container> + +</template> + +<script> +import arrays from "../../../utils/arrays"; +import jsonLd from "../../../utils/json-ld"; +import fields from "../../../utils/fields"; +import PITextField from "./PITextField"; +import PITextFieldSuggest from "./PITextFieldSuggest"; +import PITitle from "./PITitle"; +import PIEntity from "./PIEntity"; +import PIEntityExtended from "./PIEntityExtended"; +import PIDateEdtf from "./PIDateEdtf"; +import PISelect from "./PISelect"; +import PISelectText from "./PISelectText"; +import PISubjectGnd from "./PISubjectGnd"; +import PISpatialGetty from "./PISpatialGetty"; +import PISpatialText from "./PISpatialText"; +import PIDimension from "./PIDimension"; +import PIDuration from "./PIDuration"; +import PIProject from "./PIProject"; +import PIFunder from "./PIFunder"; +import PIAssociation from "./PIAssociation"; +import PISeries from "./PISeries"; +import PIContainedIn from "./PIContainedIn"; +import PICitation from "./PICitation"; +import PIBfPublication from "./PIBfPublication"; +import PIAdaptation from "./PIAdaptation"; +import PIFilenameReadonly from "./PIFilenameReadonly"; +import PIFilename from "./PIFilename"; +import PIFile from "./PIFile"; +import PISpatialGettyReadonly from "./PISpatialGettyReadonly"; +import PIVocabExtReadonly from "./PIVocabExtReadonly"; +import PIUnknownReadonly from "./PIUnknownReadonly"; +import PILiteral from "./PILiteral"; +import PIStudyPlan from "./PIStudyPlan"; +import PIKeyword from "./PIKeyword"; +import PTemplates from "../../templates/PTemplates"; +export default { + name: "p-i-form", + components: { + PITextField, + PITextFieldSuggest, + PITitle, + PIEntity, + PIEntityExtended, + PIDateEdtf, + PISelect, + PISelectText, + PISubjectGnd, + PISpatialGetty, + PISpatialText, + PIDimension, + PIDuration, + PIStudyPlan, + PIProject, + PIFunder, + PIAssociation, + PISeries, + PIContainedIn, + PICitation, + PIBfPublication, + PIAdaptation, + PILiteral, + PIKeyword, + PIFilenameReadonly, + PIFilename, + PIFile, + PIVocabExtReadonly, + PISpatialGettyReadonly, + PIUnknownReadonly, + PTemplates + }, + props: { + form: { + type: Object + }, + targetpid: { + type: String + }, + owner: { + // if defined, phaidra will transfer ownership to this user + // IIF the current user is authorized to do so in phaidra-api + type: String + }, + addbutton: { + type: Boolean, + default: true + }, + templating: { + type: Boolean, + default: true + }, + floatingsavebutton: { + type: Boolean, + default: false + } + }, + computed: { + submittype: function() { + for (let s of this.form.sections) { + if (s.fields && s.type !== "member") { + for (let field of s.fields) { + if (field.predicate === "dcterms:type") { + return this.getObjectType(field.value); + } + } + } + } + return null; + }, + filteredMetadatafields() { + let list = fields.getEditableFields(); + if (this.searchfieldsinput) { + return list.filter( + f => + f.fieldname + .toLowerCase() + .includes(this.searchfieldsinput.toLowerCase()) || + f.definition + .toLowerCase() + .includes(this.searchfieldsinput.toLowerCase()) + ); + } else { + return list; + } + } + }, + data() { + return { + activetab: null, + loadedMetadata: [], + loading: false, + fab: false, + addfieldselection: [], + templatedialog: "", + templatename: "", + previewMember: "", + searchfieldsinput: "", + metadatapreview: {} + }; + }, + methods: { + getMetadata: function() { + let jsonlds; + if (!this.targetpid && this.submittype === "container") { + jsonlds = jsonLd.containerForm2json(this.form); + } else { + jsonlds = jsonLd.form2json(this.form); + } + let md = { metadata: { "json-ld": jsonlds } }; + let colorder = []; + let i = 0; + for (let s of this.form.sections) { + if (s.type === "member") { + i++; + colorder.push({ member: "member_" + s.id, pos: i }); + } + if (s.type === "accessrights") { + md["metadata"]["rights"] = s.rights; + } + if (s.type === "resourcelink") { + md["metadata"]["resourcelink"] = s.resourcelink; + } + } + if (colorder.length > 0) { + md["metadata"]["membersorder"] = colorder; + } + if (this.previewMember) { + md["metadata"]["relationships"] = [ + { + s: "member_" + this.previewMember, + p: "http://phaidra.org/XML/V1.0/relations#isThumbnailFor", + o: "container" + } + ]; + } + if (this.owner) { + md["metadata"]["ownerid"] = this.owner; + } + return md; + }, + loadTemplates: function() { + if (this.$refs.templates) { + this.$refs.templates.loadTemplates(); + } + }, + loadTemplate: function(form) { + this.$emit("load-form", form); + this.activetab = 0; + }, + saveAsTemplate: async function() { + var httpFormData = new FormData(); + this.loading = true; + httpFormData.append("name", this.templatename); + httpFormData.append("form", JSON.stringify(this.form)); + try { + let response = await this.$http.request({ + method: "POST", + url: this.$store.state.instanceconfig.api + "/jsonld/template/add", + headers: { + "Content-Type": "multipart/form-data", + "X-XSRF-TOKEN": this.$store.state.user.token + }, + data: httpFormData + }); + if (response.data.alerts && response.data.alerts.length > 0) { + this.$store.commit("setAlerts", response.data.alerts); + } + } catch (error) { + console.log(error); + this.$store.commit("setAlerts", [{ type: "danger", msg: error }]); + } finally { + this.loading = false; + this.templatedialog = false; + } + }, + getObjectType: function(contentmodel) { + switch (contentmodel) { + case "https://pid.phaidra.org/vocabulary/44TN-P1S0": + return "picture"; + case "https://pid.phaidra.org/vocabulary/8YB5-1M0J": + return "audio"; + case "https://pid.phaidra.org/vocabulary/B0Y6-GYT8": + return "video"; + case "https://pid.phaidra.org/vocabulary/69ZZ-2KGX": + return "document"; + case "https://pid.phaidra.org/vocabulary/8MY0-BQDQ": + return "container"; + case "https://pid.phaidra.org/vocabulary/T8GH-F4V8": + return "resource"; + case "https://pid.phaidra.org/vocabulary/GXS7-ENXJ": + return "collection"; + default: + return "unknown"; + } + }, + submit: async function() { + this.loading = true; + var httpFormData = new FormData(); + switch (this.submittype) { + case "container": + for (var i = 0; i < this.form.sections.length; i++) { + var s = this.form.sections[i]; + if (s.type === "member") { + for (var j = 0; j < s.fields.length; j++) { + if (s.fields[j].component === "p-file") { + if (s.fields[j].file !== "") { + httpFormData.append("member_" + s.id, s.fields[j].file); + } + } + } + } + } + break; + default: + for (i = 0; i < this.form.sections.length; i++) { + s = this.form.sections[i]; + if (s.fields) { + for (j = 0; j < s.fields.length; j++) { + if (s.fields[j].component === "p-file") { + if (s.fields[j].file !== "") { + httpFormData.append("file", s.fields[j].file); + } + } + } + } + } + break; + } + httpFormData.append("metadata", JSON.stringify(this.getMetadata())); + try { + let response = await this.$http.request({ + method: "POST", + url: + this.$store.state.instanceconfig.api + + "/" + + this.submittype + + "/create", + headers: { + "Content-Type": "multipart/form-data", + "X-XSRF-TOKEN": this.$store.state.user.token + }, + data: httpFormData + }); + if (response.data.alerts && response.data.alerts.length > 0) { + this.$store.commit("setAlerts", response.data.alerts); + } + if (response.data.status === 200) { + if (response.data.pid) { + this.$emit("object-created", response.data.pid); + } + } + } catch (error) { + console.log(error); + this.$store.commit("setAlerts", [{ type: "danger", msg: error }]); + } finally { + this.$vuetify.goTo(0); + this.loading = false; + } + }, + save: async function() { + this.loading = true; + var httpFormData = new FormData(); + httpFormData.append("metadata", JSON.stringify(this.getMetadata())); + try { + let response = await this.$http.request({ + method: "POST", + url: + this.$store.state.instanceconfig.api + + "/object/" + + this.targetpid + + "/metadata", + headers: { + "Content-Type": "multipart/form-data", + "X-XSRF-TOKEN": this.$store.state.user.token + }, + data: httpFormData + }); + if (response.data.alerts && response.data.alerts.length > 0) { + if (response.data.status === 401) { + response.data.alerts.push({ type: "danger", msg: "Please log in" }); + } + this.$store.commit("setAlerts", response.data.alerts); + } + if (response.data.status === 200) { + if (response.data.pid) { + this.$emit("object-saved", this.targetpid); + } + } + } catch (error) { + console.log(error); + this.$store.commit("setAlerts", [{ type: "danger", msg: error }]); + } finally { + this.$vuetify.goTo(0); + this.loading = false; + } + }, + addField: function(arr, f) { + var newField = arrays.duplicate(arr, f); + if (newField) { + newField.id = new Date().getTime(); + newField.firstname = ""; + newField.lastname = ""; + newField.identifierText = ""; + newField.removable = true; + } + }, + addEntityClear: function(arr, f) { + var newField = arrays.duplicate(arr, f); + if (newField) { + newField.id = new Date().getTime(); + newField.role = ""; + newField.name = ""; + newField.firstname = ""; + newField.lastname = ""; + newField.identifierText = ""; + newField.affiliation = ""; + newField.affiliationText = ""; + newField.affiliationType = "select"; + newField.organization = ""; + newField.organizationText = ""; + newField.organizationType = "select"; + newField.type = "schema:Person"; + newField.removable = true; + } + }, + removeField: function(arr, f) { + arrays.remove(arr, f); + }, + sortFieldUp: function(arr, f) { + var i = arr.indexOf(f); + if (arr[i - 1]) { + if (arr[i - 1].ordergroup === f.ordergroup) { + arrays.moveUp(arr, f); + } + } + }, + sortFieldDown: function(arr, f) { + var i = arr.indexOf(f); + if (arr[i + 1]) { + if (arr[i + 1].ordergroup === f.ordergroup) { + arrays.moveDown(arr, f); + } + } + }, + addContainedInRole: function(arr, f) { + var newField = arrays.duplicate(arr, f); + if (newField) { + newField.id = new Date().getTime(); + newField.removable = true; + } + }, + removeContainedInRole: function(arr, f) { + if (arr.length > 1) { + arrays.remove(arr, f); + } + }, + sortContainedInRoleUp: function(arr, f) { + var i = arr.indexOf(f); + if (arr[i - 1]) { + if (arr[i - 1].ordergroup === f.ordergroup) { + arrays.moveUp(arr, f); + } + } + }, + sortContainedInRoleDown: function(arr, f) { + var i = arr.indexOf(f); + if (arr[i + 1]) { + if (arr[i + 1].ordergroup === f.ordergroup) { + arrays.moveDown(arr, f); + } + } + }, + sortMemberUp: function(s) { + var i = this.form.sections.indexOf(s); + if (this.form.sections[i - 1]) { + if (this.form.sections[i - 1].type === "member") { + arrays.moveUp(this.form.sections, s); + } + } + }, + sortMemberDown: function(s) { + var i = this.form.sections.indexOf(s); + if (this.form.sections[i + 1]) { + if (this.form.sections[i + 1].type === "member") { + arrays.moveDown(this.form.sections, s); + } + } + }, + addSection: function(s) { + var ns = arrays.duplicate(this.form.sections, s); + ns.id = new Date().getTime(); + ns.removable = true; + for (var i = 0; i < ns.fields.length; i++) { + var id = new Date().getTime(); + if (i > 0) { + id = ns.fields[i - 1].id + 1; + } + ns.fields[i].id = id; + ns.fields[i].value = ""; + ns.fields[i].language = ""; + } + }, + removeSection: function(s) { + arrays.remove(this.form.sections, s); + }, + selectJournal: function(f, event) { + if (event.title) { + f.title = event.title; + } + if (event.issn) { + f.issn = event.issn; + } + }, + affiliationSelectInput: function(f, event) { + f.affiliation = ""; + f.affiliationSelectedName = []; + if (event) { + f.affiliation = event["@id"]; + var preflabels = event["skos:prefLabel"]; + Object.entries(preflabels).forEach(([key, value]) => { + f.affiliationSelectedName.push({ "@value": value, "@language": key }); + }); + } + }, + publisherSelectInput: function(f, event) { + f.publisherOrgUnit = ""; + f.publisherSelectedName = []; + if (event) { + f.publisherOrgUnit = event["@id"]; + var preflabels = event["skos:prefLabel"]; + Object.entries(preflabels).forEach(([key, value]) => { + f.publisherSelectedName.push({ "@value": value, "@language": key }); + }); + } + }, + publisherSuggestInput: function(f, event) { + if (event) { + f.publisherName = event["name"]; + } + }, + organizationSelectInput: function(f, event) { + f.organization = ""; + f.organizationSelectedName = []; + if (event) { + f.organization = event["@id"]; + var preflabels = event["skos:prefLabel"]; + Object.entries(preflabels).forEach(([key, value]) => { + f.organizationSelectedName.push({ + "@value": value, + "@language": key + }); + }); + } + }, + setSelected: function(f, property, event) { + if (event) { + f[property] = event["@id"]; + } + this.$emit("form-input-" + f.component, f); + // eg on + // v-on:input-identifier-type="setSelected(f, 'identifierType', $event)" + // the identifierType property of the component should be updated via + // v-bind.sync="f" + // because we changed f in this method + // but it seems to not work lately.... + this.$forceUpdate(); + }, + updateSubject: function(f, event) { + f["skos:prefLabel"] = event["skos:prefLabel"]; + if (f["skos:prefLabel"]) { + if (f["skos:prefLabel"].length > 0) { + // needed to init the search input if loading from template + // will be synced with component's initquery prop + f.initquery = f["skos:prefLabel"][0]["@value"]; + } + } + f["rdfs:label"] = event["rdfs:label"]; + this.$emit("form-input-" + f.component, f); + }, + updatePlace: function(f, event) { + f["skos:prefLabel"] = event["skos:prefLabel"]; + if (f["skos:prefLabel"]) { + if (f["skos:prefLabel"].length > 0) { + // needed to init the search input if loading from template + // will be synced with component's initquery prop + f.initquery = f["skos:prefLabel"][0]["@value"]; + } + } + f["rdfs:label"] = event["rdfs:label"]; + f.coordinates = event.coordinates; + this.$emit("form-input-" + f.component, f); + }, + selectInput: function(f, event) { + if (event) { + f.value = event["@id"]; + if (event["@type"]) { + f.type = event["@type"]; + } + if (event["skos:prefLabel"]) { + let preflabels = event["skos:prefLabel"]; + f["skos:prefLabel"] = []; + Object.entries(preflabels).forEach(([key, value]) => { + f["skos:prefLabel"].push({ "@value": value, "@language": key }); + }); + } + if (event["rdfs:label"]) { + let rdfslabels = event["rdfs:label"]; + if (rdfslabels) { + f["rdfs:label"] = []; + Object.entries(rdfslabels).forEach(([key, value]) => { + f["rdfs:label"].push({ "@value": value, "@language": key }); + }); + } + } + if (event["skos:notation"]) { + f["skos:notation"] = event["skos:notation"]; + } + } else { + f.value = ""; + f["skos:prefLabel"] = []; + f["rdfs:label"] = []; + f["skos:notation"] = []; + } + this.$emit("form-input-" + f.component, f); + }, + roleInput: function(f, event) { + f.role = event["@id"]; + this.$emit("form-input-" + f.component, f); + }, + containedInRoleInput: function(f, event) { + for (let r of f.roles) { + if (r.id === event.role.id) { + if (event.roleTerm) { + r.role = event.roleTerm["@id"]; + } + if (event.name) { + r.name = event.name; + } + if (event.firstname) { + r.firstname = event.firstname; + } + if (event.lastname) { + r.lastname = event.lastname; + } + } + } + }, + setFilename: function(f, event) { + f.value = event.name; + f.file = event; + this.$emit("form-input-" + f.component, f); + }, + addFieldAutocompleteFilter: function(item, queryText) { + const lab = this.$t(item["fieldname"]).toLowerCase(); + const query = queryText.toLowerCase(); + return lab.indexOf(query) > -1; + }, + removeFieldChip(item) { + const index = this.addfieldselection.indexOf(item); + if (index >= 0) this.addfieldselection.splice(index, 1); + }, + addFields(section) { + for (var i = 0; i < this.addfieldselection.length; i++) { + let f = fields.getField(this.addfieldselection[i].id); + f.removable = true; + section.fields.push(f); + } + this.addfieldselection = []; + section["adddialogue"] = false; + } + }, + mounted: function() { + this.$store.dispatch("loadLanguages", this.$i18n.locale); + } +}; +</script> + +<style scoped> +.v-btn { + margin: 0; +} +.prewrap { + white-space: pre-wrap; +} +</style> \ No newline at end of file diff --git a/src/components/input/phaidra_inputs/PIFunder.vue b/src/components/input/phaidra_inputs/PIFunder.vue new file mode 100644 index 0000000000000000000000000000000000000000..d7a4cd13561075791d117701fa08d8895920750c --- /dev/null +++ b/src/components/input/phaidra_inputs/PIFunder.vue @@ -0,0 +1,87 @@ +<template> + <v-layout row> + <v-flex xs4> + <v-text-field + :value="name" + :label="$t('Funder name')" + v-on:blur="$emit('input-name',$event.target.value)" + box + ></v-text-field> + </v-flex> + <v-flex xs2> + <v-autocomplete + :value="getTerm('lang', nameLanguage)" + v-on:input="$emit('input-name-language', $event )" + :items="vocabularies['lang'].terms" + :filter="autocompleteFilter" + hide-no-data + :label="$t('Language')" + box + return-object + clearable + > + <template slot="item" slot-scope="{ item }"> + <v-list-tile-content two-line> + <v-list-tile-title v-html="`${getLocalizedTermLabel('lang', item['@id'])}`"></v-list-tile-title> + <v-list-tile-sub-title v-html="`${item['@id']}`"></v-list-tile-sub-title> + </v-list-tile-content> + </template> + <template slot="selection" slot-scope="{ item }"> + <v-list-tile-content> + <v-list-tile-title v-html="`${getLocalizedTermLabel('lang', item['@id'])}`"></v-list-tile-title> + </v-list-tile-content> + </template> + </v-autocomplete> + </v-flex> + <v-flex xs4> + <v-text-field + :value="identifier" + :label="'Funder identifier'" + v-on:blur="$emit('input-identifier',$event.target.value)" + box + ></v-text-field> + </v-flex> + <v-flex xs1 v-if="actions.length"> + <v-menu open-on-hover bottom offset-y> + <v-btn slot="activator" icon> + <v-icon>more_vert</v-icon> + </v-btn> + <v-list> + <v-list-tile v-for="(action, i) in actions" :key="i" @click="$emit(action.event, $event)"> + <v-list-tile-title>{{ action.title }}</v-list-tile-title> + </v-list-tile> + </v-list> + </v-menu> + </v-flex> + </v-layout> +</template> + +<script> +import { vocabulary } from "../../../mixins/vocabulary"; +import { fieldproperties } from "../../../mixins/fieldproperties"; + +export default { + name: "p-i-funder", + mixins: [vocabulary, fieldproperties], + props: { + name: { + type: String + }, + nameLanguage: { + type: String + }, + identifier: { + type: String + } + } +}; +</script> + +<style scoped> +.v-btn { + margin: 0; +} +.vertical-center { + align-items: center; +} +</style> diff --git a/src/components/input/phaidra_inputs/PIKeyword.vue b/src/components/input/phaidra_inputs/PIKeyword.vue new file mode 100644 index 0000000000000000000000000000000000000000..2771f1a8fec1ac2ae1cc2d4074702a4dc70da847 --- /dev/null +++ b/src/components/input/phaidra_inputs/PIKeyword.vue @@ -0,0 +1,193 @@ +<template> + <v-layout row> + <v-flex xs8> + <v-combobox + v-model="model" + v-on:input="$emit('input', htmlToPlaintext($event))" + v-on:change="$emit('input', htmlToPlaintext($event))" + :items="items" + :loading="loading" + :search-input.sync="search" + :required="required" + :rules="required ? [ v => !!v || 'Required'] : []" + hide-no-data + item-text="text" + item-value="value" + :label="$t(label)" + multiple + clearable + chips + deletable-chips + box + > + <template slot="item" slot-scope="{ item }"> + <v-list-tile-content two-line> + <v-list-tile-title inset v-html="item"></v-list-tile-title> + </v-list-tile-content> + </template> + <template slot="selection" slot-scope="data"> + <v-chip + close + :selected="data.selected" + :disabled="data.disabled" + class="v-chip--select-multi" + @input="data.parent.selectItem(data.item)" + >{{ htmlToPlaintext(data.item) }}</v-chip> + </template> + </v-combobox> + </v-flex> + <v-flex xs2 v-if="multilingual"> + <v-autocomplete + :value="getTerm('lang', language)" + v-on:input="$emit('input-language', $event )" + :items="vocabularies['lang'].terms" + :filter="autocompleteFilter" + hide-no-data + :label="$t('Language')" + box + return-object + clearable + > + <template slot="item" slot-scope="{ item }"> + <v-list-tile-content two-line> + <v-list-tile-title v-html="`${getLocalizedTermLabel('lang', item['@id'])}`"></v-list-tile-title> + <v-list-tile-sub-title v-html="`${item['@id']}`"></v-list-tile-sub-title> + </v-list-tile-content> + </template> + <template slot="selection" slot-scope="{ item }"> + <v-list-tile-content> + <v-list-tile-title v-html="`${getLocalizedTermLabel('lang', item['@id'])}`"></v-list-tile-title> + </v-list-tile-content> + </template> + </v-autocomplete> + </v-flex> + <v-flex xs1 v-if="actions.length"> + <v-menu open-on-hover bottom offset-y> + <v-btn slot="activator" icon> + <v-icon>more_vert</v-icon> + </v-btn> + <v-list> + <v-list-tile v-for="(action, i) in actions" :key="i" @click="$emit(action.event, $event)"> + <v-list-tile-title>{{ action.title }}</v-list-tile-title> + </v-list-tile> + </v-list> + </v-menu> + </v-flex> + </v-layout> +</template> + +<script> +import qs from "qs"; +import { vocabulary } from "../../../mixins/vocabulary"; +import { fieldproperties } from "../../../mixins/fieldproperties"; + +export default { + name: "p-i-keyword", + mixins: [vocabulary, fieldproperties], + props: { + value: { + type: String, + required: true + }, + language: { + type: String + }, + label: { + type: String, + required: true + }, + required: { + type: Boolean + }, + multilingual: { + type: Boolean + }, + suggester: { + type: String, + required: true + }, + debounce: { + type: Number, + default: 500 + } + }, + data() { + return { + items: [], + loading: false, + model: this.value ? this.value.split(",") : this.value, + search: null + }; + }, + watch: { + search(val) { + val && this.querySuggestionsDebounce(val); + } + }, + methods: { + htmlToPlaintext: function(text) { + return text ? String(text).replace(/<[^>]+>/gm, "") : ""; + }, + querySuggestionsDebounce(value) { + this.showList = true; + + if (this.debounce) { + if (this.debounceTask !== undefined) clearTimeout(this.debounceTask); + this.debounceTask = setTimeout(() => { + return this.querySuggestions(value); + }, this.debounce); + } else { + return this.querySuggestions(value); + } + }, + querySuggestions(q) { + var self = this; + + if (q.length < this.min || !this.suggester) return; + + self.loading = true; + + var params = { + suggest: true, + "suggest.dictionary": self.suggester, + wt: "json", + "suggest.q": q + }; + + var query = qs.stringify(params); + + fetch(self.$store.state.settings.instance.solr + "/suggest?" + query, { + method: "GET", + mode: "cors" + }) + .then(function(response) { + return response.json(); + }) + .then(function(json) { + self.items = []; + for ( + var i = 0; + i < json.suggest[self.suggester][q].suggestions.length; + i++ + ) { + self.items.push( + json.suggest[self.suggester][q].suggestions[i].term + ); + } + self.loading = false; + }) + .catch(function(error) { + //console.log(error) + self.loading = false; + }); + } + } +}; +</script> + +<style scoped> +.v-btn { + margin: 0; +} +</style> + diff --git a/src/components/input/phaidra_inputs/PILiteral.vue b/src/components/input/phaidra_inputs/PILiteral.vue new file mode 100644 index 0000000000000000000000000000000000000000..969fef50780c2bd1e58ad2fa4ec56a9c72a91f3f --- /dev/null +++ b/src/components/input/phaidra_inputs/PILiteral.vue @@ -0,0 +1,48 @@ +<template> + <v-layout row> + <v-flex xs4> + <v-text-field + :value="value" + v-on:blur="$emit('input-value',$event.target.value)" + :label="$t(label)" + box + ></v-text-field> + </v-flex> + <v-flex xs1 v-if="actions.length"> + <v-menu open-on-hover bottom offset-y> + <v-btn slot="activator" icon> + <v-icon>more_vert</v-icon> + </v-btn> + <v-list> + <v-list-tile v-for="(action, i) in actions" :key="i" @click="$emit(action.event, $event)"> + <v-list-tile-title>{{ action.title }}</v-list-tile-title> + </v-list-tile> + </v-list> + </v-menu> + </v-flex> + </v-layout> +</template> + +<script> +import { fieldproperties } from "../../../mixins/fieldproperties"; + +export default { + name: "p-i-literal", + mixins: [fieldproperties], + props: { + value: { + type: String + }, + label: { + type: String, + required: true + } + } +}; +</script> + +<style scoped> +.v-btn { + margin: 0; +} +</style> diff --git a/src/components/input/phaidra_inputs/PIProject.vue b/src/components/input/phaidra_inputs/PIProject.vue new file mode 100644 index 0000000000000000000000000000000000000000..83937849d7376ddd6d04c870b36e5cc8defadad6 --- /dev/null +++ b/src/components/input/phaidra_inputs/PIProject.vue @@ -0,0 +1,167 @@ +<template> + <v-layout row> + <v-flex xs10> + <v-card> + <v-card-title class="subheading grey white--text"> + <span>{{ $t('Project') }}</span> + <v-spacer></v-spacer> + <v-menu open-on-hover bottom offset-y v-if="actions.length"> + <v-btn slot="activator" icon dark> + <v-icon dark>more_vert</v-icon> + </v-btn> + <v-list> + <v-list-tile + v-for="(action, i) in actions" + :key="i" + @click="$emit(action.event, $event)" + > + <v-list-tile-title>{{ action.title }}</v-list-tile-title> + </v-list-tile> + </v-list> + </v-menu> + </v-card-title> + <v-divider></v-divider> + <v-card-text class="mt-4"> + <v-layout column> + <v-flex> + <v-layout row> + <v-flex xs8> + <v-text-field + :value="name" + :label="$t('Name')" + v-on:blur="$emit('input-name',$event.target.value)" + box + ></v-text-field> + </v-flex> + <v-flex xs3> + <v-autocomplete + :value="getTerm('lang', nameLanguage)" + v-on:input="$emit('input-name-language', $event )" + :items="vocabularies['lang'].terms" + :filter="autocompleteFilter" + hide-no-data + :label="$t('Language')" + box + return-object + clearable + > + <template slot="item" slot-scope="{ item }"> + <v-list-tile-content two-line> + <v-list-tile-title v-html="`${getLocalizedTermLabel('lang', item['@id'])}`"></v-list-tile-title> + <v-list-tile-sub-title v-html="`${item['@id']}`"></v-list-tile-sub-title> + </v-list-tile-content> + </template> + <template slot="selection" slot-scope="{ item }"> + <v-list-tile-content> + <v-list-tile-title v-html="`${getLocalizedTermLabel('lang', item['@id'])}`"></v-list-tile-title> + </v-list-tile-content> + </template> + </v-autocomplete> + </v-flex> + </v-layout> + + <v-layout row> + <v-flex xs8> + <v-text-field + :value="description" + :label="'Description'" + v-on:input="$emit('input-description', $event)" + box + ></v-text-field> + </v-flex> + <v-flex xs3> + <v-autocomplete + :value="getTerm('lang', descriptionLanguage)" + v-on:input="$emit('input-description-language', $event )" + :items="vocabularies['lang'].terms" + :filter="autocompleteFilter" + hide-no-data + :label="$t('Language')" + box + return-object + clearable + > + <template slot="item" slot-scope="{ item }"> + <v-list-tile-content two-line> + <v-list-tile-title v-html="`${getLocalizedTermLabel('lang', item['@id'])}`"></v-list-tile-title> + <v-list-tile-sub-title v-html="`${item['@id']}`"></v-list-tile-sub-title> + </v-list-tile-content> + </template> + <template slot="selection" slot-scope="{ item }"> + <v-list-tile-content> + <v-list-tile-title v-html="`${getLocalizedTermLabel('lang', item['@id'])}`"></v-list-tile-title> + </v-list-tile-content> + </template> + </v-autocomplete> + </v-flex> + </v-layout> + + <v-layout row> + <v-flex xs6> + <v-text-field + :value="identifier" + :label="'Identifier'" + v-on:blur="$emit('input-identifier',$event.target.value)" + homepage + box + ></v-text-field> + </v-flex> + + <v-flex xs6> + <v-text-field + :value="homepage" + :label="'Homepage'" + v-on:blur="$emit('input-homepage',$event.target.value)" + box + ></v-text-field> + </v-flex> + </v-layout> + </v-flex> + </v-layout> + </v-card-text> + </v-card> + </v-flex> + </v-layout> +</template> + +<script> +import { vocabulary } from "../../../mixins/vocabulary"; +import { fieldproperties } from "../../../mixins/fieldproperties"; + +export default { + name: "p-i-project", + mixins: [vocabulary, fieldproperties], + props: { + type: { + type: String + }, + name: { + type: String + }, + nameLanguage: { + type: String + }, + identifier: { + type: String + }, + description: { + type: String + }, + descriptionLanguage: { + type: String + }, + homepage: { + type: String + } + } +}; +</script> + +<style scoped> +.v-btn { + margin: 0; +} +.vertical-center { + align-items: center; +} +</style> diff --git a/src/components/input/phaidra_inputs/PISelect.vue b/src/components/input/phaidra_inputs/PISelect.vue new file mode 100644 index 0000000000000000000000000000000000000000..ab1e200be667f7922b4aba57b6f5d529d4b433cd --- /dev/null +++ b/src/components/input/phaidra_inputs/PISelect.vue @@ -0,0 +1,107 @@ +<template> + <v-layout row> + <v-flex xs8> + <v-autocomplete + :value="getTerm(vocabulary, value)" + :required="required" + v-on:input="$emit('input', $event )" + :rules="required ? [ v => !!v || 'Required'] : []" + :items="vocabularies[vocabulary].terms" + :loading="loading" + :filter="autocompleteFilter" + hide-no-data + :label="$t(label)" + box + return-object + clearable + :disabled="disabled" + > + <template slot="item" slot-scope="{ item }"> + <v-list-tile-content two-line> + <v-list-tile-title v-html="`${getLocalizedTermLabel(vocabulary, item['@id'])}`"></v-list-tile-title> + <v-list-tile-sub-title v-html="`${item['@id']}`"></v-list-tile-sub-title> + </v-list-tile-content> + </template> + <template slot="selection" slot-scope="{ item }"> + <v-list-tile-content> + <v-list-tile-title v-html="`${getLocalizedTermLabel(vocabulary, item['@id'])}`"></v-list-tile-title> + </v-list-tile-content> + </template> + </v-autocomplete> + </v-flex> + <v-flex xs1 v-if="actions.length"> + <v-menu open-on-hover bottom offset-y> + <v-btn slot="activator" icon> + <v-icon>more_vert</v-icon> + </v-btn> + <v-list> + <v-list-tile v-for="(action, i) in actions" :key="i" @click="$emit(action.event, $event)"> + <v-list-tile-title>{{ action.title }}</v-list-tile-title> + </v-list-tile> + </v-list> + </v-menu> + </v-flex> + </v-layout> +</template> + +<script> +import { vocabulary } from "../../../mixins/vocabulary"; +import { fieldproperties } from "../../../mixins/fieldproperties"; + +export default { + name: "p-i-select", + mixins: [vocabulary, fieldproperties], + props: { + value: { + type: String + }, + label: { + type: String, + required: true + }, + required: { + type: Boolean + }, + vocabulary: { + type: String, + required: true + }, + disabled: { + type: Boolean, + default: false + } + }, + data() { + return { + loading: false + }; + }, + mounted: function() { + this.$nextTick(function() { + if (this.vocabulary) { + this.loading = !this.vocabularies[this.vocabulary].loaded; + // emit input to set skos:prefLabel in parent + if (this.value) { + for ( + var i = 0; + i < this.vocabularies[this.vocabulary].terms.length; + i++ + ) { + if ( + this.vocabularies[this.vocabulary].terms[i]["@id"] === this.value + ) { + this.$emit("input", this.vocabularies[this.vocabulary].terms[i]); + } + } + } + } + }); + } +}; +</script> + +<style scoped> +.v-btn { + margin: 0; +} +</style> diff --git a/src/components/input/phaidra_inputs/PISelectText.vue b/src/components/input/phaidra_inputs/PISelectText.vue new file mode 100644 index 0000000000000000000000000000000000000000..efb5e86a994dc8198a6ff4b7bac6041449cac800 --- /dev/null +++ b/src/components/input/phaidra_inputs/PISelectText.vue @@ -0,0 +1,172 @@ +<template> + <v-layout row> + <v-flex xs3> + <v-autocomplete + :value="getTerm(vocabulary, selectvalue)" + :required="required" + v-on:input="updateLocation('select', $event)" + :rules="required ? [ v => !!v || 'Required'] : []" + :items="vocabularies[vocabulary].terms" + :filter="autocompleteFilter" + hide-no-data + :label="$t(selectlabel)" + box + return-object + clearable + > + <template slot="item" slot-scope="{ item }"> + <v-list-tile-content two-line> + <v-list-tile-title v-html="`${getLocalizedTermLabel(vocabulary, item['@id'])}`"></v-list-tile-title> + <v-list-tile-sub-title v-html="`${item['@id']}`"></v-list-tile-sub-title> + </v-list-tile-content> + </template> + <template slot="selection" slot-scope="{ item }"> + <v-list-tile-content> + <v-list-tile-title v-html="`${getLocalizedTermLabel(vocabulary, item['@id'])}`"></v-list-tile-title> + </v-list-tile-content> + </template> + </v-autocomplete> + </v-flex> + <v-flex xs4> + <v-text-field + :value="textvalue" + v-on:input="updateLocation('text', $event)" + :label="$t(label)" + :required="required" + :rules="required ? [ v => !!v || 'Required'] : []" + box + ></v-text-field> + </v-flex> + <v-flex xs3 v-if="multilingual"> + <v-autocomplete + :value="getTerm('lang', language)" + v-on:input="$emit('input-language', $event )" + :items="vocabularies['lang'].terms" + :filter="autocompleteFilter" + hide-no-data + :label="$t('Language')" + box + return-object + clearable + > + <template slot="item" slot-scope="{ item }"> + <v-list-tile-content two-line> + <v-list-tile-title v-html="`${getLocalizedTermLabel('lang', item['@id'])}`"></v-list-tile-title> + <v-list-tile-sub-title v-html="`${item['@id']}`"></v-list-tile-sub-title> + </v-list-tile-content> + </template> + <template slot="selection" slot-scope="{ item }"> + <v-list-tile-content> + <v-list-tile-title v-html="`${getLocalizedTermLabel('lang', item['@id'])}`"></v-list-tile-title> + </v-list-tile-content> + </template> + </v-autocomplete> + </v-flex> + <v-flex xs1 v-if="actions.length"> + <v-menu open-on-hover bottom offset-y> + <v-btn slot="activator" icon> + <v-icon>more_vert</v-icon> + </v-btn> + <v-list> + <v-list-tile v-for="(action, i) in actions" :key="i" @click="$emit(action.event, $event)"> + <v-list-tile-title>{{ action.title }}</v-list-tile-title> + </v-list-tile> + </v-list> + </v-menu> + </v-flex> + </v-layout> +</template> + +<script> +import { vocabulary } from "../../../mixins/vocabulary"; +import { fieldproperties } from "../../../mixins/fieldproperties"; + +export default { + name: "p-i-select-text", + mixins: [vocabulary, fieldproperties], + props: { + textvalue: { + type: String + }, + language: { + type: String + }, + selectvalue: { + type: String + }, + label: { + type: String, + required: true + }, + selectlabel: { + type: String, + required: true + }, + selectdisabled: { + type: Boolean, + default: false + }, + vocabulary: { + type: String, + required: true + }, + required: { + type: Boolean + }, + multilingual: { + type: Boolean + } + }, + data() { + return { + location: "", + svalue: "", + tvalue: "" + }; + }, + methods: { + updateLocation: function(source, value) { + this.location = ""; + if (source === "select") { + if (value) { + this.svalue = value["@id"]; + this.$emit("input-select", this.svalue); + } else { + this.svalue = ""; + } + } + if (source === "text") { + if (value) { + this.tvalue = value; + this.$emit("input-text", this.tvalue); + } + } + if (this.svalue) { + this.location = this.svalue; + } + if (this.tvalue) { + if (this.location !== "") { + this.location = this.location + "; "; + } + this.location = this.location + this.tvalue; + } + this.$emit("input", this.location); + } + }, + mounted: function() { + if (this.selectvalue) { + this.location = this.selectvalue; + } + if (this.value) { + this.location = this.location + "; " + this.value; + } + this.$emit("input", this.location); + } +}; +</script> + +<style scoped> +.v-btn { + margin: 0; +} +</style> diff --git a/src/components/input/phaidra_inputs/PISeries.vue b/src/components/input/phaidra_inputs/PISeries.vue new file mode 100644 index 0000000000000000000000000000000000000000..3af496aa8739f82c1c0b0749598d95bf0394e8fa --- /dev/null +++ b/src/components/input/phaidra_inputs/PISeries.vue @@ -0,0 +1,183 @@ +<template> + <v-layout row> + <v-flex xs12> + <v-card> + <v-card-title class="subheading grey white--text"> + <span>{{ $t(label) }}</span> + <v-spacer></v-spacer> + <v-menu open-on-hover bottom offset-y v-if="actions.length"> + <v-btn slot="activator" icon dark> + <v-icon dark>more_vert</v-icon> + </v-btn> + <v-list> + <v-list-tile + v-for="(action, i) in actions" + :key="i" + @click="$emit(action.event, $event)" + > + <v-list-tile-title>{{ action.title }}</v-list-tile-title> + </v-list-tile> + </v-list> + </v-menu> + </v-card-title> + <v-divider></v-divider> + <v-card-text class="mt-4"> + <v-layout column> + <v-flex> + <v-layout row> + <v-flex xs8> + <v-text-field + :value="title" + :label="$t('Title')" + v-on:blur="$emit('input-title',$event.target.value)" + box + ></v-text-field> + </v-flex> + <v-flex xs4> + <v-autocomplete + :value="getTerm('lang', titleLanguage)" + v-on:input="$emit('input-title-language', $event )" + :items="vocabularies['lang'].terms" + :filter="autocompleteFilter" + hide-no-data + :label="$t('Language')" + box + return-object + clearable + > + <template slot="item" slot-scope="{ item }"> + <v-list-tile-content two-line> + <v-list-tile-title v-html="`${getLocalizedTermLabel('lang', item['@id'])}`"></v-list-tile-title> + <v-list-tile-sub-title v-html="`${item['@id']}`"></v-list-tile-sub-title> + </v-list-tile-content> + </template> + <template slot="selection" slot-scope="{ item }"> + <v-list-tile-content> + <v-list-tile-title v-html="`${getLocalizedTermLabel('lang', item['@id'])}`"></v-list-tile-title> + </v-list-tile-content> + </template> + </v-autocomplete> + </v-flex> + </v-layout> + + <v-layout row> + <v-flex xs4 v-if="!hideVolume"> + <v-text-field + :value="volume" + :label="$t('Volume')" + v-on:blur="$emit('input-volume',$event.target.value)" + box + ></v-text-field> + </v-flex> + + <v-flex xs4 v-if="!hideIssue"> + <v-text-field + :value="issue" + :label="$t('Issue')" + v-on:blur="$emit('input-issue',$event.target.value)" + box + ></v-text-field> + </v-flex> + + <v-flex xs4 v-if="!hideIssued"> + <v-text-field + :value="issued" + :label="$t('Issued')" + v-on:blur="$emit('input-issued',$event.target.value)" + :hint="'Format YYYY-MM-DD'" + :rules="[validationrules.date]" + box + ></v-text-field> + </v-flex> + </v-layout> + + <v-layout row> + <v-flex xs6 v-if="!hideIssn"> + <v-text-field + :value="issn" + :label="$t('ISSN')" + v-on:blur="$emit('input-issn',$event.target.value)" + box + ></v-text-field> + </v-flex> + + <v-flex xs6 v-if="!hideIdentifier"> + <v-text-field + :value="identifier" + :label="$t('Identifier')" + v-on:blur="$emit('input-identifier',$event.target.value)" + box + ></v-text-field> + </v-flex> + </v-layout> + </v-flex> + </v-layout> + </v-card-text> + </v-card> + </v-flex> + </v-layout> +</template> + +<script> +import { vocabulary } from "../../../mixins/vocabulary"; +import { fieldproperties } from "../../../mixins/fieldproperties"; +import { validationrules } from "../../../mixins/validationrules"; + +export default { + name: "p-i-series", + mixins: [vocabulary, fieldproperties, validationrules], + props: { + type: { + type: String + }, + label: { + type: String + }, + title: { + type: String + }, + titleLanguage: { + type: String + }, + hideVolume: { + type: Boolean + }, + volume: { + type: String + }, + hideIssue: { + type: Boolean + }, + issue: { + type: String + }, + hideIssued: { + type: Boolean + }, + issued: { + type: String + }, + hideIssn: { + type: Boolean + }, + issn: { + type: String + }, + hideIdentifier: { + type: Boolean + }, + identifier: { + type: String + } + } +}; +</script> + +<style scoped> +.v-btn { + margin: 0; +} +.vertical-center { + align-items: center; +} +</style> diff --git a/src/components/input/phaidra_inputs/PISpatialGetty.vue b/src/components/input/phaidra_inputs/PISpatialGetty.vue new file mode 100644 index 0000000000000000000000000000000000000000..324fced699266c40044e4ab520152afeb5612c97 --- /dev/null +++ b/src/components/input/phaidra_inputs/PISpatialGetty.vue @@ -0,0 +1,239 @@ +<template> + <v-layout row> + <v-flex xs3> + <v-autocomplete + v-on:input="$emit('input-place-type', $event)" + :label="$t('Type of place')" + :items="vocabularies['placepredicate'].terms" + :value="getTerm('placepredicate', type)" + :filter="autocompleteFilter" + :disabled="disabletype" + box + return-object + clearable + > + <template slot="item" slot-scope="{ item }"> + <v-list-tile-content two-line> + <v-list-tile-title v-html="`${getLocalizedTermLabel('placepredicate', item['@id'])}`"></v-list-tile-title> + <v-list-tile-sub-title v-html="`${item['@id']}`"></v-list-tile-sub-title> + </v-list-tile-content> + </template> + <template slot="selection" slot-scope="{ item }"> + <v-list-tile-content> + <v-list-tile-title v-html="`${getLocalizedTermLabel('placepredicate', item['@id'])}`"></v-list-tile-title> + </v-list-tile-content> + </template> + </v-autocomplete> + </v-flex> + <v-flex xs6> + <v-autocomplete + v-model="model" + v-on:input="$emit('input', $event)" + :items="items" + :loading="loading" + :search-input.sync="search" + cache-items + hide-no-data + hide-selected + item-text="text" + item-value="value" + :label="$t(label)" + box + clearable + :messages="resolved" + ></v-autocomplete> + </v-flex> + <v-flex xs1 v-if="actions.length"> + <v-menu open-on-hover bottom offset-y> + <v-btn slot="activator" icon> + <v-icon>more_vert</v-icon> + </v-btn> + <v-list> + <v-list-tile v-for="(action, i) in actions" :key="i" @click="$emit(action.event, $event)"> + <v-list-tile-title>{{ action.title }}</v-list-tile-title> + </v-list-tile> + </v-list> + </v-menu> + </v-flex> + </v-layout> +</template> + +<script> +import qs from "qs"; +import { vocabulary } from "../../../mixins/vocabulary"; +import { fieldproperties } from "../../../mixins/fieldproperties"; + +export default { + name: "p-i-spatial-getty", + mixins: [vocabulary, fieldproperties], + props: { + value: { + type: String, + required: true + }, + type: { + type: String + }, + label: { + type: String, + required: true + }, + initquery: { + type: String + }, + required: { + type: Boolean + }, + debounce: { + type: Number, + default: 500 + }, + disabletype: { + type: Boolean + } + }, + watch: { + search(val) { + val && this.querySuggestionsDebounce(val); + }, + value(val) { + val && this.resolve(val); + } + }, + data() { + return { + items: [], + loading: false, + model: null, + search: null, + debounceTask: undefined, + preflabel: "", + rdfslabel: "", + coordinates: [], + resolved: "" + }; + }, + methods: { + resolve: function(uri) { + var self = this; + + if (uri) { + self.loading = true; + + var params = { + uri: uri + }; + + var query = qs.stringify(params); + + fetch(self.$store.state.settings.instance.api + "/resolve/?" + query, { + method: "GET", + mode: "cors" + }) + .then(function(response) { + return response.json(); + }) + .then(function(json) { + self.loading = false; + self.preflabel = json[uri]["skos:prefLabel"]; + self.rdfslabel = json[uri]["rdfs:label"]; + for (var i = 0; i < self.rdfslabel.length; i++) { + self.resolved = + '<a href="' + + uri + + '" target="_blank">' + + self.rdfslabel[i]["@value"] + + "</a>"; + } + if (json[uri]["schema:GeoCoordinates"]) { + self.coordinates = [ + { + "@type": "schema:GeoCoordinates", + "schema:latitude": [ + json[uri]["schema:GeoCoordinates"]["schema:latitude"] + ], + "schema:longitude": [ + json[uri]["schema:GeoCoordinates"]["schema:longitude"] + ] + } + ]; + } + self.$emit("resolve", { + "skos:prefLabel": self.preflabel, + "rdfs:label": self.rdfslabel, + coordinates: self.coordinates + }); + }) + .catch(function(error) { + console.log(error); + self.loading = false; + }); + } + }, + querySuggestionsDebounce(q) { + if (this.debounce) { + if (this.debounceTask !== undefined) clearTimeout(this.debounceTask); + this.debounceTask = setTimeout(() => { + return this.querySuggestions(q); + }, this.debounce); + } else { + return this.querySuggestions(q); + } + }, + querySuggestions(q) { + var self = this; + + self.loading = true; + + var params = { + voc: "tgn", + count: 20, + searchstring: q + }; + + var query = qs.stringify(params); + + fetch(self.$store.state.settings.global.suggesters.getty + "?" + query, { + method: "GET", + mode: "cors" + }) + .then(function(response) { + return response.json(); + }) + .then(function(json) { + for (var i = 0; i < json[1].length; i++) { + self.items.push({ text: json[1][i], value: json[3][i] }); + } + self.loading = false; + }) + .catch(function(error) { + console.log(error); + }) + .finally(() => (self.loading = false)); + } + }, + mounted: function() { + this.$nextTick(function() { + this.loading = !this.vocabularies["placepredicate"].loaded; + // emit input to set skos:prefLabel in parent + if (this.type) { + this.$emit( + "input-place-type", + this.getTerm("placepredicate", this.type) + ); + } + }); + if (this.initquery) { + this.items = [{ value: this.value, text: this.initquery }]; + this.model = { value: this.value, text: this.initquery }; + this.resolve(this.value); + } + } +}; +</script> + +<style scoped> +.v-btn { + margin: 0; +} +</style> diff --git a/src/components/input/phaidra_inputs/PISpatialGettyReadonly.vue b/src/components/input/phaidra_inputs/PISpatialGettyReadonly.vue new file mode 100644 index 0000000000000000000000000000000000000000..4b952e7e75f4257f8ee6265f3f2f1b43ae6c87da --- /dev/null +++ b/src/components/input/phaidra_inputs/PISpatialGettyReadonly.vue @@ -0,0 +1,85 @@ +<template> + <v-layout row> + <v-flex xs8> + <v-text-field + :value="prefLabel" + :persistent-hint="true" + :messages="value" + :label="$t(label)" + readonly + box + ></v-text-field> + </v-flex> + <v-flex xs1 v-if="actions.length"> + <v-menu open-on-hover bottom offset-y> + <v-btn slot="activator" icon> + <v-icon>more_vert</v-icon> + </v-btn> + <v-list> + <v-list-tile v-for="(action, i) in actions" :key="i" @click="$emit(action.event, $event)"> + <v-list-tile-title v-if="action.event === 'remove'">{{ action.title }}</v-list-tile-title> + </v-list-tile> + </v-list> + </v-menu> + </v-flex> + </v-layout> +</template> +<script> +import { fieldproperties } from "../../../mixins/fieldproperties"; + +export default { + name: "p-i-spatial-getty-readonly", + mixins: [fieldproperties], + computed: { + rdfsLabels: function() { + var i; + var arr = []; + if (this["rdfs:label"]) { + for (i = 0; i < this["rdfs:label"].length; i++) { + arr.push(this["rdfs:label"][i]["@value"]); + } + } + return arr; + }, + prefLabel: function() { + var i; + var prefLabel = ""; + // just return any now + if (this["skos:prefLabel"]) { + for (i = 0; i < this["skos:prefLabel"].length; i++) { + return this["skos:prefLabel"][i]["@value"]; + } + } + return prefLabel; + } + }, + props: { + "skos:prefLabel": { + type: Array, + required: true + }, + "rdfs:label": { + type: Array + }, + value: { + type: String, + required: true + }, + coordinates: { + type: Array + }, + label: { + type: String, + required: true + }, + predicate: { + type: String, + required: true + }, + removable: { + type: Boolean, + default: true + } + } +}; +</script> \ No newline at end of file diff --git a/src/components/input/phaidra_inputs/PISpatialText.vue b/src/components/input/phaidra_inputs/PISpatialText.vue new file mode 100644 index 0000000000000000000000000000000000000000..7325625b1bfe7a011794dbebd2e1640c3afc02e9 --- /dev/null +++ b/src/components/input/phaidra_inputs/PISpatialText.vue @@ -0,0 +1,142 @@ +<template> + <v-layout row> + <v-flex xs5> + <v-autocomplete + v-on:input="$emit('input-place-type', $event)" + :label="$t('Type of place')" + :items="vocabularies['placepredicate'].terms" + :value="getTerm('placepredicate', type)" + :filter="autocompleteFilter" + :disabled="disabletype" + box + return-object + clearable + > + <template slot="item" slot-scope="{ item }"> + <v-list-tile-content two-line> + <v-list-tile-title v-html="`${getLocalizedTermLabel('placepredicate', item['@id'])}`"></v-list-tile-title> + <v-list-tile-sub-title v-html="`${item['@id']}`"></v-list-tile-sub-title> + </v-list-tile-content> + </template> + <template slot="selection" slot-scope="{ item }"> + <v-list-tile-content> + <v-list-tile-title v-html="`${getLocalizedTermLabel('placepredicate', item['@id'])}`"></v-list-tile-title> + </v-list-tile-content> + </template> + </v-autocomplete> + </v-flex> + <v-flex xs4> + <v-text-field + v-if="!multiline" + :value="value" + v-on:input="$emit('input', $event)" + :label="$t(label)" + :required="required" + :rules="required ? [ v => !!v || 'Required'] : []" + box + ></v-text-field> + <v-textarea + v-if="multiline" + :value="value" + v-on:input="$emit('input', $event)" + :label="$t(label)" + :required="required" + :rules="required ? [ v => !!v || 'Required'] : []" + box + ></v-textarea> + </v-flex> + <v-flex xs2 v-if="multilingual"> + <v-autocomplete + :value="getTerm('lang', language)" + v-on:input="$emit('input-language', $event )" + :items="vocabularies['lang'].terms" + :filter="autocompleteFilter" + hide-no-data + :label="$t('Language')" + box + return-object + clearable + > + <template slot="item" slot-scope="{ item }"> + <v-list-tile-content two-line> + <v-list-tile-title v-html="`${getLocalizedTermLabel('lang', item['@id'])}`"></v-list-tile-title> + <v-list-tile-sub-title v-html="`${item['@id']}`"></v-list-tile-sub-title> + </v-list-tile-content> + </template> + <template slot="selection" slot-scope="{ item }"> + <v-list-tile-content> + <v-list-tile-title v-html="`${getLocalizedTermLabel('lang', item['@id'])}`"></v-list-tile-title> + </v-list-tile-content> + </template> + </v-autocomplete> + </v-flex> + <v-flex xs1 v-if="actions.length"> + <v-menu open-on-hover bottom offset-y> + <v-btn slot="activator" icon> + <v-icon>more_vert</v-icon> + </v-btn> + <v-list> + <v-list-tile v-for="(action, i) in actions" :key="i" @click="$emit(action.event, $event)"> + <v-list-tile-title>{{ action.title }}</v-list-tile-title> + </v-list-tile> + </v-list> + </v-menu> + </v-flex> + </v-layout> +</template> + +<script> +import { vocabulary } from "../../../mixins/vocabulary"; +import { fieldproperties } from "../../../mixins/fieldproperties"; + +export default { + name: "p-i-text-field", + mixins: [vocabulary, fieldproperties], + props: { + value: { + type: String, + required: true + }, + type: { + type: String + }, + language: { + type: String + }, + label: { + type: String, + required: true + }, + required: { + type: Boolean + }, + multiline: { + type: Boolean + }, + multilingual: { + type: Boolean + }, + disabletype: { + type: Boolean + } + }, + mounted: function() { + this.$nextTick(function() { + this.loading = !this.vocabularies["placepredicate"].loaded; + // emit input to set skos:prefLabel in parent + if (this.type) { + this.$emit( + "input-place-type", + this.getTerm("placepredicate", this.type) + ); + } + }); + } +}; +</script> + +<style scoped> +.v-btn { + margin: 0; +} +</style> diff --git a/src/components/input/phaidra_inputs/PIStudyPlan.vue b/src/components/input/phaidra_inputs/PIStudyPlan.vue new file mode 100644 index 0000000000000000000000000000000000000000..117702bab748e7af03e15edd9ce6063b98ba860f --- /dev/null +++ b/src/components/input/phaidra_inputs/PIStudyPlan.vue @@ -0,0 +1,99 @@ +<template> + <v-layout row> + <v-flex xs4> + <v-text-field + :value="notation" + v-on:blur="$emit('input-notation',$event.target.value)" + :label="$t('Study plan notation')" + :required="required" + :rules="required ? [ v => !!v || 'Required'] : []" + box + ></v-text-field> + </v-flex> + <v-flex xs4> + <v-text-field + :value="name" + v-on:blur="$emit('input-name',$event.target.value)" + :label="$t('Study plan name')" + :required="required" + :rules="required ? [ v => !!v || 'Required'] : []" + box + ></v-text-field> + </v-flex> + <v-flex xs2 v-if="multilingual"> + <v-autocomplete + :value="getTerm('lang', nameLanguage)" + v-on:input="$emit('input-name-language', $event )" + :items="vocabularies['lang'].terms" + :filter="autocompleteFilter" + hide-no-data + :label="$t('Name language')" + box + return-object + clearable + > + <template slot="item" slot-scope="{ item }"> + <v-list-tile-content two-line> + <v-list-tile-title v-html="`${getLocalizedTermLabel('lang', item['@id'])}`"></v-list-tile-title> + <v-list-tile-sub-title v-html="`${item['@id']}`"></v-list-tile-sub-title> + </v-list-tile-content> + </template> + <template slot="selection" slot-scope="{ item }"> + <v-list-tile-content> + <v-list-tile-title v-html="`${getLocalizedTermLabel('lang', item['@id'])}`"></v-list-tile-title> + </v-list-tile-content> + </template> + </v-autocomplete> + </v-flex> + <v-flex xs1 v-if="actions.length"> + <v-menu open-on-hover bottom offset-y> + <v-btn slot="activator" icon> + <v-icon>more_vert</v-icon> + </v-btn> + <v-list> + <v-list-tile v-for="(action, i) in actions" :key="i" @click="$emit(action.event, $event)"> + <v-list-tile-title>{{ action.title }}</v-list-tile-title> + </v-list-tile> + </v-list> + </v-menu> + </v-flex> + </v-layout> +</template> + +<script> +import { vocabulary } from '../../../mixins/vocabulary' +import { fieldproperties } from '../../../mixins/fieldproperties' + +export default { + name: 'p-i-study-plan', + mixins: [vocabulary, fieldproperties], + props: { + notation: { + type: String, + required: true + }, + name: { + type: String, + required: true + }, + nameLanguage: { + type: String + }, + required: { + type: Boolean + }, + multiline: { + type: Boolean + }, + multilingual: { + type: Boolean + } + } +} +</script> + +<style scoped> +.v-btn { + margin: 0; +} +</style> diff --git a/src/components/input/phaidra_inputs/PISubjectGnd.vue b/src/components/input/phaidra_inputs/PISubjectGnd.vue new file mode 100644 index 0000000000000000000000000000000000000000..589a80c44893f6c574e26f43064a60948ee825f3 --- /dev/null +++ b/src/components/input/phaidra_inputs/PISubjectGnd.vue @@ -0,0 +1,206 @@ +<template> + <v-layout row> + <v-flex xs8> + <v-autocomplete + v-model="model" + v-on:input="$emit('input', $event)" + :items="items" + :loading="loading" + :search-input.sync="search" + cache-items + hide-no-data + hide-selected + item-text="text" + item-value="value" + :label="$t(label)" + box + clearable + :messages="resolved" + ></v-autocomplete> + </v-flex> + <v-flex xs1 v-if="actions.length"> + <v-menu open-on-hover bottom offset-y> + <v-btn slot="activator" icon> + <v-icon>more_vert</v-icon> + </v-btn> + <v-list> + <v-list-tile v-for="(action, i) in actions" :key="i" @click="$emit(action.event, $event)"> + <v-list-tile-title>{{ action.title }}</v-list-tile-title> + </v-list-tile> + </v-list> + </v-menu> + </v-flex> + </v-layout> +</template> + +<script> +import qs from "qs"; +import { fieldproperties } from "../../../mixins/fieldproperties"; + +export default { + name: "p-i-subject-gnd", + mixins: [fieldproperties], + props: { + value: { + type: String, + required: true + }, + type: { + type: String + }, + voc: { + type: String, + default: "SubjectHeading" + }, + exactvoc: { + type: String + }, + label: { + type: String, + required: true + }, + initquery: { + type: String + }, + required: { + type: Boolean + }, + debounce: { + type: Number, + default: 500 + } + }, + watch: { + search(val) { + val && this.querySuggestionsDebounce(val); + }, + value(val) { + val && this.resolve(val); + } + }, + data() { + return { + items: [], + loading: false, + model: null, + search: null, + debounceTask: undefined, + preflabel: "", + rdfslabel: "", + coordinates: [], + resolved: "" + }; + }, + methods: { + resolve: function(uri) { + var self = this; + + if (uri) { + self.loading = true; + + var params = { + uri: uri + }; + + var query = qs.stringify(params); + + fetch(self.$store.state.settings.instance.api + "/resolve/?" + query, { + method: "GET", + mode: "cors" + }) + .then(function(response) { + return response.json(); + }) + .then(function(json) { + self.loading = false; + self.preflabel = json[uri]["skos:prefLabel"]; + self.rdfslabel = json[uri]["rdfs:label"]; + if (self.rdfslabel) { + var rdfslabelarr = []; + for (var i = 0; i < self.rdfslabel.length; i++) { + rdfslabelarr.push(self.rdfslabel[i]["@value"]); + } + self.resolved = + 'Synonym: <a href="' + + uri + + '" target="_blank">' + + rdfslabelarr.join(", ") + + "</a>"; + } else { + self.resolved = ""; + } + self.$emit("resolve", { + "skos:prefLabel": self.preflabel, + "rdfs:label": self.rdfslabel + }); + }) + .catch(function(error) { + console.log(error); + self.loading = false; + }); + } + }, + querySuggestionsDebounce(q) { + if (this.debounce) { + if (this.debounceTask !== undefined) clearTimeout(this.debounceTask); + this.debounceTask = setTimeout(() => { + return this.querySuggestions(q); + }, this.debounce); + } else { + return this.querySuggestions(q); + } + }, + querySuggestions(q) { + var self = this; + + self.loading = true; + + var params = { + count: 20, + searchterm: q + }; + + if (this.voc) { + params["type"] = this.voc; + } + + if (this.exactvoc) { + params["exact_type"] = this.exactvoc; + } + + var query = qs.stringify(params); + + fetch(self.$store.state.settings.global.suggesters.gnd + "?" + query, { + method: "GET", + mode: "cors" + }) + .then(function(response) { + return response.json(); + }) + .then(function(json) { + for (var i = 0; i < json[1].length; i++) { + self.items.push({ text: json[1][i], value: json[3][i] }); + } + self.loading = false; + }) + .catch(function(error) { + console.log(error); + }) + .finally(() => (self.loading = false)); + } + }, + mounted: function() { + if (this.initquery) { + this.items = [{ value: this.value, text: this.initquery }]; + this.model = { value: this.value, text: this.initquery }; + this.resolve(this.value); + } + } +}; +</script> + +<style scoped> +.v-btn { + margin: 0; +} +</style> diff --git a/src/components/input/phaidra_inputs/PITextField.vue b/src/components/input/phaidra_inputs/PITextField.vue new file mode 100644 index 0000000000000000000000000000000000000000..c6f3dd0d8c86a41d7ee8d4881e493634f49a2173 --- /dev/null +++ b/src/components/input/phaidra_inputs/PITextField.vue @@ -0,0 +1,97 @@ +<template> + <v-layout row> + <v-flex xs8> + <v-text-field v-if="!multiline" + :value="value" + v-on:blur="$emit('input',$event.target.value)" + :label="$t(label)" + :required="required" + :rules="required ? [ v => !!v || 'Required'] : []" + box + ></v-text-field> + <v-textarea v-if="multiline" + :value="value" + v-on:blur="$emit('input',$event.target.value)" + :label="$t(label)" + :required="required" + :rules="required ? [ v => !!v || 'Required'] : []" + box + ></v-textarea> + </v-flex> + <v-flex xs2 v-if="multilingual"> + <v-autocomplete + :value="getTerm('lang', language)" + v-on:input="$emit('input-language', $event )" + :items="vocabularies['lang'].terms" + :filter="autocompleteFilter" + hide-no-data + :label="$t('Language')" + box + return-object + clearable + > + <template slot="item" slot-scope="{ item }"> + <v-list-tile-content two-line> + <v-list-tile-title v-html="`${getLocalizedTermLabel('lang', item['@id'])}`"></v-list-tile-title> + <v-list-tile-sub-title v-html="`${item['@id']}`"></v-list-tile-sub-title> + </v-list-tile-content> + </template> + <template slot="selection" slot-scope="{ item }"> + <v-list-tile-content> + <v-list-tile-title v-html="`${getLocalizedTermLabel('lang', item['@id'])}`"></v-list-tile-title> + </v-list-tile-content> + </template> + </v-autocomplete> + </v-flex> + <v-flex xs1 v-if="actions.length"> + <v-menu open-on-hover bottom offset-y> + <v-btn slot="activator" icon> + <v-icon>more_vert</v-icon> + </v-btn> + <v-list> + <v-list-tile v-for="(action, i) in actions" :key="i" @click="$emit(action.event, $event)"> + <v-list-tile-title>{{ action.title }}</v-list-tile-title> + </v-list-tile> + </v-list> + </v-menu> + </v-flex> + </v-layout> +</template> + +<script> +import { vocabulary } from '../../../mixins/vocabulary' +import { fieldproperties } from '../../../mixins/fieldproperties' + +export default { + name: 'p-i-text-field', + mixins: [vocabulary, fieldproperties], + props: { + value: { + type: String, + required: true + }, + language: { + type: String + }, + label: { + type: String, + required: true + }, + required: { + type: Boolean + }, + multiline: { + type: Boolean + }, + multilingual: { + type: Boolean + } + } +} +</script> + +<style scoped> +.v-btn { + margin: 0; +} +</style> diff --git a/src/components/input/phaidra_inputs/PITextFieldSuggest.vue b/src/components/input/phaidra_inputs/PITextFieldSuggest.vue new file mode 100644 index 0000000000000000000000000000000000000000..942ec78ba2337a197dd4bc06e74850d0f1119921 --- /dev/null +++ b/src/components/input/phaidra_inputs/PITextFieldSuggest.vue @@ -0,0 +1,199 @@ +<template> + <v-layout row> + <v-flex xs8> + <v-combobox + v-model="model" + v-on:input="$emit('input', htmlToPlaintext($event))" + v-on:change="$emit('input', htmlToPlaintext($event))" + :items="items" + :loading="loading" + :search-input.sync="search" + :required="required" + :rules="required ? [ v => !!v || 'Required'] : []" + cache-items + hide-no-data + hide-selected + item-text="text" + item-value="value" + :label="$t(label)" + box + clearable + > + <template slot="item" slot-scope="{ item }"> + <v-list-tile-content two-line> + <v-list-tile-title inset v-html="item"></v-list-tile-title> + </v-list-tile-content> + </template> + <template slot="selection" slot-scope="{ item }"> + <v-list-tile-content> + <v-list-tile-title inset>{{ htmlToPlaintext(item) }}</v-list-tile-title> + </v-list-tile-content> + </template> + </v-combobox> + </v-flex> + <v-flex xs2 v-if="multilingual"> + <v-autocomplete + :value="getTerm('lang', language)" + v-on:input="$emit('input-language', $event )" + :items="vocabularies['lang'].terms" + :filter="autocompleteFilter" + hide-no-data + :label="$t('Language')" + box + return-object + clearable + > + <template slot="item" slot-scope="{ item }"> + <v-list-tile-content two-line> + <v-list-tile-title v-html="`${getLocalizedTermLabel('lang', item['@id'])}`"></v-list-tile-title> + <v-list-tile-sub-title v-html="`${item['@id']}`"></v-list-tile-sub-title> + </v-list-tile-content> + </template> + <template slot="selection" slot-scope="{ item }"> + <v-list-tile-content> + <v-list-tile-title v-html="`${getLocalizedTermLabel('lang', item['@id'])}`"></v-list-tile-title> + </v-list-tile-content> + </template> + </v-autocomplete> + </v-flex> + <v-flex xs1 v-if="actions.length"> + <v-menu open-on-hover bottom offset-y> + <v-btn slot="activator" icon> + <v-icon>more_vert</v-icon> + </v-btn> + <v-list> + <v-list-tile v-for="(action, i) in actions" :key="i" @click="$emit(action.event, $event)"> + <v-list-tile-title>{{ action.title }}</v-list-tile-title> + </v-list-tile> + </v-list> + </v-menu> + </v-flex> + </v-layout> +</template> + +<script> + import qs from 'qs' + import { vocabulary } from '../../../mixins/vocabulary' + import { fieldproperties } from '../../../mixins/fieldproperties' + + export default { + name: 'p-i-text-field-suggest', + mixins: [vocabulary, fieldproperties], + props: { + value: { + type: String, + required: true + }, + language: { + type: String + }, + label: { + type: String, + required: true + }, + required: { + type: Boolean + }, + multilingual: { + type: Boolean + }, + suggester: { + type: String, + required: true + }, + debounce: { + type: Number, + default: 500 + } + }, + data () { + return { + items: [], + loading: false, + model: this.value, + search: null + } + }, + watch: { + search (val) { + val && this.querySuggestionsDebounce(val) + } + }, + methods: { + htmlToPlaintext: function (text) { + return text ? String(text).replace(/<[^>]+>/gm, '') : '' + }, + querySuggestionsDebounce (value) { + this.showList = true + + if (this.debounce) { + if (this.debounceTask !== undefined) clearTimeout(this.debounceTask) + this.debounceTask = setTimeout(() => { + return this.querySuggestions(value) + }, this.debounce) + } else { + return this.querySuggestions(value) + } + }, + querySuggestions (q) { + var self = this + + if (q.length < this.min || !this.suggester) return + + self.loading = true + + var params = { + suggest: true, + 'suggest.dictionary': self.suggester, + wt: 'json', + 'suggest.q': q + } + + var query = qs.stringify(params) + + fetch(self.$store.state.settings.instance.solr + '/suggest?' + query, { + method: 'GET', + mode: 'cors' + }) + .then(function (response) { return response.json() }) + .then(function (json) { + self.items = [] + for (var i = 0; i < json.suggest[self.suggester][q].suggestions.length; i++) { + self.items.push(json.suggest[self.suggester][q].suggestions[i].term) + } + self.loading = false + }) + .catch(function (error) { + //console.log(error) + self.loading = false + }) + } + } + + } +</script> + +<style scoped> +.searchbox{ + font-size: 14px; + box-sizing: border-box; + border: none; + box-shadow: none; + outline: 0; + background: 0 0; + width: 100%; + padding: 0 15px; + line-height: 40px; + height: 40px; +} + +.autocomplete { + position: absolute; + z-index: 999; + margin-top: 2px; +} +.v-btn { + margin: 0; +} +</style> + diff --git a/src/components/input/phaidra_inputs/PITitle.vue b/src/components/input/phaidra_inputs/PITitle.vue new file mode 100644 index 0000000000000000000000000000000000000000..b0ccb4aed6de5c00d083339af232670554a1a562 --- /dev/null +++ b/src/components/input/phaidra_inputs/PITitle.vue @@ -0,0 +1,109 @@ +<template> + <v-layout row> + here in pi title + <v-flex xs4> + <v-text-field + :value="title" + :label="$t( titleLabel ? titleLabel : type )" + v-on:blur="$emit('input-title',$event.target.value)" + box + ></v-text-field> + </v-flex> + <v-flex xs4 v-if="!hideSubtitle"> + <v-text-field + :value="subtitle" + :label="$t( subtitleLabel ? subtitleLabel : 'Subtitle' )" + v-on:blur="$emit('input-subtitle',$event.target.value)" + box + ></v-text-field> + </v-flex> + <v-flex xs2 v-if="multilingual"> + <v-autocomplete + :value="getTerm('lang', language)" + v-on:input="$emit('input-language', $event )" + :items="vocabularies['lang'].terms" + :filter="autocompleteFilter" + hide-no-data + :label="$t('Language')" + box + return-object + clearable + > + <template slot="item" slot-scope="{ item }"> + <v-list-tile-content two-line> + <v-list-tile-title v-html="`${getLocalizedTermLabel('lang', item['@id'])}`"></v-list-tile-title> + <v-list-tile-sub-title v-html="`${item['@id']}`"></v-list-tile-sub-title> + </v-list-tile-content> + </template> + <template slot="selection" slot-scope="{ item }"> + <v-list-tile-content> + <v-list-tile-title v-html="`${getLocalizedTermLabel('lang', item['@id'])}`"></v-list-tile-title> + </v-list-tile-content> + </template> + </v-autocomplete> + </v-flex> + <v-flex xs1 v-if="actions.length"> + <v-menu open-on-hover bottom offset-y> + <v-btn slot="activator" icon> + <v-icon>more_vert</v-icon> + </v-btn> + <v-list> + <v-list-tile v-for="(action, i) in actions" :key="i" @click="$emit(action.event, $event)"> + <v-list-tile-title>{{ action.title }}</v-list-tile-title> + </v-list-tile> + </v-list> + </v-menu> + </v-flex> + </v-layout> +</template> + +<script> +import { vocabulary } from "../../../mixins/vocabulary"; +import { fieldproperties } from "../../../mixins/fieldproperties"; + +export default { + name: "p-i-title", + mixins: [vocabulary, fieldproperties], + props: { + title: { + type: String + }, + titleLabel: { + type: String + }, + type: { + type: String + }, + subtitle: { + type: String + }, + subtitleLabel: { + type: String + }, + hideSubtitle: { + type: Boolean + }, + language: { + type: String + }, + required: { + type: Boolean + }, + multilingual: { + type: Boolean + } + }, + data() { + return { + datepicker: false, + selectedDate: "" + }; + } +}; +</script> + +<style scoped> +.v-btn { + margin: 0; +} +</style> diff --git a/src/components/input/phaidra_inputs/PIUnknownReadonly.vue b/src/components/input/phaidra_inputs/PIUnknownReadonly.vue new file mode 100644 index 0000000000000000000000000000000000000000..86b65871d04251b7bed52dde166b212250e6bdc5 --- /dev/null +++ b/src/components/input/phaidra_inputs/PIUnknownReadonly.vue @@ -0,0 +1,28 @@ +<template> + <v-layout> + <v-flex> + <v-flex class="primary--text" xs3>{{ label }}</v-flex> + <vue-json-pretty :data="jsonld"></vue-json-pretty> + </v-flex> + </v-layout> +</template> + +<script> +import VueJsonPretty from 'vue-json-pretty' + +export default { + name: 'p-i-unknown-readonly', + components: { + VueJsonPretty + }, + props: { + jsonld: { + type: Object + }, + label: { + type: String, + required: true + } + } +} +</script> \ No newline at end of file diff --git a/src/components/input/phaidra_inputs/PIVocabExtReadonly.vue b/src/components/input/phaidra_inputs/PIVocabExtReadonly.vue new file mode 100644 index 0000000000000000000000000000000000000000..147b6b622678cf864f3b5c660d9ed6646abd6418 --- /dev/null +++ b/src/components/input/phaidra_inputs/PIVocabExtReadonly.vue @@ -0,0 +1,109 @@ +<template> + <v-layout row> + <v-flex xs8> + <v-text-field + :value="prefLabel" + :persistent-hint="true" + :messages="messages" + :label="$t(label)" + readonly + box + ></v-text-field> + </v-flex> + <v-flex xs1 v-if="actions.length"> + <v-menu open-on-hover bottom offset-y> + <v-btn slot="activator" icon> + <v-icon>more_vert</v-icon> + </v-btn> + <v-list> + <v-list-tile v-for="(action, i) in actions" :key="i" @click="$emit(action.event, $event)"> + <v-list-tile-title v-if="action.event === 'remove'">{{ action.title }}</v-list-tile-title> + </v-list-tile> + </v-list> + </v-menu> + </v-flex> + </v-layout> +</template> +<script> +import { fieldproperties } from "../../../mixins/fieldproperties"; + +export default { + name: "p-i-vocab-ext-readonly", + mixins: [fieldproperties], + computed: { + rdfsLabels: function() { + var i; + var arr = []; + if (this["rdfs:label"]) { + for (i = 0; i < this["rdfs:label"].length; i++) { + arr.push(this["rdfs:label"][i]["@value"]); + } + } + return arr; + }, + prefLabel: function() { + var i; + var prefLabel = ""; + // just return any now + if (this["skos:prefLabel"]) { + for (i = 0; i < this["skos:prefLabel"].length; i++) { + return this["skos:prefLabel"][i]["@value"]; + } + } + return prefLabel; + }, + notation: function() { + var i; + if (this["skos:notation"]) { + for (i = 0; i < this["skos:notation"].length; i++) { + return this["skos:notation"][i]; + } + } + return false; + }, + messages: function() { + var ret; + if (this["skos:exactMatch"]) { + ret = + '<a href="' + + this["skos:exactMatch"][0] + + '" target="_blank">' + + this["skos:exactMatch"][0] + + "</a>"; + } + if (this["skos:notation"]) { + ret = ret + (ret ? ret + " " : "") + "Notation: " + notation; + } + return ret; + } + }, + props: { + "skos:prefLabel": { + type: Array, + required: true + }, + "rdfs:label": { + type: Array + }, + "skos:exactMatch": { + type: Array, + required: true + }, + "skos:notation": { + type: Array + }, + label: { + type: String, + required: true + }, + predicate: { + type: String, + required: true + }, + removable: { + type: Boolean, + default: true + } + } +}; +</script> \ No newline at end of file diff --git a/src/components/management/PMDelete.vue b/src/components/management/PMDelete.vue new file mode 100644 index 0000000000000000000000000000000000000000..9c39a2e0823c3ba076f69f2298d18678c29f6a71 --- /dev/null +++ b/src/components/management/PMDelete.vue @@ -0,0 +1,95 @@ +<template> + <v-card > + <v-card-title class="subheading grey white--text">{{ $t('Delete') }}</v-card-title> + <v-divider></v-divider> + <v-card-text class="mt-4"> + <v-alert :type="'info'" :value="true" transition="slide-y-transition" v-if="(cmodel === 'Container') && (members.length > 0)">{{ $t('MEMBERS_DELETE_ALERT_CONTAINER', { nrmembers: members.length }) }}</v-alert> + <v-flex v-else>{{ $t('DELETE_OBJECT', { pid: 'https://' + instance.baseurl + '/' + pid }) }}</v-flex> + </v-card-text> + <v-card-actions> + <v-spacer></v-spacer> + <v-flex> + <v-dialog v-model="dialog" width="500" > + <template v-slot:activator="{ on }"> + <v-btn color="red" class="white--text" v-on="on" :disabled="(members.length > 0) || !pid || !cmodel">{{ $t('Delete') }}</v-btn> + </template> + <v-card> + <v-card-title class="headline grey lighten-2" primary-title >{{ $t('Delete') }}</v-card-title> + <v-card-text>{{ $t('DELETE_OBJECT_CONFIRM', { pid: 'https://' + instance.baseurl + '/' + pid })}}</v-card-text> + <v-divider></v-divider> + <v-card-actions> + <v-spacer></v-spacer> + <v-btn color="red" class="white--text" :loading="loading" :disabled="loading" @click="deleteObject(pid)">{{ $t('Delete') }}</v-btn> + <v-btn :disabled="loading" @click="dialog = false">{{ $t('Cancel') }}</v-btn> + </v-card-actions> + </v-card> + </v-dialog> + </v-flex> + </v-card-actions> + </v-card> +</template> + +<script> + +export default { + name: 'p-m-delete', + props: { + pid: { + type: String + }, + cmodel: { + type: String + }, + members: { + type: Array, + default: [] + } + }, + computed: { + instance: function() { + return this.$store.state.settings.instance + } + }, + data () { + return { + loading: false, + dialog: false + } + }, + methods: { + deleteObject: function (pid) { + var self = this + self.loading = true + var url = self.$store.state.settings.instance.api + '/object/' + pid + '/delete' + var promise = fetch(url, { + method: 'POST', + mode: 'cors', + headers: { + 'X-XSRF-TOKEN': this.$store.state.user.token + } + }) + .then(function (response) { return response.json() }) + .then(function (json) { + if (json.status === 200) { + self.$emit('object-deleted', self.pid) + } else { + if (json.alerts && json.alerts.length > 0) { + self.$store.commit('setAlerts', json.alerts) + } + } + self.loading = false + self.dialog = false + self.$vuetify.goTo(0) + }) + .catch(function (error) { + self.$store.commit('setAlerts', [{ type: 'danger', msg: 'Error deleting object: ' + error}]) + console.log(error) + self.loading = false + self.dialog = false + self.$vuetify.goTo(0) + }) + return promise + } + } +} +</script> diff --git a/src/components/management/PMFiles.vue b/src/components/management/PMFiles.vue new file mode 100644 index 0000000000000000000000000000000000000000..7485601b382408f85ede26eccb2d71e8babbcfbf --- /dev/null +++ b/src/components/management/PMFiles.vue @@ -0,0 +1,32 @@ +<template> + <v-card > + <v-card-title class="subheading grey white--text">{{ $t('Files') }}</v-card-title> + <v-divider></v-divider> + <v-card-text class="mt-4"> + <v-flex>{{ $t('Here you can add or remove some of the files this digital object is composed of.') }}</v-flex> + </v-card-text> + </v-card> +</template> + +<script> + +export default { + name: 'p-m-files', + props: { + pid: { + type: String, + required: true + } + }, + computed: { + instance: function() { + return this.$store.state.settings.instance + } + }, + data () { + return { + loading: false + } + } +} +</script> diff --git a/src/components/management/PMRelationships.vue b/src/components/management/PMRelationships.vue new file mode 100644 index 0000000000000000000000000000000000000000..1b527afc4634c36057de6378fecd39e043c84cbf --- /dev/null +++ b/src/components/management/PMRelationships.vue @@ -0,0 +1,32 @@ +<template> + <v-card > + <v-card-title class="subheading grey white--text">{{ $t('Relationships') }}</v-card-title> + <v-divider></v-divider> + <v-card-text class="mt-4"> + <v-flex>{{ $t('Here you can add or remove relationships to other objects inside this repository.') }}</v-flex> + </v-card-text> + </v-card> +</template> + +<script> + +export default { + name: 'p-m-relationships', + props: { + pid: { + type: String, + required: true + } + }, + computed: { + instance: function() { + return this.$store.state.settings.instance + } + }, + data () { + return { + loading: false + } + } +} +</script> diff --git a/src/components/management/PMRights.vue b/src/components/management/PMRights.vue new file mode 100644 index 0000000000000000000000000000000000000000..373f99125b7556d7995f6547a95ed942b739a86f --- /dev/null +++ b/src/components/management/PMRights.vue @@ -0,0 +1,32 @@ +<template> + <v-card > + <v-card-title class="subheading grey white--text">{{ $t('Access rights') }}</v-card-title> + <v-divider></v-divider> + <v-card-text class="mt-4"> + <v-flex>{{ $t('Here you can restrict access to this object. Any other objects, like members (if this is a container or a collection), pages (if this is a book) different versions or related objects will not be affected.') }}</v-flex> + </v-card-text> + </v-card> +</template> + +<script> + +export default { + name: 'p-m-rights', + props: { + pid: { + type: String, + required: true + } + }, + computed: { + instance: function() { + return this.$store.state.settings.instance + } + }, + data () { + return { + loading: false + } + } +} +</script> diff --git a/src/components/management/PMSort.vue b/src/components/management/PMSort.vue new file mode 100644 index 0000000000000000000000000000000000000000..e823200095cb326084fd95b44e8b0a3301d5efba --- /dev/null +++ b/src/components/management/PMSort.vue @@ -0,0 +1,104 @@ +<template> + <v-card > + <v-card-title class="subheading grey white--text">{{ $t('Sort') }}</v-card-title> + <v-divider></v-divider> + <v-card-text class="mt-4" v-if="members.length > 0"> + <v-flex>{{ $t('Here you can sort members of this object (drag & drop).') }}</v-flex> + <SortableList lockAxis="y" v-model="memberscomputed"> + <SortableSolrDoc v-for="(item, index) in memberscomputed" :index="index" :key="index" :item="item"/> + </SortableList> + </v-card-text> + <v-card-actions v-if="members.length > 0"> + <v-spacer></v-spacer> + <v-btn color="primary" :disabled="loading" :loading="loading" @click="save()">{{ $t('Save') }}</v-btn> + </v-card-actions> + </v-card> +</template> + +<script> +import SortableList from '../utils/SortableList' +import SortableSolrDoc from '../utils/SortableSolrDoc' + +export default { + name: 'p-m-sort', + components: { + SortableSolrDoc, + SortableList + }, + props: { + pid: { + type: String + }, + cmodel: { + type: String + }, + members: { + type: Array, + default: [] + } + }, + computed: { + instance: function() { + return this.$store.state.settings.instance + }, + memberscomputed: { + get: function () { + if (this.membersdata.length === 0) { + this.membersdata = this.members + } + return this.membersdata + }, + set: function (newValue) { + this.membersdata = newValue + } + } + }, + data () { + return { + loading: false, + membersdata: [] + } + }, + methods: { + save: function () { + var self = this + self.loading = true + let colorder = [] + let i = 0 + for (let m of this.membersdata) { + i++ + colorder.push({pid: m.pid, pos: i}) + } + var httpFormData = new FormData() + httpFormData.append('metadata', JSON.stringify({metadata: {members: colorder}})) + fetch(self.instance.api + '/' + this.cmodel.toLowerCase() + '/' + self.pid + '/members/order', { + method: 'POST', + mode: 'cors', + headers: { + 'X-XSRF-TOKEN': self.$store.state.user.token + }, + body: httpFormData + }) + .then(response => response.json()) + .then(function (json) { + if (json.alerts && json.alerts.length > 0) { + if (json.status === 401) { + json.alerts.push({ type: 'danger', msg: 'Please log in' }) + } + self.$store.commit('setAlerts', json.alerts) + } + self.loading = false + if (json.status === 200){ + self.$emit('order-saved', self.pid) + } + self.$vuetify.goTo(0) + }) + .catch(function (error) { + self.$store.commit('setAlerts', [{ type: 'danger', msg: error }]) + self.loading = false + self.$vuetify.goTo(0) + }) + } + } +} +</script> diff --git a/src/components/search/Autocomplete.vue b/src/components/search/Autocomplete.vue new file mode 100644 index 0000000000000000000000000000000000000000..571c9874e3ca3984a052486360232d212a8d5220 --- /dev/null +++ b/src/components/search/Autocomplete.vue @@ -0,0 +1,269 @@ +<template> + <div :class="`${getClassName('wrapper')} autocomplete-wrapper`"> + <input + class="searchbox elevation-1" + ref="input" + type="text" + :id="id" + :class="`${getClassName('input')} autocomplete-input`" + :placeholder="placeholder" + :name="name" + v-model="type" + @input="handleInput" + @blur="handleBlur" + @keydown="handleKeyDown" + @focus="handleFocus" + autocomplete="off" + /> + <div :class="`${getClassName('list')} autocomplete autocomplete-list elevation-2`" v-show="showList && suggestions && suggestions.length"> + <v-list> + <v-list-tile v-for="(data, i) in suggestions" :class="activeClass(i)" :key="i" @click.prevent="selectList(data)"> + <v-list-tile-content> + <v-list-tile-title v-html="data.term"></v-list-tile-title> + </v-list-tile-content> + </v-list-tile> + </v-list> + </div> + </div> +</template> + +<script> +import qs from 'qs' + +export default { + props: { + id: String, + name: String, + className: String, + classes: { + type: Object, + default: () => ({ + wrapper: false, + input: false, + list: false, + item: false + }) + }, + placeholder: String, + required: Boolean, + + // Intial Value + initValue: { + type: String, + default: '' + }, + + // Debounce time + debounce: Number, + + suggester: { + type: String, + required: true + }, + + // minimum length + min: { + type: Number, + default: 0 + }, + + onSelect: Function + }, + + data () { + return { + showList: false, + type: '', + focusList: '', + debounceTask: undefined, + suggestions: [] + } + }, + + computed: { + solr: function () { // TODO: pass in app settings + return this.$root.$store.state.settings.instance.solr + }, + }, + methods: { + getClassName (part) { + const { classes, className } = this + if (classes[part]) return `${classes[part]}` + return className ? `${className}-${part}` : '' + }, + + // Netralize Autocomplete (XXX not used anywhere) + // clearInput () { + // debugger + // this.showList = false + // this.type = '' + // this.suggestions = [] + // this.focusList = '' + // }, + + // Get the original data (TODO move to single used place) + cleanUp (data) { + return JSON.parse(JSON.stringify(data)) + }, + + handleInput (e) { + const { value } = e.target + this.showList = true + + // If Debounce + if (this.debounce) { + if (this.debounceTask !== undefined) clearTimeout(this.debounceTask) + this.debounceTask = setTimeout(() => { + return this.getData(value) + }, this.debounce) + } else { + return this.getData(value) + } + }, + + handleKeyDown (e) { + let key = e.keyCode + + // Disable when list isn't showing up + if (!this.showList) return + + // Key List + const DOWN = 40 + const UP = 38 + const ENTER = 13 + const ESC = 27 + + // Prevent Default for Prevent Cursor Move & Form Submit + switch (key) { + case DOWN: + e.preventDefault() + this.focusList++ + break + case UP: + e.preventDefault() + this.focusList-- + break + case ENTER: + e.preventDefault() + if (this.focusList === 0) { + this.onSelect ? this.onSelect({ term: this.type }) : null + } else { + this.selectList(this.suggestions[this.focusList]) + } + this.showList = false + break + case ESC: + this.showList = false + break + } + + const listLength = this.suggestions.length - 1 + const outOfRangeBottom = this.focusList > listLength + const outOfRangeTop = this.focusList < 0 + const topItemIndex = 0 + const bottomItemIndex = listLength + + let nextFocusList = this.focusList + if (outOfRangeBottom) nextFocusList = topItemIndex + if (outOfRangeTop) nextFocusList = bottomItemIndex + this.focusList = nextFocusList + }, + + // unused? + // setValue (val) { // TODO used anywhere? + // debugger + // this.type = val + // }, + + handleBlur () { + setTimeout(() => { + this.showList = false + }, 250) + }, + + handleFocus () { + this.focusList = 0 + }, + + // unused? + // mousemove (i) { + // debugger + // this.focusList = i + // }, + + activeClass (i) { + const focusClass = i === this.focusList ? 'grey lighten-4' : '' + return `${focusClass}` + }, + + selectList (data) { + // Deep clone of the original object + const clean = this.cleanUp(data) + // Put the selected data to type (model) + this.type = clean['payload'] + // Hide List + this.showList = false + + this.onSelect ? this.onSelect(clean) : null + }, + + getData (value) { + if (value.length < this.min || !this.suggester) return + this.suggest(value) + }, + + async suggest (value) { + let params = { + suggest: true, + 'suggest.dictionary': this.suggester, + wt: 'json', + 'suggest.q': value + } + let query = qs.stringify(params) + + let response = await fetch(this.solr + '/suggest', { + method: 'POST', + mode: 'cors', + headers: { + 'Content-Type': 'application/x-www-form-urlencoded' + }, + body: query + }) + let json = await response.json() + this.suggestions = json.suggest[this.suggester][value].suggestions + } + }, + + created () { + // Sync parent model with initValue Props + this.type = this.initValue ? this.initValue : null + }, + + mounted () { + if (this.required) + this.$refs.input.setAttribute('required', this.required) + } +} +</script> + +<style scoped> +.searchbox{ + font-size: 14px; + box-sizing: border-box; + border: none; + box-shadow: none; + outline: 0; + background: 0 0; + width: 100%; + padding: 0 15px; + line-height: 40px; + height: 40px; +} + +.autocomplete { + position: absolute; + z-index: 999; + margin-top: 2px; +} + +</style> diff --git a/src/components/search/PSearch.vue b/src/components/search/PSearch.vue new file mode 100644 index 0000000000000000000000000000000000000000..d02bf5865d208f05410c7b830dd028d102db9b63 --- /dev/null +++ b/src/components/search/PSearch.vue @@ -0,0 +1,224 @@ +<template> + <v-layout row > + <v-flex xs9 class="border-right" pr-2> + <v-layout column> + <v-flex> + <autocomplete + placeholder="Search..." + name="autocomplete" + :initValue="q" + :suggester="'titlesuggester'" + :customParams="{ token: 'dev' }" + :classes="{ input: 'form-control', wrapper: 'input-wrapper'}" + :onSelect="handleSelect" + ></autocomplete> + </v-flex> + <v-flex xs12> + <v-layout row class="pt-3 pb-2"> + <v-flex xs2><span>{{ total }} {{ $t('objects') }}</span></v-flex> + <v-spacer /> + <v-flex xs4> + <search-toolbar + :setSort="setSort" + :sortIsActive="sortIsActive" + :link="link" /> + </v-flex> + </v-layout> + <v-flex v-if="inCollection" class="display-2 primary--text">{{ $t('Members of') }} {{ inCollection }} <icon name="material-navigation-close" class="primary--text" height="100%" @click.native="removeCollectionFilter()"></icon></v-flex> + <search-results :docs="docs"></search-results> + <v-flex class="text-xs-center"> + <v-pagination + v-if="total>pagesize" + v-bind:length="totalPages" + total-visible="10" + v-model="page" + class="mb-3" /> + </v-flex> + </v-flex> + </v-layout> + </v-flex> + <v-flex xs3 class="pa-2"> + <h3 class="border-bottom display-2 pa-2 primary--text">Filters</h3> + <search-filters + :search="search" + :facetQueries="facetQueries" + :pers_authors="pers_authors" + :corp_authors="corp_authors" + :rolesProp="roles" + :ownerProp="owner" + ></search-filters> + </v-flex> + </v-layout> +</template> + +<script> +import qs from 'qs' +import Autocomplete from './Autocomplete' +import SearchResults from './SearchResults' +import SearchFilters from './SearchFilters' +import SearchToolbar from './SearchToolbar' +import '@/compiled-icons/fontello-sort-name-up' +import '@/compiled-icons/fontello-sort-name-down' +import '@/compiled-icons/fontello-sort-number-up' +import '@/compiled-icons/fontello-sort-number-down' +import '@/compiled-icons/material-content-link' +import '@/compiled-icons/material-action-bookmark' +import { facetQueries, updateFacetQueries, pers_authors, corp_authors } from './facets' +import { buildParams, buildSearchDef, sortdef } from './utils' +import { setSearchParams } from './location' + +export default { + name: 'p-search', + components: { + Autocomplete, + SearchResults, + SearchFilters, + SearchToolbar + }, + computed: { + page: { + get () { + return this.currentPage + }, + set (value) { + this.currentPage = value + this.search() + } + }, + totalPages: function () { + return Math.ceil(this.total / this.pagesize) + }, + solr: function () { // TODO: pass in app settings + return this.$root.$store.state.settings.instance.solr + }, + }, + props: { + collection: { + type: String, + default: '' + } + }, + methods: { + search: async function (options) { + // `options` are combined into the PSearch component. The later are sent + // over from child components: e.g. SearchFilters. + // This allows us the buildSearchDef/buildParams functions to pick out + // whatever properties they might need. + + // exclude 'collection' from above manipulation, since it's passed a prop + let { collection } = options || {} + if (collection) { + this.inCollection = collection + delete options.collection + } + + Object.assign(this, options) + + let { searchdefarr, ands } = buildSearchDef(this) + let params = buildParams(this, ands) + + this.link = location.protocol + '//' + location.host + '/#/search?' + searchdefarr.join('&') + window.history.replaceState(null, this.$t('Search results'), this.link) + + let query = qs.stringify(params, { encodeValuesOnly: true, indices: false }) + let url = this.solr + '/select' + let response = await fetch(url, { + method: 'POST', + mode: 'cors', + headers: { + 'Content-Type': 'application/x-www-form-urlencoded' + }, + body: query + }) + let json = await response.json() + this.docs = json.response.docs + this.total = json.response.numFound + this.facet_counts = json.facet_counts + window.scrollTo({ + top: 0, + left: 0, + behavior: 'smooth' + }); + updateFacetQueries(json.facet_counts.facet_queries, facetQueries) + }, + handleSelect: function ({ term, payload }) { + // called from Autocomplete + // When an item has been clicked on explicitly - issue a quoted search on it's title, + // otherwise too many unrealted results are returned + this.q = payload ? `"${payload}"` : term + this.search() + }, + setSort: function (sort) { + for (let i = 0; i < this.sortdef.length; i++) { + if (this.sortdef[i].id === sort) { + this.sortdef[i].active = !this.sortdef[i].active + } else { + this.sortdef[i].active = false + } + } + this.search() + }, + sortIsActive: function (sort) { + for (let i = 0; i < this.sortdef.length; i++) { + if (this.sortdef[i].id === sort) { + return this.sortdef[i].active + } + } + }, + removeCollectionFilter: function () { + this.inCollection = '' + this.search() + } + }, + mounted: function () { + setSearchParams(this, this.$route.query) + + // This call is delayed because at this point + // `setInstanceSolr` has not yet been executed and + // the solr url is missing. + setTimeout(() => { this.search()} , 100) + }, + watch: { + collection: function (col) { // used by demo app + this.inCollection = col + this.search() + } + }, + data () { + return { + link: '', + linkdialog: false, + q: '', + inCollection: this.collection, + currentPage: 1, + pagesize: 10, + sortdef, + lang: 'en', + facetQueries, + + corp_authors, + pers_authors, + roles: [], + owner: '', + + docs: [], + total: 0, + facet_counts: null, + } + } +} +</script> + +<style scoped> +.border-right { + border-right: 1px solid #bdbdbd; +} + +.border-bottom { + border-bottom: 1px solid #bdbdbd; +} + +svg { + cursor: pointer +} +</style> diff --git a/src/components/search/SearchFilters.vue b/src/components/search/SearchFilters.vue new file mode 100644 index 0000000000000000000000000000000000000000..ecc9b62416e257e5a555089a3049ed988590fecd --- /dev/null +++ b/src/components/search/SearchFilters.vue @@ -0,0 +1,357 @@ +<template> + <v-container fluid grid-list-md> + <ul class="main-ul"> + <li v-for="(f, i) in facetQueries" :key="i"> + <icon @click.native="showFacet(f)" v-if="f.show" name="univie-stop2" class="primary--text"></icon> + <icon @click.native="showFacet(f)" v-if="!f.show" name="univie-checkbox-unchecked" class="primary--text"></icon> + <span @click="showFacet(f)" class="facet-label primary--text" :class="{ active: f.show }">{{ $t(f.label) }}</span> + <ul v-if="f.show"> + <li v-for="(q, j) in f.queries" :key="j"> + <span @click="toggleFacet(q,f)"> + <icon v-if="q.active" name="univie-stop2" class="primary--text"></icon> + <icon v-if="!q.active" name="univie-checkbox-unchecked" class="primary--text"></icon> + <span :class="{ active: q.active }" class="facet-label primary--text">{{ $t(q.label) }}</span> + <span class="facet-count grey--text" v-if="q.count > 0">({{q.count}})</span> + </span> + <ul v-if="q.active && q.childFacet" > + <li v-for="(q1, k) in q.childFacet.queries" :key="k"> + <span @click="toggleFacet(q1,q.childFacet)"> + <icon v-if="q1.active" name="univie-stop2" class="primary--text"></icon> + <icon v-if="!q1.active" name="univie-checkbox-unchecked" class="primary--text"></icon> + <span :class="{ active: q1.active }" class="facet-label primary--text">{{ $t(q1.label) }}</span> + <span class="facet-count grey--text" v-if="q1.count > 0">({{q1.count}})</span> + </span> + <ul v-if="q1.active && q1.childFacet" > + <li v-for="(q2, l) in q1.childFacet.queries" :key="l"> + <span @click="toggleFacet(q2,q1.childFacet)"> + <icon v-if="q2.active" name="univie-stop2" class="primary--text"></icon> + <icon v-if="!q2.active" name="univie-checkbox-unchecked" class="primary--text"></icon> + <span :class="{ active: q2.active }" class="facet-label primary--text">{{ $t(q2.label) }}</span> + <span class="facet-count grey--text" v-if="q2.count>0">({{q2.count}})</span> + </span> + </li> + </ul> + </li> + </ul> + </li> + </ul> + </li> + <li> + <v-layout column> + <v-flex> + <v-layout row> + <v-flex> + <icon @click.native="toggleOwnerFilter()" v-if="showOwnerFilter" name="univie-stop2" class="primary--text"></icon> + <icon @click.native="toggleOwnerFilter()" v-if="!showOwnerFilter" name="univie-checkbox-unchecked" class="primary--text"></icon> + <span @click="toggleOwnerFilter()" class="facet-label primary--text" :class="{ active: showOwnerFilter }">{{ $t('Owner') }}</span> + </v-flex> + </v-layout> + <autocomplete + v-if="showOwnerFilter" + searchaction="search" + placeholder="Search..." + name="autocomplete" + :initValue="owner" + :suggester="'ownersuggester'" + :customParams="{ token: 'dev' }" + :classes="{ input: 'form-control', wrapper: 'input-wrapper'}" + :onSelect="handleOwnerSelect" + ></autocomplete> + </v-flex> + </v-layout> + </li> + <li> + <v-layout column> + <v-flex> + <v-layout row> + <v-flex> + <icon @click.native="toggleAuthorFilter()" v-if="showAuthorFilter" name="univie-stop2" class="primary--text"></icon> + <icon @click.native="toggleAuthorFilter()" v-if="!showAuthorFilter" name="univie-checkbox-unchecked" class="primary--text"></icon> + <span @click="toggleAuthorFilter()" class="facet-label primary--text" :class="{ active: showAuthorFilter }">{{ $t('Authors') }}</span> + </v-flex> + </v-layout> + <v-layout row v-if="showAuthorFilter"> + <v-flex xs2> + <icon name="material-social-person" class="primary--text" height="100%"></icon> + </v-flex> + <v-flex xs10> + <v-combobox + :placeholder="selectPlaceholder('pers_authors')" + chips + clearable + multiple + v-model="persAuthorsValues" /> + </v-flex> + </v-layout> + <v-layout row v-if="showAuthorFilter"> + <v-flex xs2> + <icon name="material-action-account-balance" class="primary--text" height="100%"></icon> + </v-flex> + <v-flex xs10> + <v-combobox + :placeholder="selectPlaceholder('corp_authors')" + chips + clearable + multiple + v-model="corpAuthorsValues" /> + </v-flex> + </v-layout> + </v-flex> + </v-layout> + </li> + <li> + <v-layout column> + <v-flex> + <v-layout row> + <v-flex> + <icon @click.native="toggleRoleFilter()" v-if="showRoleFilter" name="univie-stop2" class="primary--text"></icon> + <icon @click.native="toggleRoleFilter()" v-if="!showRoleFilter" name="univie-checkbox-unchecked" class="primary--text"></icon> + <span @click="toggleRoleFilter()" class="facet-label primary--text" :class="{ active: showRoleFilter }">{{ $t('Roles') }}</span> + </v-flex> + </v-layout> + <v-layout column v-if="showRoleFilter"> + <v-select + :placeholder="$t('Add role') + '...'" + :hint="$t('Personal')" + :items="marcRolesArray" + v-model="selectedRole.pers" + @input="addRoleFilter('pers')" + :menu-props="{maxHeight:'400'}" + persistent-hint + ></v-select> + <v-select + :placeholder="$t('Add role') + '...'" + :hint="$t('Corporate')" + :items="marcRolesArray" + v-model="selectedRole.corp" + @input="addRoleFilter('corp')" + :menu-props="{maxHeight:'400'}" + persistent-hint + ></v-select> + <div v-for="(role, i) in roles" :key="i" v-if="roles.length > 0" > + <v-layout row> + <v-flex xs2> + <icon v-if="role.type==='pers'" name="material-social-person" class="primary--text" height="100%"></icon> + <icon v-if="role.type==='corp'" name="material-action-account-balance" class="primary--text" height="100%"></icon> + </v-flex> + <v-flex xs8> + <v-combobox + :placeholder="$t('ADD_PREFIX') + ' ' + $t(role.label) + ' ' + $t('ADD_SUFFIX') + '...'" + chips + clearable + multiple + :items="role.values" + v-model="role.values" + @input="setRoleFilterValues(role)" /> + </v-flex> + <v-flex xs2> + <icon name="material-navigation-close" class="primary--text" height="100%" @click.native="removeRoleFilter(role)"></icon> + </v-flex> + </v-layout> + </div> + </v-layout> + + </v-flex> + </v-layout> + </li> + </ul> + </v-container> +</template> + +<script> +import Autocomplete from './Autocomplete' +import '@/compiled-icons/univie-stop2' +import '@/compiled-icons/univie-checkbox-unchecked' +import '@/compiled-icons/material-action-account-balance' +import '@/compiled-icons/material-social-person' +import '@/compiled-icons/material-navigation-close' +import { marcRoles } from './filters' +import { toggleFacet, showFacet } from './facets' + +export default { + name: 'search-filters', + components: { + Autocomplete + }, + computed: { + persAuthorsValues: { + get () { + return this.pers_authors.values() + }, + set (values) { + this.pers_authors[0].values = values + } + }, + corpAuthorsValues: { + get () { + return this.corp_authors.values() + }, + set (values) { + // it seems chips are manipulating the array directly anyways + // maybe should provide own filtering function + this.corp_authors[0].values = values + } + } + }, + props: { + search: { + type: Function, + required: true + }, + facetQueries: { + type: Array, + required: true + }, + pers_authors: { + type: Array, + required: true + }, + corp_authors: { + type: Array, + required: true + }, + rolesProp: { + type: Array, + required: true + }, + ownerProp: { + type: String, + required: true + } + }, + data () { + return { + showOwnerFilter: false, + showAuthorFilter: false, + showRoleFilter: false, + selectedRole: { pers: '', corp: '' }, + marcRoles, + marcRolesArray: [], + roles: [], + owner: '', + } + }, + methods: { + showFacet: function (f) { + showFacet(f) + this.search({ facetQueries: this.facetQueries }) + }, + toggleFacet: function (q, f) { + toggleFacet(q, f) + this.search({ page: 1, facetQueries: this.facetQueries }) + }, + handleOwnerSelect: function (query) { + this.owner = query.payload + this.search({ owner: this.owner }) + }, + toggleOwnerFilter: function () { + this.showOwnerFilter = !this.showOwnerFilter + if (!this.showOwnerFilter) { + this.owner = '' // TODO change '' to null whereever it's used + this.search({ owner: this.owner }) // TODO: should this be in if clause? + } + }, + toggleAuthorFilter: function () { + this.showAuthorFilter = !this.showAuthorFilter + if (!this.showAuthorFilter) { + this.pers_authors[0].values = [] + this.corp_authors[0].values = [] + this.search({ pers_authors: this.pers_authors, corp_authors: this.corp_authors }) // TODO: should this be in if clause? + } + }, + toggleRoleFilter: function () { + this.showRoleFilter = !this.showRoleFilter + if (!this.showRoleFilter) { + this.roles = [] + } + this.search({ roles: this.roles }) + }, + addRoleFilter: function (type) { + if (this.selectedRole[type]) { + this.roles.push({ + field: 'bib_roles_' + type + '_' + this.selectedRole[type], + label: this.$t(this.marcRoles[this.selectedRole[type]]), + values: [], + type: type + }) + } + }, + removeRoleFilter: function (role) { + this.roles.splice(this.roles.indexOf(role), 1) + this.search({ roles: this.roles }) + }, + setRoleFilterValues: function (role) { + this.roles[this.roles.indexOf(role)].values = role.values + this.search({ roles: this.roles }) + }, + removeRoleFilterValue: function (role, value) { + this.roles[this.roles.indexOf(role)].values.splice(this.roles[this.roles.indexOf(role)].values.indexOf(value), 1) + this.search({ roles: this.roles }) + }, + selectPlaceholder: function (source) { + let label = '' + if (this[source].length) { + label = this[source][0].label + } else { + console.warn(`Label for ${source} not found`, this[source]) // eslint-disable-line no-console + } + return this.$t('ADD_PREFIX') + ' ' + + this.$t(label) + ' ' + + this.$t('ADD_SUFFIX') + '...' + } + }, + mounted () { + for (let role in this.marcRoles) { + this.marcRolesArray.push({ value: role, text: this.$t(this.marcRoles[role]) }) + } + }, + watch: { + rolesProp: function (v) { + this.roles = v + if (v.length) { + this.showRoleFilter = true + } + }, + ownerProp: function (v) { + this.owner = v + if (v.length) { + this.showOwnerFilter = true + } + }, + pers_authors: function (v) { + if (v[0].values.length) { + this.showAuthorFilter = true + // this.selectedRole.pers // TODO might need something like this + } + }, + corp_authors: function (v) { + if (v[0].values.length) { + this.showAuthorFilter = true + } + }, + } +} +</script> + +<style lang="stylus" scoped> +.container + padding-top: 1em + padding-left: 0 + +ul + list-style: none + padding-left: 1em + +.facet-label + cursor: pointer + +.facet-count + margin-left: 5px + +svg + margin-bottom: 3px + cursor: pointer + +svg.primary--text + margin-right: 4px +</style> diff --git a/src/components/search/SearchResults.vue b/src/components/search/SearchResults.vue new file mode 100644 index 0000000000000000000000000000000000000000..193e903aee834506309b8e2e7284f8d0917c6de3 --- /dev/null +++ b/src/components/search/SearchResults.vue @@ -0,0 +1,149 @@ +<template> + <v-container fluid grid-list-lg> + <v-layout column> + <v-flex xs12> + <v-expansion-panel popout expand> + <v-expansion-panel-content + v-for="(doc) in this.docs" + :key="doc.pid" + v-model="doc.showMore" + > + <div slot="header"> + <v-card flat> + <v-container fluid grid-list-lg pa-3> + <v-layout column> + <v-layout row> + <v-flex xs2> + <p-img + :src="'https://' + instance.baseurl + '/preview/' + doc.pid + '///120'" + class="elevation-1" + > + <v-layout slot="placeholder" fill-height align-center justify-center ma-0> + <v-progress-circular indeterminate color="grey lighten-5"></v-progress-circular> + </v-layout> + </p-img> + </v-flex> + <v-flex xs9> + <v-layout column> + <v-card-title primary-title> + <v-container fluid> + <v-layout row> + <v-flex xs10> + <h3 class="display-2" @click.stop v-if="doc.dc_title"> + <router-link + :to="{ name: 'detail', params: { pid: doc.pid } }" + >{{ doc.dc_title[0] }}</router-link> + </h3> + <v-spacer></v-spacer> + </v-flex> + <v-flex xs2 class="text-xs-right"> + <span + v-if="doc.created" + class="grey--text" + >{{ doc.created | date }}</span> + </v-flex> + </v-layout> + </v-container> + </v-card-title> + <v-card-text> + <v-layout column> + <v-flex> + <span> + <span v-for="(aut,i) in doc.bib_roles_pers_aut" :key="'pers'+i"> + {{aut}} + <span v-if="(i+1) < doc.bib_roles_pers_aut.length">;</span> + </span> + <span v-for="(aut,i) in doc.bib_roles_corp_aut" :key="'corp'+i"> + {{aut}} + <span v-if="(i+1) < doc.bib_roles_corp_aut.length">;</span> + </span> + </span> + </v-flex> + </v-layout> + </v-card-text> + <v-spacer></v-spacer> + </v-layout> + </v-flex> + </v-layout> + </v-layout> + </v-container> + </v-card> + </div> + <!--header --> + <v-container fluid grid-list-lg pa-3> + <v-layout column> + <v-flex + v-if="doc.dc_description" + class="search-description pb-3" + >{{ doc.dc_description[0] }}</v-flex> + <v-flex class="text-xs-right"> + <!--<v-btn :to="{ name: 'detail', params: { pid: doc.pid } }" raised>{{ $t('Details') }}</v-btn>--> + <v-btn + :href="instance.api + '/object/' + doc.pid + '/diss/Content/get'" + primary + >{{ $t('View') }}</v-btn> + <v-btn + :href="instance.api + '/object/' + doc.pid + '/diss/Content/download'" + primary + >{{ $t('Download') }}</v-btn> + </v-flex> + <v-flex class="pt-3"> + <v-layout row> + <v-flex> + <p-d-license class="pa-0" v-if="doc.dc_license" :o="doc.dc_license[0]"></p-d-license> + </v-flex> + <v-spacer></v-spacer> + <v-flex class="text-xs-right"> + <span class="grey--text">https://{{ instance.baseurl }}/{{ doc.pid }}</span> + </v-flex> + </v-layout> + </v-flex> + </v-layout> + </v-container> + </v-expansion-panel-content> + </v-expansion-panel> + </v-flex> + </v-layout> + </v-container> +</template> + +<script> +import PDLicense from "../display/phaidra_display/PDLicense"; +import PImg from "../utils/PImg"; + +export default { + name: "search-results", + components: { + PDLicense, + PImg + }, + props: { + docs: { + type: Array + } + }, + computed: { + instance() { + return this.$store.state.settings.instance; + } + } +}; +</script> + +<style scoped> +.card__title--primary { + padding-top: 10px; +} + +.search-description { + white-space: pre-wrap; +} + +.card__text { + padding-top: 0px; +} + +.container { + padding: 0; +} +</style> diff --git a/src/components/search/SearchToolbar.vue b/src/components/search/SearchToolbar.vue new file mode 100644 index 0000000000000000000000000000000000000000..4b3da7d6ee05c5baeff54112db544b55a5c13a07 --- /dev/null +++ b/src/components/search/SearchToolbar.vue @@ -0,0 +1,78 @@ +<template lang="html"> + <v-container class="toolbar" grid-list-md> + <v-layout row wrap> + <v-flex> + <v-tooltip bottom> + <icon @click.native="setSort('title asc')" name="fontello-sort-name-up" :color="sortIsActive('title asc') ? '#1A74B0' : '#777777'" slot="activator"></icon> + <span>{{ $t('Title ascending')}}</span> + </v-tooltip> + </v-flex> + <v-flex> + <v-tooltip bottom> + <icon @click.native="setSort('title desc')" name="fontello-sort-name-down" :color="sortIsActive('title desc') ? '#1A74B0' : '#777777'" slot="activator"></icon> + <span>{{ $t('Title descending')}}</span> + </v-tooltip> + </v-flex> + <v-flex> + <v-tooltip bottom> + <icon @click.native="setSort('created asc')" name="fontello-sort-number-up" :color="sortIsActive('created asc') ? '#1A74B0' : '#777777'" slot="activator"></icon> + <span>{{ $t('Upload date ascending')}}</span> + </v-tooltip> + </v-flex> + <v-flex> + <v-tooltip bottom> + <icon @click.native="setSort('created desc')" name="fontello-sort-number-down" :color="sortIsActive('created desc') ? '#1A74B0' : '#777777'" slot="activator"></icon> + <span>{{ $t('Upload date descending')}}</span> + </v-tooltip> + </v-flex> + <v-flex> + <v-dialog v-model="linkdialog" max-width="800px"> + <v-card> + <v-card-title> + <h3 class="display-2">{{ $t('Link to search results') }}</h3> + </v-card-title> + <v-card-text>{{ link }}</v-card-text> + <v-card-actions> + <v-spacer></v-spacer> + <v-btn color="primary" flat @click.stop="linkdialog=false">Close</v-btn> + </v-card-actions> + </v-card> + </v-dialog> + <v-tooltip bottom> + <icon @click.native="linkdialog=true" name="material-content-link" slot="activator"></icon> + <span>{{ $t('Link to search results')}}</span> + </v-tooltip> + </v-flex> + </v-layout> + </v-container> +</template> + +<script> +export default { + name: 'search-toolbar', + props: { + setSort: { + type: Function, + required: true + }, + sortIsActive: { + type: Function, + required: true + }, + link: { + type: String + } + }, + data() { + return { + linkdialog: false + } + } +} +</script> + +<style scoped> +.container .toolbar { + padding: 0px; +} +</style> diff --git a/src/components/search/facets.js b/src/components/search/facets.js new file mode 100644 index 0000000000000000000000000000000000000000..4df7f0def4e9c0e2a442bfd2782a6ab0a809dd49 --- /dev/null +++ b/src/components/search/facets.js @@ -0,0 +1,340 @@ +import Vue from 'vue' + +export const facetQueries = [ + { + label: 'Access', + field: 'datastreams', + id: 'datastreams', + exclusive: 1, + show: 0, + queries: [ + { + id: 'restricted', + query: 'datastreams:POLICY', + label: 'Restricted' + }, + { + id: 'unrestricted', + query: '-datastreams:POLICY', + label: 'Unrestricted' + } + ] + }, + { + label: 'Type', + field: 'resourcetype', + id: 'resourcetype', + show: 1, + queries: [ + { + id: 'image', + query: 'resourcetype:image', + label: 'Image' + }, + { + id: 'book', + query: 'resourcetype:book', + label: 'Book' + }, + { + id: 'article', + query: 'resourcetype:journalarticle', + label: 'Article' + }, + { + id: 'text', + query: 'resourcetype:text', + label: 'Text' + }, + { + id: 'collection', + query: 'resourcetype:collection', + label: 'Collection' + }, + { + id: 'video', + query: 'resourcetype:video', + label: 'Video' + }, + { + id: 'other', + query: 'resourcetype:other', + label: 'Data' + }, + { + id: 'dataset', + query: 'resourcetype:dataset', + label: 'Container' + }, + { + id: 'map', + query: 'resourcetype:map', + label: 'Map' + }, + { + id: 'resource', + query: 'resourcetype:interactiveresource', + label: 'Resource' + }, + { + id: 'sound', + query: 'resourcetype:sound', + label: 'Sound' + } + ] + }, + { + label: 'Size', + field: 'tsize', + id: 'size', + show: 0, + queries: [ + { + id: 'less10', + query: 'tsize:[0 TO 10485760]', + label: 'less 10MB' + }, + { + id: '10to50', + query: 'tsize:[10485760 TO 52428800]', + label: '10MB - 50MB' + }, + { + id: '50to100', + query: 'tsize:[52428800 TO 104857600]', + label: '50MB - 100MB' + }, + { + id: '100to200', + query: 'tsize:[104857600 TO 209715200]', + label: '100MB - 200MB' + }, + { + id: '200to500', + query: 'tsize:[209715200 TO 524288000]', + label: '200MB - 500MB' + }, + { + id: '500to1000', + query: 'tsize:[524288000 TO 1073741824]', + label: '500MB - 1GB' + }, + { + id: 'more1000', + query: 'tsize:[1073741824 TO *]', + label: 'more 1GB' + } + ] + }, + { + label: 'License', + field: 'dc_license', + id: 'license', + show: 0, + queries: [ + { + id: 'all-rights-reserved', + query: 'dc_license:\'All rights reserved\'', + label: 'All rights reserved' + }, + { + id: 'gplv3', + query: 'dc_license:\'GPLv3\'', + label: 'GPLv3' + }, + { + id: 'pdm', + query: 'dc_license:\'Public Domain Mark\'', + label: 'Public Domain Mark' + }, + { + id: 'cc-by', + query: '(dc_license:\'CC BY 2.0 AT\' OR dc_license:\'CC BY 2.0 Generic\' OR dc_license:\'CC BY 3.0 AT\' OR dc_license:\'CC BY 3.0 Unported\' OR dc_license:\'CC BY 4.0 International\')', + label: 'CC BY' + }, + { + id: 'cc-by-sa', + query: '(dc_license:\'CC BY-SA 2.0 AT\' OR dc_license:\'CC BY-SA 2.0 Generic\' OR dc_license:\'CC BY-SA 3.0 AT\' OR dc_license:\'CC BY-SA 3.0 Unported\' OR dc_license:\'CC BY-SA 4.0 International\')', + label: 'CC BY-SA' + }, + { + id: 'cc-by-nc', + query: '(dc_license:\'CC BY-NC 2.0 AT\' OR dc_license:\'CC BY-NC 2.0 Generic\' OR dc_license:\'CC BY-NC 3.0 AT\' OR dc_license:\'CC BY-NC 3.0 Unported\' OR dc_license:\'CC BY-NC 4.0 International\')', + label: 'CC BY-NC' + }, + { + id: 'cc-by-nd', + query: '(dc_license:\'CC BY-ND 2.0 AT\' OR dc_license:\'CC BY-ND 2.0 Generic\' OR dc_license:\'CC BY-ND 3.0 AT\' OR dc_license:\'CC BY-ND 3.0 Unported\' OR dc_license:\'CC BY-ND 4.0 International\')', + label: 'CC BY-ND' + }, + { + id: 'cc-by-nc-sa', + query: '(dc_license:\'CC BY-NC-SA 2.0 AT\' OR dc_license:\'CC BY-NC-SA 2.0 Generic\' OR dc_license:\'CC BY-NC-SA 3.0 AT\' OR dc_license:\'CC BY-NC-SA 3.0 Unported\' OR dc_license:\'CC BY-NC-SA 4.0 International\')', + label: 'CC BY-NC-SA' + }, + { + id: 'cc-by-nc-nd', + query: '(dc_license:\'CC BY-NC-ND 2.0 AT\' OR dc_license:\'CC BY-NC-ND 2.0 Generic\' OR dc_license:\'CC BY-NC-ND 3.0 AT\' OR dc_license:\'CC BY-NC-ND 3.0 Unported\' OR dc_license:\'CC BY-NC-ND 4.0 International\')', + label: 'CC BY-NC-ND' + } + ] + } +] + +function buildDateFacet() { + let months31 = [1, 3, 5, 7, 8, 10, 12] + let months30 = [4, 6, 9, 11] + let startYear = 2008 + let currYear = new Date().getFullYear() + let yearsFacet = { + label: 'Date', + field: 'tcreated', + id: 'created', + show: 0, + queries: [] + } + + for (let year = startYear; year <= currYear; year++) { + let monthsFacet = { + label: 'Months of ' + year, + field: 'tcreated', + id: 'months-' + year, + queries: [] + } + + for (let month = 1; month <= 12; month++) { + let daysOfMonth + if (months30.indexOf(month) > -1) { + daysOfMonth = 30 + } else { + if (months31.indexOf(month) > -1) { + daysOfMonth = 31 + } else { + let isLeap = ((year % 4 === 0) && (year % 100 !== 0)) || (year % 400 === 0) + if (isLeap) { + daysOfMonth = 29 + } else { + daysOfMonth = 28 + } + } + } + + let daysFacet = { + label: 'Days of ' + month + '.' + year, + field: 'tcreated', + id: 'days-' + year + '-' + month, + queries: [] + } + + for (let day = 1; day <= daysOfMonth; day++) { + if (day < 10) { + day = '0' + day + } + daysFacet.queries.push({ + query: 'tcreated:[' + year + '-' + month + '-' + day + 'T00:00:00Z TO ' + year + '-' + month + '-' + day + 'T23:59:59Z]', + id: year + '-' + month + '-' + day, + label: day + '.' + month + '.' + year + }) + } + + monthsFacet.queries.push({ + query: 'tcreated:[' + year + '-' + month + '-01T00:00:00Z TO ' + year + '-' + month + '-' + daysOfMonth + 'T00:00:00Z]', + id: year + '-' + month, + label: month + '.' + year, + childFacet: daysFacet + }) + } + + yearsFacet.queries.push({ + query: 'tcreated:[' + year + '-01-01T00:00:00Z TO ' + year + '-12-31T00:00:00Z]', + id: year, + label: year, + childFacet: monthsFacet + }) + } + + return yearsFacet +} + +// TODO: FIXME +export function updateFacetQueries (facet_queries, facetQueries) { + // called by the `search` function + Object.keys(facet_queries).forEach(function (key) { + for (let i = 0; i < facetQueries.length; i++) { + for (let j = 0; j < facetQueries[i].queries.length; j++) { + if (facetQueries[i].queries[j].query === key) { + Vue.set(facetQueries[i].queries[j], 'count', facet_queries[key]) + } + if (facetQueries[i].queries[j].childFacet) { + let lvl1 = facetQueries[i].queries[j].childFacet + for (let k = 0; k < lvl1.queries.length; k++) { + if (lvl1.queries[k].query === key) { + Vue.set(lvl1.queries[k], 'count', facet_queries[key]) + } + if (lvl1.queries[k].childFacet) { + let lvl2 = lvl1.queries[k].childFacet + for (let l = 0; l < lvl2.queries.length; l++) { + if (lvl2.queries[l].query === key) { + Vue.set(lvl2.queries[l], 'count', facet_queries[key]) + } + } + } + } + } + } + } + }) +} + +export function toggleFacet (q, f) { + q.active = !q.active + + if (f.exclusive) { + for (let i = 0; i < f.queries.length; i++) { + if (f.queries[i] !== q) { + f.queries[i].active = 0 + } + } + } +} + +export function showFacet (f) { + f.show = !f.show + + if (!f.show) { + // when hiding facet, remove it's filters + for (var i = 0; i < f.queries.length; i++) { + f.queries[i].active = false; + if(f.queries[i].childFacet){ + var lvl1 = f.queries[i].childFacet; + for (var j = 0; j < lvl1.queries.length; j++) { + lvl1.queries[j].active = false; + if(lvl1.queries[j].childFacet){ + var lvl2 = lvl1.queries[j].childFacet; + for (var k = 0; k < lvl2.queries.length; k++) { + lvl2.queries[k].active = false; + } + } + } + } + } + } +} + +export const pers_authors = [ + { + field: 'bib_roles_pers_aut', + label: 'Author', + values: [] + } +] + +export const corp_authors = [ + { + field: 'bib_roles_corp_aut', + label: 'Author', + values: [] + } +] + +facetQueries.push(buildDateFacet()) diff --git a/src/components/search/filters.js b/src/components/search/filters.js new file mode 100644 index 0000000000000000000000000000000000000000..97ae693410b8d085e81dbf969c963bba93867178 --- /dev/null +++ b/src/components/search/filters.js @@ -0,0 +1,106 @@ +// facetLabels: { +// datastreams: 'Access', +// resourcetype: 'Resource type', +// dc_license: 'License', +// tcreated: 'Created', +// tsize: 'Size' +// }, +// resourcetypeLabels: { +// image: 'Image', +// book: 'Book', +// journalarticle: 'Journal article', +// text: 'Text', +// collection: 'Collection', +// video: 'Video', +// other: 'Other', +// dataset: 'Dataset', +// map: 'Map', +// interactiveresource: 'Resource', +// sound: 'Sound' +// }, +export const marcRoles = { + 'initiator': 'Initiator', + 'evaluator': 'Evaluator', + 'technicalinspector': 'Technical inspector', + 'textprocessor': 'Textprocessor', + 'pedagogicexpert': 'Pedagogic expert', + 'interpreter': 'Interpreter', + 'digitiser': 'Digitiser', + 'keeperoftheoriginal': 'Keeper of the original', + 'adviser': 'Adviser', + 'degreegrantor': 'Degree grantor', + 'uploader': 'Uploader', + 'dtc': 'Data contributor', + // 'aut': 'Author', has a separate input box + 'pbl': 'Publisher', + 'edt': 'Editor', + 'dsr': 'Designer', + 'trl': 'Translator', + 'exp': 'Expert', + 'oth': 'Other', + 'art': 'Artist', + 'dnr': 'Donor', + 'pht': 'Photographer', + 'jud': 'Judge', + 'prf': 'Performer', + 'wde': 'Wood engraver', + 'rce': 'Recording engineer', + 'sce': 'Scenarist', + 'ths': 'Thesis advisor', + 'sds': 'Sound designer', + 'lyr': 'Lyricist', + 'ilu': 'Illuminator', + 'eng': 'Engineer', + 'cnd': 'Conductor', + 'dto': 'Dedicator', + 'opn': 'Opponent', + 'cmp': 'Composer', + 'ctg': 'Cartographer', + 'dub': 'Dubious author', + 'wam': 'Writer of accompanying material', + 'arc': 'Architect', + 'vdg': 'Videographer', + 'scl': 'Sculptor', + 'aus': 'Screenwriter', + 'own': 'Owner', + 'fmo': 'Former owner', + 'mus': 'Musician', + 'ive': 'Interviewee', + 'ill': 'Illustrator', + 'cng': 'Cinematographer', + 'dte': 'Dedicatee', + 'sad': 'Scientific advisor', + 'mte': 'Metal-engraver', + 'arr': 'Arranger', + 'etr': 'Etcher', + 'dis': 'Dissertant', + 'prt': 'Printer', + 'flm': 'Film editor', + 'rev': 'Reviewer', + 'pro': 'Producer', + 'att': 'Attributed name', + 'lbt': 'Librettist', + 'ivr': 'Interviewer', + 'egr': 'Engraver', + 'msd': 'Musical director', + 'ard': 'Artistic director', + 'chr': 'Choreographer', + 'com': 'Compiler', + 'sng': 'Singer', + 'act': 'Actor', + 'adp': 'Adapter' +} + +export function getMarcRoleLabel (r) { + // if possible, return label directly + if (marcRoles[r]) return marcRoles[r] + + // try to use the last element for lookup, e.g. from 'bib_roles_pers_uploader' + // extracts 'Uploader' + let splitted = r.split('_') + if (splitted.length) { + let last = splitted[splitted.length - 1] + return marcRoles[last] || r + } +} + diff --git a/src/components/search/location.js b/src/components/search/location.js new file mode 100644 index 0000000000000000000000000000000000000000..44ea1f27fb77cad1c87921d032fbbdf02638dbd6 --- /dev/null +++ b/src/components/search/location.js @@ -0,0 +1,110 @@ +import { getMarcRoleLabel } from './filters' + +export function setSearchParams (self, { q, page, pagesize, sortdef, owner, collection, fq, fr }) { + if (q) { + self.q = q + } + + if (page) { + self.currentPage = parseInt(page) + } + + if (pagesize) { + self.pagesize = parseInt(pagesize) + } + + if (sortdef) { + for (let i = 0; i < self.sortdef.length; i++) { + if (self.sortdef[i].id === sortdef) { + self.sortdef[i].active = true + } + } + } + + if (owner) { + self.owner = owner + // SearchFilters.watch will set "showOwnerFilter = true" + } + + if (collection) { + self.inCollection = collection + } + + if (fq) { + if (typeof fq === 'string') { + fq = [fq] + } + for (let n = 0; n < fq.length; n++) { + let fqa = fq[n].split('_') + let facetId = fqa[0] + let queryId = fqa[1] + for (let j = 0; j < self.facetQueries.length; j++) { + if (self.facetQueries[j].id === facetId) { + self.facetQueries[j].show = 1 + for (let k = 0; k < self.facetQueries[j].queries.length; k++) { + if (self.facetQueries[j].queries[k].id === queryId) { + self.facetQueries[j].queries[k].active = 1 + } + if (self.facetQueries[j].queries[k].childFacet) { + let lvl1 = self.facetQueries[j].queries[k].childFacet + for (let l = 0; l < lvl1.queries.length; l++) { + if (lvl1.queries[l].id === queryId) { + lvl1.queries[l].active = 1 + self.facetQueries[j].queries[k].active = 1 + } + if (lvl1.queries[l].childFacet) { + let lvl2 = lvl1.queries[l].childFacet + for (let m = 0; m < lvl2.queries.length; m++) { + if (lvl2.queries[m].id === queryId) { + lvl2.queries[m].active = 1 + lvl1.queries[l].active = 1 + self.facetQueries[j].queries[k].active = 1 + } + } + } + } + } + } + } + } + } + } + + if (fr) { + if (typeof fr === 'string') { + fr = [fr] + } + let roles = {} + for (let o = 0; o < fr.length; o++) { + let idx = fr[o].lastIndexOf('_') + let role = fr[o].substring(0, idx) + let value = fr[o].substring(idx + 1) + + if (roles[role]) { + roles[role].values.push(value) + } else { + roles[role] = { values: [value] } + } + } + + Object.keys(roles).forEach(function (role) { + if (role === 'bib_roles_pers_aut') { + self.pers_authors[0].values = roles[role].values + // SearchFilters.watch will set "showAuthorFilter = true" + } else { + if (role === 'bib_roles_corp_aut') { + self.corp_authors[0].values = roles[role].values + // SearchFilters.watch will set "showAuthorFilter = true" + } else { + self.roles.push({ + field: role, + label: getMarcRoleLabel(role), + values: roles[role].values, + type: role.includes('_pers_') ? 'pers' : 'corp' + }) + // SearchFilters.watch will set "showRoleFilter = true" + } + } + }) + } +} diff --git a/src/components/search/utils.js b/src/components/search/utils.js new file mode 100644 index 0000000000000000000000000000000000000000..03c2720e18d7474c44fad9da1b770145ec4a9229 --- /dev/null +++ b/src/components/search/utils.js @@ -0,0 +1,205 @@ +export function buildSearchDef({ sortdef, q, page, pagesize, facetQueries, corp_authors, pers_authors, roles, owner, inCollection: collection }) { + let searchdefarr = [] + + for (let i = 0; i < sortdef.length; i++) { + if (sortdef[i].active) { + searchdefarr.push('sortdef=' + window.encodeURIComponent(sortdef[i].id)) + } + } + + if (q) { + searchdefarr.push('q=' + window.encodeURIComponent(q)) + } + searchdefarr.push('page=' + page) + if (pagesize) { + searchdefarr.push('pagesize=' + pagesize) + } + + let ands = [] + for (let i = 0; i < facetQueries.length; i++) { + let ors = [] + for (let j = 0; j < facetQueries[i].queries.length; j++) { + if (facetQueries[i].queries[j].active) { + // tag '{!tag=' + state.facetQueries[i].id + '}' + + if (facetQueries[i].queries[j].childFacet) { + // there are two levels, only take the lowest active levels + let lvl1 = facetQueries[i].queries[j].childFacet + let foundActiveLvl1Query = false + for (let k = 0; k < lvl1.queries.length; k++) { + if (lvl1.queries[k].active) { + foundActiveLvl1Query = true + + let lvl2 = lvl1.queries[k].childFacet + let foundActiveLvl2Query = false + for (let l = 0; l < lvl2.queries.length; l++) { + if (lvl2.queries[l].active) { + foundActiveLvl2Query = true + ors.push(lvl2.queries[l].query) + searchdefarr.push('fq=' + facetQueries[i].id + '_' + lvl2.queries[l].id) + } + } + + if (!foundActiveLvl2Query) { + ors.push(lvl1.queries[k].query) + searchdefarr.push('fq=' + facetQueries[i].id + '_' + lvl1.queries[k].id) + } + } + } + + if (!foundActiveLvl1Query) { + ors.push(facetQueries[i].queries[j].query) + searchdefarr.push('fq=' + facetQueries[i].id + '_' + facetQueries[i].queries[j].id) + } + } else { + ors.push(facetQueries[i].queries[j].query) + searchdefarr.push('fq=' + facetQueries[i].id + '_' + facetQueries[i].queries[j].id) + } + } + } + if (ors.length > 0) { + if (ors.length > 1) { + ands.push('(' + ors.join(' OR ') + ')') + } else { + ands.push(ors[0]) + } + } + } + + for (let i = 0; i < corp_authors.length; i++) { + let field = corp_authors[i] + for (let j = 0; j < field.values.length; j++) { + let v = field.values[j] + if (v !== '') { + ands.push('(' + field.field + ':"' + v + '")') + searchdefarr.push('fr=' + field.field + '_' + window.encodeURIComponent(v)) + } + } + } + + for (let i = 0; i < pers_authors.length; i++) { + let field = pers_authors[i] + for (let j = 0; j < field.values.length; j++) { + let v = field.values[j] + if (v !== '') { + ands.push('(' + field.field + ':"' + v + '")') + searchdefarr.push('fr=' + field.field + '_' + window.encodeURIComponent(v)) + } + } + } + + for (let i = 0; i < roles.length; i++) { + let field = roles[i] + for (let j = 0; j < field.values.length; j++) { + let v = field.values[j] + if (v !== '') { + ands.push('(' + field.field + ':"' + v + '")') + searchdefarr.push('fr=' + field.field + '_' + window.encodeURIComponent(v)) + } + } + } + + if (owner) { + ands.push('owner:"' + owner + '"') + searchdefarr.push('owner=' + owner) + } else { + // an object should have at least an owner, else it's garbage + ands.push('owner:*') + } + + if (collection) { + ands.push('ispartof:"' + collection + '"') + searchdefarr.push('collection=' + collection) + } + + return { searchdefarr, ands } +} + +export function buildParams({ q, page, pagesize, sortdef, lang, facetQueries }, ands) { + let params = { + q, + defType: 'edismax', + wt: 'json', + qf: 'pid^5 dc_title^4 dc_creator^3 dc_subject^2 _text_', + start: (page - 1) * pagesize, + rows: pagesize, + sort: '', + facet: true, + 'facet.query': [] + } + + if (q === '' || q === null) { + params.q = '*:*' + params.sort = 'created desc' + } + + for (let i = 0; i < sortdef.length; i++) { + if (sortdef[i].active) { + if ((sortdef[i].id === 'title asc') || (sortdef[i].id === 'title desc')) { + params.sort = sortdef[i].def[lang] + } else { + params.sort = sortdef[i].def + } + } + } + + // TODO: new fn: serializefacetQueries (careful, current implementation might be using mutation) + for (let i = 0; i < facetQueries.length; i++) { + if (facetQueries[i].show) { + for (let j = 0; j < facetQueries[i].queries.length; j++) { + // exclude '{!ex=' + state.facetQueries[i].id + '}' + + if (facetQueries[i].queries[j].active && facetQueries[i].queries[j].childFacet) { + let childFacetLvl1 = facetQueries[i].queries[j].childFacet + for (let k = 0; k < childFacetLvl1.queries.length; k++) { + if (childFacetLvl1.queries[k].active && childFacetLvl1.queries[k].childFacet) { + let childFacetLvl2 = childFacetLvl1.queries[k].childFacet + for (let l = 0; l < childFacetLvl2.queries.length; l++) { + // days + params['facet.query'].push(childFacetLvl2.queries[l].query) + } + } + // months + params['facet.query'].push(childFacetLvl1.queries[k].query) + } + } + params['facet.query'].push(facetQueries[i].queries[j].query) + } + } + } + + if (ands.length > 0) { + params['fq'] = ands.join(' AND ') + } + + return params +} + +export const sortdef = [ + { + id: 'title asc', + active: false, + def: { + 'en': 'sort_eng_dc_title asc,sort_dc_title asc', + 'de': 'sort_deu_dc_title asc,sort_dc_title asc', + 'it': 'sort_ita_dc_title asc,sort_dc_title asc' + } + }, + { + id: 'title desc', + active: false, + def: { + 'en': 'sort_eng_dc_title desc,sort_dc_title desc', + 'de': 'sort_deu_dc_title desc,sort_dc_title desc', + 'it': 'sort_ita_dc_title desc,sort_dc_title desc' + } + }, + { + id: 'created asc', + active: false, + def: 'created asc' + }, + { + id: 'created desc', + active: false, + def: 'created desc' + } +] diff --git a/src/components/select/CollectionDialog.vue b/src/components/select/CollectionDialog.vue new file mode 100644 index 0000000000000000000000000000000000000000..0697890bfe1f77e85fc929ecfc16cbf2425b9d1b --- /dev/null +++ b/src/components/select/CollectionDialog.vue @@ -0,0 +1,116 @@ +<template> + <v-dialog v-model="dialog" width="700px"> + <v-card> + <v-card-title class="grey white--text">{{ $t('Select a collection') }}</v-card-title> + <v-card-text> + <v-text-field + v-model="collectionsSearch" + append-icon="search" + :label="$t('Search...')" + single-line + hide-details + class="mb-4" + ></v-text-field> + <v-data-table + hide-default-header + :headers="collectionsHeaders" + :items="collections" + :search="collectionsSearch" + :custom-filter="filterTitle" + :loading="loading" + :loading-text="$t('Loading...')" + :items-per-page="5" + > + <template v-slot:item.title="{ item }"> + <span v-if="item.dc_title">{{ item.dc_title[0] | truncate(50) }}</span> + </template> + <template v-slot:item.created="{ item }"> + {{ item.created | date }} + </template> + <template v-slot:item.actions="{ item }"> + <v-btn text color="primary" @click="selectCollection(item)">{{ $t('Select') }}</v-btn> + </template> + </v-data-table> + </v-card-text> + <v-divider></v-divider> + <v-card-actions> + <v-container> + <v-row justify="end" class="px-4"> + <v-btn color="grey" dark @click="dialog = false">{{ $t('Cancel') }}</v-btn> + </v-row> + </v-container> + </v-card-actions> + </v-card> + </v-dialog> +</template> + +<script> +export default { + name: 'collection-dialog', + computed: { + instance: function () { + return this.$store.state.instanceconfig + } + }, + data () { + return { + dialog: false, + loading: false, + collectionsSearch: '', + collectionsHeaders: [ + { text: 'Pid', align: 'left', value: 'pid' }, + { text: 'Title', align: 'left', value: 'title' }, + { text: 'Created', align: 'right', value: 'created' }, + { text: 'Actions', align: 'right', value: 'actions', sortable: false } + ], + collections: [] + } + }, + methods: { + filterTitle (value, search, item) { + if (item.dc_title) { + if (item.dc_title.length > 0) { + return item.dc_title[0].indexOf(search) !== -1 + } else { + return false + } + } else { + return false + } + }, + open: async function () { + this.dialog = true + this.loading = true + let params = { + q: '*:*', + defType: 'edismax', + wt: 'json', + start: 0, + rows: 1000, + sort: 'created desc', + fq: [ 'resourcetype:collection', 'owner:' + this.$store.state.user.username ] + } + try { + let response = await this.$http.request({ + method: 'POST', + url: this.instance.solr + '/select', + headers: { + 'X-XSRF-TOKEN': this.$store.state.user.token + }, + params: params + }) + this.collections = response.data.response.docs + } catch (error) { + console.log(error) + this.$store.commit('setAlerts', [{ type: 'danger', msg: error }]) + } finally { + this.loading = false + } + }, + selectCollection: function (item) { + this.$emit('collection-selected', item) + this.dialog = false + } + } +} +</script> diff --git a/src/components/select/ListDialog.vue b/src/components/select/ListDialog.vue new file mode 100644 index 0000000000000000000000000000000000000000..97ae7ca679e430bb427c862226c67b6abd6e7092 --- /dev/null +++ b/src/components/select/ListDialog.vue @@ -0,0 +1,102 @@ +<template> + <v-dialog v-model="dialog" width="700px"> + <v-card> + <v-card-title class="grey white--text">{{ $t('Select a list') }}</v-card-title> + <v-card-text> + <v-text-field + v-model="listsSearch" + append-icon="search" + :label="$t('Search...')" + single-line + hide-details + class="mb-4" + ></v-text-field> + <v-data-table + hide-default-header + :headers="listsHeaders" + :items="lists" + :search="listsSearch" + :loading="loading" + :loading-text="$t('Loading...')" + :items-per-page="5" + > + <template v-slot:item.name="{ item }"> + <v-tooltip bottom> + <template v-slot:activator="{ on }"> + <span v-on="on">{{ item.name | truncate(50) }}</span> + </template> + <span>{{ item.listid }}</span> + </v-tooltip> + </template> + <template v-slot:item.created="{ item }"> + {{ item.created | unixtime }} + </template> + <template v-slot:item.updated="{ item }"> + {{ item.updated | unixtime }} + </template> + <template v-slot:item.actions="{ item }"> + <v-btn text color="primary" @click="selectList(item)">{{ $t('Select') }}</v-btn> + </template> + </v-data-table> + </v-card-text> + <v-divider></v-divider> + <v-card-actions> + <v-container> + <v-row justify="end" class="px-4"> + <v-btn color="grey" dark @click="dialog = false">{{ $t('Cancel') }}</v-btn> + </v-row> + </v-container> + </v-card-actions> + </v-card> + </v-dialog> +</template> + +<script> +export default { + name: 'list-dialog', + computed: { + instance: function () { + return this.$store.state.instanceconfig + } + }, + data () { + return { + dialog: false, + loading: false, + listsSearch: '', + listsHeaders: [ + { text: 'Name', align: 'left', value: 'name' }, + { text: 'Created', align: 'right', value: 'created' }, + { text: 'Updated', align: 'right', value: 'updated' }, + { text: 'Actions', align: 'right', value: 'actions', sortable: false } + ], + lists: [] + } + }, + methods: { + open: async function () { + this.dialog = true + this.loading = true + try { + let response = await this.$http.request({ + method: 'GET', + url: this.instance.api + '/lists', + headers: { + 'X-XSRF-TOKEN': this.$store.state.user.token + } + }) + this.lists = response.data.lists + } catch (error) { + console.log(error) + this.$store.commit('setAlerts', [{ type: 'danger', msg: error }]) + } finally { + this.loading = false + } + }, + selectList: function (item) { + this.$emit('list-selected', item) + this.dialog = false + } + } +} +</script> diff --git a/src/components/select/OrgUnitsTreeDialog.vue b/src/components/select/OrgUnitsTreeDialog.vue new file mode 100644 index 0000000000000000000000000000000000000000..a3ba47a001591d47ee5863a692c2643998f2f815 --- /dev/null +++ b/src/components/select/OrgUnitsTreeDialog.vue @@ -0,0 +1,77 @@ +<template> + <v-dialog v-model="dialog" width="700px"> + <v-card :loading="loading"> + <v-card-title class="grey white--text">{{ $t('Select an organizational unit') }}</v-card-title> + <v-card-text> + <v-treeview :items="orgunits" item-children="subunits" item-key="@id" hoverable activatable @update:active="selectUnit($event)"></v-treeview> + </v-card-text> + <v-divider></v-divider> + <v-card-actions> + <v-container> + <v-row justify="end" class="px-4"> + <v-btn color="grey" dark @click="dialog = false">{{ $t('Cancel') }}</v-btn> + </v-row> + </v-container> + </v-card-actions> + </v-card> + </v-dialog> +</template> + +<script> +export default { + name: 'org-units-tree-dialog', + computed: { + instance: function () { + return this.$store.state.instanceconfig + } + }, + data () { + return { + dialog: false, + loading: false, + orgunits: [] + } + }, + methods: { + open: async function () { + this.dialog = true + this.loading = true + try { + let response = await this.$http.request({ + method: 'GET', + url: this.$store.state.instanceconfig.api + '/directory/org_get_units', + headers: { + 'X-XSRF-TOKEN': this.$store.state.user.token + } + }) + if (response.data.alerts && response.data.alerts.length > 0) { + this.$store.commit('setAlerts', response.data.alerts) + } + this.orgunits = response.data.units + this.addNames(this.orgunits) + } catch (error) { + console.log(error) + this.$store.commit('setAlerts', [{ type: 'danger', msg: error }]) + } finally { + this.loading = false + } + }, + addNames: function (units) { + for (let u of units) { + if (u['skos:prefLabel']) { + u['name'] = u['skos:prefLabel'][this.$i18n.locale] + } + if (u['subunits']) { + if (u.subunits.length > 0) { + this.addNames(u.subunits) + } + } + } + }, + selectUnit: function (item) { + this.$emit('unit-selected', item[0]) + this.dialog = false + } + } +} +</script> diff --git a/src/components/templates/PTList.vue b/src/components/templates/PTList.vue new file mode 100644 index 0000000000000000000000000000000000000000..7b4f6aba3bf2415ef385904077f0fe63de99bff3 --- /dev/null +++ b/src/components/templates/PTList.vue @@ -0,0 +1,142 @@ +<template> + + <v-data-table + :headers="headers" + :items="templates" + :loading="loading" + class="elevation-1" + > + <template slot="items" slot-scope="props"> + <td> + <v-tooltip bottom> + <template v-slot:activator="{ on }"> + <span v-on="on">{{ props.item.name }}</span> + </template> + <span>{{ props.item.tid }}</span> + </v-tooltip> + </td> + <td class="text-xs-right">{{ props.item.created | unixtime }}</td> + <td class="text-xs-right" > + <v-btn flat color="primary" @click="loadTemplate(props.item.tid)">{{ $t('Load') }}</v-btn> + <v-btn flat color="grey" @click="deleteTemplate(props.item.tid)">{{ $t('Delete') }}</v-btn> + <!-- for some reason opening the dialog from here causes infinite cycle.. looks like vuetify bug.. + <v-dialog v-model="deletetempconfirm" width="300" > + <template v-slot:activator="{ on }"> + <v-btn flat color="grey" v-on="on">{{ $t('Delete') }}</v-btn> + </template> + <v-card> + <v-card-title class="headline grey lighten-2" primary-title >{{ $t('Delete') }}</v-card-title> + <v-card-text>{{ $t('Are you sure you want to delete template') }} "{{props.item.name}}" ?</v-card-text> + <v-divider></v-divider> + <v-card-actions> + <v-spacer></v-spacer> + <v-btn color="red" class="white--text" :loading="loading" :disabled="loading" @click="deleteTemplate(props.item.tid)">{{ $t('Delete') }}</v-btn> + <v-btn :disabled="loading" @click="deletetempconfirm = false">{{ $t('Cancel') }}</v-btn> + </v-card-actions> + </v-card> + </v-dialog> + --> + </td> + </template> + </v-data-table> + +</template> + +<script> + +export default { + name: 'p-t-list', + data () { + return { + headers: [ + { text: 'Name', align: 'left', value: 'name' }, + { text: 'Created', align: 'right', value: 'created' }, + { text: 'Actions', align: 'right', value: 'load', sortable: false } + ], + templates: [], + deletetempconfirm: false, + loading: false + } + }, + methods: { + loadTemplate: function (tid) { + var self = this + this.loading = true + var url = self.$store.state.settings.instance.api + '/jsonld/template/' + tid + var promise = fetch(url, { + method: 'GET', + mode: 'cors', + headers: { + 'X-XSRF-TOKEN': this.$store.state.user.token + } + }) + .then(function (response) { return response.json() }) + .then(function (json) { + if (json.alerts && json.alerts.length > 0) { + self.$store.commit('setAlerts', json.alerts) + } + self.$emit('load-template', json.template.form) + self.loading = false + }) + .catch(function (error) { + //console.log(error) + self.loading = false + }) + return promise + }, + deleteTemplate: function (tid) { + var self = this + this.loading = true + var url = self.$store.state.settings.instance.api + '/jsonld/template/' + tid + '/remove' + var promise = fetch(url, { + method: 'POST', + mode: 'cors', + headers: { + 'X-XSRF-TOKEN': this.$store.state.user.token + } + }) + .then(function (response) { return response.json() }) + .then(function (json) { + if (json.alerts && json.alerts.length > 0) { + self.$store.commit('setAlerts', json.alerts) + } + self.loading = false + self.deletetempconfirm = false + self.loadTemplates() + }) + .catch(function (error) { + //console.log(error) + self.loading = false + }) + return promise + }, + loadTemplates: function (pid) { + var self = this + this.loading = true + var url = self.$store.state.settings.instance.api + '/jsonld/templates' + var promise = fetch(url, { + method: 'GET', + mode: 'cors', + headers: { + 'X-XSRF-TOKEN': this.$store.state.user.token + } + }) + .then(function (response) { return response.json() }) + .then(function (json) { + self.templates = json.templates + self.loading = false + }) + .catch(function (error) { + //console.log(error) + self.loading = false + }) + return promise + } + }, + mounted: function () { + if (this.$store.state.user.token) { + this.loadTemplates() + } + } +} +</script> diff --git a/src/components/templates/PTemplates.vue b/src/components/templates/PTemplates.vue new file mode 100644 index 0000000000000000000000000000000000000000..5f5d7898ae5c1595bd0caf09d8a03ada50ea65d2 --- /dev/null +++ b/src/components/templates/PTemplates.vue @@ -0,0 +1,123 @@ +<template> + + <v-data-table + :headers="headers" + :items="templates" + :loading="loading" + class="elevation-1" + > + <template v-slot:item.name="{ item }"> + <v-tooltip bottom> + <template v-slot:activator="{ on }"> + <span v-on="on">{{ item.name }}</span> + </template> + <span>{{ item.tid }}</span> + </v-tooltip> + </template> + <template v-slot:item.created="{ item }"> + {{ item.created | unixtime }} + </template> + <template v-slot:item.load="{ item }"> + <v-btn text color="primary" @click="loadTemplate(item.tid)">{{ $t('Load') }}</v-btn> + <v-btn text color="grey" @click="deleteTemplate(item.tid)">{{ $t('Delete') }}</v-btn> + </template> + </v-data-table> + +</template> + +<script> +export default { + name: 'p-templates', + props: { + tag: { + type: String + } + }, + data () { + return { + headers: [ + { text: 'Name', align: 'left', value: 'name' }, + { text: 'Created', align: 'right', value: 'created' }, + { text: 'Actions', align: 'right', value: 'load', sortable: false } + ], + templates: [], + deletetempconfirm: false, + loading: false + } + }, + methods: { + loadTemplate: async function (tid) { + this.loading = true + try { + let response = await this.$http.request({ + method: 'GET', + url: this.$store.state.instanceconfig.api + '/jsonld/template/' + tid, + headers: { + 'X-XSRF-TOKEN': this.$store.state.user.token + } + }) + if (response.data.alerts && response.data.alerts.length > 0) { + this.$store.commit('setAlerts', response.data.alerts) + } + this.$emit('load-template', response.data.template.form) + } catch (error) { + console.log(error) + this.$store.commit('setAlerts', [{ type: 'danger', msg: error }]) + } finally { + this.loading = false + } + }, + deleteTemplate: async function (tid) { + if (confirm(this.$t('Are you sure you want to delete this template?'))) { + this.loading = true + try { + let response = await this.$http.request({ + method: 'GET', + url: this.$store.state.instanceconfig.api + '/jsonld/template/' + tid + '/remove', + headers: { + 'X-XSRF-TOKEN': this.$store.state.user.token + } + }) + if (response.data.alerts && response.data.alerts.length > 0) { + this.$store.commit('setAlerts', response.data.alerts) + } + this.deletetempconfirm = false + this.loadTemplates() + } catch (error) { + console.log(error) + this.$store.commit('setAlerts', [{ type: 'danger', msg: error }]) + } finally { + this.loading = false + } + } + }, + loadTemplates: async function () { + this.loading = true + try { + let response = await this.$http.request({ + method: 'GET', + url: this.$store.state.instanceconfig.api + '/jsonld/templates' + ((this.tag && this.tag.length > 1) ? '?tag=' + this.tag : ''), + headers: { + 'X-XSRF-TOKEN': this.$store.state.user.token + } + }) + if (response.data.alerts && response.data.alerts.length > 0) { + this.$store.commit('setAlerts', response.data.alerts) + } + this.templates = response.data.templates + this.loading = false + } catch (error) { + console.log(error) + this.$store.commit('setAlerts', [{ type: 'danger', msg: error }]) + } finally { + this.loading = false + } + } + }, + mounted: function () { + if (this.$store.state.user.token) { + this.loadTemplates() + } + } +} +</script> \ No newline at end of file