diff --git a/src/admin/components/edition/taxonomy-edition-form.vue b/src/admin/components/edition/taxonomy-edition-form.vue new file mode 100644 index 000000000..098375bfb --- /dev/null +++ b/src/admin/components/edition/taxonomy-edition-form.vue @@ -0,0 +1,370 @@ + + + + + + + + + + + + + + + + + + + + + + + + + {{ statusOption.label }} + + + + + + + + + + + + + + + + + {{ labelNewTerms() }} + + + + + + + + {{ $i18n.get('cancel') }} + + + {{ $i18n.get('save') }} + + + {{ formErrorMessage }} + + + + + + + + + + + + + + + + + diff --git a/src/admin/components/lists/taxonomies-list.vue b/src/admin/components/lists/taxonomies-list.vue new file mode 100644 index 000000000..cf89f4601 --- /dev/null +++ b/src/admin/components/lists/taxonomies-list.vue @@ -0,0 +1,287 @@ + + + + + + + {{ $i18n.get('label_select_all_taxonomies_page') }} + + + + + + {{ $i18n.get('label_bulk_actions') }} + + + + + {{ $i18n.get('label_delete_selected_taxonomies') }} + + {{ $i18n.get('label_edit_selected_taxonomies') + ' (Not ready)' }} + + + + + + + + + + + + + + + + + {{ $i18n.get('label_name') }} + + + + {{ $i18n.get('label_description') }} + + + + + + + + + + + + + + + + + + {{ taxonomy.name }} + + + + + {{ taxonomy.description }} + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/admin/pages/lists/taxonomies-page.vue b/src/admin/pages/lists/taxonomies-page.vue new file mode 100644 index 000000000..490628790 --- /dev/null +++ b/src/admin/pages/lists/taxonomies-page.vue @@ -0,0 +1,236 @@ + + + + + + + + {{ $i18n.getFrom('taxonomies', 'new_item') }} + + + + + + + + {{ $i18n.get('label_all_items') }} + {{ $i18n.get('label_draft_items') }} + {{ $i18n.get('label_trash_items') }} + + + + + + + + + + + + + {{ $i18n.get('info_no_taxonomy_created') }} + {{ $i18n.get('info_no_taxonomy_draft') }} + {{ $i18n.get('info_no_taxonomy_trash') }} + + {{ $i18n.getFrom('taxonomies', 'new_item') }} + + + + + + + + {{ + $i18n.get('info_showing_taxonomies') + + (taxonomiesPerPage * (page - 1) + 1) + + $i18n.get('info_to') + + getLastTaxonomyNumber() + + $i18n.get('info_of') + totalCategories + '.' + }} + + + + + 12 + 24 + 48 + 96 + + + + + + + + + + + + + + + + + + diff --git a/src/admin/pages/singles/taxonomy-page.vue b/src/admin/pages/singles/taxonomy-page.vue new file mode 100644 index 000000000..ff6ec2a87 --- /dev/null +++ b/src/admin/pages/singles/taxonomy-page.vue @@ -0,0 +1,62 @@ + + + + + + + {{ taxonomy.name }} + + + {{ taxonomy.description }} + + + + + + + + + + + \ No newline at end of file diff --git a/src/classes/filter-types/taxonomy/Checkbox.vue b/src/classes/filter-types/taxonomy/Checkbox.vue new file mode 100644 index 000000000..ac0e0b3aa --- /dev/null +++ b/src/classes/filter-types/taxonomy/Checkbox.vue @@ -0,0 +1,129 @@ + + + + {{ option.name }} + + + + + diff --git a/src/classes/filter-types/taxonomy/Selectbox.vue b/src/classes/filter-types/taxonomy/Selectbox.vue new file mode 100644 index 000000000..349b5c523 --- /dev/null +++ b/src/classes/filter-types/taxonomy/Selectbox.vue @@ -0,0 +1,131 @@ + + + + {{ $i18n.get('label_selectbox_init') }}... + {{ option.name }} + + + + + diff --git a/src/classes/filter-types/taxonomy/Taginput.vue b/src/classes/filter-types/taxonomy/Taginput.vue new file mode 100644 index 000000000..56219c2dc --- /dev/null +++ b/src/classes/filter-types/taxonomy/Taginput.vue @@ -0,0 +1,142 @@ + + + + + + + diff --git a/src/classes/filter-types/taxonomy/class-tainacan-taxonomycheckbox.php b/src/classes/filter-types/taxonomy/class-tainacan-taxonomycheckbox.php new file mode 100644 index 000000000..7e9d08780 --- /dev/null +++ b/src/classes/filter-types/taxonomy/class-tainacan-taxonomycheckbox.php @@ -0,0 +1,27 @@ +set_supported_types(['term']); + $this->set_component('tainacan-filter-taxonomy-checkbox'); + } + + /** + * @param $filter + * @return string + */ + + public function render( $filter ){ + return ''; + } +} \ No newline at end of file diff --git a/src/classes/filter-types/taxonomy/class-tainacan-taxonomyselectbox.php b/src/classes/filter-types/taxonomy/class-tainacan-taxonomyselectbox.php new file mode 100644 index 000000000..ff8a067c1 --- /dev/null +++ b/src/classes/filter-types/taxonomy/class-tainacan-taxonomyselectbox.php @@ -0,0 +1,27 @@ +set_supported_types(['term']); + $this->set_component('tainacan-filter-taxonomy-selectbox'); + } + + /** + * @param $filter + * @return string + */ + + public function render( $filter ){ + return ''; + } +} \ No newline at end of file diff --git a/src/classes/filter-types/taxonomy/class-tainacan-taxonomytaginput.php b/src/classes/filter-types/taxonomy/class-tainacan-taxonomytaginput.php new file mode 100644 index 000000000..e933a7b4b --- /dev/null +++ b/src/classes/filter-types/taxonomy/class-tainacan-taxonomytaginput.php @@ -0,0 +1,27 @@ +set_supported_types(['term']); + $this->set_component('tainacan-filter-taxonomy-taginput'); + } + + /** + * @param $filter + * @return string + */ + + public function render( $filter ){ + return ''; + } +} \ No newline at end of file diff --git a/src/classes/metadata-types/taxonomy/AddNewTerm.vue b/src/classes/metadata-types/taxonomy/AddNewTerm.vue new file mode 100644 index 000000000..7b3a2bc4f --- /dev/null +++ b/src/classes/metadata-types/taxonomy/AddNewTerm.vue @@ -0,0 +1,132 @@ + + + + {{ $i18n.get('label_new_term') }} + + + + + + + + + + + + + ---{{ $i18n.get('label_parent_term') }}--- + + + + + {{ $i18n.get('save') }} + + + + + + + + + \ No newline at end of file diff --git a/src/classes/metadata-types/taxonomy/FormTaxonomy.vue b/src/classes/metadata-types/taxonomy/FormTaxonomy.vue new file mode 100644 index 000000000..e67582e64 --- /dev/null +++ b/src/classes/metadata-types/taxonomy/FormTaxonomy.vue @@ -0,0 +1,197 @@ + + + + + {{ $i18n.get('label_select_taxonomy') }} * + + + + {{ $i18n.get('label_selectbox_init') }}... + + {{ option.name }} + + + + + + + {{ $i18n.get('label_select_taxonomy_input_type') }} + + + + + {{ option }} + + + + + + + {{ option }} + + + + + + + + {{ $i18n.get('label_taxonomy_allow_new_terms') }} + + + + + {{ labelNewTerms() }} + + + + + + + + \ No newline at end of file diff --git a/src/classes/metadata-types/taxonomy/Taxonomy.vue b/src/classes/metadata-types/taxonomy/Taxonomy.vue new file mode 100644 index 000000000..2a6c752d1 --- /dev/null +++ b/src/classes/metadata-types/taxonomy/Taxonomy.vue @@ -0,0 +1,144 @@ + + + + + + + + + \ No newline at end of file diff --git a/src/classes/metadata-types/taxonomy/TaxonomyCheckbox.vue b/src/classes/metadata-types/taxonomy/TaxonomyCheckbox.vue new file mode 100644 index 000000000..74c18473d --- /dev/null +++ b/src/classes/metadata-types/taxonomy/TaxonomyCheckbox.vue @@ -0,0 +1,55 @@ + + + + + {{ option.name }} + + + + + + + \ No newline at end of file diff --git a/src/classes/metadata-types/taxonomy/TaxonomyRadio.vue b/src/classes/metadata-types/taxonomy/TaxonomyRadio.vue new file mode 100644 index 000000000..91c24a373 --- /dev/null +++ b/src/classes/metadata-types/taxonomy/TaxonomyRadio.vue @@ -0,0 +1,51 @@ + + + + + {{ option.name }} + + + + + + + \ No newline at end of file diff --git a/src/classes/metadata-types/taxonomy/TaxonomySelectbox.vue b/src/classes/metadata-types/taxonomy/TaxonomySelectbox.vue new file mode 100644 index 000000000..242b5bb35 --- /dev/null +++ b/src/classes/metadata-types/taxonomy/TaxonomySelectbox.vue @@ -0,0 +1,60 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/src/classes/metadata-types/taxonomy/TaxonomyTaginput.vue b/src/classes/metadata-types/taxonomy/TaxonomyTaginput.vue new file mode 100644 index 000000000..ef5fa9135 --- /dev/null +++ b/src/classes/metadata-types/taxonomy/TaxonomyTaginput.vue @@ -0,0 +1,83 @@ + + + + + + \ No newline at end of file diff --git a/src/classes/metadata-types/taxonomy/class-tainacan-taxonomy.php b/src/classes/metadata-types/taxonomy/class-tainacan-taxonomy.php new file mode 100644 index 000000000..2c829b911 --- /dev/null +++ b/src/classes/metadata-types/taxonomy/class-tainacan-taxonomy.php @@ -0,0 +1,180 @@ +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 'get_value() ).'\' + name="'.$itemMetadata->get_metadatum()->get_name().'">'; + } + + 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; + + } + +} \ No newline at end of file diff --git a/src/js/store/modules/taxonomy/actions.js b/src/js/store/modules/taxonomy/actions.js new file mode 100644 index 000000000..8b9359293 --- /dev/null +++ b/src/js/store/modules/taxonomy/actions.js @@ -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 ); + }); + }); +}; diff --git a/src/js/store/modules/taxonomy/getters.js b/src/js/store/modules/taxonomy/getters.js new file mode 100644 index 000000000..275ecd734 --- /dev/null +++ b/src/js/store/modules/taxonomy/getters.js @@ -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; +}; \ No newline at end of file diff --git a/src/js/store/modules/taxonomy/index.js b/src/js/store/modules/taxonomy/index.js new file mode 100644 index 000000000..7c045ee93 --- /dev/null +++ b/src/js/store/modules/taxonomy/index.js @@ -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 +} \ No newline at end of file diff --git a/src/js/store/modules/taxonomy/mutations.js b/src/js/store/modules/taxonomy/mutations.js new file mode 100644 index 000000000..0873dfa4d --- /dev/null +++ b/src/js/store/modules/taxonomy/mutations.js @@ -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); + } +}; \ No newline at end of file
{{ formErrorMessage }}
+ {{ taxonomy.name }}
+ {{ taxonomy.description }}
+ +
{{ $i18n.get('info_no_taxonomy_created') }}
{{ $i18n.get('info_no_taxonomy_draft') }}
{{ $i18n.get('info_no_taxonomy_trash') }}
+ {{ taxonomy.name }} +
+ {{ taxonomy.description }} +