Merge branch 'release/0.19.3'

This commit is contained in:
vnmedeiros 2022-11-30 10:51:13 -03:00
commit 8da474ce46
37 changed files with 1107 additions and 204 deletions

View File

@ -333,9 +333,9 @@ class REST_Items_Controller extends REST_Controller {
if (! $item instanceof Entities\Item) {
return new \WP_REST_Response([
'error_message' => __('An item with this ID was not found', 'tainacan' ),
'item_id' => $item_id
], 400);
'error_message' => __('An item with this ID was not found', 'tainacan' ),
'item_id' => $item_id
], 400);
}
$response = $this->prepare_item_for_response($item, $request);
@ -355,9 +355,9 @@ class REST_Items_Controller extends REST_Controller {
if (! $item instanceof Entities\Item) {
return new \WP_REST_Response([
'error_message' => __('An item with this ID was not found', 'tainacan' ),
'item_id' => $item_id
], 400);
'error_message' => __('An item with this ID was not found', 'tainacan' ),
'item_id' => $item_id
], 400);
}
$args = $this->prepare_filters($request);
@ -410,7 +410,6 @@ class REST_Items_Controller extends REST_Controller {
return $rest_response;
}
/**
* @param array $args array of query arguments.
*
@ -850,10 +849,10 @@ class REST_Items_Controller extends REST_Controller {
$collection = $this->collections_repository->fetch($request['collection_id']);
if ($collection instanceof Entities\Collection) {
return current_user_can($collection->get_items_capabilities()->edit_posts);
}
return current_user_can($collection->get_items_capabilities()->edit_posts);
}
return false;
return false;
}
/**
@ -869,9 +868,9 @@ class REST_Items_Controller extends REST_Controller {
if (! $item instanceof Entities\Item) {
return new \WP_REST_Response([
'error_message' => __('An item with this ID was not found', 'tainacan' ),
'item_id' => $item_id
], 400);
'error_message' => __('An item with this ID was not found', 'tainacan' ),
'item_id' => $item_id
], 400);
}
if($permanently == true) {

View File

@ -188,7 +188,8 @@ class Media {
finfo_close( $finfo );
return $mime_type;
}
return '';
$filetypes = \wp_check_filetype($filename);
return isset($filetypes['type']) && $filetypes['type'] != false ? $filetypes['type'] : '';
}
/**

View File

@ -13,13 +13,13 @@ class Private_Files {
public $dir_separator;
public static function get_instance() {
if(!isset(self::$instance)) {
self::$instance = new self();
}
public static function get_instance() {
if(!isset(self::$instance)) {
self::$instance = new self();
}
return self::$instance;
}
return self::$instance;
}
protected function __construct() {

View File

@ -143,13 +143,13 @@ class Items extends Repository {
'title' => __( 'Thumbnail', 'tainacan' ),
'description' => __( 'Squared reduced-size version of a picture that helps recognizing and organizing files', 'tainacan' )
],
'comment_status' => [
'map' => 'comment_status',
'title' => __( 'Comment Status', 'tainacan' ),
'type' => 'string',
'description' => __( 'Item comment status: "open" means comments are allowed, "closed" means comments are not allowed.', 'tainacan' ),
'default' => get_default_comment_status(Entities\Collection::get_post_type()),
'validation' => v::optional(v::stringType()->in( [ 'open', 'closed' ] )),
'comment_status' => [
'map' => 'comment_status',
'title' => __( 'Comment Status', 'tainacan' ),
'type' => 'string',
'description' => __( 'Item comment status: "open" means comments are allowed, "closed" means comments are not allowed.', 'tainacan' ),
'default' => get_default_comment_status(Entities\Collection::get_post_type()),
'validation' => v::optional(v::stringType()->in( [ 'open', 'closed' ] )),
],
] );
}
@ -356,6 +356,10 @@ class Items extends Repository {
$args = $this->parse_relationship_metaquery($args);
}
if ( !defined('TAINACAN_DISABLE_CORE_METADATA_ON_ADVANCED_SEARCH') || false === TAINACAN_DISABLE_CORE_METADATA_ON_ADVANCED_SEARCH ) {
$args = $this->parse_core_metadata_for_advanced_search($args, $collections_objects);
}
$args = apply_filters( 'tainacan-fetch-args', $args, 'items' );
$should_filter = is_user_logged_in() && sizeof($cpt) > 1;
@ -757,4 +761,76 @@ class Items extends Repository {
return $where;
}
/**
* checks if there is `tainacan_core_title` or `tainacan_core_description` as a key for a meta_query,
* and replaces the ids of the metadata referring to `title_core` and `description_core`.
*
* @param array $args WP_Query args
* @param array $collections Array \Taainacan\Entities\Collection
*
* @return \WP_Query|Array;
*/
private function parse_core_metadata_for_advanced_search($args, $collections = [])
{
if (
isset($args["meta_query"]) &&
!empty($args["meta_query"]) &&
is_array($args["meta_query"])
) {
$core_title_meta_query = [];
$core_description_meta_query = [];
foreach ($args["meta_query"] as $key => $meta_query) {
// Finds a special key value, that should represent all core title or core description
if (
isset($meta_query["key"]) &&
($meta_query["key"] === "tainacan_core_title" ||
$meta_query["key"] === "tainacan_core_description")
) {
// Gets every collection to build the OR query
if( empty($collections) ) {
$collections = \Tainacan\Repositories\Collections::get_instance()->fetch(
[],
"OBJECT"
);
}
foreach ($collections as $collection) {
if ($meta_query["key"] === "tainacan_core_title") {
$title_meta = $collection->get_core_title_metadatum();
// Builds inner meta_queries for each collection, using the same settings of the special one
$core_title_meta_query[] = [
"key" => $title_meta->get_id(),
"compare" => $meta_query["compare"],
"value" => $meta_query["value"],
];
} elseif (
$meta_query["key"] === "tainacan_core_description"
) {
$description_meta = $collection->get_core_description_metadatum();
// Builds inner meta_queries for each collection, using the same settings of the special one
$core_description_meta_query[] = [
"key" => $description_meta->get_id(),
"compare" => $meta_query["compare"],
"value" => $meta_query["value"],
];
}
}
unset($args["meta_query"][$key]);
}
}
if (count($core_title_meta_query)) {
$core_title_meta_query["relation"] = "OR";
$args["meta_query"][] = $core_title_meta_query;
}
if (count($core_description_meta_query)) {
$core_description_meta_query["relation"] = "OR";
$args["meta_query"][] = $core_description_meta_query;
}
}
return $args;
}
}

View File

@ -4,7 +4,7 @@ Tags: museums, libraries, archives, GLAM, collections, repository
Requires at least: 5.0
Tested up to: 6.1
Requires PHP: 5.6
Stable tag: 0.19.2
Stable tag: 0.19.3
License: GPLv2 or later
License URI: http://www.gnu.org/licenses/gpl-3.0.html

View File

@ -4,17 +4,17 @@ Plugin Name: Tainacan
Plugin URI: https://tainacan.org/
Description: Open source, powerful and flexible repository platform for WordPress. Manage and publish you digital collections as easily as publishing a post to your blog, while having all the tools of a professional repository platform.
Author: Tainacan.org
Version: 0.19.2
Version: 0.19.3
Requires at least: 5.0
Tested up to: 6.1
Requires PHP: 5.6
Stable tag: 0.19.2
Stable tag: 0.19.3
Text Domain: tainacan
License: GPLv2 or later
License URI: http://www.gnu.org/licenses/gpl-3.0.html
*/
const TAINACAN_VERSION = '0.19.2';
const TAINACAN_VERSION = '0.19.3';
defined( 'ABSPATH' ) or die( 'No script kiddies please!' );
$TAINACAN_BASE_URL = plugins_url('', __FILE__);

View File

@ -3,7 +3,7 @@
style="min-height: initial; position: relative"
class="tainacan-cards-container">
<ul
v-if="collections.length <= 0 && !isLoading && $userCaps.hasCapability('tnc_rep_edit_collections')"
v-if="!$adminOptions.hideHomeCollectionCreateNewButton && collections.length <= 0 && !isLoading && $userCaps.hasCapability('tnc_rep_edit_collections')"
class="new-collection-menu">
<li>
<router-link
@ -74,7 +74,7 @@
</li>
</ul>
<ul v-if="collections.length > 0 && !isLoading">
<li v-if="$userCaps.hasCapability('tnc_rep_edit_collections')">
<li v-if="!$adminOptions.hideHomeCollectionCreateNewButton && $userCaps.hasCapability('tnc_rep_edit_collections')">
<router-link
tag="a"
:to="$routerHelper.getNewCollectionPath()"
@ -147,7 +147,7 @@
class="tainacan-card"
:class="{ 'always-visible-collections': $adminOptions.homeCollectionsPerPage }">
<ul class="menu-list">
<li v-if="!$adminOptions.hideHomeCollectionItemsButton">
<li>
<router-link
tag="a"
:to="{ path: $routerHelper.getCollectionItemsPath(collection.id, '') }"

View File

@ -18,7 +18,7 @@
<span
style="margin-left: 10px"
v-if="totalPages > 1 && allItemsOnPageSelected && items.length > 1">
v-if="totalPages > 1 && allItemsOnPageSelected && Array.isArray(items) && items.length > 1">
<b-checkbox
v-model="isAllItemsSelected">
{{ $i18n.getWithVariables('label_select_all_%s_items', [totalItems]) }}
@ -56,7 +56,7 @@
<b-dropdown
:mobile-modal="true"
position="is-bottom-left"
v-if="items.length > 0"
v-if="Array.isArray(items) && items.length > 0"
:disabled="selectedItems.length <= 1"
id="bulk-actions-dropdown"
aria-role="list"
@ -1405,9 +1405,11 @@ export default {
return this.selectedItems.length > 0;
},
allItemsOnPageSelected() {
for (var i = 0; i < this.items.length; i++){
if (this.selectedItems.indexOf(this.items[i].id) === -1)
return false;
if ( this.items && Array.isArray(this.items) ) {
for (let i = 0; i < this.items.length; i++){
if (this.selectedItems.indexOf(this.items[i].id) === -1)
return false;
}
}
return true;
},

View File

@ -11,6 +11,8 @@ defined( 'ABSPATH' ) or die( 'No script kiddies please!' );
*/
class Date extends Metadata_Type {
private $format;
function __construct() {
// call metadatum type constructor
parent::__construct();
@ -26,17 +28,17 @@ class Date extends Metadata_Type {
</div>
');
$this->output_date_format = get_option('date_format');
$this->format = 'Y-m-d';
}
public function validate( Item_Metadata_Entity $item_metadata) {
$value = $item_metadata->get_value();
$format = 'Y-m-d';
if (is_array($value)) {
foreach ($value as $date_value) {
if(!empty($date_value)) {
$d = \DateTime::createFromFormat($format, $date_value);
if (!$d || $d->format($format) !== $date_value) {
$d = \DateTime::createFromFormat($this->format, $date_value);
if (!$d || $d->format($this->format) !== $date_value) {
$this->add_error($this->format_error_msg($date_value));
return false;
}
@ -45,8 +47,8 @@ class Date extends Metadata_Type {
return true;
}
$d = \DateTime::createFromFormat($format, $value);
if (!$d || $d->format($format) !== $value) {
$d = \DateTime::createFromFormat($this->format, $value);
if (!$d || $d->format($this->format) !== $value) {
$this->add_error($this->format_error_msg($value));
return false;
}
@ -92,7 +94,8 @@ class Date extends Metadata_Type {
private function format_error_msg($value) {
return sprintf(
__('Invalid date format. Expected format is MM/DD/YYYY, got %s.', 'tainacan'),
__('Invalid date format. Expected format is %s, got %s.', 'tainacan'),
$this->format,
$value
);
}

View File

@ -0,0 +1,279 @@
<template>
<div
aria-labelledby="collection-creation-title"
autofocus
role="dialog"
tabindex="-1"
aria-modal
class="tainacan-modal-content"
style="width: auto"
ref="collectionCreationModal">
<header class="tainacan-modal-title">
<h2
id="collection-creation-title"
v-if="selectedEstrategy == 'mappers'">
{{ $i18n.get('label_create_collection_from_mapper') }}
</h2>
<h2
id="collection-creation-title"
v-else-if="selectedEstrategy == 'presets'">
{{ $i18n.get('label_create_collection_from_preset') }}
</h2>
<h2
id="collection-creation-title"
v-else>
{{ $i18n.get('label_create_collection') }}
</h2>
<a
@click="selectedEstrategy = hasPresetsHook ? undefined : 'mappers'"
v-if="(hasPresetsHook && selectedEstrategy != undefined) || (!hasPresetsHook && selectedEstrategy == 'mappers')"
class="back-link">
{{ $i18n.get('back') }}
</a>
<hr>
</header>
<section class="tainacan-form">
<div
v-if="selectedEstrategy == undefined"
class="collection-creation-options-container">
<button
class="collection-creation-option"
aria-role="listitem"
@click="selectedEstrategy = 'mappers'">
<h3>{{ $i18n.get('label_from_a_mapper') }}</h3>
<p>{{ $i18n.get('info_create_collection_from_mapper') }}</p>
</button>
<button
class="collection-creation-option"
aria-role="listitem"
@click="selectedEstrategy = 'presets'">
<h3>{{ $i18n.get('label_using_a_preset') }}</h3>
<p>{{ $i18n.get('info_create_collection_from_preset') }}</p>
</button>
</div>
<div
v-if="selectedEstrategy == 'mappers'"
class="collection-creation-options-container"
role="list">
<button
class="collection-creation-option"
@click="$router.push($routerHelper.getNewMappedCollectionPath(metadatumMapper.slug)); $parent.close();"
:key="metadatumMapper.slug"
v-for="metadatumMapper in metadatumMappers"
v-if="metadatumMapper.metadata != false"
aria-role="listitem">
<h3>{{ metadatumMapper.name }}</h3>
<p>{{ metadatumMapper.description }}</p>
</button>
</div>
<div
v-if="selectedEstrategy == 'presets'"
class="collection-creation-options-container"
role="list">
<button
class="collection-creation-option"
@click="onNewCollectionPreset(collectionPreset)"
:key="collectionPreset.slug"
v-for="collectionPreset in getPresetsHook"
aria-role="listitem">
<h3>{{ collectionPreset.name }}</h3>
<p>{{ collectionPreset.description }}</p>
</button>
</div>
<b-loading
:is-full-page="false"
:active.sync="isLoadingMetadatumMappers"
:can-cancel="false"/>
<b-loading
:is-full-page="false"
:active.sync="isCreatingCollectionPreset"
:can-cancel="false"/>
<footer class="field is-grouped form-submit">
<div class="control">
<button
class="button is-outlined"
type="button"
@click="$parent.close()">{{ $i18n.get('close') }}</button>
</div>
</footer>
</section>
</div>
</template>
<script>
import { mapActions, mapGetters } from 'vuex';
import axios from 'axios';
import { tainacanErrorHandler } from '../../js/axios';
export default {
name: 'CollectionCreationModal',
data(){
return {
selectedEstrategy: 'mappers',
isLoadingMetadatumMappers: true,
collectionPresets: [],
isCreatingCollectionPreset: false
}
},
computed: {
metadatumMappers() {
return this.getMetadatumMappers();
},
hasPresetsHook() {
if (wp !== undefined && wp.hooks !== undefined)
return wp.hooks.hasFilter(`tainacan_collections_presets`);
return false;
},
getPresetsHook() {
if (wp !== undefined && wp.hooks !== undefined)
return wp.hooks.applyFilters(`tainacan_collections_presets`, this.collectionPresets);
return this.collectionPresets;
},
},
watch: {
hasPresetsHook: {
handler() {
this.selectedEstrategy = this.hasPresetsHook ? undefined : 'mappers';
},
immediate: true
}
},
mounted() {
this.isLoadingMetadatumTypes = true;
this.fetchMetadatumMappers()
.then(() => {
this.isLoadingMetadatumMappers = false;
})
.catch(() => {
this.isLoadingMetadatumMappers = false;
});
if (this.$refs.collectionCreationModal)
this.$refs.collectionCreationModal.focus()
},
methods: {
...mapActions('metadata', [
'fetchMetadatumMappers'
]),
...mapGetters('metadata', [
'getMetadatumMappers'
]),
onNewCollectionPreset(collectionPreset) {
this.isCreatingCollectionPreset = true;
axios.post(collectionPreset.endpoint)
.then(() => {
const successMessage = typeof collectionPreset.onSuccess === 'function' ? collectionPreset.onSuccess() : this.$i18n.get('label_preset_success');
this.$buefy.snackbar.open({
message: successMessage,
type: 'is-success',
position: 'is-bottom-right',
pauseOnHover: true,
duration: 3500,
queue: false
});
this.isCreatingCollectionPreset = false;
this.$router.push(this.$routerHelper.getCollectionsPath());
this.$parent.close();
})
.catch((error) =>{
if (typeof collectionPreset.onError === 'function') {
const errorMessage = collectionPreset.onError();
this.$buefy.snackbar.open({
message: errorMessage,
type: 'is-danger',
position: 'is-bottom-right',
pauseOnHover: true,
duration: 3500,
queue: false
});
} else {
tainacanErrorHandler(error);
}
this.isCreatingCollectionPreset = false;
});
}
}
}
</script>
<style lang="scss" scoped>
.tainacan-modal-title {
margin-bottom: 24px;
h2 {
margin-bottom: 0;
}
.back-link {
color: var(--tainacan-secondary);
cursor: pointer;
}
}
.collection-creation-options-container {
display: flex;
flex-wrap: wrap;
gap: 24px;
p {
font-size: 1em;
color: var(--tainacan-gray5);
padding: 0em 1.25em;
margin-top: 0.75em;
margin-bottom: 0;
}
.collection-creation-option {
border: 1px solid var(--tainacan-input-border-color);
background-color: var(--tainacan-background-color);
text-align: left;
padding: 15px;
cursor: pointer;
flex-basis: calc(50% - 12px);
flex-grow: 1;
font-size: 1em;
transition: border 0.3s ease;
@media screen and (max-width: 768px) {
max-width: 100%;
margin: 12px;
}
h3 {
color: var(--tainacan-heading-color);
font-size: 1em !important;
font-weight: 500;
padding: 0em 0.5em;
margin: 0;
}
p {
font-size: 0.75em;
color: var(--tainacan-gray5);
padding: 0em 0.5em;
margin-bottom: 0;
}
&:hover {
border: 1px solid var(--tainacan-gray5);
}
}
}
</style>

View File

@ -27,10 +27,15 @@
tag="a"
to="/"
:aria-label="$i18n.get('label_plugin_home_page')">
<img
class="tainacan-logo"
alt="Tainacan Logo"
:src="logoHeader">
<h1>
<img
class="tainacan-logo"
alt="Tainacan Logo"
:src="logoHeader">
<span
v-if="$adminOptions.tainacanHeaderExtraLabel"
v-text="$adminOptions.tainacanHeaderExtraLabel" />
</h1>
</router-link>
</div>
</div>
@ -198,11 +203,25 @@
}
.logo-area {
height: $header-height;
width: 10em;
min-width: 10em;
cursor: pointer;
&:focus {
box-shadow: none;
h1 {
display: flex;
align-items: center;
white-space: nowrap;
margin: 0px;
span {
color: var(--tainacan-blue5);
text-decoration-color: var(--tainacan-blue5);
text-transform: uppercase;
overflow: hidden;
text-overflow: ellipsis;
font-weight: bold;
font-size: 1.25rem;
margin: 0px 0.5rem 0px 0.75rem;
}
}
.tainacan-logo {
height: 1.5em;

View File

@ -2,7 +2,7 @@
<div
id="tainacan-repository-subheader"
class="level secondary-page"
:class="{'is-menu-compressed': isMenuCompressed, 'is-repository-level' : isRepositoryLevel}">
:class="{'is-menu-compressed': isMenuCompressed, 'is-menu-hidden': $adminOptions.hidePrimaryMenu, 'is-repository-level' : isRepositoryLevel}">
<div
v-if="$adminOptions.hideCollectionSubheader"
@ -198,6 +198,9 @@ export default {
&.is-menu-compressed {
padding-left: calc((var(--tainacan-one-column) - 2.083333333px) + 50px);
}
&.is-menu-hidden {
padding-left: calc(var(--tainacan-one-column) - 2.083333333px) !important;
}
h1 {
font-size: 1.125em;

View File

@ -284,9 +284,55 @@
}).then((resp) => {
resp.request
.then((metadata) => {
this.metadataAsArray = JSON.parse(JSON.stringify(metadata));
// In repository level, if set, we add fake options to search on every title and description
if (this.isRepositoryLevel && tainacan_plugin.tainacan_enable_core_metadata_on_advanced_search == true) {
this.metadataAsArray.unshift({
collection_id: 'default',
id: 'tainacan_core_description',
metadata_section_id: 'default_section',
metadata_type: 'Tainacan\\Metadata_Types\\Core_Description',
metadata_type_object: {
className: "Tainacan\\Metadata_Types\\Core_Description",
component: "tainacan-textarea",
core: true,
errors: null,
form_component: "tainacan-form-textarea",
name: this.$i18n.get('label_core_description'),
},
metadata_type_options: [],
name: this.$i18n.get('label_core_description'),
parent: 0,
repository_level: null,
slug: 'tainacan-core-description'
});
this.metadataAsArray.unshift({
collection_id: 'default',
id: 'tainacan_core_title',
metadata_section_id: 'default_section',
metadata_type: 'Tainacan\\Metadata_Types\\Core_Title',
metadata_type_object: {
className: "Tainacan\\Metadata_Types\\Core_Title",
component: "tainacan-text",
core: true,
errors: null,
form_component: "tainacan-form-text",
name: this.$i18n.get('label_core_title'),
},
metadata_type_options: [],
name: this.$i18n.get('label_core_title'),
parent: 0,
repository_level: null,
slug: 'tainacan-core-title'
});
}
// We create and object keyed by IDs to easily match the query params,
// but keep an array version to use the order in the select
this.metadataAsArray = JSON.parse(JSON.stringify(metadata));
metadata.forEach(metadatum => {
this.metadataAsObject[metadatum.id] = metadatum;
});
@ -591,7 +637,7 @@
#advanced-search-container {
width: calc(100% - (2 * var(--tainacan-one-column)));
margin: 0 var(--tainacan-one-column) 0.875em;
background: var(--tainacan-input-background-color);
background: var(--tainacan-background-color);
border: 1px solid var(--tainacan-input-border-color);
border-radius: 1px;
transition: height 0.2s ease;

View File

@ -567,11 +567,13 @@ AdminOptionsHelperPlugin.install = function (Vue, options = {}) {
* hideHomeCollectionFiltersButton
* hideHomeCollectionActivitiesButton
* hideHomeCollectionThemeCollectionButton
* hideHomeCollectionCreateNewButton
* showHomeCollectionCreateItemButton // Default is false
* homeCollectionsPerPage // Default is 9
* homeCollectionsOrderBy // Default is 'modified'
* homeCollectionsOrder // Default is 'desc'
* hideTainacanHeader
* tainacanHeaderExtraLabel // Adds a textual label aside the Tainacan Logo.
* hideTainacanHeaderHomeButton
* hideTainacanHeaderSearchInput
* hideTainacanHeaderAdvancedSearch
@ -593,6 +595,8 @@ AdminOptionsHelperPlugin.install = function (Vue, options = {}) {
* hideRepositorySubheaderViewCollectionsButton
* hideRepositorySubheaderExportButton
* hideCollectionSubheader
* hideCollectionsListCreationDropdown
* hideItemsListPageTitle
* hideItemsListMultipleSelection

View File

@ -9,8 +9,8 @@ const i18nGet = function (key) {
return (string !== undefined && string !== null && string !== '' ) ? string : "ERROR: Invalid i18n key!";
};
const tainacanErrorHandler = function(error) {
console.log(error)
export const tainacanErrorHandler = function(error) {
console.error(error)
if (error.response && error.response.status) {
// The request was made and the server responded with a status code
// that falls out of the range of 2xx
@ -65,10 +65,10 @@ const tainacanErrorHandler = function(error) {
// The request was made but no response was received
// `error.request` is an instance of XMLHttpRequest in the browser and an instance of
// http.ClientRequest in node.js
console.log('Tainacan Error Handler: ', error.request);
console.error('Tainacan Error Handler: ', error.request);
} else {
// Something happened in setting up the request that triggered an Error
console.log('Tainacan Error Handler: ', error.message);
console.error('Tainacan Error Handler: ', error.message);
}
return Promise.reject(error);
}
@ -80,6 +80,11 @@ export const tainacan = axios.create({
if (tainacan_plugin.nonce) {
tainacan.defaults.headers.common['X-WP-Nonce'] = tainacan_plugin.nonce;
}
if (tainacan_plugin.admin_request_options) {
Object.keys(tainacan_plugin.admin_request_options).forEach(requestOption => {
tainacan.defaults.headers[requestOption] = tainacan_plugin.admin_request_options[requestOption];
});
}
tainacan.interceptors.response.use(
(response) => response,
(error) => tainacanErrorHandler(error)
@ -101,4 +106,4 @@ export const CancelToken = axios.CancelToken;
export const isCancel = axios.isCancel;
export const all = axios.all;
export default { tainacan, wp, CancelToken, isCancel, all};
export default { tainacan, wp, CancelToken, isCancel, all, tainacanErrorHandler};

View File

@ -298,6 +298,9 @@ export default {
setTotalItems(totalItems) {
this.$store.dispatch('search/setTotalItems', totalItems);
},
setSentenceMode(sentenceMode) {
this.$store.dispatch('search/setSentenceMode', sentenceMode);
},
setSearchQuery(searchQuery) {
this.$store.dispatch('search/setSearchQuery', searchQuery);
this.updateURLQueries();

View File

@ -118,6 +118,11 @@ export const setSearchQuery = ({ commit }, searchQuery ) => {
commit('setSearchQuery', searchQuery );
};
// Set sentence mode
export const setSentenceMode = ({ commit }, sentenceMode ) => {
commit('setSentenceMode', sentenceMode );
};
// Set ViewMode (view_mode)
export const setViewMode = ({ commit }, viewMode ) => {
commit('setViewMode', viewMode );

View File

@ -49,6 +49,10 @@ export const getSearchQuery = state => {
return state.postquery.search || state.postquery.s;
};
export const getSentenceMode = state => {
return state.postquery.sentence;
};
export const getStatus = state => {
return state.postquery.status;
};

View File

@ -147,6 +147,15 @@ export const setSearchQuery = ( state, searchQuery ) => {
delete state.postquery.s;
};
export const setSentenceMode = ( state, sentenceMode ) => {
if (sentenceMode == true)
Vue.set(state.postquery, 'sentence', sentenceMode);
else {
Vue.set(state.postquery, 'sentence', sentenceMode); // Needed to trigger getter
delete state.postquery.sentence;
}
};
export const setStatus = ( state, status ) => {
state.postquery.status = status;
};

View File

@ -7,7 +7,7 @@
<!-- New Collection button -->
<div
v-if="$userCaps.hasCapability('tnc_rep_edit_collections')"
v-if="!$adminOptions.hideCollectionsListCreationDropdown && $userCaps.hasCapability('tnc_rep_edit_collections')"
class="header-item">
<b-dropdown
aria-role="list"
@ -31,17 +31,15 @@
<small class="is-small">{{ $i18n.get('info_choose_your_metadata') }}</small>
</router-link>
</b-dropdown-item>
<b-dropdown-item
:key="metadatum_mapper.slug"
v-for="metadatum_mapper in metadatum_mappers"
v-if="metadatum_mapper.metadata != false"
aria-role="listitem">
<router-link
:id="'a-create-collection-' + metadatum_mapper.slug"
<b-dropdown-item aria-role="listitem">
<div
id="a-preset-collection"
tag="div"
:to="{ path: $routerHelper.getNewMappedCollectionPath(metadatum_mapper.slug) }">
{{ $i18n.get(metadatum_mapper.name) }}
</router-link>
@click="onOpenCollectionCreationModal">
{{ $i18n.get('label_preset_collections') }}
<br>
<small class="is-small">{{ $i18n.get('info_preset_collections') }}</small>
</div>
</b-dropdown-item>
<b-dropdown-item aria-role="listitem">
<div
@ -261,9 +259,8 @@
{{ $i18n.get('info_no_collections_' + statusOption.slug) }}
</p>
<div v-if="$userCaps.hasCapability('tnc_rep_edit_collections') && status == undefined || status == ''">
<div v-if="!$adminOptions.hideCollectionsListCreationDropdown && $userCaps.hasCapability('tnc_rep_edit_collections') && status == undefined || status == ''">
<b-dropdown
:disabled="isLoadingMetadatumMappers"
id="collection-creation-options-dropdown"
aria-role="list"
trap-focus>
@ -285,17 +282,15 @@
<small class="is-small">{{ $i18n.get('info_choose_your_metadata') }}</small>
</router-link>
</b-dropdown-item>
<b-dropdown-item
:key="metadatum_mapper.slug"
v-for="metadatum_mapper in metadatum_mappers"
v-if="metadatum_mapper.metadata != false"
aria-role="listitem">
<router-link
:id="'a-create-collection-' + metadatum_mapper.slug"
<b-dropdown-item aria-role="listitem">
<div
id="a-preset-collection"
tag="div"
:to="{ path: $routerHelper.getNewMappedCollectionPath(metadatum_mapper.slug) }">
{{ $i18n.get(metadatum_mapper.name) }}
</router-link>
@click="onOpenCollectionCreationModal">
{{ $i18n.get('label_preset_collections') }}
<br>
<small class="is-small">{{ $i18n.get('info_preset_collections') }}</small>
</div>
</b-dropdown-item>
<b-dropdown-item aria-role="listitem">
<div
@ -364,6 +359,7 @@
<script>
import CollectionsList from '../../components/lists/collections-list.vue';
import AvailableImportersModal from '../../components/modals/available-importers-modal.vue';
import CollectionCreationModal from '../../components/modals/collection-creation-modal.vue';
import { mapActions, mapGetters } from 'vuex';
export default {
@ -391,11 +387,6 @@ export default {
}
},
computed: {
metadatum_mappers: {
get() {
return this.getMetadatumMappers();
}
},
collections() {
return this.getCollections();
},
@ -419,15 +410,6 @@ export default {
created() {
this.collectionsPerPage = this.$userPrefs.get('collections_per_page');
this.isLoadingMetadatumTypes = true;
this.fetchMetadatumMappers()
.then(() => {
this.isLoadingMetadatumMappers = false;
})
.catch(() => {
this.isLoadingMetadatumMappers = false;
});
this.isLoadingCollectionTaxonomies = true;
this.fetchCollectionTaxonomies()
.then(() => {
@ -437,7 +419,8 @@ export default {
this.isLoadingCollectionTaxonomies= false;
});
},
mounted(){
mounted() {
if (this.collectionsPerPage != this.$userPrefs.get('collections_per_page'))
this.collectionsPerPage = this.$userPrefs.get('collections_per_page');
if (!this.collectionsPerPage) {
@ -452,7 +435,6 @@ export default {
this.$userPrefs.set('collections_order', 'asc');
}
if (this.orderBy != this.$userPrefs.get('collections_order_by'))
this.orderBy = this.$userPrefs.get('collections_order_by');
if (!this.orderBy) {
@ -476,9 +458,6 @@ export default {
'getRepositoryTotalCollections',
'getCollectionTaxonomies'
]),
...mapGetters('metadata', [
'getMetadatumMappers'
]),
onChangeTab(status) {
this.page = 1;
this.status = status;
@ -569,6 +548,16 @@ export default {
closeButtonAriaLabel: this.$i18n.get('close')
});
},
onOpenCollectionCreationModal() {
this.$buefy.modal.open({
parent: this,
component: CollectionCreationModal,
hasModalCard: true,
trapFocus: true,
customClass: 'tainacan-modal',
closeButtonAriaLabel: this.$i18n.get('close')
});
},
searchCollections() {
this.page = 1;
this.loadCollections();

View File

@ -58,18 +58,54 @@
<div
role="search"
class="search-area">
<b-input
size="is-small"
:placeholder="$i18n.get('instruction_search')"
type="search"
:aria-label="$i18n.get('instruction_search') + ' ' + $i18n.get('items')"
:value="searchQuery"
@input.native="futureSearchQuery = $event.target.value"
@keyup.enter.native="updateSearch()"
icon-right="magnify"
icon-right-clickable
@icon-right-click="updateSearch()"
:disabled="openAdvancedSearch" />
<b-dropdown
ref="tainacan-textual-search-input"
class="tainacan-textual-search-input"
aria-role="dialog"
:mobile-modal="false"
:disabled="openAdvancedSearch"
:triggers="hasSearchByMoreThanOneWord ? ['click','contextmenu','focus'] : []">
<b-input
slot="trigger"
size="is-small"
:placeholder="$i18n.get('instruction_search')"
type="search"
:aria-label="$i18n.get('instruction_search') + ' ' + $i18n.get('items')"
:value="searchQuery"
@input.native="typeFutureSearch"
@keyup.enter.native="updateSearch()"
icon-right="magnify"
icon-right-clickable
@icon-right-click="updateSearch()"
:disabled="openAdvancedSearch" />
<b-dropdown-item
@click="updateSearch()"
:focusable="false">
<span v-html="$i18n.get('instruction_press_enter_to_search_for')"/>&nbsp;
<em>{{ sentenceMode == true ? futureSearchQuery : ('"' + futureSearchQuery + '"') }}.</em>
</b-dropdown-item>
<b-dropdown-item
custom
:focusable="false">
<b-checkbox
:value="sentenceMode"
@input="$eventBusSearch.setSentenceMode($event)">
{{ $i18n.get('label_use_search_separated_words') }}
</b-checkbox>
<small class="is-small help">{{ $i18n.get('info_use_search_separated_words') }}</small>
</b-dropdown-item>
<b-dropdown-item
v-if="!$adminOptions.hideItemsListAdvancedSearch"
:focusable="false"
@click="openAdvancedSearch = !openAdvancedSearch; $eventBusSearch.clearAllFilters();">
{{ $i18n.get('info_for_more_metadata_search_options_use') }}&nbsp;
<a
@click="openAdvancedSearch = !openAdvancedSearch; $eventBusSearch.clearAllFilters();"
class="has-text-secondary">
{{ $i18n.get('advanced_search') }}
</a>
</b-dropdown-item>
</b-dropdown>
<a
v-if="!$adminOptions.hideItemsListAdvancedSearch"
@click="openAdvancedSearch = !openAdvancedSearch; $eventBusSearch.clearAllFilters();"
@ -555,7 +591,7 @@
:is-repository-level="isRepositoryLevel"
@updateIsLoading="(newIsLoadingState) => isLoadingItems = newIsLoadingState"/>
<!-- Empty Placeholder (only used in Admin) -->
<!-- Empty Placeholder -->
<section
v-if="!isLoadingItems && totalItems == 0"
class="section">
@ -565,7 +601,9 @@
<i class="tainacan-icon tainacan-icon-30px tainacan-icon-items" />
</span>
</p>
<p v-if="status == undefined || status == '' || status == 'publish,private,draft'">{{ (hasFiltered || openAdvancedSearch) ? $i18n.get('info_no_item_found_filter') : (isSortingByCustomMetadata ? $i18n.get('info_no_item_found') : $i18n.get('info_no_item_created')) }}</p>
<p v-if="status == undefined || status == '' || status == 'publish,private,draft'">
{{ (hasFiltered || openAdvancedSearch || searchQuery) ? $i18n.get('info_no_item_found_filter') : (isSortingByCustomMetadata ? $i18n.get('info_no_item_found') : $i18n.get('info_no_item_created')) }}
</p>
<p
v-for="(statusOption, index) of $statusHelper.getStatuses()"
:key="index"
@ -588,6 +626,23 @@
@click="onOpenCollectionsModal">
{{ $i18n.get('add_one_item') }}
</button>
<p v-if="searchQuery">
<template v-if="!sentenceMode">
<span v-html="searchedForSentence" />. {{ $i18n.get('info_try_enabling_search_by_word') }}
<br>
{{ $i18n.get('info_details_about_search_by_word') }}
</template>
<template v-else>
<span v-html="searchedForSentence" />. {{ $i18n.get('info_try_disabling_search_by_word') }}
</template>
<br>
<b-checkbox
:value="sentenceMode"
@input="$eventBusSearch.setSentenceMode($event); updateSearch();">
{{ $i18n.get('label_use_search_separated_words') }}
</b-checkbox>
</p>
</div>
</section>
@ -675,6 +730,9 @@
status() {
return this.getStatus();
},
sentenceMode() {
return this.getSentenceMode();
},
adminViewMode() {
const currentAdminViewMode = this.getAdminViewMode();
return ['table', 'cards', 'records', 'grid', 'masonry', 'list'].indexOf(currentAdminViewMode) >= 0 ? currentAdminViewMode : 'table';
@ -697,6 +755,14 @@
metakey: this.$route.query.metakey
}, this.sortingMetadata);
return this.$route.query.metakey ? metadatumName : this.$i18n.get(metadatumName);
},
hasSearchByMoreThanOneWord() {
return this.futureSearchQuery && this.futureSearchQuery.split(' ').length > 1;
},
searchedForSentence() {
if (this.searchQuery)
return this.$i18n.getWithVariables('info_you_searched_for_%s', ['<em>"' + this.searchQuery + '"</em>']);
return '';
}
},
watch: {
@ -706,6 +772,7 @@
openAdvancedSearch(newValue) {
if (newValue == false){
this.$eventBusSearch.$emit('closeAdvancedSearch');
this.futureSearchQuery = '';
this.isFiltersModalActive = true;
} else {
this.isFiltersModalActive = false;
@ -830,6 +897,7 @@
]),
...mapGetters('search', [
'getSearchQuery',
'getSentenceMode',
'getStatus',
'getOrderBy',
'getOrder',
@ -1197,6 +1265,17 @@
this.openMetatadaSortingWarningDialog({ offerCheckbox: true });
}
},
typeFutureSearch($event) {
this.futureSearchQuery = $event.target.value;
// If we have more than one word and the dropdown is not active, open it
if ( this.hasSearchByMoreThanOneWord && this.$refs['tainacan-textual-search-input'] && !this.$refs['tainacan-textual-search-input'].isActive && typeof this.$refs['tainacan-textual-search-input'].toggle === 'function' )
this.$refs['tainacan-textual-search-input'].toggle();
// If we don't have more than one word any more and the dropdown is still active, close it
if ( !this.hasSearchByMoreThanOneWord && this.$refs['tainacan-textual-search-input'] && this.$refs['tainacan-textual-search-input'].isActive && typeof this.$refs['tainacan-textual-search-input'].toggle === 'function' )
this.$refs['tainacan-textual-search-input'].toggle();
},
openMetatadaSortingWarningDialog({ offerCheckbox }) {
this.$buefy.modal.open({
parent: this,
@ -1534,6 +1613,22 @@
max-width: calc(16.66667vw - 60px);
padding-right: 15px;
.tainacan-textual-search-input {
width: 100%;
/deep/ .dropdown-trigger {
width: 100%;
}
/deep/ .dropdown-menu {
z-index: 99999991;
.dropdown-item:last-child {
line-height: 2.25em;
background: var(--tainacan-item-hover-background-color);
}
}
}
.control {
width: 100%;
margin-bottom: 5px;

View File

@ -101,7 +101,7 @@ button.link-style:active {
}
}
kbd {
padding: 0.25rem 0.5rem 0.25rem 0.35rem;
padding: 0.125rem 0.5rem 0.125rem 0.35rem;
background: radial-gradient(var(--tainacan-white, #ffffff), var(--tainacan-gray1, #f2f2f2) );
color: inherit;
display: inline-block;

View File

@ -327,6 +327,7 @@ class Admin {
'api_max_items_per_page' => $TAINACAN_API_MAX_ITEMS_PER_PAGE,
'wp_elasticpress' => \Tainacan\Elastic_Press::get_instance()->is_active(),
'item_submission_captcha_site_key' => get_option("tnc_option_recaptch_site_key"),
'tainacan_enable_core_metadata_on_advanced_search' => ( !defined('TAINACAN_DISABLE_CORE_METADATA_ON_ADVANCED_SEARCH') || false === TAINACAN_DISABLE_CORE_METADATA_ON_ADVANCED_SEARCH ),
'tainacan_enable_relationship_metaquery' => ( defined('TAINACAN_ENABLE_RELATIONSHIP_METAQUERY') && true === TAINACAN_ENABLE_RELATIONSHIP_METAQUERY )
];
@ -374,6 +375,11 @@ class Admin {
$settings['wp_post_types'] = $wp_post_types;
// Key-valued array with extra options to be passed to every request in the admin (goes the header)
$admin_request_options = [];
$admin_request_options = apply_filters('tainacan-admin-extra-request-options', $admin_request_options);
$settings['admin_request_options'] = $admin_request_options;
return $settings;
}
@ -394,7 +400,7 @@ class Admin {
$admin_options = apply_filters('set_tainacan_admin_options', $_GET);
$admin_options = apply_filters('tainacan-admin-ui-options', $_GET);
$admin_options = json_encode($admin_options);
// TODO move it to a separate file and start the Vue project
echo "<div id='tainacan-admin-app' data-module='admin' data-options='$admin_options'></div>";
}

View File

@ -98,6 +98,14 @@
"type": "string",
"default": ""
},
"orderBy": {
"type": "string",
"default": "date"
},
"orderByMetaKey": {
"type": "string",
"default": ""
},
"blockId": {
"type": "string",
"default": ""

View File

@ -1,6 +1,205 @@
const { useBlockProps } = (tainacan_blocks.wp_version < '5.2' ? wp.editor : wp.blockEditor );
export default [
/* Deprecated on 0.19.3 due to the introduction of orderBy and orderByMetaKey */
{
attributes: {
"content": {
"type": "array",
"source": "children",
"selector": "div"
},
"collectionId": {
"type": "string",
"default": ""
},
"items": {
"type": "array",
"default": []
},
"showImage": {
"type": "boolean",
"default": true
},
"showName": {
"type": "boolean",
"default": true
},
"layout": {
"type": "string",
"default": "grid"
},
"isModalOpen": {
"type": "boolean",
"default": false
},
"gridMargin": {
"type": "number",
"default": 0
},
"searchURL": {
"type": "string",
"default": ""
},
"itemsRequestSource": {
"type": "string",
"default": ""
},
"maxItemsNumber": {
"type": "number",
"default": 12
},
"isLoading": {
"type": "boolean",
"default": false
},
"isLoadingCollection": {
"type": "boolean",
"default": false
},
"showSearchBar": {
"type": "boolean",
"default": false
},
"showCollectionHeader": {
"type": "boolean",
"default": false
},
"showCollectionLabel": {
"type": "boolean",
"default": false
},
"collection": {
"type": "object",
"default": {}
},
"searchString": {
"type": "string",
"default": ""
},
"selectedItems": {
"type": "array",
"default": []
},
"loadStrategy": {
"type": "string",
"default": "search"
},
"order": {
"type": "string",
"default": ""
},
"blockId": {
"type": "string",
"default": ""
},
"collectionBackgroundColor": {
"type": "string",
"default": "#454647"
},
"collectionTextColor": {
"type": "string",
"default": "#ffffff"
},
"mosaicHeight": {
"type": "number",
"default": 280
},
"mosaicGridColumns": {
"type": "number",
"default": 3
},
"mosaicGridRows": {
"type": "number",
"default": 3
},
"sampleBackgroundImage": {
"type": "string",
"default": ""
},
"mosaicItemFocalPoint": {
"type": "object",
"default": {
"x": 0.5,
"y": 0.5
}
},
"mosaicDensity": {
"type": "number",
"default": 5
},
"maxColumnsCount": {
"type": "number",
"default": 4
},
"imageSize": {
"type": "string",
"default": "tainacan-medium"
}
},
save: function({ attributes, className }) {
const {
content,
blockId,
collectionId,
loadStrategy,
selectedItems,
showImage,
showName,
layout,
gridMargin,
searchURL,
maxItemsNumber,
order,
showSearchBar,
showCollectionHeader,
showCollectionLabel,
collectionBackgroundColor,
collectionTextColor,
mosaicHeight,
mosaicGridRows,
mosaicGridColumns,
mosaicItemFocalPoint,
mosaicDensity,
maxColumnsCount,
imageSize
} = attributes;
// Gets attributes such as style, that are automatically added by the editor hook
const blockProps = tainacan_blocks.wp_version < '5.6' ? { className: className } : useBlockProps.save();
return <div
{ ...blockProps }
data-module="dynamic-items-list"
search-url={ searchURL }
selected-items={ JSON.stringify(selectedItems) }
collection-id={ collectionId }
show-image={ '' + showImage }
show-name={ '' + showName }
show-search-bar={ '' + showSearchBar }
show-collection-header={ '' + showCollectionHeader }
show-collection-label={ '' + showCollectionLabel }
image-size={ imageSize }
layout={ layout }
load-strategy={ loadStrategy }
mosaic-height={ mosaicHeight }
mosaic-density={ mosaicDensity }
mosaic-grid-rows={ mosaicGridRows }
mosaic-grid-columns={ mosaicGridColumns }
mosaic-item-focal-point-x={ (mosaicItemFocalPoint && mosaicItemFocalPoint.x ? mosaicItemFocalPoint.x : 0.5) }
mosaic-item-focal-point-y={ (mosaicItemFocalPoint && mosaicItemFocalPoint.y ? mosaicItemFocalPoint.y : 0.5) }
max-columns-count={ maxColumnsCount }
collection-background-color={ collectionBackgroundColor }
collection-text-color={ collectionTextColor }
grid-margin={ gridMargin }
max-items-number={ maxItemsNumber }
order={ order }
tainacan-api-root={ tainacan_blocks.root }
tainacan-base-url={ tainacan_blocks.base_url }
id={ 'wp-block-tainacan-dynamic-items-list_' + blockId }>
{ content }
</div>
}
},
/* Deprecated on 0.19 to replace cropImagesToSquare by imageSize feature */
{
migrate( attributes ) {

View File

@ -31,6 +31,8 @@ export default function({ attributes, setAttributes, className, isSelected, clie
itemsRequestSource,
maxItemsNumber,
order,
orderBy,
orderByMetaKey,
searchString,
selectedItems,
isLoading,
@ -302,16 +304,36 @@ export default function({ attributes, setAttributes, className, isSelected, clie
}
// Set up sorting order
if (order != '' && showSearchBar)
queryObject.order = order;
else if (queryObject.order != '')
if (queryObject.order != '' && !showSearchBar)
setAttributes({ order: queryObject.order });
else if (order != '')
queryObject.order = order;
else {
queryObject.order = 'asc';
setAttributes({ order: 'asc' });
}
// Set up sorting orderby
if (queryObject.orderby != '')
setAttributes({ orderBy: queryObject.orderby });
else if (orderBy != 'date')
queryObject.orderby = orderBy;
else {
queryObject.orderby = 'date';
setAttributes({ orderBy: 'date' });
}
// Set up sorting order
// Set up sorting metakey (used by some orderby)
if (queryObject.metakey != '')
setAttributes({ orderByMetaKey: queryObject.metakey });
else if (orderByMetaKey != '')
queryObject.metakey = orderByMetaKey;
else {
queryObject.metakey = '';
setAttributes({ orderByMetaKey: '' });
}
// Set up search string
if (searchString != undefined)
queryObject.search = searchString;
else if (queryObject.search != undefined)

View File

@ -14,6 +14,8 @@ export default function({ attributes, className }) {
searchURL,
maxItemsNumber,
order,
orderBy,
orderByMetaKey,
showSearchBar,
showCollectionHeader,
showCollectionLabel,
@ -56,6 +58,8 @@ export default function({ attributes, className }) {
grid-margin={ gridMargin }
max-items-number={ maxItemsNumber }
order={ order }
orderBy={ orderBy }
orderByMetaKey={ orderByMetaKey }
tainacan-api-root={ tainacan_blocks.root }
tainacan-base-url={ tainacan_blocks.base_url }
id={ 'wp-block-tainacan-dynamic-items-list_' + blockId }>

View File

@ -44,6 +44,8 @@ export default (element) => {
maxColumnsCount: 4,
imageSize: 'tainacan-medium',
order: 'asc',
orderBy: 'date',
orderByMetaKey: '',
showSearchBar: false,
showCollectionHeader: false,
showCollectionLabel: false,
@ -75,6 +77,8 @@ export default (element) => {
loadStrategy: this.loadStrategy,
maxItemsNumber: this.maxItemsNumber,
order: this.order,
orderBy: this.orderBy,
orderByMetaKey: this.orderByMetaKey,
showSearchBar: this.showSearchBar,
showCollectionHeader: this.showCollectionHeader,
showCollectionLabel: this.showCollectionLabel,
@ -107,6 +111,8 @@ export default (element) => {
this.imageSize = this.$el.attributes['image-size'] != undefined ? this.$el.attributes['image-size'].value : 'tainacan-medium';
this.maxItemsNumber = this.$el.attributes['max-items-number'] != undefined ? this.$el.attributes['max-items-number'].value : undefined;
this.order = this.$el.attributes['order'] != undefined ? this.$el.attributes['order'].value : undefined;
this.orderBy = this.$el.attributes['order-by'] != undefined ? this.$el.attributes['order-by'].value : undefined;
this.orderByMetaKey = this.$el.attributes['order-by-meta-key'] != undefined ? this.$el.attributes['order-by-meta-key'].value : undefined;
this.showSearchBar = this.$el.attributes['show-search-bar'] != undefined ? this.$el.attributes['show-search-bar'].value == 'true' : false;
this.showCollectionHeader = this.$el.attributes['show-collection-header'] != undefined ? this.$el.attributes['show-collection-header'].value == 'true' : false;
this.showCollectionLabel = this.$el.attributes['show-collection-label'] != undefined ? this.$el.attributes['show-collection-label'].value == 'true' : false;

View File

@ -299,6 +299,8 @@ export default {
maxColumnsCount: Number,
imageSize: String,
order: String,
orderBy: String,
orderByMetaKey: String,
showSearchBar: Boolean,
showCollectionHeader: Boolean,
showCollectionLabel: Boolean,
@ -415,7 +417,15 @@ export default {
this.localOrder = 'asc';
}
// Set up sorting order
// Set up orderBy
if (this.orderBy != undefined)
queryObject.orderby = this.orderBy;
// Set up orderByMetaKey
if (this.orderByMetaKey != undefined)
queryObject.metakey = this.orderByMetaKey;
// Set up search string
if (this.searchString != undefined)
queryObject.search = this.searchString;
else if (queryObject.search != undefined)

View File

@ -2,19 +2,8 @@
<div class="table-container">
<div class="table-wrapper">
<!-- Empty result placeholder -->
<section
v-if="!isLoading && items.length <= 0"
class="section">
<div class="content has-text-gray4 has-text-centered">
<p>
<span class="icon is-large">
<i class="tainacan-icon tainacan-icon-36px tainacan-icon-items" />
</span>
</p>
<p>{{ $i18n.get('info_no_item_found') }}</p>
</div>
</section>
<!-- Empty result placeholder, rendered in the parent component -->
<slot />
<!-- SKELETON LOADING -->
<div

View File

@ -2,19 +2,8 @@
<div class="table-container">
<div class="table-wrapper">
<!-- Empty result placeholder -->
<section
v-if="!isLoading && items.length <= 0"
class="section">
<div class="content has-text-gray4 has-text-centered">
<p>
<span class="icon is-large">
<i class="tainacan-icon tainacan-icon-36px tainacan-icon-items" />
</span>
</p>
<p>{{ $i18n.get('info_no_item_found') }}</p>
</div>
</section>
<!-- Empty result placeholder, rendered in the parent component -->
<slot />
<!-- SKELETON LOADING -->
<div

View File

@ -2,19 +2,8 @@
<div class="table-container">
<div class="table-wrapper">
<!-- Empty result placeholder -->
<section
v-if="!isLoading && items.length <= 0"
class="section">
<div class="content has-text-gray4 has-text-centered">
<p>
<span class="icon is-large">
<i class="tainacan-icon tainacan-icon-36px tainacan-icon-items" />
</span>
</p>
<p>{{ $i18n.get('info_no_item_found') }}</p>
</div>
</section>
<!-- Empty result placeholder, rendered in the parent component -->
<slot />
<!-- SKELETON LOADING -->
<div

View File

@ -4,19 +4,8 @@
ref="masonryWrapper"
class="table-wrapper">
<!-- Empty result placeholder -->
<section
v-if="!isLoading && items.length <= 0"
class="section">
<div class="content has-text-gray4 has-text-centered">
<p>
<span class="icon is-large">
<i class="tainacan-icon tainacan-icon-36px tainacan-icon-items" />
</span>
</p>
<p>{{ $i18n.get('info_no_item_found') }}</p>
</div>
</section>
<!-- Empty result placeholder, rendered in the parent component -->
<slot />
<!-- SKELETON LOADING -->
<div

View File

@ -1,19 +1,9 @@
<template>
<div class="table-container">
<div class="table-wrapper">
<!-- Empty result placeholder -->
<section
v-if="!isLoading && items.length <= 0"
class="section">
<div class="content has-text-gray4 has-text-centered">
<p>
<span class="icon is-large">
<i class="tainacan-icon tainacan-icon-36px tainacan-icon-items" />
</span>
</p>
<p>{{ $i18n.get('info_no_item_found') }}</p>
</div>
</section>
<!-- Empty result placeholder, rendered in the parent component -->
<slot />
<!-- SKELETON LOADING -->
<table

View File

@ -68,18 +68,54 @@
<div
role="search"
class="search-area">
<b-input
size="is-small"
:placeholder="$i18n.get('instruction_search')"
type="search"
:aria-label="$i18n.get('instruction_search') + ' ' + $i18n.get('items')"
:value="searchQuery"
@input.native="futureSearchQuery = $event.target.value"
@keyup.enter.native="updateSearch()"
icon-right="magnify"
icon-right-clickable
@icon-right-click="updateSearch()"
:disabled="openAdvancedSearch" />
<b-dropdown
ref="tainacan-textual-search-input"
class="tainacan-textual-search-input"
aria-role="dialog"
:mobile-modal="false"
:disabled="openAdvancedSearch"
:triggers="hasSearchByMoreThanOneWord ? ['click','contextmenu','focus'] : []">
<b-input
slot="trigger"
size="is-small"
:placeholder="$i18n.get('instruction_search')"
type="search"
:aria-label="$i18n.get('instruction_search') + ' ' + $i18n.get('items')"
:value="searchQuery"
@input.native="typeFutureSearch"
@keyup.enter.native="updateSearch()"
icon-right="magnify"
icon-right-clickable
@icon-right-click="updateSearch()"
:disabled="openAdvancedSearch" />
<b-dropdown-item
@click="updateSearch()"
:focusable="false">
<span v-html="$i18n.get('instruction_press_enter_to_search_for')"/>&nbsp;
<em>{{ sentenceMode == true ? futureSearchQuery : ('"' + futureSearchQuery + '"') }}.</em>
</b-dropdown-item>
<b-dropdown-item
custom
:focusable="false">
<b-checkbox
:value="sentenceMode"
@input="$eventBusSearch.setSentenceMode($event)">
{{ $i18n.get('label_use_search_separated_words') }}
</b-checkbox>
<small class="is-small help">{{ $i18n.get('info_use_search_separated_words') }}</small>
</b-dropdown-item>
<b-dropdown-item
v-if="!hideAdvancedSearch"
:focusable="false"
@click="openAdvancedSearch = !openAdvancedSearch; $eventBusSearch.clearAllFilters();">
{{ $i18n.get('info_for_more_metadata_search_options_use') }}&nbsp;
<a
@click="openAdvancedSearch = !openAdvancedSearch; $eventBusSearch.clearAllFilters();"
class="has-text-secondary">
{{ $i18n.get('advanced_search') }}
</a>
</b-dropdown-item>
</b-dropdown>
<a
v-if="!hideAdvancedSearch"
@click="openAdvancedSearch = !openAdvancedSearch; $eventBusSearch.clearAllFilters();"
@ -415,6 +451,12 @@
ref="items-list-area"
class="items-list-area">
<!-- JS-side hook for extra form content -->
<div
v-if="hooks['items_list_area_before']"
class="faceted-search-hook faceted-search-hook-items-list-area-before"
v-html="hooks['items_list_area_before']" />
<!-- ADVANCED SEARCH -->
<transition name="filter-item">
<div
@ -556,7 +598,43 @@
:is-loading="showLoading"
:enabled-view-modes="enabledViewModes"
:initial-item-position="initialItemPosition"
:is="registeredViewModes[viewMode] != undefined ? registeredViewModes[viewMode].component : ''"/>
:is="registeredViewModes[viewMode] != undefined ? registeredViewModes[viewMode].component : ''">
<!-- Empty Placeholder, rendered in a slot inside the view modes -->
<section
v-if="!showLoading && totalItems == 0"
class="section">
<div class="content has-text-grey has-text-centered">
<p>
<span class="icon is-large">
<i class="tainacan-icon tainacan-icon-30px tainacan-icon-items" />
</span>
</p>
<p>
{{ (hasFiltered || openAdvancedSearch || searchQuery) ? $i18n.get('info_no_item_found_filter') : $i18n.get('info_no_item_found') }}
</p>
<p v-if="searchQuery">
<template v-if="!sentenceMode">
<span v-html="searchedForSentence" />. {{ $i18n.get('info_try_enabling_search_by_word') }}
<br>
{{ $i18n.get('info_details_about_search_by_word') }}
</template>
<template v-else>
<span v-html="searchedForSentence" />. {{ $i18n.get('info_try_disabling_search_by_word') }}
</template>
<br>
<b-checkbox
:value="sentenceMode"
@input="$eventBusSearch.setSentenceMode($event); updateSearch();">
{{ $i18n.get('label_use_search_separated_words') }}
</b-checkbox>
</p>
</div>
</section>
</component>
<!-- JS-side hook for extra form content -->
<div
@ -588,6 +666,12 @@
v-html="hooks['pagination_after']" />
</template>
<!-- JS-side hook for extra form content -->
<div
v-if="hooks['items_list_area_after']"
class="faceted-search-hook faceted-search-hook-items-list-area-after"
v-html="hooks['items_list_area_after']" />
<!-- This is used by intersection observers to set filters menu as fixed on the bottom -->
<div
id="items-list-results-bottom"
@ -595,6 +679,7 @@
class="sr-only"
style="bottom: 0px" />
</div>
</div>
</div>
@ -699,6 +784,9 @@
searchQuery() {
return this.getSearchQuery();
},
sentenceMode() {
return this.getSentenceMode();
},
viewMode() {
return this.getViewMode();
},
@ -720,6 +808,14 @@
metakey: this.$route.query.metakey
}, this.sortingMetadata);
return this.$route.query.metakey ? metadatumName : (metadatumName ? this.$i18n.get(metadatumName) : '');
},
hasSearchByMoreThanOneWord() {
return this.futureSearchQuery && this.futureSearchQuery.split(' ').length > 1;
},
searchedForSentence() {
if (this.searchQuery)
return this.$i18n.getWithVariables('info_you_searched_for_%s', ['<em>"' + this.searchQuery + '"</em>']);
return '';
}
},
watch: {
@ -918,6 +1014,7 @@
'getOrder',
'getViewMode',
'getTotalItems',
'getSentenceMode',
'getMetaKey',
'getPage',
'getItemsPerPage'
@ -965,6 +1062,11 @@
if (filterTagsAfterFiltersCollection)
this.hooks['filter_tags_after'] = filterTagsAfterFiltersCollection;
const itemsListAreaBeforeFilters = wp.hooks.hasFilter(`tainacan_faceted_search_items_area_list_before`) && wp.hooks.applyFilters(`tainacan_faceted_search_items_area_list_before`, '');
const itemsListAreaBeforeFiltersCollection = (wp.hooks.hasFilter(`tainacan_faceted_search_collection_${this.collectionId}_items_area_list_before`) || itemsListAreaBeforeFilters) && wp.hooks.applyFilters(`tainacan_faceted_search_collection_${this.collectionId}_items_area_list_before`, itemsListAreaBeforeFilters);
if (itemsListAreaBeforeFiltersCollection)
this.hooks['items_area_list_before'] = itemsListAreaBeforeFiltersCollection;
const itemsListBeforeFilters = wp.hooks.hasFilter(`tainacan_faceted_search_items_list_before`) && wp.hooks.applyFilters(`tainacan_faceted_search_items_list_before`, '');
const itemsListBeforeFiltersCollection = (wp.hooks.hasFilter(`tainacan_faceted_search_collection_${this.collectionId}_items_list_before`) || itemsListBeforeFilters) && wp.hooks.applyFilters(`tainacan_faceted_search_collection_${this.collectionId}_items_list_before`, itemsListBeforeFilters);
if (itemsListBeforeFiltersCollection)
@ -984,6 +1086,11 @@
const paginationAfterFiltersCollection = (wp.hooks.hasFilter(`tainacan_faceted_search_collection_${this.collectionId}_pagination_after`) || paginationAfterFilters) && wp.hooks.applyFilters(`tainacan_faceted_search_collection_${this.collectionId}_pagination_after`, paginationAfterFilters);
if (paginationAfterFiltersCollection)
this.hooks['pagination_after'] = paginationAfterFiltersCollection;
const itemsListAreaAfterFilters = wp.hooks.hasFilter(`tainacan_faceted_search_items_area_list_after`) && wp.hooks.applyFilters(`tainacan_faceted_search_items_area_list_after`, '');
const itemsListAreaAfterFiltersCollection = (wp.hooks.hasFilter(`tainacan_faceted_search_collection_${this.collectionId}_items_area_list_after`) || itemsListAreaAfterFilters) && wp.hooks.applyFilters(`tainacan_faceted_search_collection_${this.collectionId}_items_area_list_after`, itemsListAreaAfterFilters);
if (itemsListAreaAfterFiltersCollection)
this.hooks['items_area_list_after'] = itemsListAreaAfterFiltersCollection;
}
},
openExposersModal() {
@ -1274,6 +1381,17 @@
this.openMetatadaSortingWarningDialog({ offerCheckbox: true });
}
},
typeFutureSearch($event) {
this.futureSearchQuery = $event.target.value;
// If we have more than one word and the dropdown is not active, open it
if ( this.hasSearchByMoreThanOneWord && this.$refs['tainacan-textual-search-input'] && !this.$refs['tainacan-textual-search-input'].isActive && typeof this.$refs['tainacan-textual-search-input'].toggle === 'function' )
this.$refs['tainacan-textual-search-input'].toggle();
// If we don't have more than one word any more and the dropdown is still active, close it
if ( !this.hasSearchByMoreThanOneWord && this.$refs['tainacan-textual-search-input'] && this.$refs['tainacan-textual-search-input'].isActive && typeof this.$refs['tainacan-textual-search-input'].toggle === 'function' )
this.$refs['tainacan-textual-search-input'].toggle();
},
openMetatadaSortingWarningDialog({ offerCheckbox }) {
this.$buefy.modal.open({
parent: this,
@ -1568,6 +1686,27 @@
width: 100%;
max-width: 16.66667vw;
.tainacan-textual-search-input {
width: 100%;
/deep/ .dropdown-trigger {
width: 100%;
}
/deep/ .dropdown-menu {
z-index: 99999991;
.dropdown-item:last-child {
line-height: 2.25em;
background: var(--tainacan-item-hover-background-color);
@media screen and (max-width: 768px) {
white-space: initial;
line-height: 2.125em;
}
}
}
}
.control {
width: 100%;
margin: -2px 0 5px 0;

View File

@ -523,6 +523,7 @@ return apply_filters( 'tainacan-i18n', [
'label_%s_items_copy_success' => __( '%s item copies were created with success!', 'tainacan' ),
'label_one_item_copy_success' => __( 'The item copy was created with success!', 'tainacan' ),
'label_item_copy_failure' => __( 'Something wrong happened... Item copy failed!', 'tainacan' ),
'label_preset_success' => __( 'The preset was applied with success!', 'tainacan' ),
'label_create_another_taxonomy' => __( 'Create another Taxonomy', 'tainacan' ),
'label_make_copies_of_item' => __( 'Make copies of item', 'tainacan' ),
'label_number_of_copies' => __( 'Number of copies', 'tainacan' ),
@ -654,7 +655,13 @@ return apply_filters( 'tainacan-i18n', [
'label_create_item' => __( 'Create item', 'tainacan' ),
'label_ready_to_create_item' => __( 'Ready to create this item?', 'tainacan' ),
'label_only_required' => __( 'Only required', 'tainacan' ),
'label_create_collection_from_mapper' => __( 'Create a new collection from a mapper', 'tainacan' ),
'label_create_collection_from_preset' => __( 'Create a preset collection', 'tainacan' ),
'label_preset_collections' => __( 'Preset collections', 'tainacan' ),
'label_from_a_mapper' => __( 'From a metadata mapper', 'tainacan' ),
'label_using_a_preset' => __( 'Using a preset', 'tainacan' ),
'label_use_search_separated_words' => __( 'Search each word separatelly', 'tainacan' ),
// Instructions. More complex sentences to guide user and placeholders
'instruction_delete_selected_collections' => __( 'Delete selected collections', 'tainacan' ),
'instruction_delete_selected_items' => __( 'Delete selected items', 'tainacan' ),
@ -681,7 +688,9 @@ return apply_filters( 'tainacan-i18n', [
'instruction_select_document_file_for_item' => __( 'Select a document file for item', 'tainacan' ),
'instruction_insert_url' => __( 'Insert URL', 'tainacan' ),
'instruction_write_text' => __( 'Write Text', 'tainacan' ),
/* translators: The verb to search, used in search box placeholder */
'instruction_search' => __( 'Search', 'tainacan' ),
/* translators: The verb to search, used in search box placeholder */
'instruction_search_in_repository' => __( 'Search in repository', 'tainacan' ),
'instruction_select_a_target_collection' => __( 'Select a target collection.', 'tainacan' ),
'instruction_select_a_mapper' => __( 'Select a mapper', 'tainacan' ),
@ -727,6 +736,8 @@ return apply_filters( 'tainacan-i18n', [
'instruction_click_to_easily_see' => __( 'Click to easily see', 'tainacan' ),
'instruction_create_item_select_status' => __( 'Select a status for the item visiblity on the site. Remember, whichever you select will still be restricted by the collection status as well.', 'tainacan' ),
'instruction_edit_item_status' => __( 'To alter the item status, select a different update strategy in the footer below.', 'tainacan' ),
/* translators: At the end of this sentence there will be a search query typed by the user wrapped in quotes. */
'instruction_press_enter_to_search_for' => __( 'Press <kbd>ENTER</kbd> to search for', 'tainacan' ),
// Info. Other feedback to user.
'info_items_tab_all' => __( 'Every item, except by those sent to trash.', 'tainacan' ),
@ -970,6 +981,16 @@ return apply_filters( 'tainacan-i18n', [
'info_metadata_mapper_helper' => __( 'Select the corresponding metadata so they can be exposed according to the mapper', 'tainacan'),
'info_default_orderby' => __( 'These settings only affect the initial state of the items sorting. After changed, the value used will be the latest selected by the user.', 'tainacan' ),
'info_collection_thumbnail_and_header' => __( 'The thumbnail is a squared image that will represent the collection in listings. The header image is a complementary, decorative image that may or not be displayed by your theme in the items list. Keep in mind that it might be cropped.', 'tainacan'),
'info_preset_collections' => __( 'Use mappers or standards as pre configuration', 'tainacan' ),
'info_create_collection_from_mapper' => __( 'Have the metadata preset by an installed mapper, such as Dublin core, then set the rest manually.', 'tainacan' ),
'info_create_collection_from_preset' => __( 'Have metadata, taxonomies, terms and related collections preset according to a standard.', 'tainacan' ),
'info_use_search_separated_words' => __( 'You may wrap the words with quotes to group them.', 'tainacan' ),
/* translators: At the end of this sentence there will be a link for the advanced search */
'info_for_more_metadata_search_options_use' => __( 'For more options of metadata search, use the', 'tainacan' ),
'info_you_searched_for_%s' => __( 'You searched for %s', 'tainacan' ),
'info_try_enabling_search_by_word' => __( 'Try enabling the search by words.', 'tainacan' ),
'info_try_disabling_search_by_word' => __( 'Try disabling the search by words, to search for the complete phrase.', 'tainacan' ),
'info_details_about_search_by_word' => __( 'They may be located on different metadata and order, but you will still be able to use quotes to group them.', 'tainacan' ),
/* Activity actions */
'action_update-metadata-value' => __( 'Item Metadata Value Updates', 'tainacan'),

View File

@ -10,7 +10,7 @@ const terserPlugin = new TerserPlugin({
comments: /translators:/i,
},
mangle: {
reserved: ["__"],
reserved: [ '__', '_n', '_nx', '_x' ]
}
},
});
@ -19,7 +19,7 @@ module.exports = merge(common, {
mode: 'production',
devtool: undefined,
optimization: {
minimize: true,
concatenateModules: false,
minimizer: [terserPlugin]
},
resolve: {