Merge pull request #890 from tainacan/feature/887

Creates basic date and numeric intersection filters. #887
This commit is contained in:
Mateus Machado Luna 2024-06-07 16:11:18 -03:00 committed by GitHub
commit eb7dae2c66
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
15 changed files with 1458 additions and 49 deletions

View File

@ -454,7 +454,7 @@ class REST_Items_Controller extends REST_Controller {
* @return array
* @throws \Exception
*/
private function prepare_filters_arguments ( $args, $collection_id = false ) {
private function prepare_filters_arguments ( $args, $collection_id = false, $ignore_filter_arguments = [] ) {
$filters_arguments = array();
$meta_query = isset($args['meta_query']) ? $args['meta_query'] : [];
if(isset($meta_query['value'])) $meta_query = [$meta_query];
@ -498,8 +498,7 @@ class REST_Items_Controller extends REST_Controller {
}
foreach($meta_query as $meta) {
if ( !isset($meta['key']) || !isset($meta['value']) )
if ( !isset($meta['key']) || !isset($meta['value']) || ( in_array($meta['key'], $ignore_filter_arguments) ))
continue;
$meta_id = $meta['key'];
@ -554,6 +553,7 @@ class REST_Items_Controller extends REST_Controller {
$date_format = get_option( 'date_format' ) != false ? get_option( 'date_format' ) : 'Y-m-d';
return empty($date) == false ? mysql2date($date_format, $date) : "";
}, $meta_label);
$meta_type = 'DATE';
break;
case 'item':
$meta_label = array_map(function($item_id) {
@ -643,7 +643,15 @@ class REST_Items_Controller extends REST_Controller {
if($request['collection_id']) {
$collection_id = $request['collection_id'];
}
$filters_args = $this->prepare_filters_arguments($args, $collection_id);
$metaqueries = isset($request['metaquery']) ? $request['metaquery'] : [];
$ignore_filter_arguments = array_map(
function($metaquery) { return $metaquery['key']; },
array_filter(
$metaqueries,
function($metaquery) { return isset($metaquery['key']) && isset($metaquery['secondary']) && $metaquery['secondary'] == 'true'; }
)
);
$filters_args = $this->prepare_filters_arguments($args, $collection_id, $ignore_filter_arguments);
if(isset($args['meta_query']) && !empty($args['meta_query']) && is_array($filters_args) && !empty($filters_args)) {
foreach($filters_args as $filters_arg) {
if($filters_arg['filter'] !== false) {

View File

@ -252,13 +252,17 @@ import { formHooks } from "../../js/mixins";
import FormFilterNumeric from '../filter-types/numeric/FormNumeric.vue';
import FormFilterNumericInterval from '../filter-types/numeric-interval/FormNumericInterval.vue';
import FormFilterNumericListInterval from '../filter-types/numeric-list-interval/FormNumericListInterval.vue';
import FormFilterNumericsIntersection from '../filter-types/numerics-intersection/FormNumericsIntersection.vue';
import FormFilterDatesIntersection from '../filter-types/dates-intersection/FormDatesIntersection.vue';
export default {
name: 'FilterEditionForm',
components: {
'tainacan-filter-form-numeric': FormFilterNumeric,
'tainacan-filter-form-numeric-interval': FormFilterNumericInterval,
'tainacan-filter-form-numeric-list-interval': FormFilterNumericListInterval
'tainacan-filter-form-numeric-list-interval': FormFilterNumericListInterval,
'tainacan-filter-form-numerics-intersection': FormFilterNumericsIntersection,
'tainacan-filter-form-dates-intersection': FormFilterDatesIntersection
},
mixins: [ formHooks ],
props: {
@ -418,7 +422,7 @@ export default {
-webkit-column-gap: 0;
-webkit-column-rule: none;
column-count: 2;
column-gap: 4em;
column-gap: 3em;
column-rule: none;
padding-bottom: 0.5em;

View File

@ -3,7 +3,7 @@
<b-datepicker
v-model="dateInit"
:aria-labelledby="'filter-label-id-' + filter.id"
:placeholder="$i18n.get('label_selectbox_init')"
:placeholder="$i18n.get('instruction_select_a_date')"
editable
:trap-focus="false"
:date-formatter="(date) => dateFormatter(date)"
@ -19,6 +19,20 @@
$i18n.get('datepicker_short_friday'),
$i18n.get('datepicker_short_saturday'),
]"
:month-names="[
$i18n.get('datepicker_month_january'),
$i18n.get('datepicker_month_february'),
$i18n.get('datepicker_month_march'),
$i18n.get('datepicker_month_april'),
$i18n.get('datepicker_month_may'),
$i18n.get('datepicker_month_june'),
$i18n.get('datepicker_month_july'),
$i18n.get('datepicker_month_august'),
$i18n.get('datepicker_month_september'),
$i18n.get('datepicker_month_october'),
$i18n.get('datepicker_month_november'),
$i18n.get('datepicker_month_december')
]"
@focus="isTouched = true"
@update:model-value="($event) => { resetPage(); validadeValues($event) }" />
<p
@ -45,6 +59,20 @@
$i18n.get('datepicker_short_friday'),
$i18n.get('datepicker_short_saturday'),
]"
:month-names="[
$i18n.get('datepicker_month_january'),
$i18n.get('datepicker_month_february'),
$i18n.get('datepicker_month_march'),
$i18n.get('datepicker_month_april'),
$i18n.get('datepicker_month_may'),
$i18n.get('datepicker_month_june'),
$i18n.get('datepicker_month_july'),
$i18n.get('datepicker_month_august'),
$i18n.get('datepicker_month_september'),
$i18n.get('datepicker_month_october'),
$i18n.get('datepicker_month_november'),
$i18n.get('datepicker_month_december')
]"
@update:model-value="validadeValues()"
@focus="isTouched = true" />
</div>

View File

@ -0,0 +1,318 @@
<template>
<div>
<b-field
:addons="false"
:type="metadataType"
:message="metadataMessage">
<label class="label is-inline">
{{ $i18n.getHelperTitle('tainacan-filter-dates-intersection', 'secondary_filter_metadatum_id') }}<span :class="metadataType">&nbsp;*&nbsp;</span>
<span style="font-size: 1.35em;">
<help-button
:title="$i18n.getHelperTitle('tainacan-filter-dates-intersection', 'secondary_filter_metadatum_id')"
:message="$i18n.getHelperMessage('tainacan-filter-dates-intersection', 'secondary_filter_metadatum_id')" />
</span>
</label>
<b-select
v-model="secondDateMetadatumId"
name="dates_intersect[secondary_filter_metadatum_id]"
:placeholder="$i18n.get('instruction_select_second_date_to_compare' )"
:loading="loading"
expanded
@change="onUpdateSecondDateMetadatumId()"
@focus="clear()">
<option :value="''">
{{ $i18n.get('instruction_select_second_date_to_compare' ) }}
</option>
<option
v-for="option in metadata.filter(aMetadatum => aMetadatum.id != filter.metadatumId )"
:key="option.id"
:value="option.id">
{{ option.name }}
</option>
</b-select>
</b-field>
<fieldset
v-if="secondDateMetadatumId"
class="intersection-explainer-section">
<legend>
<p>
<strong>{{ $i18n.get('info_intersection_explainer') }}</strong>
<span style="font-size: 1.35em;">
<help-button
:title="$i18n.get('label_comparators')"
:message="$i18n.get('info_intersection_rules')" />
</span>
</p>
</legend>
<b-field :addons="false">
<b-select
v-if="showEditFirstComparatorOptions"
v-model="firstComparator"
@update:model-value="emitValues()">
<option
v-for="(comparatorObject, comparatorKey) in comparatorsObject"
:key="comparatorKey"
:value="comparatorKey"
v-html="comparatorObject.symbol + '&nbsp;' + comparatorObject.label" />
</b-select>
<strong
v-else
v-html="comparatorsObject[firstComparator].symbol" />
<p v-if="filter.metadatum">
&nbsp;
<em>{{ filter.metadatum.metadatum_name }}</em>
</p>
<button
v-if="!showEditFirstComparatorOptions"
class="button is-white is-pulled-right"
@click.prevent="showEditFirstComparatorOptions = true">
<span
v-tooltip="{
content: $i18n.get('edit'),
autoHide: true,
placement: 'bottom',
popperClass: ['tainacan-tooltip', 'tooltip']
}"
class="icon">
<i class="tainacan-icon tainacan-icon-18px tainacan-icon-edit has-text-secondary" />
</span>
</button>
<button
v-else
class="button is-white is-pulled-right"
@click.prevent="showEditFirstComparatorOptions = false">
<span
v-tooltip="{
content: $i18n.get('close'),
autoHide: true,
placement: 'bottom',
popperClass: ['tainacan-tooltip', 'tooltip']
}"
class="icon">
<i class="tainacan-icon tainacan-icon-18px tainacan-icon-approved has-text-secondary" />
</span>
</button>
</b-field>
<div class="logic-divider">
<span>{{ $i18n.get('label_and') }}</span>
</div>
<b-field :addons="false">
<b-select
v-if="showEditSecondComparatorOptions"
v-model="secondComparator"
@update:model-value="emitValues()">
<option
v-for="(comparatorObject, comparatorKey) in comparatorsObject"
:key="comparatorKey"
:value="comparatorKey"
v-html="comparatorObject.symbol + '&nbsp;' + comparatorObject.label" />
</b-select>
<strong
v-else
v-html="comparatorsObject[secondComparator].symbol" />
<p>&nbsp;<em>{{ secondDateMetadatumName }}</em></p>
<button
v-if="!showEditSecondComparatorOptions"
class="button is-white is-pulled-right"
@click.prevent="showEditSecondComparatorOptions = true">
<span
v-tooltip="{
content: $i18n.get('edit'),
autoHide: true,
placement: 'bottom',
popperClass: ['tainacan-tooltip', 'tooltip']
}"
class="icon">
<i class="tainacan-icon tainacan-icon-18px tainacan-icon-edit has-text-secondary" />
</span>
</button>
<button
v-else
class="button is-white is-pulled-right"
@click.prevent="showEditSecondComparatorOptions = false">
<span
v-tooltip="{
content: $i18n.get('close'),
autoHide: true,
placement: 'bottom',
popperClass: ['tainacan-tooltip', 'tooltip']
}"
class="icon">
<i class="tainacan-icon tainacan-icon-18px tainacan-icon-approved has-text-secondary" />
</span>
</button>
</b-field>
</fieldset>
<!-- Much more complicated logic, will be possible if we implement #889 -->
<!-- <b-field
:addons="false"
:label="$i18n.getHelperTitle('tainacan-filter-dates-intersection', 'accept_date_interval')"
style="margin-top: 1.125rem;"
:type="errors && errors['accept_date_interval'] != undefined ? 'is-danger' : ''"
:message="errors && errors['accept_date_interval'] != undefined ? errors['accept_date_interval'] : ''">
&nbsp;
<b-switch
v-model="acceptDateInterval"
size="is-small"
:true-value="'yes'"
:false-value="'no'"
:native-value="acceptDateInterval == 'yes' ? 'yes' : 'no'"
name="accept_date_interval"
@update:model-value="emitValues()">
<help-button
:title="$i18n.getHelperTitle('tainacan-filter-dates-intersection', 'accept_date_interval')"
:message="$i18n.getHelperMessage('tainacan-filter-dates-intersection', 'accept_date_interval')" />
</b-switch>
</b-field> -->
</div>
</template>
<script>
import { tainacanApi } from '../../../js/axios';
export default {
props: {
filter: Object,
modelValue: Object,
errors: Object
},
emits: [
'update:model-value',
],
data() {
return {
metadata: [],
loading: true,
metadataType: '',
metadataMessage: '',
secondDateMetadatumId: [Number, String],
secondDateMetadatumName: String,
firstComparator: String,
secondComparator: String,
comparatorsObject: {},
acceptDateInterval: String,
showEditFirstComparatorOptions: false,
showEditSecondComparatorOptions: false
}
},
watch: {
errors(){
if ( this.errors && this.errors.secondary_filter_metadatum_id !== '' )
this.setErrorsAttributes( 'is-danger', this.errors.secondary_filter_metadatum_id );
else
this.setErrorsAttributes( '', '' );
}
},
created() {
this.secondDateMetadatumId = this.modelValue && this.modelValue.secondary_filter_metadatum_id ? this.modelValue.secondary_filter_metadatum_id : '';
this.secondDateMetadatumName = this.modelValue && this.modelValue.secondary_filter_metadatum_name ? this.modelValue.secondary_filter_metadatum_name : '';
this.firstComparator = this.modelValue && this.modelValue.first_comparator ? this.modelValue.first_comparator : '>=';
this.secondComparator = this.modelValue && this.modelValue.second_comparator ? this.modelValue.second_comparator : '<=';
this.acceptDateInterval = this.modelValue && this.modelValue.accept_date_interval ? this.modelValue.accept_date_interval : 'no';
this.loading = true;
this.fetchMetadata();
this.comparatorsObject = {
'=': {
symbol: '&#61;',
label: this.$i18n.get('is_equal_to')
},
'!=': {
symbol: '&#8800;',
label: this.$i18n.get('is_not_equal_to')
},
'>': {
symbol: '&#62;',
label: this.$i18n.get('after')
},
'>=': {
symbol: '&#8805;',
label: this.$i18n.get('after_or_on_day')
},
'<': {
symbol: '&#60;',
label: this.$i18n.get('before')
},
'<=': {
symbol: '&#8804;',
label: this.$i18n.get('before_or_on_day')
}
};
},
methods: {
async fetchMetadata() {
let endpoint = this.filter.collection_id && this.filter.collection_id !== 'default' ? ( '/collection/' + this.filter.collection_id + '/metadata' ) : '/metadata';
endpoint += '?metaquery[0][key]=metadata_type&metaquery[0][value]=Tainacan\\Metadata_Types\\Date&nopaging=1&exclude=' + this.filter.metadatum_id;
return await tainacanApi.get(endpoint)
.then(res => {
this.loading = false;
this.metadata = res.data ? res.data : [];
})
.catch(error => {
this.loading = false;
this.$console.log(error);
});
},
onUpdateSecondDateMetadatumId() {
const selectedMetadatum = this.metadata.find( aMetadatum => aMetadatum.id == this.secondDateMetadatumId );
this.secondDateMetadatumName = selectedMetadatum ? selectedMetadatum.name : '';
this.secondDateMetadatumId = selectedMetadatum ? selectedMetadatum.id : '';
this.emitValues();
},
emitValues() {
this.$emit('update:model-value', {
first_comparator: this.firstComparator,
second_comparator: this.secondComparator,
secondary_filter_metadatum_id: this.secondDateMetadatumId,
secondary_filter_metadatum_name: this.secondDateMetadatumName,
accept_date_interval: this.acceptDateInterval
});
},
setErrorsAttributes( type, message ) {
this.metadataType = type;
this.metadataMessage = message;
},
clear(){
this.metadataType = '';
this.metadataMessage = '';
},
}
}
</script>
<style lang="scss" scoped>
.intersection-explainer-section {
margin-top: 1.25rem;
padding: 0.75em 0.75em 0.25em 0.75em;
border: 1px solid var(--tainacan-gray1);
legend {
margin: -0.75em 0 0em 0;
background-color: var(--tainacan-background-color);
padding: 5px 5px 5px 0px;
}
.field {
display: flex;
gap: 0.5em;
margin: 0 -0.5em 0.5em 0em;
align-items: center;
strong {
margin-left: 0.75em;
}
}
button {
border-radius: 100em !important;
margin-left: auto;
}
.logic-divider {
display: none;
}
}
</style>

View File

@ -0,0 +1,227 @@
<template>
<div>
<b-datepicker
v-model="dateInit"
:aria-labelledby="'filter-label-id-' + filter.id"
:placeholder="$i18n.get('instruction_select_a_date')"
editable
:trap-focus="false"
:date-formatter="(date) => dateFormatter(date)"
:date-parser="(date) => dateParser(date)"
icon="calendar-today"
:years-range="[-200, 100]"
:day-names="[
$i18n.get('datepicker_short_sunday'),
$i18n.get('datepicker_short_monday'),
$i18n.get('datepicker_short_tuesday'),
$i18n.get('datepicker_short_wednesday'),
$i18n.get('datepicker_short_thursday'),
$i18n.get('datepicker_short_friday'),
$i18n.get('datepicker_short_saturday'),
]"
:month-names="[
$i18n.get('datepicker_month_january'),
$i18n.get('datepicker_month_february'),
$i18n.get('datepicker_month_march'),
$i18n.get('datepicker_month_april'),
$i18n.get('datepicker_month_may'),
$i18n.get('datepicker_month_june'),
$i18n.get('datepicker_month_july'),
$i18n.get('datepicker_month_august'),
$i18n.get('datepicker_month_september'),
$i18n.get('datepicker_month_october'),
$i18n.get('datepicker_month_november'),
$i18n.get('datepicker_month_december')
]"
@focus="isTouched = true"
@update:model-value="($event) => { resetPage(); validadeValues($event) }" />
<p
v-if="filterTypeOptions.accept_date_interval === 'yes'"
style="font-size: 0.75em; margin-bottom: 0.125em;"
class="has-text-centered is-marginless">
{{ $i18n.get('label_until') }}
</p>
<b-datepicker
v-if="filterTypeOptions.accept_date_interval === 'yes'"
v-model="dateEnd"
:aria-labelledby="'filter-label-id-' + filter.id"
:placeholder="$i18n.get('instruction_select_a_date')"
editable
:trap-focus="false"
:date-formatter="(date) => dateFormatter(date)"
:date-parser="(date) => dateParser(date)"
icon="calendar-today"
:years-range="[-200, 50]"
:day-names="[
$i18n.get('datepicker_short_sunday'),
$i18n.get('datepicker_short_monday'),
$i18n.get('datepicker_short_tuesday'),
$i18n.get('datepicker_short_wednesday'),
$i18n.get('datepicker_short_thursday'),
$i18n.get('datepicker_short_friday'),
$i18n.get('datepicker_short_saturday'),
]"
:month-names="[
$i18n.get('datepicker_month_january'),
$i18n.get('datepicker_month_february'),
$i18n.get('datepicker_month_march'),
$i18n.get('datepicker_month_april'),
$i18n.get('datepicker_month_may'),
$i18n.get('datepicker_month_june'),
$i18n.get('datepicker_month_july'),
$i18n.get('datepicker_month_august'),
$i18n.get('datepicker_month_september'),
$i18n.get('datepicker_month_october'),
$i18n.get('datepicker_month_november'),
$i18n.get('datepicker_month_december')
]"
@update:model-value="validadeValues()"
@focus="isTouched = true" />
</div>
</template>
<script>
import { dateInter } from "../../../js/mixins";
import { filterTypeMixin } from '../../../js/filter-types-mixin';
import moment from 'moment';
export default {
mixins: [
dateInter,
filterTypeMixin
],
emits: [
'input',
],
data(){
return {
dateInit: undefined,
dateEnd: undefined,
isTouched: false
}
},
watch: {
isTouched( val ){
if ( val && this.dateInit === null)
this.dateInit = new Date();
if ( val && this.dateEnd === null)
this.dateEnd = new Date();
},
'query': {
handler() {
this.updateSelectedValues();
},
deep: true
}
},
mounted() {
this.updateSelectedValues();
},
methods: {
// only validate if the first value is higher than first
validadeValues: _.debounce( function (){
if ( this.dateInit === undefined )
this.dateInit = new Date();
if ( this.dateEnd === undefined )
this.dateEnd = new Date();
if ( this.filterTypeOptions.accept_date_interval === 'yes' && this.dateInit > this.dateEnd ) {
this.showErrorMessage();
return
}
this.emit();
}, 800),
showErrorMessage(){
if ( !this.isTouched ) return false;
this.$buefy.toast.open({
duration: 3000,
message: this.$i18n.get('info_error_first_value_greater'),
position: 'is-bottom',
type: 'is-danger'
})
},
dateFormatter(dateObject){
return moment(dateObject, moment.ISO_8601).format(this.dateFormat);
},
dateParser(dateString){
return moment(dateString, this.dateFormat).toDate();
},
updateSelectedValues(){
if ( !this.query || !this.query.metaquery || !Array.isArray( this.query.metaquery ) )
return false;
let index = this.query.metaquery.findIndex(newMetadatum => newMetadatum.key == this.metadatumId);
if (index >= 0) {
let metadata = this.query.metaquery[ index ];
if (metadata.value ) {
if ( Array.isArray(metadata.value) && metadata.value.length > 0 ) {
const dateValueInit = new Date(metadata.value[0].replace(/-/g, '/'));
this.dateInit = moment(dateValueInit, moment.ISO_8601).toDate();
const dateValueEnd = new Date(metadata.value[1].replace(/-/g, '/'));
this.dateEnd = moment(dateValueEnd, moment.ISO_8601).toDate();
} else {
const dateValueInit = new Date(metadata.value.replace(/-/g, '/'));
this.dateInit = moment(dateValueInit, moment.ISO_8601).toDate();
}
}
} else {
this.dateInit = null;
this.dateEnd = null;
}
},
// emit the operation for listeners
emit() {
let values = [];
if (this.dateInit === null && this.dateEnd === null) {
values = [];
} else {
let dateInit = this.dateInit.getUTCFullYear() + '-' +
('00' + (this.dateInit.getUTCMonth() + 1)).slice(-2) + '-' +
('00' + this.dateInit.getUTCDate()).slice(-2);
let dateEnd = this.dateEnd.getUTCFullYear() + '-' +
('00' + (this.dateEnd.getUTCMonth() + 1)).slice(-2) + '-' +
('00' + this.dateEnd.getUTCDate()).slice(-2);
values = [ dateInit, dateEnd ];
}
if ( this.filterTypeOptions.accept_date_interval !== 'yes' ) {
this.$emit('input', {
filter: 'intersection',
type: 'DATE',
compare: this.filterTypeOptions.first_comparator,
metadatum_id: this.metadatumId,
collection_id: this.collectionId,
value: values[0]
});
this.$emit('input', {
filter: 'intersection',
type: 'DATE',
compare: this.filterTypeOptions.second_comparator,
metadatum_id: this.filterTypeOptions.secondary_filter_metadatum_id,
collection_id: this.collectionId,
value: values[0],
secondary: true
});
} else {
// Much more complicated logic to be implemented in the future. See #889
}
}
}
}
</script>
<style scoped>
.field {
margin-bottom: 0.125em !important;
}
.dropdown-trigger input {
font-size: 0.75em;
}
</style>

View File

@ -0,0 +1,130 @@
<?php
namespace Tainacan\Filter_Types;
defined( 'ABSPATH' ) or die( 'No script kiddies please!' );
/**
* Class TainacanFilterType
*/
class Dates_Intersection extends Filter_Type {
function __construct(){
$this->set_name( __('Dates Intersection', 'tainacan') );
$this->set_supported_types(['date']);
$this->set_component('tainacan-filter-dates-intersection');
$this->set_form_component('tainacan-filter-form-dates-intersection');
$this->set_default_options([
'secondary_filter_metadatum_id' => '',
'secondary_filter_metadatum_name' => '',
'first_comparator' => '>=',
'second_comparator' => '<=',
'accept_date_interval' => 'no'
]);
$this->set_use_max_options(false);
$this->set_preview_template('
<div>
<div class="datepicker control is-small">
<div class="dropdown is-bottom-left is-mobile-modal">
<div role="button" class="dropdown-trigger">
<div class="control has-icons-left is-small is-clearfix">
<input type="text" autocomplete="off" placeholder=" '. __('Select a date', 'tainacan') .'" class="input is-small">
<span class="icon is-left is-small"><i class="mdi mdi-calendar-today"></i></span>
</div>
</div>
</div>
</div>
</div>
');
}
/**
* @inheritdoc
*/
public function get_form_labels(){
return [
'secondary_filter_metadatum_id' => [
'title' => __( 'Second date metadatum', 'tainacan' ),
'description' => __( 'The other metadatum to which this filter will compare values to find if there is an intersection of dates.', 'tainacan' ),
],
'secondary_filter_metadatum_name' => [
'title' => __( 'Second date metadatum', 'tainacan' ),
'description' => __( 'Label of the other metadatum to which this filter will compare values to find if there is an intersection of dates.', 'tainacan' ),
],
'first_comparator' => [
'title' => __( 'First comparator', 'tainacan' ),
'description' => __( 'Comparator to be used for checking the first metadata value.', 'tainacan' ),
],
'second_comparator' => [
'title' => __( 'Second comparator', 'tainacan' ),
'description' => __( 'Comparator to be used for checking the second metadata value.', 'tainacan' ),
],
'accept_date_interval' => [
'title' => __( 'Accept date interval', 'tainacan' ),
'description' => __( 'If checked, the filter will accept date intervals as values.', 'tainacan' ),
]
];
}
/**
* @param \Tainacan\Entities\Filter $filter
* @return array|bool true if is validate or array if has error
*/
public function validate_options(\Tainacan\Entities\Filter $filter) {
if ( !in_array($filter->get_status(), apply_filters('tainacan-status-require-validation', ['publish','future','private'])) )
return true;
$errors = [];
if ( empty($this->get_option('secondary_filter_metadatum_id')) )
$errors['secondary_filter_metadatum_id'] = __('The secondary date metadatum is required.','tainacan');
if ( empty($this->get_option('first_comparator')) )
$errors['first_comparator'] = __('The first comparator is required.','tainacan');
if ( empty($this->get_option('second_comparator')) )
$errors['second_comparator'] = __('The second comparator is required.','tainacan');
if ( empty($this->get_option('accept_date_interval')) )
$errors['accept_date_interval'] = __('The filter should define if it accepts date interval.','tainacan');
return count($errors) > 0 ? $errors : true;
}
}
class Dates_Intersection_Interval_Helper {
use \Tainacan\Traits\Singleton_Instance;
protected function init() {
add_filter( 'tainacan-api-items-tainacan-filter-dates-intersection-filter-arguments', [$this, 'format_filter_arguments']);
}
function format_filter_arguments( $filter_arguments ) {
if (
!isset($filter_arguments['compare']) ||
!isset($filter_arguments['label'])
) {
return $filter_arguments;
}
if (
is_array($filter_arguments['label']) &&
count($filter_arguments['label']) === 2
) {
$filter_arguments['label'] = $filter_arguments['label'][0] . ' - ' . $filter_arguments['label'][1];
}
if (
isset( $filter_arguments['filter'] ) &&
isset( $filter_arguments['filter']['filter_type_options'] ) &&
isset( $filter_arguments['filter']['filter_type_options']['secondary_filter_metadatum_name'] ) &&
!empty( $filter_arguments['filter']['filter_type_options']['secondary_filter_metadatum_name'] )
) {
$filter_arguments['filter']['name'] = $filter_arguments['filter']['name'] . ' - ' . $filter_arguments['filter']['filter_type_options']['secondary_filter_metadatum_name'];
}
return $filter_arguments;
}
}
Dates_Intersection_Interval_Helper::get_instance();

View File

@ -38,10 +38,12 @@ class Filter_Type_Helper {
$this->Tainacan_Filters->register_filter_type('Tainacan\Filter_Types\Selectbox');
$this->Tainacan_Filters->register_filter_type('Tainacan\Filter_Types\Autocomplete');
$this->Tainacan_Filters->register_filter_type('Tainacan\Filter_Types\Date_Interval');
$this->Tainacan_Filters->register_filter_type('Tainacan\Filter_Types\Dates_Intersection');
$this->Tainacan_Filters->register_filter_type('Tainacan\Filter_Types\Numeric_Interval');
$this->Tainacan_Filters->register_filter_type('Tainacan\Filter_Types\TaxonomyTaginput');
$this->Tainacan_Filters->register_filter_type('Tainacan\Filter_Types\TaxonomyCheckbox');
$this->Tainacan_Filters->register_filter_type('Tainacan\Filter_Types\Numeric_List_Interval');
$this->Tainacan_Filters->register_filter_type('Tainacan\Filter_Types\Numerics_Intersection');
// the priority should see less than on function
// `load_admin_page()` of class `Admin` in file /src/views/class-tainacan-admin.php

View File

@ -0,0 +1,410 @@
<template>
<div>
<b-field :addons="false">
<label class="label is-inline">
{{ $i18n.getHelperTitle('tainacan-filter-numeric-interval', 'step') }}<span>&nbsp;*&nbsp;</span>
<span style="font-size: 1.35em;">
<help-button
:title="$i18n.getHelperTitle('tainacan-filter-numeric-interval', 'step')"
:message="$i18n.getHelperMessage('tainacan-filter-numeric-interval', 'step')" />
</span>
</label>
<div
v-if="!showEditStepOptions"
class="is-flex">
<b-select
v-model="step"
name="step_options"
@update:model-value="emitValues()">
<option value="0.001">
0.001
</option>
<option value="0.01">
0.01
</option>
<option value="0.1">
0.1
</option>
<option value="1">
1
</option>
<option value="2">
2
</option>
<option value="5">
5
</option>
<option value="10">
10
</option>
<option value="100">
100
</option>
<option value="1000">
1000
</option>
<option
v-if="step && ![0.001,0.01,0.1,1,2,5,10,100,1000].find( (element) => element == step )"
:value="step">
{{ step }}</option>
</b-select>
<button
class="button is-white is-pulled-right"
:aria-label="$i18n.get('edit')"
@click.prevent="showEditStepOptions = true">
<span
v-tooltip="{
content: $i18n.get('edit'),
autoHide: true,
placement: 'bottom',
popperClass: ['tainacan-tooltip', 'tooltip']
}"
class="icon">
<i class="tainacan-icon tainacan-icon-18px tainacan-icon-edit has-text-secondary" />
</span>
</button>
</div>
<div
v-if="showEditStepOptions"
class="is-flex">
<b-input
v-model="step"
name="max_options"
type="number"
step="1"
@update:model-value="emitValues()" />
<button
class="button is-white is-pulled-right"
@click.prevent="showEditStepOptions = false">
<span
v-tooltip="{
content: $i18n.get('close'),
autoHide: true,
placement: 'bottom',
popperClass: ['tainacan-tooltip', 'tooltip']
}"
class="icon">
<i class="tainacan-icon tainacan-icon-18px tainacan-icon-close has-text-secondary" />
</span>
</button>
</div>
</b-field>
<b-field
:addons="false"
:type="metadataType"
:message="metadataMessage">
<label class="label is-inline">
{{ $i18n.getHelperTitle('tainacan-filter-numerics-intersection', 'secondary_filter_metadatum_id') }}<span :class="metadataType">&nbsp;*&nbsp;</span>
<help-button
:title="$i18n.getHelperTitle('tainacan-filter-numerics-intersection', 'secondary_filter_metadatum_id')"
:message="$i18n.getHelperMessage('tainacan-filter-numerics-intersection', 'secondary_filter_metadatum_id')" />
</label>
<b-select
v-model="secondNumericMetadatumId"
name="numerics_intersect[secondary_filter_metadatum_id]"
:placeholder="$i18n.get('instruction_select_second_numeric_to_compare' )"
:loading="loading"
expanded
@change="onUpdateSecondNumericMetadatumId()"
@focus="clear()">
<option :value="''">
{{ $i18n.get('instruction_select_second_numeric_to_compare' ) }}
</option>
<option
v-for="option in metadata.filter(aMetadatum => aMetadatum.id != filter.metadatumId )"
:key="option.id"
:value="option.id">
{{ option.name }}
</option>
</b-select>
</b-field>
<fieldset
v-if="secondNumericMetadatumId"
class="intersection-explainer-section">
<legend>
<p>
<strong>{{ $i18n.get('info_intersection_explainer') }}</strong>
<span style="font-size: 1.35em;">
<help-button
:title="$i18n.get('label_comparators')"
:message="$i18n.get('info_intersection_rules')" />
</span>
</p>
</legend>
<b-field :addons="false">
<b-select
v-if="showEditFirstComparatorOptions"
v-model="firstComparator"
@update:model-value="emitValues()">
<option
v-for="(comparatorObject, comparatorKey) in comparatorsObject"
:key="comparatorKey"
:value="comparatorKey"
v-html="comparatorObject.symbol + '&nbsp;' + comparatorObject.label" />
</b-select>
<strong
v-else
v-html="comparatorsObject[firstComparator].symbol" />
<p v-if="filter.metadatum">
&nbsp;
<em>{{ filter.metadatum.metadatum_name }}</em>
</p>
<button
v-if="!showEditFirstComparatorOptions"
class="button is-white is-pulled-right"
@click.prevent="showEditFirstComparatorOptions = true">
<span
v-tooltip="{
content: $i18n.get('edit'),
autoHide: true,
placement: 'bottom',
popperClass: ['tainacan-tooltip', 'tooltip']
}"
class="icon">
<i class="tainacan-icon tainacan-icon-18px tainacan-icon-edit has-text-secondary" />
</span>
</button>
<button
v-else
class="button is-white is-pulled-right"
@click.prevent="showEditFirstComparatorOptions = false">
<span
v-tooltip="{
content: $i18n.get('close'),
autoHide: true,
placement: 'bottom',
popperClass: ['tainacan-tooltip', 'tooltip']
}"
class="icon">
<i class="tainacan-icon tainacan-icon-18px tainacan-icon-approved has-text-secondary" />
</span>
</button>
</b-field>
<div class="logic-divider">
<span>{{ $i18n.get('label_and') }}</span>
</div>
<b-field :addons="false">
<b-select
v-if="showEditSecondComparatorOptions"
v-model="secondComparator"
@update:model-value="emitValues()">
<option
v-for="(comparatorObject, comparatorKey) in comparatorsObject"
:key="comparatorKey"
:value="comparatorKey"
v-html="comparatorObject.symbol + '&nbsp;' + comparatorObject.label" />
</b-select>
<strong
v-else
v-html="comparatorsObject[secondComparator].symbol" />
<p>&nbsp;<em>{{ secondNumericMetadatumName }}</em></p>
<button
v-if="!showEditSecondComparatorOptions"
class="button is-white is-pulled-right"
@click.prevent="showEditSecondComparatorOptions = true">
<span
v-tooltip="{
content: $i18n.get('edit'),
autoHide: true,
placement: 'bottom',
popperClass: ['tainacan-tooltip', 'tooltip']
}"
class="icon">
<i class="tainacan-icon tainacan-icon-18px tainacan-icon-edit has-text-secondary" />
</span>
</button>
<button
v-else
class="button is-white is-pulled-right"
@click.prevent="showEditSecondComparatorOptions = false">
<span
v-tooltip="{
content: $i18n.get('close'),
autoHide: true,
placement: 'bottom',
popperClass: ['tainacan-tooltip', 'tooltip']
}"
class="icon">
<i class="tainacan-icon tainacan-icon-18px tainacan-icon-approved has-text-secondary" />
</span>
</button>
</b-field>
</fieldset>
<!-- Much more complicated logic, will be possible if we implement #889 -->
<!-- <b-field
:addons="false"
:label="$i18n.getHelperTitle('tainacan-filter-numerics-intersection', 'accept_numeric_interval')"
style="margin-top: 1.125rem;"
:type="errors && errors['accept_numeric_interval'] != undefined ? 'is-danger' : ''"
:message="errors && errors['accept_numeric_interval'] != undefined ? errors['accept_numeric_interval'] : ''">
&nbsp;
<b-switch
v-model="acceptNumericInterval"
size="is-small"
:true-value="'yes'"
:false-value="'no'"
:native-value="acceptNumericInterval == 'yes' ? 'yes' : 'no'"
name="accept_numeric_interval"
@update:model-value="emitValues()">
<help-button
:title="$i18n.getHelperTitle('tainacan-filter-numerics-intersection', 'accept_numeric_interval')"
:message="$i18n.getHelperMessage('tainacan-filter-numerics-intersection', 'accept_numeric_interval')" />
</b-switch>
</b-field> -->
</div>
</template>
<script>
import { tainacanApi } from '../../../js/axios';
export default {
props: {
filter: Object,
modelValue: Object,
errors: Object
},
emits: [
'update:model-value',
],
data() {
return {
step: [Number, String],
showEditStepOptions: false,
metadata: [],
loading: true,
metadataType: '',
metadataMessage: '',
secondNumericMetadatumId: [Number, String],
secondNumericMetadatumName: String,
firstComparator: String,
secondComparator: String,
comparatorsObject: {},
acceptNumericInterval: String,
showEditFirstComparatorOptions: false,
showEditSecondComparatorOptions: false
}
},
watch: {
errors() {
if ( this.errors && this.errors.secondary_filter_metadatum_id !== '' )
this.setErrorsAttributes( 'is-danger', this.errors.secondary_filter_metadatum_id );
else
this.setErrorsAttributes( '', '' );
}
},
created() {
this.step = this.modelValue && this.modelValue.step ? this.modelValue.step : 1;
this.secondNumericMetadatumId = this.modelValue && this.modelValue.secondary_filter_metadatum_id ? this.modelValue.secondary_filter_metadatum_id : '';
this.secondNumericMetadatumName = this.modelValue && this.modelValue.secondary_filter_metadatum_name ? this.modelValue.secondary_filter_metadatum_name : '';
this.firstComparator = this.modelValue && this.modelValue.first_comparator ? this.modelValue.first_comparator : '>=';
this.secondComparator = this.modelValue && this.modelValue.second_comparator ? this.modelValue.second_comparator : '<=';
this.acceptNumericInterval = this.modelValue && this.modelValue.accept_numeric_interval ? this.modelValue.accept_numeric_interval : 'no';
this.loading = true;
this.fetchMetadata();
this.comparatorsObject = {
'=': {
symbol: '&#61;',
label: this.$i18n.get('is_equal_to')
},
'!=': {
symbol: '&#8800;',
label: this.$i18n.get('is_not_equal_to')
},
'>': {
symbol: '&#62;',
label: this.$i18n.get('greater_than')
},
'>=': {
symbol: '&#8805;',
label: this.$i18n.get('greater_than_or_equal_to')
},
'<': {
symbol: '&#60;',
label: this.$i18n.get('less_than')
},
'<=': {
symbol: '&#8804;',
label: this.$i18n.get('less_than_or_equal_to')
}
};
},
methods: {
async fetchMetadata() {
let endpoint = this.filter.collection_id && this.filter.collection_id !== 'default' ? ( '/collection/' + this.filter.collection_id + '/metadata' ) : '/metadata';
endpoint += '?metaquery[0][key]=metadata_type&metaquery[0][value]=Tainacan\\Metadata_Types\\Numeric&nopaging=1&exclude=' + this.filter.metadatum_id;
return await tainacanApi.get(endpoint)
.then(res => {
this.loading = false;
this.metadata = res.data ? res.data : [];
})
.catch(error => {
this.loading = false;
this.$console.log(error);
});
},
onUpdateSecondNumericMetadatumId() {
const selectedMetadatum = this.metadata.find( aMetadatum => aMetadatum.id == this.secondNumericMetadatumId );
this.secondNumericMetadatumName = selectedMetadatum ? selectedMetadatum.name : '';
this.secondNumericMetadatumId = selectedMetadatum ? selectedMetadatum.id : '';
this.emitValues();
},
emitValues() {
this.$emit('update:model-value', {
step: this.step,
first_comparator: this.firstComparator,
second_comparator: this.secondComparator,
secondary_filter_metadatum_id: this.secondNumericMetadatumId,
secondary_filter_metadatum_name: this.secondNumericMetadatumName,
accept_numeric_interval: this.acceptNumericInterval
});
},
setErrorsAttributes( type, message ) {
this.metadataType = type;
this.metadataMessage = message;
},
clear(){
this.metadataType = '';
this.metadataMessage = '';
},
}
}
</script>
<style lang="scss" scoped>
.intersection-explainer-section {
margin-top: 1.25rem;
padding: 0.75em 0.75em 0.25em 0.75em;
border: 1px solid var(--tainacan-gray1);
legend {
margin: -0.75em 0 0em 0;
background-color: var(--tainacan-background-color);
padding: 5px 5px 5px 0px;
}
.field {
display: flex;
gap: 0.5em;
margin: 0 -0.5em 0.5em 0em;
align-items: center;
strong {
margin-left: 0.75em;
}
}
button {
border-radius: 100em !important;
margin-left: auto;
}
.logic-divider {
display: none;
}
}
</style>

View File

@ -0,0 +1,147 @@
<template>
<div>
<b-numberinput
v-model="valueInit"
:aria-labelledby="'filter-label-id-' + filter.id"
:aria-minus-label="$i18n.get('label_decrease')"
:aria-plus-label="$i18n.get('label_increase')"
size="is-small"
:step="filterTypeOptions.step"
@update:model-value="($event) => { resetPage(); validadeValues($event) }"
/>
<p
v-if="filterTypeOptions.accept_numeric_interval === 'yes'"
style="font-size: 0.75em; margin-bottom: 0.125em;"
class="has-text-centered is-marginless">
{{ $i18n.get('label_until') }}
</p>
<b-numberinput
v-if="filterTypeOptions.accept_numeric_interval === 'yes'"
v-model="valueEnd"
:aria-labelledby="'filter-label-id-' + filter.id"
:aria-minus-label="$i18n.get('label_decrease')"
:aria-plus-label="$i18n.get('label_increase')"
size="is-small"
:step="filterTypeOptions.step"
@update:model-value="($event) => { resetPage(); validadeValues($event) }" />
</div>
</template>
<script>
import { filterTypeMixin } from '../../../js/filter-types-mixin';
export default {
mixins: [ filterTypeMixin ],
emits: [
'input',
],
data(){
return {
valueInit: null,
valueEnd: null
}
},
watch: {
'query': {
handler() {
this.updateSelectedValues();
},
deep: true
}
},
mounted() {
this.updateSelectedValues();
},
methods: {
// only validate if the first value is higher than first
validadeValues: _.debounce( function () {
if ( this.filterTypeOptions.accept_numeric_interval !== 'yes' )
this.valueEnd = this.valueInit;
if (this.valueInit == null || this.valueEnd == null )
return
if (this.valueInit.constructor == Number)
this.valueInit = this.valueInit.valueOf();
if (this.valueEnd.constructor == Number)
this.valueEnd = this.valueEnd.valueOf();
this.valueInit = parseFloat(this.valueInit);
this.valueEnd = parseFloat(this.valueEnd);
if ( isNaN(this.valueInit) || isNaN(this.valueEnd) )
return
if ( this.filterTypeOptions.accept_numeric_interval === 'yes' && this.valueInit > this.valueEnd ) {
this.showErrorMessage();
return;
}
this.emit();
}, 600),
// message for error
showErrorMessage(){
this.$buefy.toast.open({
duration: 3000,
message: this.$i18n.get('info_error_first_value_greater'),
position: 'is-bottom',
type: 'is-danger'
})
},
// emit the operation for listeners
emit() {
let values = [ this.valueInit, this.valueEnd ];
let type = ! Number.isInteger( this.valueInit ) || ! Number.isInteger( this.valueEnd ) ? 'DECIMAL(20,3)' : 'NUMERIC';
if ( this.filterTypeOptions.accept_numeric_interval !== 'yes' ) {
this.$emit('input', {
filter: 'intersection',
type: type,
compare: this.filterTypeOptions.first_comparator,
metadatum_id: this.metadatumId,
collection_id: this.collectionId,
value: this.filterTypeOptions.accept_numeric_interval === 'yes' ? values : values[0]
});
this.$emit('input', {
filter: 'intersection',
type: type,
compare: this.filterTypeOptions.second_comparator,
metadatum_id: this.filterTypeOptions.secondary_filter_metadatum_id,
collection_id: this.collectionId,
value: this.filterTypeOptions.accept_numeric_interval === 'yes' ? values : values[0]
});
} else {
// Much more complicated logic to be implemented in the future. See #889
}
},
updateSelectedValues(){
if ( !this.query || !this.query.metaquery || !Array.isArray( this.query.metaquery ) )
return false;
let index = this.query.metaquery.findIndex(newMetadatum => newMetadatum.key == this.metadatumId );
if ( index >= 0 ) {
let metaquery = this.query.metaquery[ index ];
if ( metaquery.value ) {
if ( Array.isArray(metaquery.value) && metaquery.value.length > 1 ) {
this.valueInit = new Number(metaquery.value[0]);
this.valueEnd = new Number(metaquery.value[1]);
} else {
this.valueInit = new Number(metaquery.value);
}
}
} else {
this.valueInit = null;
this.valueEnd = null;
}
},
}
}
</script>
<style scoped>
.field {
margin-bottom: 0.125em !important;
}
</style>

View File

@ -0,0 +1,141 @@
<?php
namespace Tainacan\Filter_Types;
defined( 'ABSPATH' ) or die( 'No script kiddies please!' );
/**
* Class TainacanFilterType
*/
class Numerics_Intersection extends Filter_Type {
function __construct(){
$this->set_name( __('Numerics Intersection', 'tainacan') );
$this->set_supported_types(['float']);
$this->set_component('tainacan-filter-numerics-intersection');
$this->set_form_component('tainacan-filter-form-numerics-intersection');
$this->set_default_options([
'step' => 1,
'secondary_filter_metadatum_id' => '',
'secondary_filter_metadatum_name' => '',
'first_comparator' => '>=',
'second_comparator' => '<=',
'accept_numeric_interval' => 'no'
]);
$this->set_use_max_options(false);
$this->set_preview_template('
<div>
<div class="b-numberinput field is-grouped">
<p class="control">
<button type="button" class="button is-primary is-small">
<span class="icon is-small">
<i class="mdi mdi-minus"></i>
</span>
</button>
</p>
<div class="control is-small is-clearfix">
<input type="number" step="0.01" class="input is-small" value="6">
</div>
<p class="control">
<button type="button" class="button is-primary is-small">
<span class="icon is-small">
<i class="mdi mdi-plus"></i>
</span>
</button>
</p>
</div>
</div>
');
}
public function get_form_labels(){
return [
'step' => [
'title' => __( 'Step', 'tainacan' ),
'description' => __( 'The amount to be increased or decreased when clicking on the filter control buttons. This also defines whether the input accepts decimal numbers.', 'tainacan' ),
],
'secondary_filter_metadatum_id' => [
'title' => __( 'Second numeric metadatum', 'tainacan' ),
'description' => __( 'The other metadatum to which this filter will compare values to find if there is an intersection of numeric values.', 'tainacan' ),
],
'secondary_filter_metadatum_name' => [
'title' => __( 'Second numeric metadatum', 'tainacan' ),
'description' => __( 'Label of the other metadatum to which this filter will compare values to find if there is an intersection of numeric values.', 'tainacan' ),
],
'first_comparator' => [
'title' => __( 'First comparator', 'tainacan' ),
'description' => __( 'Comparator to be used for checking the first metadata value.', 'tainacan' ),
],
'second_comparator' => [
'title' => __( 'Second comparator', 'tainacan' ),
'description' => __( 'Comparator to be used for checking the second metadata value.', 'tainacan' ),
],
'accept_numeric_interval' => [
'title' => __( 'Accept numeric interval', 'tainacan' ),
'description' => __( 'If checked, the filter will accept numeric intervals as values.', 'tainacan' ),
]
];
}
/**
* @param \Tainacan\Entities\Filter $filter
* @return array|bool true if is validate or array if has error
*/
public function validate_options(\Tainacan\Entities\Filter $filter) {
if ( !in_array($filter->get_status(), apply_filters('tainacan-status-require-validation', ['publish','future','private'])) )
return true;
$errors = [];
if ( empty($this->get_option('secondary_filter_metadatum_id')) )
$errors['secondary_filter_metadatum_id'] = __('The secondary numeric metadatum is required.','tainacan');
if ( empty($this->get_option('first_comparator')) )
$errors['first_comparator'] = __('The first comparator is required.','tainacan');
if ( empty($this->get_option('second_comparator')) )
$errors['second_comparator'] = __('The second comparator is required.','tainacan');
if ( empty($this->get_option('accept_numeric_interval')) )
$errors['accept_numeric_interval'] = __('The filter should define if it accepts a numeric interval.','tainacan');
return count($errors) > 0 ? $errors : true;
}
}
class Numerics_Intersection_Interval_Helper {
use \Tainacan\Traits\Singleton_Instance;
protected function init() {
add_filter( 'tainacan-api-items-tainacan-filter-numerics-intersection-filter-arguments', [$this, 'format_filter_arguments']);
}
function format_filter_arguments( $filter_arguments ) {
if (
!isset($filter_arguments['compare']) ||
!isset($filter_arguments['label'])
) {
return $filter_arguments;
}
if (
is_array($filter_arguments['label']) &&
count($filter_arguments['label']) === 2
) {
$filter_arguments['label'] = $filter_arguments['label'][0] . ' - ' . $filter_arguments['label'][1];
}
if (
isset( $filter_arguments['filter'] ) &&
isset( $filter_arguments['filter']['filter_type_options'] ) &&
isset( $filter_arguments['filter']['filter_type_options']['secondary_filter_metadatum_name'] ) &&
!empty( $filter_arguments['filter']['filter_type_options']['secondary_filter_metadatum_name'] )
) {
$filter_arguments['filter']['name'] = $filter_arguments['filter']['name'] . ' - ' . $filter_arguments['filter']['filter_type_options']['secondary_filter_metadatum_name'];
}
return $filter_arguments;
}
}
Numerics_Intersection_Interval_Helper::get_instance();

View File

@ -100,8 +100,10 @@
TainacanFilterTaxonomyCheckbox: defineAsyncComponent(() => import('./taxonomy/TainacanFilterCheckbox.vue')),
TainacanFilterTaxonomyTaginput: defineAsyncComponent(() => import('./taxonomy/TainacanFilterTaginput.vue')),
TainacanFilterDateInterval: defineAsyncComponent(() => import('./date-interval/TainacanFilterDateInterval.vue')),
TainacanFilterDatesIntersection: defineAsyncComponent(() => import('./dates-intersection/TainacanFilterDatesIntersection.vue')),
TainacanFilterNumericInterval: defineAsyncComponent(() => import('./numeric-interval/TainacanFilterNumericInterval.vue')),
TainacanFilterNumericListInterval: defineAsyncComponent(() => import('./numeric-list-interval/TainacanFilterNumericListInterval.vue'))
TainacanFilterNumericListInterval: defineAsyncComponent(() => import('./numeric-list-interval/TainacanFilterNumericListInterval.vue')),
TainacanFilterNumericsIntersection: defineAsyncComponent(() => import('./numerics-intersection/TainacanFilterNumericsIntersection.vue'))
},
props: {
filter: Object,

View File

@ -21,52 +21,39 @@ export const setAdvancedSearchQuery = (state, advancedSearchQuery) => {
export const addMetaQuery = ( state, filter ) => {
state.postquery.metaquery = ( ! state.postquery.metaquery || state.postquery.metaquery.length == undefined ) ? [] : state.postquery.metaquery;
let index = state.postquery.metaquery.findIndex( item => item.key === filter.metadatum_id);
if ( index >= 0 ) {
Object.assign(
state.postquery.metaquery,
{
[index]: {
key: filter.metadatum_id,
value: filter.value,
compare: filter.compare,
type: filter.type
}
}
);
} else {
state.postquery.metaquery.push({
key: filter.metadatum_id,
value: filter.value,
compare: filter.compare,
type: filter.type
});
let metaquery = {
key: filter.metadatum_id,
value: filter.value
}
if ( filter.compare )
metaquery.compare = filter.compare;
if ( filter.type )
metaquery.type = filter.type;
if ( filter.secondary )
metaquery.secondary = filter.secondary;
let index = state.postquery.metaquery.findIndex( item => item.key === filter.metadatum_id);
if ( index >= 0 )
Object.assign( state.postquery.metaquery, { [index]: metaquery } );
else
state.postquery.metaquery.push(metaquery);
};
export const addTaxQuery = ( state, filter ) => {
state.postquery.taxquery = ( ! state.postquery.taxquery || state.postquery.taxquery.length == undefined ) ? [] : state.postquery.taxquery;
let index = state.postquery.taxquery.findIndex( item => item.taxonomy === filter.taxonomy);
if ( index >= 0 ) {
Object.assign(
state.postquery.taxquery,
{
[index]: {
taxonomy: filter.taxonomy,
terms: filter.terms,
compare: filter.compare
}
}
);
} else {
state.postquery.taxquery.push({
taxonomy: filter.taxonomy,
terms: filter.terms,
compare: filter.compare
});
let taxquery = {
taxonomy: filter.taxonomy,
terms: filter.terms
}
if ( filter.compare )
taxquery.compare = filter.compare;
let index = state.postquery.taxquery.findIndex( item => item.taxonomy === filter.taxonomy);
if ( index >= 0 )
Object.assign( state.postquery.taxquery, { [index]: taxquery } );
else
state.postquery.taxquery.push(taxquery);
};
export const addFetchOnly = ( state, metadatum ) => {

View File

@ -976,7 +976,7 @@ export default {
}
&:not(.available-metadata-area){
margin-right: var(--tainacan-one-column);
margin-right: 30px;
flex-grow: 2;
@media screen and (max-width: 769px) {

View File

@ -529,6 +529,7 @@ return apply_filters( 'tainacan-i18n', [
'label_view_all_%s_collections' => __( 'View all %s collections', 'tainacan' ),
'label_view_collections_list' => __( 'View collections list', 'tainacan' ),
'label_comparator' => __( 'Comparator', 'tainacan' ),
'label_comparators' => __( 'Comparators', 'tainacan' ),
'label_table_of_items' => __( 'Table of Items', 'tainacan' ),
'label_create_another_item' => __( 'Create another item', 'tainacan' ),
'label_recent_collections' => __( 'Recent Collections', 'tainacan' ),
@ -788,6 +789,8 @@ return apply_filters( 'tainacan-i18n', [
'instruction_click_to_add_a_point' => __( 'Drag to reposition or click to insert a marker', 'tainacan' ),
'instruction_select_geocoordinate_metadatum' => __( 'Select a geocoordinate metadatum', 'tainacan' ),
'instruction_multiple_terms_insertion' => __( 'Type or paste here a list of names using a separator to create multiple terms at once.', 'tainacan' ),
'instruction_select_second_date_to_compare' => __( 'Select the second date metadatum', 'tainacan' ),
'instruction_select_second_numeric_to_compare' => __( 'Select the second numeric metadatum', 'tainacan' ),
// Info. Other feedback to user.
'info_items_tab_all' => __( 'Every item, except by those sent to trash.', 'tainacan' ),
@ -1064,6 +1067,8 @@ return apply_filters( 'tainacan-i18n', [
'info_terms_creation_failed_due_to_value_%s' => __( 'Terms creation failed due to value: %s.', 'tainacan' ),
'info_terms_creation_failed_due_to_values_%s' => __( 'Terms creation failed due to values: %s.', 'tainacan' ),
'info_autodraft_updated' => __( 'Autodraft updated. Please create the item to keep your changes.', 'tainacan' ),
'info_intersection_explainer' => __( 'Will show items if the selected value is:', 'tainacan' ),
'info_intersection_rules' => __( 'The value must match both rules to appear in the filter.', 'tainacan' ),
/* Activity actions */
'action_update-metadata-value' => __( 'Item Metadata Value Updates', 'tainacan'),

View File

@ -97,7 +97,7 @@ class Filters extends TAINACAN_UnitTestCase {
$Tainacan_Filters = \Tainacan\Repositories\Filters::get_instance();
$all_filter_types = $Tainacan_Filters->fetch_filter_types();
$this->assertEquals( 11, count( $all_filter_types ) );
$this->assertEquals( 13, count( $all_filter_types ) );
$float_filters = $Tainacan_Filters->fetch_supported_filter_types('float');
$this->assertTrue( count( $float_filters ) > 0 );