Rename category files to taxonomy

This commit is contained in:
weryques 2018-06-13 09:30:55 -03:00
parent 430b57c135
commit 77f253358d
22 changed files with 2601 additions and 0 deletions

View File

@ -0,0 +1,370 @@
<template>
<div>
<div class="page-container primary-page">
<tainacan-title />
<b-tabs v-model="activeTab">
<b-tab-item :label="$i18n.get('taxonomy')">
<form
v-if="taxonomy != null && taxonomy != undefined"
class="tainacan-form"
label-width="120px">
<!-- Name -------------------------------- -->
<b-field
:addons="false"
:label="$i18n.get('label_name')"
:type="editFormErrors['name'] != undefined ? 'is-danger' : ''"
:message="editFormErrors['name'] != undefined ? editFormErrors['name'] : ''">
<help-button
:title="$i18n.getHelperTitle('taxonomies', 'name')"
:message="$i18n.getHelperMessage('taxonomies', 'name')"/>
<b-input
id="tainacan-text-name"
v-model="form.name"
@focus="clearErrors('name')"
@blur="updateSlug()"/>
</b-field>
<!-- Description -------------------------------- -->
<b-field
:addons="false"
:label="$i18n.get('label_description')"
:type="editFormErrors['description'] != undefined ? 'is-danger' : ''"
:message="editFormErrors['description'] != undefined ? editFormErrors['description'] : ''">
<help-button
:title="$i18n.getHelperTitle('taxonomies', 'description')"
:message="$i18n.getHelperMessage('taxonomies', 'description')"/>
<b-input
id="tainacan-text-description"
type="textarea"
v-model="form.description"
@focus="clearErrors('description')"/>
</b-field>
<!-- Status -------------------------------- -->
<b-field
:addons="false"
:label="$i18n.get('label_status')"
:type="editFormErrors['status'] != undefined ? 'is-danger' : ''"
:message="editFormErrors['status'] != undefined ? editFormErrors['status'] : ''">
<help-button
:title="$i18n.getHelperTitle('taxonomies', 'status')"
:message="$i18n.getHelperMessage('taxonomies', 'status')"/>
<b-select
id="tainacan-select-status"
v-model="form.status"
@focus="clearErrors('status')"
:placeholder="$i18n.get('instruction_select_a_status')">
<option
v-for="statusOption in statusOptions"
:key="statusOption.value"
:value="statusOption.value"
:disabled="statusOption.disabled">{{ statusOption.label }}
</option>
</b-select>
</b-field>
<!-- Slug -------------------------------- -->
<b-field
:addons="false"
:label="$i18n.get('label_slug')"
:type="editFormErrors['slug'] != undefined ? 'is-danger' : ''"
:message="editFormErrors['slug'] != undefined ? editFormErrors['slug'] : ''">
<help-button
:title="$i18n.getHelperTitle('taxonomies', 'slug')"
:message="$i18n.getHelperMessage('taxonomies', 'slug')"/>
<b-icon :class="{'is-loading': isUpdatingSlug}"/>
<b-input
@input="updateSlug()"
id="tainacan-text-slug"
v-model="form.slug"
@focus="clearErrors('slug')"
:disabled="isUpdatingSlug"/>
</b-field>
<!-- Allow Insert -->
<b-field
:addons="false"
:label="$i18n.get('label_taxonomy_allow_new_terms')">
<help-button
:title="$i18n.getHelperTitle('taxonomies', 'allow_insert')"
:message="$i18n.getHelperMessage('taxonomies', 'allow_insert')"/>
<div class="block" >
<b-checkbox
v-model="form.allowInsert"
true-value="yes"
false-value="no">
{{ labelNewTerms() }}
</b-checkbox>
</div>
</b-field>
<!-- Submit -->
<div class="field is-grouped form-submit">
<div class="control">
<button
id="button-cancel-taxonomy-creation"
class="button is-outlined"
type="button"
@click="cancelBack">{{ $i18n.get('cancel') }}</button>
</div>
<div class="control">
<button
id="button-submit-taxonomy-creation"
@click.prevent="onSubmit"
class="button is-success">{{ $i18n.get('save') }}</button>
</div>
</div>
<p class="help is-danger">{{ formErrorMessage }}</p>
</form>
</b-tab-item>
<b-tab-item :label="$i18n.get('terms')">
<!-- Terms List -->
<terms-list :taxonomy-id="taxonomyId"/>
</b-tab-item>
<b-loading
:active.sync="isLoadingTaxonomy"
:can-cancel="false"/>
</b-tabs>
</div>
</div>
</template>
<script>
import { wpAjax } from "../../js/mixins";
import { mapActions, mapGetters } from 'vuex';
import TermsList from '../lists/terms-list.vue'
import htmlToJSON from 'html-to-json';
import CustomDialog from '../other/custom-dialog.vue';
export default {
name: 'TaxonomyEditionForm',
mixins: [ wpAjax ],
data(){
return {
taxonomyId: String,
activeTab: 0,
taxonomy: null,
isLoadingTaxonomy: false,
isUpdatingSlug: false,
form: {
name: String,
status: String,
description: String,
slug: String,
allowInsert: String
},
statusOptions: [{
value: 'publish',
label: this.$i18n.get('publish')
}, {
value: 'draft',
label: this.$i18n.get('draft')
}, {
value: 'private',
label: this.$i18n.get('private')
}, {
value: 'trash',
label: this.$i18n.get('trash')
}],
editFormErrors: {},
formErrorMessage: '',
// baseUrl: tainacan_plugin.base_url,
}
},
components: {
TermsList
},
beforeRouteLeave( to, from, next ) {
let formNotSaved = false;
if (this.taxonomy.name != this.form.name)
formNotSaved = true;
if (this.taxonomy.description != this.form.description)
formNotSaved = true;
if (this.taxonomy.slug != this.form.slug)
formNotSaved = true;
if (this.taxonomy.allow_insert != this.form.allowInsert)
formNotSaved = true;
if (this.taxonomy.status != this.form.status)
formNotSaved = true;
if (formNotSaved) {
this.$modal.open({
parent: this,
component: CustomDialog,
props: {
icon: 'alert',
title: this.$i18n.get('label_warning'),
message: this.$i18n.get('info_warning_taxonomy_not_saved'),
onConfirm: () => {
next();
}
}
});
} else {
next();
}
},
methods: {
...mapActions('taxonomy', [
'createTaxonomy',
'updateTaxonomy',
'fetchTaxonomy',
'fetchOnlySlug'
]),
...mapGetters('taxonomy',[
'getTaxonomy',
]),
onSubmit() {
this.isLoadingTaxonomy = true;
let data = {
taxonomyId: this.taxonomyId,
name: this.form.name,
description: this.form.description,
slug: this.form.slug,
status: this.form.status,
allowInsert: this.form.allowInsert
};
this.updateTaxonomy(data)
.then(updatedTaxonomy => {
this.taxonomy = updatedTaxonomy;
// Fill this.form data with current data.
this.form.name = this.taxonomy.name;
this.form.slug = this.taxonomy.slug;
this.form.description = this.taxonomy.description;
this.form.status = this.taxonomy.status;
this.form.allowInsert = this.taxonomy.allow_insert;
this.isLoadingTaxonomy = false;
this.formErrorMessage = '';
this.editFormErrors = {};
this.$router.push(this.$routerHelper.getCategoriesPath());
})
.catch((errors) => {
for (let error of errors.errors) {
for (let attribute of Object.keys(error)) {
this.editFormErrors[attribute] = error[attribute];
}
}
this.formErrorMessage = errors.error_message;
this.isLoadingTaxonomy = false;
});
},
updateSlug(){
if(!this.form.name || this.form.name.length <= 0){
return;
}
this.isUpdatingSlug = true;
this.getSamplePermalink(this.taxonomyId, this.form.name, this.form.slug)
.then(samplePermalink => {
let promise = htmlToJSON.parse(samplePermalink, {
permalink($doc) {
return $doc.find('#editable-post-name-full').text();
}
});
promise.done((result) => {
this.form.slug = result.permalink;
//this.$console.info(this.form.slug);
});
this.isUpdatingSlug = false;
this.formErrorMessage = '';
this.editFormErrors = {};
})
.catch(errors => {
this.$console.error(errors);
this.isUpdatingSlug = false;
});
},
createNewTaxonomy() {
// Puts loading on Draft Taxonomy creation
this.isLoadingTaxonomy = true;
// Creates draft Taxonomy
let data = {
name: '',
description: '',
status: 'auto-draft',
slug: '',
allowInsert: '',
};
this.createTaxonomy(data)
.then(res => {
this.taxonomyId = res.id;
this.taxonomy = res;
// Fill this.form data with current data.
this.form.name = this.taxonomy.name;
this.form.description = this.taxonomy.description;
this.form.slug = this.taxonomy.slug;
this.form.allowInsert = this.taxonomy.allow_insert;
// Pre-fill status with publish to incentivate it
this.form.status = 'publish';
this.isLoadingTaxonomy = false;
})
.catch(error => this.$console.error(error));
},
clearErrors(attribute) {
this.editFormErrors[attribute] = undefined;
},
cancelBack(){
this.$router.push(this.$routerHelper.getCategoriesPath());
},
labelNewTerms(){
return ( this.form.allowInsert === 'yes' ) ? this.$i18n.get('label_yes') : this.$i18n.get('label_no');
}
},
created(){
if (this.$route.fullPath.split("/").pop() === "new") {
this.createNewTaxonomy();
} else if (this.$route.fullPath.split("/").pop() === "edit" || this.$route.fullPath.split("/").pop() === "terms") {
this.isLoadingTaxonomy = true;
// Obtains current taxonomy ID from URL
this.pathArray = this.$route.fullPath.split("/").reverse();
this.taxonomyId = this.pathArray[1];
this.fetchTaxonomy(this.taxonomyId).then(res => {
this.taxonomy = res.taxonomy;
// Fill this.form data with current data.
this.form.name = this.taxonomy.name;
this.form.description = this.taxonomy.description;
this.form.slug = this.taxonomy.slug;
this.form.status = this.taxonomy.status;
this.form.allowInsert = this.taxonomy.allow_insert;
this.isLoadingTaxonomy = false;
});
if (this.$route.fullPath.split("/").pop() === "terms")
this.activeTab = 1;
}
}
}
</script>

View File

@ -0,0 +1,287 @@
<template>
<div
v-if="totalCategories > 0 && !isLoading"
class="table-container">
<div class="selection-control">
<div class="field select-all is-pulled-left">
<span>
<b-checkbox
@click.native="selectAllCategoriesOnPage()"
:value="allCategoriesOnPageSelected">{{ $i18n.get('label_select_all_taxonomies_page') }}</b-checkbox>
</span>
</div>
<div class="field is-pulled-right">
<b-dropdown
position="is-bottom-left"
v-if="taxonomies[0].current_user_can_edit"
:disabled="!isSelectingCategories"
id="bulk-actions-dropdown">
<button
class="button is-white"
slot="trigger">
<span>{{ $i18n.get('label_bulk_actions') }}</span>
<b-icon icon="menu-down"/>
</button>
<b-dropdown-item
id="item-delete-selected-items"
@click="deleteSelectedCategories()">
{{ $i18n.get('label_delete_selected_taxonomies') }}
</b-dropdown-item>
<b-dropdown-item disabled>{{ $i18n.get('label_edit_selected_taxonomies') + ' (Not ready)' }}
</b-dropdown-item>
</b-dropdown>
</div>
</div>
<div class="table-wrapper">
<table class="tainacan-table">
<thead>
<tr>
<!-- Checking list -->
<th>
&nbsp;
<!-- nothing to show on header -->
</th>
<!-- Name -->
<th>
<div class="th-wrap">{{ $i18n.get('label_name') }}</div>
</th>
<!-- Description -->
<th>
<div class="th-wrap">{{ $i18n.get('label_description') }}</div>
</th>
<!-- Actions -->
<th class="actions-header">
&nbsp;
<!-- nothing to show on header for actions cell-->
</th>
</tr>
</thead>
<tbody>
<tr
:class="{ 'selected-row': selectedCategories[index] }"
:key="index"
v-for="(taxonomy, index) of taxonomies">
<!-- Checking list -->
<td
:class="{ 'is-selecting': isSelectingCategories }"
class="checkbox-cell">
<b-checkbox
size="is-small"
v-model="selectedCategories[index]"/>
</td>
<!-- Name -->
<td
class="column-default-width column-main-content"
@click="goToTaxonomyEditPage(taxonomy.id)"
:label="$i18n.get('label_name')"
:aria-label="$i18n.get('label_name') + ': ' + taxonomy.name">
<p
v-tooltip="{
content: taxonomy.name,
autoHide: false,
placement: 'auto-start'
}">
{{ taxonomy.name }}</p>
</td>
<!-- Description -->
<td
class="column-large-width"
@click="goToTaxonomyEditPage(taxonomy.id)"
:label="$i18n.get('label_description')"
:aria-label="$i18n.get('label_description') + ': ' + taxonomy.description">
<p
v-tooltip="{
content: taxonomy.description,
autoHide: false,
placement: 'auto-start'
}">
{{ taxonomy.description }}</p>
</td>
<!-- Actions -->
<td
@click="goToTaxonomyEditPage(taxonomy.id)"
class="actions-cell column-default-width"
:label="$i18n.get('label_actions')">
<div class="actions-container">
<a
id="button-edit"
:aria-label="$i18n.getFrom('taxonomies','edit_item')"
@click="goToTaxonomyEditPage(taxonomy.id)">
<b-icon
type="is-secondary"
icon="pencil"/>
</a>
<a
id="button-delete"
:aria-label="$i18n.get('label_button_delete')"
@click.prevent.stop="deleteOneTaxonomy(taxonomy.id)">
<b-icon
type="is-secondary"
icon="delete"/>
</a>
</div>
</td>
</tr>
</tbody>
</table>
</div>
</div>
</template>
<script>
import { mapActions } from 'vuex';
import CustomDialog from '../other/custom-dialog.vue';
export default {
name: 'CategoriesList',
data() {
return {
selectedCategories: [],
allCategoriesOnPageSelected: false,
isSelectingCategories: false
}
},
props: {
isLoading: false,
totalCategories: 0,
page: 1,
taxonomiesPerPage: 12,
taxonomies: Array
},
watch: {
taxonomies() {
this.selectedCategories = [];
for (let i = 0; i < this.taxonomies.length; i++)
this.selectedCategories.push(false);
},
selectedCategories() {
let allSelected = true;
let isSelecting = false;
for (let i = 0; i < this.selectedCategories.length; i++) {
if (this.selectedCategories[i] == false) {
allSelected = false;
} else {
isSelecting = true;
}
}
this.allCategoriesOnPageSelected = allSelected;
this.isSelectingCategories = isSelecting;
}
},
methods: {
...mapActions('taxonomy', [
'deleteTaxonomy'
]),
selectAllCategoriesOnPage() {
for (let i = 0; i < this.selectedCategories.length; i++)
this.selectedCategories.splice(i, 1, !this.allCategoriesOnPageSelected);
},
deleteOneTaxonomy(taxonomyId) {
this.$modal.open({
parent: this,
component: CustomDialog,
props: {
icon: 'alert',
title: this.$i18n.get('label_warning'),
message: this.$i18n.get('info_warning_taxonomy_delete'),
onConfirm: () => {
this.deleteTaxonomy(taxonomyId)
.then(() => {
// this.$toast.open({
// duration: 3000,
// message: this.$i18n.get('info_taxonomy_deleted'),
// position: 'is-bottom',
// type: 'is-secondary',
// queue: true
// });
for (let i = 0; i < this.selectedCategories.length; i++) {
if (this.selectedCategories[i].id === this.taxonomyId)
this.selectedCategories.splice(i, 1);
}
})
.catch(() => {
// this.$toast.open({
// duration: 3000,
// message: this.$i18n.get('info_error_deleting_taxonomy'),
// position: 'is-bottom',
// type: 'is-danger',
// queue: true
// });
});
}
}
});
},
deleteSelectedCategories() {
this.$modal.open({
parent: this,
component: CustomDialog,
props: {
icon: 'alert',
title: this.$i18n.get('label_warning'),
message: this.$i18n.get('info_warning_selected_taxonomies_delete'),
onConfirm: () => {
for (let i = 0; i < this.taxonomies.length; i++) {
if (this.selectedCategories[i]) {
this.deleteTaxonomy(this.taxonomies[i].id)
.then(() => {
// this.loadCategories();
// this.$toast.open({
// duration: 3000,
// message: this.$i18n.get('info_taxonomy_deleted'),
// position: 'is-bottom',
// type: 'is-secondary',
// queue: false
// })
}).catch(() => {
// this.$toast.open({
// duration: 3000,
// message: this.$i18n.get('info_error_deleting_taxonomy'),
// position: 'is-bottom',
// type: 'is-danger',
// queue: false
// });
});
}
}
this.allCategoriesOnPageSelected = false;
}
}
});
},
goToTaxonomyPage(taxonomyId) {
this.$router.push(this.$routerHelper.getTaxonomyPath(taxonomyId));
},
goToTaxonomyEditPage(taxonomyId) {
this.$router.push(this.$routerHelper.getTaxonomyEditPath(taxonomyId));
}
}
}
</script>
<style lang="scss" scoped>
@import "../../scss/_variables.scss";
.selection-control {
padding: 6px 0px 0px 13px;
background: white;
height: 40px;
.select-all {
color: $gray-light;
font-size: 14px;
&:hover {
color: $gray-light;
}
}
}
</style>

View File

@ -0,0 +1,236 @@
<template>
<div>
<div class="primary-page page-container">
<tainacan-title />
<div
class="sub-header"
v-if="$userCaps.hasCapability('edit_tainacan-taxonomies')">
<div class="header-item">
<router-link
id="button-create-taxonomy"
tag="button"
class="button is-secondary"
:to="{ path: $routerHelper.getNewTaxonomyPath() }">
{{ $i18n.getFrom('taxonomies', 'new_item') }}
</router-link>
</div>
</div>
<div class="above-subheader">
<div class="tabs">
<ul>
<li
@click="onChangeTab('')"
:class="{ 'is-active': status == undefined || status == ''}"><a>{{ $i18n.get('label_all_items') }}</a></li>
<li
@click="onChangeTab('draft')"
:class="{ 'is-active': status == 'draft'}"><a>{{ $i18n.get('label_draft_items') }}</a></li>
<li
@click="onChangeTab('trash')"
:class="{ 'is-active': status == 'trash'}"><a>{{ $i18n.get('label_trash_items') }}</a></li>
</ul>
</div>
<div>
<taxonomies-list
:is-loading="isLoading"
:total-taxonomies="totalCategories"
:page="page"
:taxonomies-per-page="taxonomiesPerPage"
:taxonomies="taxonomies"/>
<!-- Empty state image -->
<div v-if="totalCategories <= 0 && !isLoading">
<section class="section">
<div class="content has-text-grey has-text-centered">
<p>
<b-icon
icon="inbox"
size="is-large"/>
</p>
<p v-if="status == undefined || status == ''">{{ $i18n.get('info_no_taxonomy_created') }}</p>
<p v-if="status == 'draft'">{{ $i18n.get('info_no_taxonomy_draft') }}</p>
<p v-if="status == 'trash'">{{ $i18n.get('info_no_taxonomy_trash') }}</p>
<router-link
v-if="status == undefined || status == ''"
id="button-create-taxonomy"
tag="button"
class="button is-primary"
:to="{ path: $routerHelper.getNewTaxonomyPath() }">
{{ $i18n.getFrom('taxonomies', 'new_item') }}
</router-link>
</div>
</section>
</div>
<!-- Footer -->
<div
class="pagination-area"
v-if="totalCategories > 0">
<div class="shown-items">
{{
$i18n.get('info_showing_taxonomies') +
(taxonomiesPerPage * (page - 1) + 1) +
$i18n.get('info_to') +
getLastTaxonomyNumber() +
$i18n.get('info_of') + totalCategories + '.'
}}
</div>
<div class="items-per-page">
<b-field
horizontal
:label="$i18n.get('label_taxonomies_per_page')">
<b-select
:value="taxonomiesPerPage"
@input="onChangeCategoriesPerPage"
:disabled="taxonomies.length <= 0">
<option value="12">12</option>
<option value="24">24</option>
<option value="48">48</option>
<option value="96">96</option>
</b-select>
</b-field>
</div>
<div class="pagination">
<b-pagination
@change="onPageChange"
:total="totalCategories"
:current.sync="page"
order="is-centered"
size="is-small"
:per-page="taxonomiesPerPage"/>
</div>
</div>
</div>
</div>
</div>
</div>
</template>
<script>
import CategoriesList from "../../components/lists/taxonomies-list.vue";
import { mapActions, mapGetters } from 'vuex';
//import moment from 'moment'
export default {
name: 'CategoriesPage',
data(){
return {
isLoading: false,
totalCategories: 0,
page: 1,
taxonomiesPerPage: 12,
status: ''
}
},
components: {
CategoriesList
},
methods: {
...mapActions('taxonomy', [
'fetchCategories',
]),
...mapGetters('taxonomy', [
'getCategories'
]),
onChangeTab(status) {
this.status = status;
this.loadCategories();
},
onChangeCategoriesPerPage(value) {
this.taxonomiesPerPage = value;
this.$userPrefs.set('taxonomies_per_page', value)
.then((newValue) => {
this.taxonomiesPerPage = newValue;
})
.catch(() => {
this.$console.log("Error settings user prefs for taxonomies per page")
});
this.loadCategories();
},
onPageChange(page) {
this.page = page;
this.loadCategories();
},
loadCategories() {
this.isLoading = true;
this.fetchCategories({ 'page': this.page, 'taxonomiesPerPage': this.taxonomiesPerPage, 'status': this.status })
.then((res) => {
this.isLoading = false;
this.totalCategories = res.total;
})
.catch(() => {
this.isLoading = false;
});
},
getLastTaxonomyNumber() {
let last = (Number(this.taxonomiesPerPage * (this.page - 1)) + Number(this.taxonomiesPerPage));
return last > this.totalCategories ? this.totalCategories : last;
}
},
computed: {
taxonomies(){
return this.getCategories();
}
},
created() {
this.taxonomiesPerPage = this.$userPrefs.get('taxonomies_per_page');
},
mounted(){
this.$userPrefs.fetch('taxonomies_per_page')
.then((value) => {
if (this.taxonomiesPerPage != value) {
this.taxonomiesPerPage = value;
this.loadCategories;
}
})
.catch(() => {
this.$userPrefs.set('taxonomies_per_page', 12);
});
this.loadCategories();
}
}
</script>
<style lang="scss" scoped>
@import '../../scss/_variables.scss';
.sub-header {
max-height: $subheader-height;
height: $subheader-height;
margin-left: -$page-side-padding;
margin-right: -$page-side-padding;
margin-top: -$page-top-padding;
padding-top: $page-small-top-padding;
padding-left: $page-side-padding;
padding-right: $page-side-padding;
border-bottom: 1px solid #ddd;
.header-item {
display: inline-block;
padding-right: 8em;
}
@media screen and (max-width: 769px) {
height: 60px;
margin-top: -0.5em;
padding-top: 0.9em;
.header-item {
padding-right: 0.5em;
}
}
}
.tabs {
padding-top: 20px;
margin-bottom: 20px;
padding-left: $page-side-padding;
padding-right: $page-side-padding;
}
.above-subheader {
margin-bottom: 0;
margin-top: 0;
height: auto;
}
</style>

View File

@ -0,0 +1,62 @@
<template>
<div class="columns is-fullheight">
<div class="page-container primary-page">
<div class="card">
<div class="card-content">
<p class="title">
{{ taxonomy.name }}
</p>
<p class="subtitle">
{{ taxonomy.description }}
</p>
</div>
<footer class="card-footer">
<router-link
class="card-footer-item"
:to="{ path: $routerHelper.getTaxonomyEditPath(taxonomyId)}">
{{ $i18n.getFrom('taxonomies','edit_item') }}
</router-link>
<a class="card-footer-item">
Edit terms
</a>
</footer>
</div>
</div>
</div>
</template>
<script>
import { mapActions, mapGetters } from 'vuex';
export default {
name: 'TaxonomyPage',
data(){
return {
taxonomyId: Number,
}
},
methods: {
...mapActions('taxonomy', [
'fetchTaxonomy'
]),
...mapGetters('taxonomy', [
'getTaxonomy'
])
},
computed: {
taxonomy(){
return this.getTaxonomy();
}
},
created(){
this.taxonomyId = parseInt(this.$route.params.taxonomyId);
this.fetchTaxonomy(this.taxonomyId);
}
}
</script>
<style scoped>
</style>

View File

@ -0,0 +1,129 @@
<template>
<div class="block">
<div
v-for="(option,index) in getOptions(0)"
:key="index"
:value="index"
class="control">
<b-checkbox
:style="{ paddingLeft: (option.level * 30) + 'px' }"
v-model="selected"
:native-value="option.id"
>{{ option.name }}</b-checkbox>
</div>
</div>
</template>
<script>
import { tainacan as axios } from '../../../js/axios/axios';
export default {
created(){
this.collection = ( this.collection_id ) ? this.collection_id : this.filter.collection_id;
this.metadatum = ( this.metadatum_id ) ? this.metadatum_id : this.filter.metadatum.metadatum_id ;
this.type = ( this.filter_type ) ? this.filter_type : this.filter.metadatum.metadata_type;
this.loadOptions();
},
data(){
return {
isLoading: false,
options: [],
type: '',
collection: '',
metadatum: '',
selected: [],
taxonomy: ''
}
},
props: {
filter: {
type: Object // concentrate all attributes metadatum id and type
},
metadatum_id: [Number], // not required, but overrides the filter metadatum id if is set
collection_id: [Number], // not required, but overrides the filter metadatum id if is set
filter_type: [String], // not required, but overrides the filter metadatum type if is set
id: '',
query: {
type: Object // concentrate all attributes metadatum id and type
}
},
watch: {
selected: function(val){
this.selected = val;
this.onSelect();
}
},
methods: {
getValuesTaxonomy( taxonomy ){
return axios.get('/taxonomy/' + taxonomy + '/terms?hideempty=0&order=asc' ).then( res => {
for (let item of res.data) {
this.taxonomy = item.taxonomy;
this.options.push(item);
}
})
.catch(error => {
this.$console.log(error);
});
},
loadOptions(){
let promise = null;
this.isLoading = true;
axios.get('/collection/'+ this.collection +'/metadata/' + this.metadatum)
.then( res => {
let metadatum = res.data;
promise = this.getValuesTaxonomy( metadatum.metadata_type_options.taxonomy_id );
promise.then( () => {
this.isLoading = false;
this.selectedValues()
})
.catch( error => {
this.$console.log('error select', error );
this.isLoading = false;
});
})
.catch(error => {
this.$console.log(error);
});
},
getOptions( parent, level = 0 ){ // retrieve only ids
let result = [];
if ( this.options ){
for( let term of this.options ){
if( term.parent == parent ){
term['level'] = level;
result.push( term );
const levelTerm = level + 1;
const children = this.getOptions( term.id, levelTerm);
result = result.concat( children );
}
}
}
return result;
},
selectedValues(){
if ( !this.query || !this.query.taxquery || !Array.isArray( this.query.taxquery ) )
return false;
let index = this.query.taxquery.findIndex(newMetadatum => newMetadatum.taxonomy === this.taxonomy );
if ( index >= 0){
let metadata = this.query.taxquery[ index ];
this.selected = metadata.terms;
} else {
return false;
}
},
onSelect(){
this.$emit('input', {
filter: 'selectbox',
taxonomy: this.taxonomy,
compare: 'IN',
metadatum_id: this.metadatum,
collection_id: this.collection,
terms: this.selected
});
}
}
}
</script>

View File

@ -0,0 +1,131 @@
<template>
<div class="block">
<b-select
:id = "id"
:loading = "isLoading"
v-model = "selected"
@input = "onSelect()"
size="is-small"
expanded>
<option value="">{{ $i18n.get('label_selectbox_init') }}...</option>
<option
v-for=" (option, index) in options"
:key="index"
:label="option.name"
:value="option.id">{{ option.name }}</option>
</b-select>
</div>
</template>
<script>
import { tainacan as axios } from '../../../js/axios/axios';
export default {
created(){
this.collection = ( this.collection_id ) ? this.collection_id : this.filter.collection_id;
this.metadatum = ( this.metadatum_id ) ? this.metadatum_id : this.filter.metadatum.metadatum_id ;
this.type = ( this.filter_type ) ? this.filter_type : this.filter.metadatum.metadata_type;
this.loadOptions();
},
data(){
return {
isLoading: false,
options: [],
collection: '',
metadatum: '',
selected: '',
taxonomy: ''
}
},
props: {
filter: {
type: Object // concentrate all attributes metadatum id and type
},
metadatum_id: [Number], // not required, but overrides the filter metadatum id if is set
collection_id: [Number], // not required, but overrides the filter metadatum id if is set
filter_type: [String], // not required, but overrides the filter metadatum type if is set
id: '',
query: {
type: Object // concentrate all attributes metadatum id and type
}
},
watch: {
selected: function(val){
this.selected = val;
this.onSelect();
}
},
methods: {
getValuesTaxonomy( taxonomy ){
return axios.get('/taxonomy/' + taxonomy + '/terms?hideempty=0' ).then( res => {
for (let item of res.data) {
this.taxonomy = item.taxonomy;
this.options.push(item);
}
})
.catch(error => {
this.$console.log(error);
});
},
loadOptions(){
let promise = null;
this.isLoading = true;
axios.get('/collection/'+ this.collection +'/metadata/' + this.metadatum)
.then( res => {
let metadatum = res.data;
promise = this.getValuesTaxonomy( metadatum.metadata_type_options.taxonomy_id );
promise.then( () => {
this.isLoading = false;
this.selectedValues();
})
.catch( error => {
this.$console.log('error select', error );
this.isLoading = false;
});
})
.catch(error => {
this.$console.log(error);
});
},
getOptions( parent, level = 0 ){ // retrieve only ids
let result = [];
if ( this.options ){
for( let term of this.options ){
if( term.parent == parent ){
term['level'] = level;
result.push( term );
const levelTerm = level + 1;
const children = this.getOptions( term.id, levelTerm);
result = result.concat( children );
}
}
}
return result;
},
selectedValues(){
if ( !this.query || !this.query.taxquery || !Array.isArray( this.query.taxquery ) )
return false;
let index = this.query.taxquery.findIndex(newMetadatum => newMetadatum.taxonomy === this.taxonomy );
if ( index >= 0){
let metadata = this.query.taxquery[ index ];
this.selected = metadata.terms;
} else {
return false;
}
},
onSelect(){
this.$emit('input', {
filter: 'selectbox',
compare: 'IN',
taxonomy: this.taxonomy,
metadatum_id: this.metadatum,
collection_id: this.collection,
terms: this.selected
});
}
}
}
</script>

View File

@ -0,0 +1,142 @@
<template>
<div class="block">
<b-taginput
size="is-small"
v-model="selected"
:data="options"
:loading="isLoading"
autocomplete
field="label"
attached
:class="{'has-selected': selected != undefined && selected != []}"
@typing="search" />
</div>
</template>
<script>
import { tainacan as axios } from '../../../js/axios/axios'
export default {
created(){
this.collection = ( this.collection_id ) ? this.collection_id : this.filter.collection_id;
this.metadatum = ( this.metadatum_id ) ? this.metadatum_id : this.filter.metadatum.metadatum_id ;
this.type = ( this.filter_type ) ? this.filter_type : this.filter.metadatum.metadata_type;
let in_route = '/collection/' + this.collection + '/metadata/' + this.metadatum;
if(this.isRepositoryLevel || this.collection == 'filter_in_repository'){
in_route = '/metadata/'+ this.metadatum;
}
axios.get(in_route)
.then( res => {
let metadatum = res.data;
this.selectedValues( metadatum.metadata_type_options.taxonomy_id );
});
},
data(){
return {
results:'',
selected:[],
options: [],
isLoading: false,
type: '',
collection: '',
metadatum: '',
taxonomy: ''
}
},
props: {
filter: {
type: Object // concentrate all attributes metadatum id and type
},
metadatum_id: [Number], // not required, but overrides the filter metadatum id if is set
collection_id: [Number], // not required, but overrides the filter metadatum id if is set
filter_type: [String], // not required, but overrides the filter metadatum type if is set
id: '',
query: {
type: Object // concentrate all attributes metadatum id and type
},
isRepositoryLevel: Boolean,
},
watch: {
selected( value ){
this.selected = value;
let values = [];
if( this.selected.length > 0 ){
for(let val of this.selected){
values.push( val.value );
}
}
this.$emit('input', {
filter: 'taginput',
compare: 'IN',
taxonomy: this.taxonomy,
metadatum_id: ( this.metadatum_id ) ? this.metadatum_id : this.filter.metadatum,
collection_id: ( this.collection_id ) ? this.collection_id : this.filter.collection_id,
terms: values
});
}
},
methods: {
search( query ){
let promise = null;
this.options = [];
const q = query;
const endpoint = this.isRepositoryLevel ? '/metadata/' + this.metadatum : '/collection/'+ this.collection +'/metadata/' + this.metadatum;
axios.get(endpoint)
.then( res => {
let metadatum = res.data;
promise = this.getValuesTaxonomy( metadatum.metadata_type_options.taxonomy_id, q );
this.isLoading = true;
promise.then( () => {
this.isLoading = false;
})
.catch( error => {
this.$console.log('error select', error );
this.isLoading = false;
});
})
.catch(error => {
this.$console.log(error);
});
},
getValuesTaxonomy( taxonomy, query ){
return axios.get('/taxonomy/' + taxonomy + '/terms?hideempty=0&order=asc' ).then( res => {
for (let term of res.data) {
if( term.name.toLowerCase().indexOf( query.toLowerCase() ) >= 0 ){
this.taxonomy = term.taxonomy;
this.options.push({label: term.name, value: term.id});
}
}
})
.catch(error => {
this.$console.log(error);
});
},
selectedValues( taxonomy ){
if ( !this.query || !this.query.taxquery || !Array.isArray( this.query.taxquery ) )
return false;
let index = this.query.taxquery.findIndex(newMetadatum => newMetadatum.taxonomy === this.taxonomy );
if ( index >= 0){
let metadata = this.query.taxquery[ index ];
for ( let id of metadata.terms ){
this.getTerm( taxonomy, id );
}
} else {
return false;
}
},
getTerm( taxonomy, id ){
return axios.get('/taxonomy/' + taxonomy + '/terms/' + id + '?order=asc&hideempty=0' ).then( res => {
this.$console.log(res);
})
.catch(error => {
this.$console.log(error);
});
}
}
}
</script>

View File

@ -0,0 +1,27 @@
<?php
namespace Tainacan\Filter_Types;
defined( 'ABSPATH' ) or die( 'No script kiddies please!' );
/**
* Class TainacanMetadatumType
*/
class TaxonomyCheckbox extends Filter_Type {
function __construct(){
$this->set_supported_types(['term']);
$this->set_component('tainacan-filter-taxonomy-checkbox');
}
/**
* @param $filter
* @return string
*/
public function render( $filter ){
return '<tainacan-filter-taxonomy-checkbox name="'.$filter->get_name().'"
filter_type="'.$filter->get_metadatum()->get_metadata_type().'"
collection_id="'.$filter->get_collection_id().'"
metadatum_id="'.$filter->get_metadatum()->get_id().'"></tainacan-filter-checkbox>';
}
}

View File

@ -0,0 +1,27 @@
<?php
namespace Tainacan\Filter_Types;
defined( 'ABSPATH' ) or die( 'No script kiddies please!' );
/**
* Class TainacanMetadatumType
*/
class TaxonomySelectbox extends Filter_Type {
function __construct(){
$this->set_supported_types(['term']);
$this->set_component('tainacan-filter-taxonomy-selectbox');
}
/**
* @param $filter
* @return string
*/
public function render( $filter ){
return '<tainacan-filter-taxonomy-selectbox name="'.$filter->get_name().'"
filter_type="'.$filter->get_metadatum()->get_metadata_type().'"
collection_id="'.$filter->get_collection_id().'"
metadatum_id="'.$filter->get_metadatum()->get_id().'"></tainacan-filter-selectbox>';
}
}

View File

@ -0,0 +1,27 @@
<?php
namespace Tainacan\Filter_Types;
defined( 'ABSPATH' ) or die( 'No script kiddies please!' );
/**
* Class Taginput
*/
class TaxonomyTaginput extends Filter_Type {
function __construct(){
$this->set_supported_types(['term']);
$this->set_component('tainacan-filter-taxonomy-taginput');
}
/**
* @param $filter
* @return string
*/
public function render( $filter ){
return '<tainacan-filter-taxonomy-taginput name="'.$filter->get_name().'"
filter_type="'.$filter->get_metadatum()->get_metadata_type().'"
collection_id="'.$filter->get_collection_id().'"
metadatum_id="'.$filter->get_metadatum()->get_id().'"></tainacan-filter-taginput>';
}
}

View File

@ -0,0 +1,132 @@
<template>
<div>
<span>
<a
class="button"
@click="showForm = !showForm"><b-icon
size="is-small"
icon="plus"/>&nbsp;{{ $i18n.get('label_new_term') }}</a>
</span>
<div class="columns">
<transition name="fade">
<section
v-if="showForm"
class="column is-one-third"
style="padding-left: 0px;">
<b-field :label="$i18n.get('label_name')">
<b-input
:class="{'has-content': name != undefined && name != ''}"
v-model="name"/>
</b-field>
<b-field :label="$i18n.get('label_parent_term')">
<b-select
v-model="parent">
<option
:value="0"
selected> ---{{ $i18n.get('label_parent_term') }}--- </option>
<option
v-for="(option,index) in options"
:key="index"
:value="option.id"
v-html="setSpaces( option.level ) + option.name"/>
</b-select>
</b-field>
<a
class="button is-primary"
@click="save">{{ $i18n.get('save') }}</a>
</section>
</transition>
</div>
</div>
</template>
<script>
import { tainacan as axios } from '../../../js/axios/axios'
export default {
data(){
return {
name: '',
parent: 0,
showForm: false,
metadatum_id: this.metadatum.metadatum.id
}
},
props: {
id: String,
item_id: [Number,String],
metadatum: [Number,String],
taxonomy_id: [Number,String],
value:[ Array, Boolean, Number ],
options: {
type: Array
}
},
methods: {
setSpaces( level ){
let result = '';
let space = '&nbsp;&nbsp;'
for(let i = 0;i < level; i++)
result += space;
return result;
},
save(){
if( this.name.trim() === ''){
this.$toast.open({
duration: 2000,
message: this.$i18n.get('info_name_is_required'),
position: 'is-bottom',
type: 'is-danger'
})
} else {
const instance = this;
axios.post(`/taxonomy/${this.taxonomy_id}/terms?hideempty=0&order=asc`, {
name: this.name,
parent: this.parent
})
.then( res => {
instance.name = '';
instance.parent = 0;
if( res.data && res.data.id || res.id ){
let id = ( res.id ) ? res.id : res.data.id;
let val = this.value;
if( !Array.isArray( val ) && this.metadatum.metadatum.multiple === 'no' ){
axios.patch(`/item/${this.item_id}/metadata/${this.metadatum_id}`, {
values: id,
}).then(() => {
instance.$emit('newTerm', id);
})
} else {
val = ( val ) ? val : [];
val.push( id );
axios.patch(`/item/${this.item_id}/metadata/${this.metadatum_id}`, {
values: val,
}).then( () => {
instance.$emit('newTerm', val);
})
}
}
});
}
}
}
}
</script>
<style scoped>
button{
font-size: 1rem;
font-weight: 400;
line-height: 1.5;
}
</style>

View File

@ -0,0 +1,197 @@
<template>
<section
v-if="isReady"
:listen="setError">
<b-field
:addons="false"
:type="taxonomyType"
:message="taxonomyMessage">
<label class="label is-inline">
{{ $i18n.get('label_select_taxonomy') }}<span :class="taxonomyType" >&nbsp;*&nbsp;</span>
<help-button
:title="$i18n.getHelperTitle('tainacan-taxonomy', 'taxonomy_id')"
:message="$i18n.getHelperMessage('tainacan-taxonomy', 'taxonomy_id')"/>
</label>
<b-select
name="field_type_options[taxonomy_id]"
placeholder="Select the taxonomy"
v-model="taxonomy_id"
@input="emitValues()"
@focus="clear"
:loading="loading">
<option value="">{{ $i18n.get('label_selectbox_init') }}...</option>
<option
v-for="option in taxonomies"
:value="option.id"
:key="option.id">
{{ option.name }}
</option>
</b-select>
</b-field>
<b-field :addons="false">
<label class="label">
{{ $i18n.get('label_select_taxonomy_input_type') }}
<help-button
:title="$i18n.getHelperTitle('tainacan-taxonomy', 'input_type')"
:message="$i18n.getHelperMessage('tainacan-taxonomy', 'input_type')"/>
</label>
<b-select
v-if="listInputType"
name="metadata_type_options[component_type]"
placeholder="Select the input type for the taxonomy metadatum"
@input="emitValues()"
v-model="input_type">
<option
v-for="(option, index) in single_types"
:value="index"
:key="index">
{{ option }}
</option>
</b-select>
<b-select
name="metadata_type_options[input_type]"
placeholder="Select the input type for the taxonomy metadatum"
v-model="input_type"
@input="emitValues()"
v-else>
<option
v-for="(option, index) in multiple_types"
:value="index"
:key="index">
{{ option }}
</option>
</b-select>
</b-field>
<b-field :addons="false">
<label class="label">
{{ $i18n.get('label_taxonomy_allow_new_terms') }}
<help-button
:title="$i18n.getHelperTitle('tainacan-taxonomy', 'allow_new_terms')"
:message="$i18n.getHelperMessage('tainacan-taxonomy', 'allow_new_terms')"/>
</label>
<div class="block">
<b-checkbox
v-model="allow_new_terms"
@input="emitValues()"
true-value="yes"
false-value="no">
{{ labelNewTerms() }}
</b-checkbox>
</div>
</b-field>
</section>
</template>
<script>
import { tainacan as axios } from '../../../js/axios/axios';
import BCheckbox from "../../../../node_modules/buefy/src/components/checkbox/Checkbox.vue";
export default {
components: {BCheckbox},
props: {
value: [ String, Object, Array ],
metadatum: [ String, Object ],
errors: [ String, Object, Array ]
},
created(){
this.fetchTaxonomies().then(() => {
if ( this.value ) {
this.taxonomy_id = this.value.taxonomy_id;
}
});
if( this.value ) {
this.allow_new_terms = ( this.value.allow_new_terms ) ? this.value.allow_new_terms : 'no';
}
this.single_types['tainacan-taxonomy-radio'] = 'Radio';
this.single_types['tainacan-taxonomy-selectbox'] = 'Selectbox';
this.multiple_types['tainacan-taxonomy-tag-input'] = 'Tag Input';
this.multiple_types['tainacan-taxonomy-checkbox'] = 'Checkbox';
this.isReady = true;
},
computed: {
listInputType(){
if( this.metadatum && this.metadatum.multiple === 'no' ){
let types = Object.keys( this.single_types );
let hasValue = this.value && this.value.input_type && types.indexOf( this.value.input_type ) >= 0;
this.setInputType( ( hasValue ) ? this.value.input_type : 'tainacan-taxonomy-radio' );
return true;
} else {
let types = Object.keys( this.multiple_types );
let hasValue = this.value && this.value.input_type && types.indexOf( this.value.input_type ) >= 0;
this.setInputType( ( hasValue ) ? this.value.input_type : 'tainacan-taxonomy-checkbox' );
return false;
}
},
setError(){
if( this.errors && this.errors.taxonomy_id !== '' ){
this.setErrorsAttributes( 'is-danger', this.errors.taxonomy_id );
} else {
this.setErrorsAttributes( '', '' );
}
return true;
}
},
data(){
return {
isReady: false,
taxonomies: [],
taxonomy_id: '',
loading: true,
allow_new_terms: 'yes',
input_type: 'tainacan-taxonomy-radio',
multiple_types: {},
single_types: {},
taxonomyType:'',
taxonomyMessage: ''
}
},
methods: {
setInputType( input ){
this.input_type = input;
},
setErrorsAttributes( type, message ){
this.taxonomyType = type;
this.taxonomyMessage = message;
},
fetchTaxonomies(){
return axios.get('/taxonomies')
.then(res => {
let taxonomies = res.data;
this.loading = false;
if( taxonomies ){
this.taxonomies = taxonomies;
} else {
this.taxonomies = [];
}
})
.catch(error => {
this.$console.log(error);
});
},
labelNewTerms(){
return ( this.allow_new_terms === 'yes' ) ? this.$i18n.get('label_yes') : this.$i18n.get('label_no');
},
clear(){
this.taxonomyType = '';
this.taxonomyMessage = '';
},
emitValues(){
this.$emit('input',{
taxonomy_id: this.taxonomy_id,
input_type: this.input_type,
allow_new_terms: this.allow_new_terms
})
}
}
}
</script>

View File

@ -0,0 +1,144 @@
<template>
<div>
<component
:is="getComponent()"
v-model="valueComponent"
:allow-new="allowNew"
:terms="terms"
:options="getOptions(0)"/>
<add-new-term
class="add-new-term"
v-if="getComponent() !== 'tainacan-taxonomy-tag-input' && allowNew"
:taxonomy_id="taxonomy"
:metadatum="metadatum"
:item_id="metadatum.item.id"
:value="valueComponent"
:options="getOptions(0)"
@newTerm="reload"/>
</div>
</template>
<script>
import { tainacan as axios } from '../../../js/axios/axios'
import TainacanTaxonomyRadio from './TaxonomyRadio.vue'
import TainacanTaxonomyCheckbox from './TaxonomyCheckbox.vue'
import TainacanTaxonomyTagInput from './TaxonomyTaginput.vue'
import TainacanTaxonomySelectbox from './TaxonomySelectbox.vue'
import AddNewTerm from './AddNewTerm.vue'
export default {
created(){
let metadata_type_options = this.metadatum.metadatum.metadata_type_options;
this.component = ( metadata_type_options && metadata_type_options.input_type )
? this.metadatum.metadatum.metadata_type_options.input_type : this.componentAttribute
this.collectionId = this.metadatum.metadatum.collection_id;
this.taxonomy = metadata_type_options.taxonomy_id;
if( metadata_type_options && metadata_type_options.allow_new_terms ){
this.allowNew = metadata_type_options.allow_new_terms === 'yes'
}
this.getTermsFromTaxonomy();
this.getTermsId();
},
components: {
TainacanTaxonomyRadio,
TainacanTaxonomyCheckbox,
TainacanTaxonomyTagInput,
TainacanTaxonomySelectbox,
AddNewTerm
},
data(){
return {
valueComponent: null,
component: '',
collectionId: '',
taxonomy: '',
terms:[], // object with names
allowNew: false
}
},
watch: {
valueComponent( val ){
this.valueComponent = val;
this.$emit('input', val);
this.$emit('blur');
}
},
props: {
metadatum: {
type: Object
},
componentAttribute: {
type: String
},
value: [ Number, String, Array,Object ],
id: ''
},
methods: {
getComponent(){
if( this.metadatum.metadatum
&& this.metadatum.metadatum.metadata_type_options && this.metadatum.metadatum.metadata_type_options.input_type ){
return this.metadatum.metadatum.metadata_type_options.input_type;
}
},
getTermsFromTaxonomy(){
axios.get('/taxonomy/' + this.taxonomy + '/terms?hideempty=0&order=asc' ).then( res => {
for (let item of res.data) {
this.terms.push( item );
}
})
.catch(error => {
this.$console.log(error);
});
},
getOptions( parent, level = 0 ){ // retrieve only ids
let result = [];
if ( this.terms ){
for( let term of this.terms ){
if( term.parent == parent ){
term['level'] = level;
result.push( term );
const levelTerm = level + 1;
const children = this.getOptions( term.id, levelTerm);
result = result.concat( children );
}
}
}
return result;
},
getTermsId(){
let values = [];
if( this.value && this.value.length > 0){
for( let term of this.value ){
if( term && term.id)
values.push(term.id);
}
}
if( values.length > 0 && this.metadatum.metadatum){
this.valueComponent = ( this.metadatum.metadatum && this.metadatum.metadatum.multiple === 'no' ) ? values[0] : values
}
},
onInput($event) {
this.inputValue = $event;
this.$emit('input', this.inputValue);
this.$emit('blur');
},
reload( val ){
this.valueComponent = val;
this.terms = [];
this.getTermsFromTaxonomy();
this.getTermsId();
}
}
}
</script>
<style scoped>
.add-new-term{
margin-top: 15px;
margin-bottom: 30px;
}
</style>

View File

@ -0,0 +1,55 @@
<template>
<div>
<div
v-for="(option, index) in options"
:key="index">
<b-checkbox
:id="id"
:style="{ paddingLeft: (option.level * 30) + 'px' }"
:key="index"
v-model="checked"
@input="onChecked(option)"
:native-value="option.id"
border>
{{ option.name }}
</b-checkbox>
<br>
</div>
</div>
</template>
<script>
export default {
created(){
if( this.value && this.value.length > 0)
this.checked = this.value;
},
data(){
return {
checked: []
}
},
watch: {
value( val ){
this.checked = val;
}
},
props: {
options: {
type: Array
},
value: [ Number, String, Array ]
},
methods: {
onChecked() {
this.$emit('blur');
this.onInput(this.checked)
},
onInput($event) {
this.inputValue = $event;
this.$emit('input', this.inputValue);
}
}
}
</script>

View File

@ -0,0 +1,51 @@
<template>
<div>
<div
v-for="(option, index) in options"
:key="index">
<b-radio
:id="id"
:style="{ paddingLeft: (option.level * 30) + 'px' }"
:key="index"
v-model="checked"
@input="onChecked(option)"
:native-value="option.id"
border>
{{ option.name }}
</b-radio>
<br>
</div>
</div>
</template>
<script>
export default {
data(){
return {
checked: ( this.value ) ? this.value : ''
}
},
watch: {
value( val ){
this.checked = val;
}
},
props: {
options: {
type: Array
},
value: [ Number, String, Array ]
},
methods: {
onChecked() {
this.$emit('blur');
this.onInput(this.checked)
},
onInput($event) {
this.inputValue = $event;
this.$emit('input', this.inputValue);
}
}
}
</script>

View File

@ -0,0 +1,60 @@
<template>
<div>
<div class="block">
<b-select
:id="id"
v-model="selected"
@input="emitChange()"
:placeholder="$i18n.get('label_select_taxonomy')"
expanded>
<option
v-for="(option, index) in options"
:key="index"
:value="option.id"
v-html="setSpaces( option.level ) + option.name"/>
</b-select>
</div>
</div>
</template>
<script>
export default {
created(){
if( this.value )
this.selected = this.value;
},
data(){
return {
selected: ''
}
},
watch: {
value( val ){
this.selected = val;
}
},
props: {
id: String,
options: {
type: Array
},
value: [ Number, String, Array ]
},
methods: {
emitChange() {
this.$emit('input', this.selected);
this.$emit('blur');
},
setSpaces( level ){
let result = '';
let space = '&nbsp;&nbsp;'
for(let i = 0;i < level; i++)
result += space;
return result;
}
}
}
</script>

View File

@ -0,0 +1,83 @@
<template>
<div class="block">
<b-taginput
size="is-small"
icon="magnify"
:allow-new="allowNew"
@input="emitChange"
v-model="selected"
:data="labels"
field="label"
attached
:class="{'has-selected': selected != undefined && selected != []}"
autocomplete
@typing="search"/>
</div>
</template>
<script>
export default {
data(){
return {
selected: [],
labels: []
}
},
watch: {
terms(){
this.selectedValues();
}
},
props: {
terms: [ Number, String, Array ],
options: {
type: Array
},
value: [ Number, String, Array ],
allowNew: [ Boolean ]
},
methods: {
search( query ){
if( this.terms && this.terms.length > 0 ){
let result = this.terms.filter( ( item ) => {
let name = item.name.toLowerCase();
let q = query.toLowerCase();
return ( name.indexOf(q) >= 0 )
});
this.labels = [];
for( let term of result){
this.labels.push({label: term.name, value: term.id})
}
}
},
selectedValues(){
if( this.value && this.value.length > 0 && this.selected.length === 0){
let result = this.terms.filter( ( item ) => {
let id = item.id;
return ( this.value.indexOf( id ) >= 0 )
});
let selected = [];
for( let term of result){
selected.push({label: term.name, value: term.id})
}
this.selected = selected;
}
},
emitChange(){
let val = this.selected;
let results = [];
for( let term of val ){
if( term.value ){
results.push( term.value );
} else {
results.push( term );
}
}
this.$emit('input', results);
this.$emit('blur');
}
}
}
</script>

View File

@ -0,0 +1,180 @@
<?php
namespace Tainacan\Metadata_Types;
use Tainacan\Entities\Metadatum;
use Tainacan\Entities\Item_Metadata_Entity;
use Tainacan\Repositories\Metadata;
defined( 'ABSPATH' ) or die( 'No script kiddies please!' );
/**
* Class TainacanMetadatumType
*/
class Taxonomy extends Metadata_Type {
function __construct(){
// call metadatum type constructor
parent::__construct();
$this->set_primitive_type('term');
$this->set_default_options([
'allow_new_terms' => false
]);
$this->set_form_component('tainacan-form-taxonomy');
$this->set_component('tainacan-taxonomy');
}
/**
* @inheritdoc
*/
public function get_form_labels(){
return [
'taxonomy_id' => [
'title' => __( 'Collection Related', 'tainacan' ),
'description' => __( 'Select the collection to fetch items', 'tainacan' ),
],
'input_type' => [
'title' => __( 'Input type', 'tainacan' ),
'description' => __( 'The html type of the terms list ', 'tainacan' ),
],
'allow_new_terms' => [
'title' => __( 'Allow new terms', 'tainacan' ),
'description' => __( 'Allows to create new terms', 'tainacan' ),
]
];
}
/**
* @param $itemMetadata Item_Metadata_Entity The instace of the entity itemMetadata
* @return string
*/
public function render( $itemMetadata ){
$options = ( isset( $this->get_options()['options'] ) ) ? $this->get_options()['options'] : '';
return '<tainacan-selectbox
options="' . $options . '"
metadatum_id ="'.$itemMetadata->get_metadatum()->get_id().'"
item_id="'.$itemMetadata->get_item()->get_id().'"
value=\''.json_encode( $itemMetadata->get_value() ).'\'
name="'.$itemMetadata->get_metadatum()->get_name().'"></tainacan-selectbox>';
}
public function validate_options( Metadatum $metadatum) {
if ( !in_array($metadatum->get_status(), apply_filters('tainacan-status-require-validation', ['publish','future','private'])) )
return true;
if (empty($this->get_option('taxonomy_id')))
return ['taxonomy_id' => __('Please select a taxonomy', 'tainacan')];
$Tainacan_Metadata = Metadata::get_instance();
$taxonomy_metadata = $Tainacan_Metadata->fetch([
'collection_id' => $metadatum->get_collection_id(),
'metadata_type' => 'Tainacan\\Metadata_Types\\Taxonomy'
], 'OBJECT');
$taxonomy_metadata = array_map(function ($metadatum_map) {
$fto = $metadatum_map->get_metadata_type_object();
return [ $metadatum_map->get_id() => $fto->get_option('taxonomy_id') ];
}, $taxonomy_metadata);
if( is_array( $taxonomy_metadata ) ){
foreach ($taxonomy_metadata as $metadatum_id => $taxonomy_metadatum) {
if ( is_array( $taxonomy_metadatum ) && key($taxonomy_metadatum) != $metadatum->get_id()
&& in_array($this->get_option('taxonomy_id'), $taxonomy_metadatum)) {
return ['taxonomy_id' => __('You can not have 2 taxonomy metadata using the same taxonomy in a collection.', 'tainacan')];
}
}
}
return true;
}
/**
* Validate item based on metadatum type taxonomies options
*
* @param Item_Metadata_Entity $item_metadata
*
* @return bool Valid or not
*/
public function validate( Item_Metadata_Entity $item_metadata) {
$item = $item_metadata->get_item();
if ( !in_array($item->get_status(), apply_filters('tainacan-status-require-validation', ['publish','future','private'])) )
return true;
$valid = true;
if (false === $this->get_option('allow_new_terms')) {
$terms = $item_metadata->get_value();
if (false === $terms)
return true;
if (!is_array($terms))
$terms = array($terms);
foreach ($terms as $term) {
if (is_object($term) && $term instanceof \Tainacan\Entities\Term) {
$term = $term->get_id();
}
if (!term_exists($term)) {
$valid = false;
break;
}
}
}
return $valid;
}
/**
* Return the value of an Item_Metadata_Entity using a metadatum of this metadatum type as an html string
* @param Item_Metadata_Entity $item_metadata
* @return string The HTML representation of the value, containing one or multiple terms, separated by comma, linked to term page
*/
public function get_value_as_html(Item_Metadata_Entity $item_metadata) {
$value = $item_metadata->get_value();
$return = '';
if ( $item_metadata->is_multiple() ) {
$count = 1;
$total = sizeof($value);
foreach ( $value as $term ) {
if ( $term instanceof \Tainacan\Entities\Term ) {
$return .= $term->_toHtml();
}
$count ++;
if ( $count <= $total ) {
$return .= ', ';
}
}
} else {
if ( $value instanceof \Tainacan\Entities\Term ) {
$return .= $value->_toHtml();
}
}
return $return;
}
}

View File

@ -0,0 +1,184 @@
import axios from '../../../axios/axios'
// CATEGORIES
export const createTaxonomy = ({commit}, taxonomy) => {
return new Promise(( resolve, reject ) => {
axios.tainacan.post('/taxonomies', {
name: taxonomy.name,
description: taxonomy.description,
status: taxonomy.status,
slug: taxonomy.slug,
allow_insert: taxonomy.allowInsert
})
.then( res => {
let taxonomy = res.data;
commit('setTaxonomy', taxonomy);
resolve( taxonomy );
})
.catch(error => {
reject( error.response );
});
});
};
export const deleteTaxonomy = ({ commit }, taxonomyId) => {
return new Promise(( resolve, reject ) => {
axios.tainacan.delete(`/taxonomies/${taxonomyId}?permanently=${true}`)
.then(res => {
commit('deleteTaxonomy', res.data);
resolve( res );
})
.catch(error => {
reject( error )
});
});
};
export const updateTaxonomy = ({ commit }, taxonomy) => {
return new Promise(( resolve, reject ) => {
axios.tainacan.patch(`/taxonomies/${taxonomy.taxonomyId}`, {
name: taxonomy.name,
description: taxonomy.description,
status: taxonomy.status,
slug: taxonomy.slug ? taxonomy.slug : '',
allow_insert: taxonomy.allowInsert
})
.then( res => {
let taxonomy = res.data;
commit('setTaxonomy', taxonomy);
resolve( taxonomy );
})
.catch(error => {
reject({ error_message: error['response']['data'].error_message, errors: error['response']['data'].errors });
});
});
};
export const fetchCategories = ({ commit }, { page, taxonomiesPerPage, status } ) => {
return new Promise((resolve, reject) => {
let endpoint = `/taxonomies?paged=${page}&perpage=${taxonomiesPerPage}&context=edit`;
if (status != undefined && status != '')
endpoint = endpoint + '&status=' + status;
axios.tainacan.get(endpoint)
.then(res => {
let taxonomies = res.data;
commit('setCategories', taxonomies);
resolve({
'taxonomies': taxonomies,
'total': res.headers['x-wp-total']
});
})
.catch(error => {
reject(error);
});
});
};
export const fetchTaxonomy = ({ commit }, taxonomyId) => {
return new Promise((resolve, reject) => {
axios.tainacan.get(`/taxonomies/${taxonomyId}`)
.then(res => {
let taxonomy = res.data;
commit('setTaxonomy', taxonomy);
resolve({
'taxonomy': taxonomy
})
})
.catch(error => {
reject(error);
})
});
};
export const fetchTaxonomyName = ({ commit }, taxonomyId) => {
return new Promise((resolve, reject) => {
axios.tainacan.get(`/taxonomies/${taxonomyId}?fetch_only=name`)
.then(res => {
let name = res.data;
commit('setTaxonomyName');
resolve(name.name)
})
.catch(error => {
reject(error)
})
});
};
// TAXONOMY TERMS
export const sendTerm = ({commit}, { taxonomyId, name, description, parent, headerImageId }) => {
return new Promise(( resolve, reject ) => {
axios.tainacan.post('/taxonomy/' + taxonomyId + '/terms/', {
name: name,
description: description,
parent: parent,
header_image_id: headerImageId,
})
.then( res => {
let term = res.data;
commit('setSingleTerm', term);
resolve( term );
})
.catch(error => {
reject({ error_message: error['response']['data'].error_message, errors: error['response']['data'].errors });
});
});
};
export const deleteTerm = ({ commit }, { taxonomyId, termId }) => {
return new Promise(( resolve, reject ) => {
axios.tainacan.delete(`/taxonomy/${taxonomyId}/terms/${termId}?permanently=${true}`)
.then(res => {
let term = res.data;
commit('deleteTerm', termId);
resolve( term );
})
.catch(error => {
reject({ error_message: error['response']['data'].error_message, errors: error['response']['data'].errors });
});
});
};
export const updateTerm = ({ commit }, { taxonomyId, termId, name, description, parent, headerImageId }) => {
return new Promise(( resolve, reject ) => {
axios.tainacan.patch(`/taxonomy/${taxonomyId}/terms/${termId}`, {
name: name,
description: description,
parent: parent,
header_image_id: headerImageId,
})
.then( res => {
let term = res.data;
commit('setSingleTerm', term);
resolve( term );
})
.catch(error => {
reject({ error_message: error['response']['data'].error_message, errors: error['response']['data'].errors });
});
});
};
export const fetchTerms = ({ commit }, taxonomyId ) => {
return new Promise((resolve, reject) => {
axios.tainacan.get(`/taxonomy/${taxonomyId}/terms/?hideempty=0&order=asc`)
.then(res => {
let terms = res.data;
commit('setTerms', terms);
resolve( terms );
})
.catch(error => {
reject( error );
});
});
};

View File

@ -0,0 +1,15 @@
export const getTaxonomy = state => {
return state.taxonomy;
};
export const getCategories = state => {
return state.taxonomies;
};
export const getTaxonomyName = state => {
return state.taxonomyName;
};
export const getTerms = state => {
return state.terms;
};

View File

@ -0,0 +1,18 @@
import * as actions from './actions';
import * as getters from './getters';
import * as mutations from './mutations';
const state = {
taxonomies: [],
taxonomy: {},
taxonomyName: String,
terms: []
};
export default {
namespaced: true,
state,
mutations,
actions,
getters
}

View File

@ -0,0 +1,44 @@
import Vue from 'vue';
// CATEGORIES
export const setTaxonomy = (state, taxonomy) => {
state.taxonomy = taxonomy;
};
export const setCategories = (state, taxonomies) => {
state.taxonomies = taxonomies;
};
export const setTaxonomyName = (state, name) => {
state.taxonomyName = name;
};
export const deleteTaxonomy = ( state, taxonomy ) => {
let index = state.taxonomies.findIndex(deletedTaxonomy => deletedTaxonomy.id === taxonomy.id);
if (index >= 0) {
state.taxonomies.splice(index, 1);
}
};
// TAXONOMY TERMS
export const setSingleTerm = (state, term) => {
let index = state.terms.findIndex(updatedTerm => updatedTerm.id === term.id);
if ( index >= 0){
Vue.set( state.terms, index, term );
} else {
state.terms.push( term );
}
};
export const setTerms = (state, terms) => {
state.terms = terms;
};
export const deleteTerm = ( state, termId ) => {
let index = state.terms.findIndex(deletedTerm => deletedTerm.id === termId);
if (index >= 0) {
state.terms.splice(index, 1);
}
};