Merge branch 'release/0.21.8'

This commit is contained in:
vnmedeiros 2024-07-29 17:29:33 -03:00
commit 59a371d71e
42 changed files with 2030 additions and 876 deletions

2222
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -8,10 +8,10 @@
"build-prod": "cross-env NODE_ENV=production webpack --config webpack.prod.js --progress --mode production" "build-prod": "cross-env NODE_ENV=production webpack --config webpack.prod.js --progress --mode production"
}, },
"dependencies": { "dependencies": {
"@ntohq/buefy-next": "^0.1.3", "@ntohq/buefy-next": "^0.1.4",
"@vue-leaflet/vue-leaflet": "^0.10.1", "@vue-leaflet/vue-leaflet": "^0.10.1",
"another-vue3-blurhash": "^0.0.1", "another-vue3-blurhash": "^0.0.1",
"apexcharts": "^3.49.1", "apexcharts": "^3.51.0",
"axios": "^1.6.8", "axios": "^1.6.8",
"blurhash": "^2.0.5", "blurhash": "^2.0.5",
"bulma": "^0.9.4", "bulma": "^0.9.4",
@ -26,37 +26,37 @@
"moment": "^2.30.1", "moment": "^2.30.1",
"node-sass": "^8.0.0", "node-sass": "^8.0.0",
"photoswipe": "^5.4.4", "photoswipe": "^5.4.4",
"qs": "^6.12.1", "qs": "^6.12.3",
"react": "^18.3.1", "react": "^18.3.1",
"react-dom": "^18.3.1", "react-dom": "^18.3.1",
"sortablejs": "^1.15.2", "sortablejs": "^1.15.2",
"sortablejs-vue3": "^1.2.11", "sortablejs-vue3": "^1.2.11",
"swiper": "^11.1.3", "swiper": "^11.1.8",
"vue": "^3.4.27", "vue": "^3.4.34",
"vue-countup-v3": "^1.4.2", "vue-countup-v3": "^1.4.2",
"vue-imask": "^7.6.1", "vue-imask": "^7.6.1",
"vue-router": "^4.3.2", "vue-router": "^4.4.0",
"vue3-apexcharts": "^1.5.3", "vue3-apexcharts": "^1.5.3",
"vuex": "^4.1.0" "vuex": "^4.1.0"
}, },
"devDependencies": { "devDependencies": {
"@babel/core": "^7.24.3", "@babel/core": "^7.24.9",
"@babel/plugin-syntax-dynamic-import": "^7.8.3", "@babel/plugin-syntax-dynamic-import": "^7.8.3",
"@babel/preset-env": "^7.24.3", "@babel/preset-env": "^7.25.0",
"@babel/preset-react": "^7.24.1", "@babel/preset-react": "^7.24.7",
"@types/leaflet": "^1.9.8", "@types/leaflet": "^1.9.12",
"@types/masonry-layout": "^4.2.7", "@types/masonry-layout": "^4.2.8",
"@vue/compiler-sfc": "3.4.21", "@vue/compiler-sfc": "3.4.34",
"acorn": "^8.11.3", "acorn": "^8.12.1",
"ajv": "^8.12.0", "ajv": "^8.17.1",
"autoprefixer": "^10.4.19", "autoprefixer": "^10.4.19",
"babel-loader": "^9.1.3", "babel-loader": "^9.1.3",
"circular-dependency-plugin": "5.2.2", "circular-dependency-plugin": "5.2.2",
"cross-env": "^7.0.3", "cross-env": "^7.0.3",
"css-loader": "^6.10.0", "css-loader": "^6.10.0",
"eslint": "^8.57.0", "eslint": "^8.57.0",
"eslint-plugin-vue": "^9.24.0", "eslint-plugin-vue": "^9.27.0",
"eslint-webpack-plugin": "^4.1.0", "eslint-webpack-plugin": "^4.2.0",
"file-loader": "^6.2.0", "file-loader": "^6.2.0",
"moment-locales-webpack-plugin": "^1.2.0", "moment-locales-webpack-plugin": "^1.2.0",
"postcss-loader": "8.1.1", "postcss-loader": "8.1.1",
@ -64,8 +64,8 @@
"style-loader": "^3.3.4", "style-loader": "^3.3.4",
"terser-webpack-plugin": "5.3.10", "terser-webpack-plugin": "5.3.10",
"vue-loader": "^17.4.2", "vue-loader": "^17.4.2",
"webpack": "^5.91.0", "webpack": "^5.93.0",
"webpack-bundle-analyzer": "^4.10.1", "webpack-bundle-analyzer": "^4.10.2",
"webpack-cli": "^5.1.4", "webpack-cli": "^5.1.4",
"webpack-dev-server": "^4.15.1", "webpack-dev-server": "^4.15.1",
"webpack-merge": "^5.10.0" "webpack-merge": "^5.10.0"

View File

@ -9,7 +9,7 @@
border: 0; } border: 0; }
/** /**
* Swiper 11.1.3 * Swiper 11.1.8
* Most modern mobile touch slider and framework with hardware accelerated transitions * Most modern mobile touch slider and framework with hardware accelerated transitions
* https://swiperjs.com * https://swiperjs.com
* *
@ -17,7 +17,7 @@
* *
* Released under the MIT License * Released under the MIT License
* *
* Released on: May 13, 2024 * Released on: July 26, 2024
*/ */
@font-face { @font-face {
font-family: 'swiper-icons'; font-family: 'swiper-icons';

File diff suppressed because one or more lines are too long

View File

@ -9,7 +9,7 @@
border: 0; } border: 0; }
/** /**
* Swiper 11.1.3 * Swiper 11.1.8
* Most modern mobile touch slider and framework with hardware accelerated transitions * Most modern mobile touch slider and framework with hardware accelerated transitions
* https://swiperjs.com * https://swiperjs.com
* *
@ -17,7 +17,7 @@
* *
* Released under the MIT License * Released under the MIT License
* *
* Released on: May 13, 2024 * Released on: July 26, 2024
*/ */
@font-face { @font-face {
font-family: 'swiper-icons'; font-family: 'swiper-icons';
@ -540,10 +540,14 @@
min-width: calc(14.285% - var(--spaceBetweenItems, var(--spaceBetweenItems, 32px) ) ); min-width: calc(14.285% - var(--spaceBetweenItems, var(--spaceBetweenItems, 32px) ) );
scroll-snap-align: start; scroll-snap-align: start;
scroll-margin: 0 calc(var(--spaceBetweenItems, 32px) / 2 ); } scroll-margin: 0 calc(var(--spaceBetweenItems, 32px) / 2 ); }
.wp-block-tainacan-carousel-items-list ul.items-list-edit li.item-list-item.variable-item-width {
min-width: unset !important;
width: auto !important; }
.wp-block-tainacan-carousel-items-list ul.items-list-edit li.item-list-item a { .wp-block-tainacan-carousel-items-list ul.items-list-edit li.item-list-item a {
color: inherit; color: inherit;
font-weight: bold; font-weight: bold;
line-height: normal; } line-height: normal;
pointer-events: none; }
.wp-block-tainacan-carousel-items-list ul.items-list-edit li.item-list-item a > span { .wp-block-tainacan-carousel-items-list ul.items-list-edit li.item-list-item a > span {
color: inherit; color: inherit;
font-weight: bold; font-weight: bold;

File diff suppressed because one or more lines are too long

View File

@ -9,7 +9,7 @@
border: 0; } border: 0; }
/** /**
* Swiper 11.1.3 * Swiper 11.1.8
* Most modern mobile touch slider and framework with hardware accelerated transitions * Most modern mobile touch slider and framework with hardware accelerated transitions
* https://swiperjs.com * https://swiperjs.com
* *
@ -17,7 +17,7 @@
* *
* Released under the MIT License * Released under the MIT License
* *
* Released on: May 13, 2024 * Released on: July 26, 2024
*/ */
@font-face { @font-face {
font-family: 'swiper-icons'; font-family: 'swiper-icons';

File diff suppressed because one or more lines are too long

View File

@ -1,5 +1,5 @@
/** /**
* Swiper 11.1.3 * Swiper 11.1.8
* Most modern mobile touch slider and framework with hardware accelerated transitions * Most modern mobile touch slider and framework with hardware accelerated transitions
* https://swiperjs.com * https://swiperjs.com
* *
@ -7,7 +7,7 @@
* *
* Released under the MIT License * Released under the MIT License
* *
* Released on: May 13, 2024 * Released on: July 26, 2024
*/ */
@font-face { @font-face {
font-family: 'swiper-icons'; font-family: 'swiper-icons';

File diff suppressed because one or more lines are too long

View File

@ -373,9 +373,28 @@ class REST_Background_Processes_Controller extends REST_Controller {
'error_message' => __('guid must be specified', 'tainacan' ) 'error_message' => __('guid must be specified', 'tainacan' )
], 400); ], 400);
} }
if (!is_user_logged_in() || !current_user_can('read') ) {
$error_def = [
"code" => "unauthorized",
"message" => "Unauthorized",
"data" => [ "status" => 403 ],
];
return new \WP_REST_Response($error_def, 403, array('content-type' => 'text/html; charset=utf-8'));
}
$guid = $request['guid']; $guid = $request['guid'];
$upload_url = wp_upload_dir(); $upload_url = wp_upload_dir();
$path = $upload_url['basedir'] . '/tainacan/' . $guid; $path = $upload_url['basedir'] . '/tainacan/' . $guid;
$real_file_path = realpath($path);
if (strpos($real_file_path, $path) !== 0) {
$error_def = [
"code" => "unauthorized_file_path",
"message" => "Unauthorized file path",
"data" => [ "status" => 403 ],
];
return new \WP_REST_Response($error_def, 403, array('content-type' => 'application/json; charset=utf-8'));
}
if ( file_exists( $path ) ) { if ( file_exists( $path ) ) {
$finfo = @finfo_open(FILEINFO_MIME_TYPE); $finfo = @finfo_open(FILEINFO_MIME_TYPE);
$mime_type = @finfo_file($finfo, $path); $mime_type = @finfo_file($finfo, $path);

View File

@ -456,6 +456,14 @@ class REST_Items_Controller extends REST_Controller {
*/ */
private function prepare_filters_arguments ( $args, $collection_id = false, $ignore_filter_arguments = [] ) { private function prepare_filters_arguments ( $args, $collection_id = false, $ignore_filter_arguments = [] ) {
$filters_arguments = array(); $filters_arguments = array();
if(!empty($collection_id)) {
$collection = $this->collections_repository->fetch($collection_id);
$order = $collection->get_filters_order();
$order = ( is_array( $order ) ) ? $order : unserialize( $order );
}
$meta_query = isset($args['meta_query']) ? $args['meta_query'] : []; $meta_query = isset($args['meta_query']) ? $args['meta_query'] : [];
if(isset($meta_query['value'])) $meta_query = [$meta_query]; if(isset($meta_query['value'])) $meta_query = [$meta_query];
$tax_query = isset($args['tax_query']) ? $args['tax_query'] : []; $tax_query = isset($args['tax_query']) ? $args['tax_query'] : [];
@ -531,7 +539,9 @@ class REST_Items_Controller extends REST_Controller {
], 'OBJECT' ], 'OBJECT'
); );
if ( !empty($filter) ) { $filter_id = empty($filter) ? false : $filter[0]->get_id();
$filter_order_index = $order ? array_search( $filter_id, array_column( $order, 'id' ) ) : false;
if ( !empty($filter_order_index) && $order[$filter_order_index]['enabled'] == true) {
$f = $filter[0]->_toArray(); $f = $filter[0]->_toArray();
$filter_type_component = $filter[0]->get_filter_type_object()->get_component(); $filter_type_component = $filter[0]->get_filter_type_object()->get_component();
$m = $f['metadatum']; $m = $f['metadatum'];
@ -933,9 +943,11 @@ class REST_Items_Controller extends REST_Controller {
$item = $this->items_repository->trash($item); $item = $this->items_repository->trash($item);
} }
$prepared_item = $this->prepare_item_for_response($item, $request); $item_deleted = array(
'id' => $item_id
);
return new \WP_REST_Response($prepared_item, 200); return new \WP_REST_Response($item_deleted, 200);
} }
/** /**

View File

@ -159,7 +159,7 @@ class REST_Metadatum_Mappers_Controller extends REST_Controller {
} else { } else {
return new \WP_REST_Response([ return new \WP_REST_Response([
'error_message' => __('One or more values are invalid.', 'tainacan'), 'error_message' => __('One or more values are invalid.', 'tainacan'),
'errors' => $prepared->get_errors(), 'errors' => $metadatum->get_errors(),
'metadatum' => $this->prepare_item_for_response($prepared, $request), 'metadatum' => $this->prepare_item_for_response($prepared, $request),
], 400); ], 400);
} }

View File

@ -476,7 +476,8 @@ class Item_Metadata_Entity extends Entity {
if ($this->is_required()) { if ($this->is_required()) {
$validation_statuses = ['publish', 'future', 'private']; $validation_statuses = ['publish', 'future', 'private'];
if (in_array($item->get_status(), apply_filters( 'tainacan-status-require-validation', $validation_statuses) )) { if (in_array($item->get_status(), apply_filters( 'tainacan-status-require-validation', $validation_statuses) )) {
$this->add_error('required', $metadatum->get_name() . ' is required'); // translators: %s = metadatum name. ex: Title is required
$this->add_error( 'required', sprintf( __('%s is required', 'tainacan'), $metadatum->get_name() ) );
return false; return false;
} else { } else {
return $this->set_as_valid(); return $this->set_as_valid();

View File

@ -1076,7 +1076,8 @@ class Theme_Helper {
'data-max-items-per-screen' => true, 'data-max-items-per-screen' => true,
'data-space-between-items' => true, 'data-space-between-items' => true,
'data-space-around-carousel' => true, 'data-space-around-carousel' => true,
'data-tainacan-api-root' => true 'data-tainacan-api-root' => true,
'data-variable-items-width' => true
] ]
]; ];

View File

@ -520,11 +520,12 @@ class Migrations {
\deactivate_plugins( 'tainacan-url-metadata-type/tainacan-metadata-type-url.php' ); \deactivate_plugins( 'tainacan-url-metadata-type/tainacan-metadata-type-url.php' );
} }
static function alter_table_tnc_bg_process_add_uuid() { static function alter_table_tnc_bg_process_add_uuid_refactor() {
global $wpdb; global $wpdb;
// update default order by "creation_date" to "date" // update default order by "creation_date" to "date"
$table_name = $wpdb->prefix . 'tnc_bg_process'; $table_name = $wpdb->prefix . 'tnc_bg_process';
$column_exists = $wpdb->get_results( "SELECT COLUMN_NAME FROM INFORMATION_SCHEMA.COLUMNS WHERE table_name = '$table_name' AND column_name = 'bg_uuid'" ); $database_name = DB_NAME;
$column_exists = $wpdb->get_results( "SELECT COLUMN_NAME FROM INFORMATION_SCHEMA.COLUMNS WHERE table_name = '$table_name' AND column_name = 'bg_uuid' AND table_schema = '$database_name'" );
if(empty($column_exists)) { if(empty($column_exists)) {
$wpdb->query(" $wpdb->query("

View File

@ -2,9 +2,9 @@
Contributors: andrebenedito, daltonmartins, fabianobn, jacsonp, leogermani, weryques, wetah, eduardohumberto, ravipassos, jessicafpx, marinagiolo, omarceloavila, vnmedeiros, tainacan, suelanesilva, ccaio, alanargomes, ateneagarcia123, rodrigo0freire, clarandreozzi Contributors: andrebenedito, daltonmartins, fabianobn, jacsonp, leogermani, weryques, wetah, eduardohumberto, ravipassos, jessicafpx, marinagiolo, omarceloavila, vnmedeiros, tainacan, suelanesilva, ccaio, alanargomes, ateneagarcia123, rodrigo0freire, clarandreozzi
Tags: museums, archives, GLAM, collections, repository Tags: museums, archives, GLAM, collections, repository
Requires at least: 5.9 Requires at least: 5.9
Tested up to: 6.5 Tested up to: 6.6
Requires PHP: 7.0 Requires PHP: 7.0
Stable tag: 0.21.7 Stable tag: 0.21.8
License: GPLv2 or later License: GPLv2 or later
License URI: http://www.gnu.org/licenses/gpl-3.0.html License URI: http://www.gnu.org/licenses/gpl-3.0.html

View File

@ -5,17 +5,17 @@ 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. 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 Author: Tainacan.org
Author URI: https://tainacan.org/ Author URI: https://tainacan.org/
Version: 0.21.7 Version: 0.21.8
Requires at least: 5.9 Requires at least: 5.9
Tested up to: 6.5 Tested up to: 6.6
Requires PHP: 7.0 Requires PHP: 7.0
Stable tag: 0.21.7 Stable tag: 0.21.8
Text Domain: tainacan Text Domain: tainacan
License: GPLv2 or later License: GPLv2 or later
License URI: http://www.gnu.org/licenses/gpl-3.0.html License URI: http://www.gnu.org/licenses/gpl-3.0.html
*/ */
const TAINACAN_VERSION = '0.21.7'; const TAINACAN_VERSION = '0.21.8';
defined( 'ABSPATH' ) or die( 'No script kiddies please!' ); defined( 'ABSPATH' ) or die( 'No script kiddies please!' );
$TAINACAN_BASE_URL = plugins_url('', __FILE__); $TAINACAN_BASE_URL = plugins_url('', __FILE__);

View File

@ -411,7 +411,7 @@
</span> </span>
</label> </label>
<help-button <help-button
v-if="metadataSection.description" v-if="metadataSection.description && metadataSection.description_bellow_name !== 'yes'"
:title="metadataSection.name" :title="metadataSection.name"
:message="metadataSection.description" /> :message="metadataSection.description" />
</span> </span>
@ -422,7 +422,7 @@
class="metadata-section-metadata-list"> class="metadata-section-metadata-list">
<p <p
v-if="metadataSection.description && metadataSection.description_bellow_name == 'yes'" v-if="metadataSection.description && metadataSection.description_bellow_name == 'yes'"
class="metadatum-description-help-info"> class="metadata-section-description-help-info metadatum-description-help-info">
{{ metadataSection.description }} {{ metadataSection.description }}
</p> </p>
@ -1326,9 +1326,16 @@ export default {
parentMetaId: 0 parentMetaId: 0
}); });
} }
/**
if ( this.collection && this.item ) * Fires action tainacan_item_edition_item_loaded
wp.hooks.doAction('tainacan_item_edition_item_loaded', JSON.parse(JSON.stringify(this.collection)), JSON.parse(JSON.stringify(this.item))); * once the existing item is loaded. We cannot reliabilily send collection here since
* it is loaded async outside of the component.
*/
wp.hooks.doAction(
'tainacan_item_edition_item_loaded',
this.collection ? JSON.parse(JSON.stringify(this.collection)) : false,
this.item ? JSON.parse(JSON.stringify(this.item)) : false
);
// Loads metadata and attachments // Loads metadata and attachments
this.loadMetadata(); this.loadMetadata();
@ -1371,8 +1378,17 @@ export default {
} }
} }
if ( this.collection && this.item && metadata) /**
wp.hooks.doAction('tainacan_item_edition_metadata_loaded', JSON.parse(JSON.stringify(this.collection)), JSON.parse(JSON.stringify(this.item)), metadata); * Fires action tainacan_item_edition_metadata_loaded
* once the metadata is loaded. We cannot reliabilily send collection here since
* it is loaded async outside of the component.
*/
wp.hooks.doAction(
'tainacan_item_edition_metadata_loaded',
this.colection ? JSON.parse(JSON.stringify(this.collection)) : false,
this.item ? JSON.parse(JSON.stringify(this.item)) : false,
metadata ? metadata : []
);
this.isLoading = false; this.isLoading = false;
}); });
@ -1645,7 +1661,7 @@ export default {
'my-attachment-media-frame', { 'my-attachment-media-frame', {
button_labels: { button_labels: {
frame_title: this.$i18n.get('instruction_select_files_to_attach_to_item'), frame_title: this.$i18n.get('instruction_select_files_to_attach_to_item'),
frame_button: this.$i18n.get('label_attach_to_item'), frame_button: this.$i18n.get('finish'),
}, },
nonce: this.item.nonces ? this.item.nonces['update-post_' + this.item.id] : null, nonce: this.item.nonces ? this.item.nonces['update-post_' + this.item.id] : null,
relatedPostId: this.itemId, relatedPostId: this.itemId,
@ -1771,8 +1787,16 @@ export default {
if (this.form.document_options !== undefined && this.form.document_options['forced_iframe_height'] !== undefined) if (this.form.document_options !== undefined && this.form.document_options['forced_iframe_height'] !== undefined)
this.urlIframeHeight = this.form.document_options['forced_iframe_height']; this.urlIframeHeight = this.form.document_options['forced_iframe_height'];
if ( this.collection && this.item ) /**
wp.hooks.doAction('tainacan_item_edition_item_loaded', JSON.parse(JSON.stringify(this.collection)), JSON.parse(JSON.stringify(this.item))); * Fires action tainacan_item_edition_item_loaded
* once the existing item is loaded. We cannot reliabilily send collection here since
* it is loaded async outside of the component.
*/
wp.hooks.doAction(
'tainacan_item_edition_item_loaded',
this.collection ? JSON.parse(JSON.stringify(this.collection)) : false,
this.item ? JSON.parse(JSON.stringify(this.item)) : false
);
this.loadMetadata(); this.loadMetadata();
this.setLastUpdated(this.item.modification_date); this.setLastUpdated(this.item.modification_date);
@ -2041,6 +2065,9 @@ export default {
margin-left: 12px; margin-left: 12px;
} }
} }
.metadata-section-description-help-info {
margin: 0.25em 0 0 1.125rem;
}
.item-edition-tab-content .tab-item>.field:last-child { .item-edition-tab-content .tab-item>.field:last-child {
margin-bottom: 187px; margin-bottom: 187px;
} }

View File

@ -5,6 +5,7 @@
:disabled="disabled" :disabled="disabled"
:placeholder="itemMetadatum.metadatum.placeholder ? itemMetadatum.metadatum.placeholder : ''" :placeholder="itemMetadatum.metadatum.placeholder ? itemMetadatum.metadatum.placeholder : ''"
:model-value="value === 0 || value ? Number(value) : null" :model-value="value === 0 || value ? Number(value) : null"
:data-is-danger="!isInputValid"
lang="en" lang="en"
:min="getMin" :min="getMin"
:max="getMax" :max="getMax"
@ -26,6 +27,11 @@
'blur', 'blur',
'mobile-special-focus' 'mobile-special-focus'
], ],
data() {
return {
isInputValid: true
}
},
computed: { computed: {
getStep() { getStep() {
if (this.itemMetadatum && this.itemMetadatum.metadatum.metadata_type_options && this.itemMetadatum.metadatum.metadata_type_options.step) if (this.itemMetadatum && this.itemMetadatum.metadatum.metadata_type_options && this.itemMetadatum.metadatum.metadata_type_options.step)
@ -49,8 +55,13 @@
methods: { methods: {
onInput(value) { onInput(value) {
const inputRef = this.$refs['tainacan-item-metadatum_id-' + this.itemMetadatum.metadatum.id + (this.itemMetadatum.parent_meta_id ? ('_parent_meta_id-' + this.itemMetadatum.parent_meta_id) : '')]; const inputRef = this.$refs['tainacan-item-metadatum_id-' + this.itemMetadatum.metadatum.id + (this.itemMetadatum.parent_meta_id ? ('_parent_meta_id-' + this.itemMetadatum.parent_meta_id) : '')];
if ( inputRef && !inputRef.checkHtml5Validity())
if ( inputRef ) {
this.isInputValid = inputRef.checkHtml5Validity();
if ( !this.isInputValid )
return; return;
}
// Allowing empty value as a state different of 0 // Allowing empty value as a state different of 0
if ( value === null || value === undefined || value === '' ) if ( value === null || value === undefined || value === '' )

View File

@ -19,7 +19,9 @@
}" }"
class="has-text-secondary tainacan-icon tainacan-icon-1-25em" /> class="has-text-secondary tainacan-icon tainacan-icon-1-25em" />
</span> </span>
<label class="label"> <label
class="label"
:class="{ 'has-text-danger': errorMessage }">
<span <span
v-if="enumerateMetadatum" v-if="enumerateMetadatum"
style="opacity: 0.65;" style="opacity: 0.65;"
@ -237,7 +239,8 @@
(this.itemMetadatum && this.itemMetadatum.metadatum && this.itemMetadatum.metadatum.placeholder ? ' has-placeholder' : '') + (this.itemMetadatum && this.itemMetadatum.metadatum && this.itemMetadatum.metadatum.placeholder ? ' has-placeholder' : '') +
(this.itemMetadatum && this.itemMetadatum.metadatum && this.itemMetadatum.metadatum.description ? ' has-description' : '') + (this.itemMetadatum && this.itemMetadatum.metadatum && this.itemMetadatum.metadatum.description ? ' has-description' : '') +
(this.itemMetadatum && this.itemMetadatum.metadatum && this.itemMetadatum.metadatum.id ? ' tainacan-metadatum-id--' + this.itemMetadatum.metadatum.id : '') + (this.itemMetadatum && this.itemMetadatum.metadatum && this.itemMetadatum.metadatum.id ? ' tainacan-metadatum-id--' + this.itemMetadatum.metadatum.id : '') +
(this.isFocused ? ' is-focused' : ''); (this.isFocused ? ' is-focused' : '') +
(this.errorMessage ? ' is-danger' : '');
} }
}, },
created() { created() {

View File

@ -113,6 +113,21 @@
:title="$i18n.getHelperTitle('tainacan-taxonomy', 'do_not_dispaly_term_as_link')" :title="$i18n.getHelperTitle('tainacan-taxonomy', 'do_not_dispaly_term_as_link')"
:message="$i18n.getHelperMessage('tainacan-taxonomy', 'do_not_dispaly_term_as_link')" /> :message="$i18n.getHelperMessage('tainacan-taxonomy', 'do_not_dispaly_term_as_link')" />
</b-field> </b-field>
<b-field
:addons="false"
:label="$i18n.getHelperTitle('tainacan-taxonomy', 'link_filtered_by_current_collection')">
&nbsp;
<b-switch
v-model="link_filtered_by_current_collection"
size="is-small"
:disabled="do_not_dispaly_term_as_link == 'yes'"
true-value="yes"
false-value="no"
@update:model-value="emitValues()" />
<help-button
:title="$i18n.getHelperTitle('tainacan-taxonomy', 'link_filtered_by_current_collection')"
:message="$i18n.getHelperMessage('tainacan-taxonomy', 'link_filtered_by_current_collection')" />
</b-field>
<b-field :addons="false"> <b-field :addons="false">
<label class="label"> <label class="label">
{{ $i18n.getHelperTitle('tainacan-taxonomy', 'link_filtered_by_collections') }} {{ $i18n.getHelperTitle('tainacan-taxonomy', 'link_filtered_by_collections') }}
@ -127,7 +142,7 @@
:data="collections.filter((collection) => !link_filtered_by_collections.includes(collection.id) && (collectionSearchString ? (collection.name.toLowerCase().indexOf(collectionSearchString.toLowerCase()) >= 0) : true) )" :data="collections.filter((collection) => !link_filtered_by_collections.includes(collection.id) && (collectionSearchString ? (collection.name.toLowerCase().indexOf(collectionSearchString.toLowerCase()) >= 0) : true) )"
field="name" field="name"
attached attached
:disabled="do_not_dispaly_term_as_link == 'yes'" :disabled="link_filtered_by_current_collection === 'yes' || do_not_dispaly_term_as_link == 'yes'"
:remove-on-keys="[]" :remove-on-keys="[]"
:aria-close-label="$i18n.get('remove_value')" :aria-close-label="$i18n.get('remove_value')"
:class="{'has-selected': link_filtered_by_collections != undefined && link_filtered_by_collections != []}" :class="{'has-selected': link_filtered_by_collections != undefined && link_filtered_by_collections != []}"
@ -194,6 +209,7 @@
allow_new_terms: 'yes', allow_new_terms: 'yes',
hide_hierarchy_path: 'no', hide_hierarchy_path: 'no',
do_not_dispaly_term_as_link: 'no', do_not_dispaly_term_as_link: 'no',
link_filtered_by_current_collection: 'no',
link_filtered_by_collections: [], link_filtered_by_collections: [],
visible_options_list: false, visible_options_list: false,
input_type: 'tainacan-taxonomy-radio', input_type: 'tainacan-taxonomy-radio',
@ -265,6 +281,7 @@
this.taxonomy_id = this.value.taxonomy_id; this.taxonomy_id = this.value.taxonomy_id;
this.allow_new_terms = ( this.value.allow_new_terms ) ? this.value.allow_new_terms : 'no'; this.allow_new_terms = ( this.value.allow_new_terms ) ? this.value.allow_new_terms : 'no';
this.hide_hierarchy_path = ( this.value.hide_hierarchy_path ) ? this.value.hide_hierarchy_path : 'no'; this.hide_hierarchy_path = ( this.value.hide_hierarchy_path ) ? this.value.hide_hierarchy_path : 'no';
this.link_filtered_by_current_collection = ( this.value.link_filtered_by_current_collection ) ? this.value.link_filtered_by_current_collection : 'no';
this.do_not_dispaly_term_as_link = ( this.value.do_not_dispaly_term_as_link ) ? this.value.do_not_dispaly_term_as_link : 'no'; this.do_not_dispaly_term_as_link = ( this.value.do_not_dispaly_term_as_link ) ? this.value.do_not_dispaly_term_as_link : 'no';
if (this.metadatum && this.metadatum.multiple === 'no') { if (this.metadatum && this.metadatum.multiple === 'no') {
@ -331,6 +348,7 @@
input_type: this.input_type, input_type: this.input_type,
allow_new_terms: this.allow_new_terms, allow_new_terms: this.allow_new_terms,
visible_options_list: this.visible_options_list, visible_options_list: this.visible_options_list,
link_filtered_by_current_collection: this.link_filtered_by_current_collection,
link_filtered_by_collections: this.link_filtered_by_collections, link_filtered_by_collections: this.link_filtered_by_collections,
hide_hierarchy_path: this.hide_hierarchy_path, hide_hierarchy_path: this.hide_hierarchy_path,
do_not_dispaly_term_as_link: this.do_not_dispaly_term_as_link, do_not_dispaly_term_as_link: this.do_not_dispaly_term_as_link,

View File

@ -21,6 +21,7 @@ class Taxonomy extends Metadata_Type {
$this->set_default_options([ $this->set_default_options([
'allow_new_terms' => 'no', 'allow_new_terms' => 'no',
'link_filtered_by_current_collection' => 'no',
'link_filtered_by_collections' => [], 'link_filtered_by_collections' => [],
'input_type' => 'tainacan-taxonomy-radio', 'input_type' => 'tainacan-taxonomy-radio',
'hide_hierarchy_path' => 'no', 'hide_hierarchy_path' => 'no',
@ -100,6 +101,10 @@ class Taxonomy extends Metadata_Type {
'title' => __( 'Allow new terms', 'tainacan' ), 'title' => __( 'Allow new terms', 'tainacan' ),
'description' => __( 'Allows to create new terms directly on the item form.', 'tainacan' ), 'description' => __( 'Allows to create new terms directly on the item form.', 'tainacan' ),
], ],
'link_filtered_by_current_collection' => [
'title' => __( 'Link filtered by current collection', 'tainacan' ),
'description' => __( 'Links to term items list filtered by the collection of the current item instead of a repository level term items page.', 'tainacan' ),
],
'link_filtered_by_collections' => [ 'link_filtered_by_collections' => [
'title' => __( 'Link filtered by collections', 'tainacan' ), 'title' => __( 'Link filtered by collections', 'tainacan' ),
'description' => __( 'Links to term items list filtered by certain collections instead of repository level term items page.', 'tainacan' ), 'description' => __( 'Links to term items list filtered by certain collections instead of repository level term items page.', 'tainacan' ),
@ -173,6 +178,7 @@ class Taxonomy extends Metadata_Type {
case 'allow_new_terms': case 'allow_new_terms':
case 'do_not_dispaly_term_as_link': case 'do_not_dispaly_term_as_link':
case 'link_filtered_by_current_collection':
if ($option_value == 'yes') if ($option_value == 'yes')
$readable_option_value = __('Yes', 'tainacan'); $readable_option_value = __('Yes', 'tainacan');
else if ($option_value == 'no') else if ($option_value == 'no')
@ -374,7 +380,7 @@ class Taxonomy extends Metadata_Type {
if ( $term instanceof \Tainacan\Entities\Term ) { if ( $term instanceof \Tainacan\Entities\Term ) {
$return .= $prefix; $return .= $prefix;
$return .= $this->get_term_hierarchy_html($term); $return .= $this->get_term_hierarchy_html($term, $item_metadata->get_item());
$return .= $suffix; $return .= $suffix;
if ( $count <= $total ) { if ( $count <= $total ) {
@ -384,24 +390,24 @@ class Taxonomy extends Metadata_Type {
} }
} else { } else {
if ( $value instanceof \Tainacan\Entities\Term ) { if ( $value instanceof \Tainacan\Entities\Term ) {
$return .= $this->get_term_hierarchy_html($value); $return .= $this->get_term_hierarchy_html($value, $item_metadata->get_item());
} }
} }
return $return; return $return;
} }
private function get_term_hierarchy_html( \Tainacan\Entities\Term $term ) { private function get_term_hierarchy_html( \Tainacan\Entities\Term $term, \Tainacan\Entities\Item $item = null) {
if ( $this->get_option('hide_hierarchy_path') == 'yes' ) if ( $this->get_option('hide_hierarchy_path') == 'yes' )
return $this->term_to_html($term); return $this->term_to_html($term, $item);
$terms = []; $terms = [];
$terms[] = $this->term_to_html($term); $terms[] = $this->term_to_html($term, $item);
while ($term->get_parent() > 0) { while ($term->get_parent() > 0) {
$term = \Tainacan\Repositories\Terms::get_instance()->fetch( (int) $term->get_parent(), $term->get_taxonomy() ); $term = \Tainacan\Repositories\Terms::get_instance()->fetch( (int) $term->get_parent(), $term->get_taxonomy() );
$terms[] = $this->term_to_html($term); $terms[] = $this->term_to_html($term, $item);
} }
$terms = \array_reverse($terms); $terms = \array_reverse($terms);
@ -410,8 +416,8 @@ class Taxonomy extends Metadata_Type {
return \implode($glue, $terms); return \implode($glue, $terms);
} }
private function term_to_html($term) { private function term_to_html($term, \Tainacan\Entities\Item $item = null) {
$collections = $this->get_option( 'link_filtered_by_collections' ); $collections = ( isset($item) && $this->get_option( 'link_filtered_by_current_collection' ) === 'yes' ) ? [ $item->get_collection_id() ] : $this->get_option( 'link_filtered_by_collections' );
$do_not_display_term_as_link = $this->get_option('do_not_dispaly_term_as_link') == 'yes'; $do_not_display_term_as_link = $this->get_option('do_not_dispaly_term_as_link') == 'yes';
if ( !empty( $collections ) ) { if ( !empty( $collections ) ) {

View File

@ -1,5 +1,19 @@
<template> <template>
<section> <section>
<b-field :addons="false">
<label class="label is-inline">
{{ $i18n.getHelperTitle('tainacan-text', 'maxlength') }}
<help-button
:title="$i18n.getHelperTitle('tainacan-text', 'maxlength')"
:message="$i18n.getHelperMessage('tainacan-text', 'maxlength')" />
</label>
<b-numberinput
v-model="maxlength"
name="maxlength"
step="1"
min="0"
@update:model-value="onUpdateMaxlength" />
</b-field>
<b-field <b-field
:addons="false" :addons="false"
:label="$i18n.getHelperTitle('tainacan-text', 'display_suggestions')"> :label="$i18n.getHelperTitle('tainacan-text', 'display_suggestions')">
@ -39,21 +53,28 @@
data() { data() {
return { return {
displaySuggestions: String, displaySuggestions: String,
mask: String mask: String,
maxlength: [Number, null]
} }
}, },
created() { created() {
this.displaySuggestions = this.value && this.value.display_suggestions ? this.value.display_suggestions : 'no'; this.displaySuggestions = this.value && this.value.display_suggestions ? this.value.display_suggestions : 'no';
this.mask = this.value && this.value.mask ? this.value.mask : ''; this.mask = this.value && this.value.mask ? this.value.mask : '';
this.maxlength = this.value && this.value.maxlength ? Number(this.value.maxlength) : null;
}, },
methods: { methods: {
onUpdateDisplaySuggestions(value) { onUpdateDisplaySuggestions(value) {
this.displaySuggestions = value; this.displaySuggestions = value;
this.$emit('update:value', { display_suggestions: value, mask: value == 'yes' ? '' : this.mask }); this.$emit('update:value', { display_suggestions: value, mask: value == 'yes' ? '' : this.mask, maxlength: this.maxlength });
}, },
onUpdateMask(value) { onUpdateMask(value) {
this.mask = value; this.mask = value;
this.$emit('update:value', { display_suggestions: this.displaySuggestions, mask: value }); this.$emit('update:value', { display_suggestions: this.displaySuggestions, mask: value, maxlength: this.maxlength });
},
onUpdateMaxlength(value) {
if (value == 0) value = null;
this.$emit('update:value', { maxlength: value, display_suggestions: this.displaySuggestions, mask: this.mask });
} }
} }
} }

View File

@ -10,10 +10,17 @@
:disabled="disabled" :disabled="disabled"
:value="value" :value="value"
:placeholder="itemMetadatum.metadatum.placeholder ? itemMetadatum.metadatum.placeholder : ''" :placeholder="itemMetadatum.metadatum.placeholder ? itemMetadatum.metadatum.placeholder : ''"
:maxlength="getMaxlength"
@focus="onMobileSpecialFocus" @focus="onMobileSpecialFocus"
@complete="($event) => getMask ? onInput($event.detail.value) : null" @complete="($event) => getMask ? onInput($event.detail.value) : null"
@input="($event) => getMask ? null : onInput($event.target.value)" @input="($event) => getMask ? null : onInput($event.target.value)"
@blur="onBlur"> @blur="onBlur">
<small
v-if="getMaxlength"
class="help counter"
:class="{ 'is-invisible': !isInputFocused }">
{{ value.length }} / {{ getMaxlength }}
</small>
</div> </div>
<b-autocomplete <b-autocomplete
v-else v-else
@ -26,6 +33,7 @@
clearable clearable
:placeholder="itemMetadatum.metadatum.placeholder ? itemMetadatum.metadatum.placeholder : ''" :placeholder="itemMetadatum.metadatum.placeholder ? itemMetadatum.metadatum.placeholder : ''"
check-infinite-scroll check-infinite-scroll
:maxlength="getMaxlength"
@blur="onBlur" @blur="onBlur"
@update:model-value="($event) => { search($event); }" @update:model-value="($event) => { search($event); }"
@select="onSelect" @select="onSelect"
@ -81,7 +89,8 @@
totalFacets: 0, totalFacets: 0,
query: '', query: '',
currentCollectionId: '', currentCollectionId: '',
filter: undefined filter: undefined,
isInputFocused: false,
} }
}, },
computed: { computed: {
@ -99,6 +108,12 @@
}; };
else else
return false; return false;
},
getMaxlength() {
if ( this.itemMetadatum && this.itemMetadatum.metadatum.metadata_type_options && this.itemMetadatum.metadatum.metadata_type_options.maxlength !== null && this.itemMetadatum.metadatum.metadata_type_options.maxlength !== undefined && this.itemMetadatum.metadatum.metadata_type_options.maxlength !== '' )
return Number(this.itemMetadatum.metadatum.metadata_type_options.maxlength);
else
return undefined;
} }
}, },
created() { created() {
@ -108,9 +123,14 @@
}, },
methods: { methods: {
onInput(value) { onInput(value) {
const inputRef = this.$refs['tainacan-item-metadatum_id-' + this.itemMetadatum.metadatum.id + (this.itemMetadatum.parent_meta_id ? ('_parent_meta_id-' + this.itemMetadatum.parent_meta_id) : '')];
if ( inputRef && this.getMaxlength && !inputRef.checkHtml5Validity() )
return;
this.$emit('update:value', value); this.$emit('update:value', value);
}, },
onBlur() { onBlur() {
this.isInputFocused = false;
this.$emit('blur'); this.$emit('blur');
}, },
onSelect(option){ onSelect(option){
@ -190,6 +210,7 @@
this.search(this.searchQuery); this.search(this.searchQuery);
}, 250), }, 250),
onMobileSpecialFocus() { onMobileSpecialFocus() {
this.isInputFocused = true;
this.$emit('mobile-special-focus'); this.$emit('mobile-special-focus');
} }
} }

View File

@ -42,6 +42,10 @@ class Text extends Metadata_Type {
__( 'Define a pattern of fixed characters that will be used to mask the input. Learn how to build mask patterns <a target="_blank" href="%1$s">here</a>.', 'tainacan' ), __( 'Define a pattern of fixed characters that will be used to mask the input. Learn how to build mask patterns <a target="_blank" href="%1$s">here</a>.', 'tainacan' ),
'https://imask.js.org/guide.html#masked-pattern' 'https://imask.js.org/guide.html#masked-pattern'
) )
],
'maxlength' => [
'title' => __( 'Maximum of characters', 'tainacan' ),
'description' => __( 'Limits the character input to a maximum value an displays a counter.', 'tainacan' ),
] ]
]; ];
} }

View File

@ -84,7 +84,7 @@
], ],
data() { data() {
return { return {
selectedStatus: !this.$adminOptions.hideItemEditionStatusPublishOption ? 'publish' : 'private' selectedStatus: this.currentUserCanPublish ? ( !this.$adminOptions.hideItemEditionStatusPublishOption ? 'publish' : 'private' ) : 'draft'
} }
}, },
computed: { computed: {

View File

@ -51,7 +51,9 @@
<template <template
v-for="(taxonomyFilter, key, index) of taxonomyFilters" v-for="(taxonomyFilter, key, index) of taxonomyFilters"
:key="index"> :key="index">
<div v-if="key == 'repository-filters'"> <div
v-if="key == 'repository-filters'"
class="repository-level-filters">
<div <div
v-if="taxonomyFilter.length > 0 && taxonomyFiltersCollectionNames != undefined && taxonomyFiltersCollectionNames[key] != undefined" v-if="taxonomyFilter.length > 0 && taxonomyFiltersCollectionNames != undefined && taxonomyFiltersCollectionNames[key] != undefined"
v-tooltip="{ v-tooltip="{
@ -95,7 +97,10 @@
<template <template
v-for="(taxonomyFilter, key, index) of taxonomyFilters" v-for="(taxonomyFilter, key, index) of taxonomyFilters"
:key="index"> :key="index">
<div v-if="key != 'repository-filters'"> <div
v-if="key != 'repository-filters'"
:id="'filters-from-collection-id-' + key"
class="collection-level-filters">
<div <div
v-if="taxonomyFilter.length > 0 && taxonomyFiltersCollectionNames != undefined && taxonomyFiltersCollectionNames[key] != undefined" v-if="taxonomyFilter.length > 0 && taxonomyFiltersCollectionNames != undefined && taxonomyFiltersCollectionNames[key] != undefined"
v-tooltip="{ v-tooltip="{
@ -143,7 +148,9 @@
<template <template
v-for="(repositoryCollectionFilter, key, index) of repositoryCollectionFilters" v-for="(repositoryCollectionFilter, key, index) of repositoryCollectionFilters"
:key="index"> :key="index">
<div v-if="key == 'repository-filters'"> <div
v-if="key == 'repository-filters'"
class="repository-level-filters">
<div <div
v-if="repositoryCollectionFilter.length > 0 && repositoryCollectionNames != undefined && repositoryCollectionNames[key] != undefined" v-if="repositoryCollectionFilter.length > 0 && repositoryCollectionNames != undefined && repositoryCollectionNames[key] != undefined"
v-tooltip="{ v-tooltip="{
@ -187,7 +194,10 @@
<template <template
v-for="(repositoryCollectionFilter, key, index) of repositoryCollectionFilters" v-for="(repositoryCollectionFilter, key, index) of repositoryCollectionFilters"
:key="index"> :key="index">
<div v-if="key != 'repository-filters'"> <div
v-if="key != 'repository-filters'"
:id="'filters-from-collection-id-' + key"
class="collection-level-filters">
<div <div
v-if="repositoryCollectionFilter.length > 0 && repositoryCollectionNames != undefined && repositoryCollectionNames[key] != undefined" v-if="repositoryCollectionFilter.length > 0 && repositoryCollectionNames != undefined && repositoryCollectionNames[key] != undefined"
v-tooltip="{ v-tooltip="{

View File

@ -426,8 +426,15 @@ StatusHelperPlugin.install = function (app, options = {}) {
}); });
this.statuses.push({ this.statuses.push({
name: tainacan_plugin.i18n['status_trash'], name: tainacan_plugin.i18n['status_trash'],
slug: 'trash'} slug: 'trash'
); });
/**
* Filter the available status in Tainacan admin.
*
* @param array Array of objects containing slug and name of each status.
*/
this.statuses = wp.hooks.applyFilters('tainacan_admin_available_statuses', JSON.parse(JSON.stringify(this.statuses)));
}) })
.catch(error => { .catch(error => {
console.error( error ); console.error( error );

View File

@ -2,45 +2,6 @@ export default {
// AttachmentControl: requires upload of new files and accepts multiple files // AttachmentControl: requires upload of new files and accepts multiple files
attachmentControl: wp.customize.MediaControl.extend({ attachmentControl: wp.customize.MediaControl.extend({
/**
* Set up gallery toolbar.
*
* @return {void}
*/
galleryToolbar() {
this.toolbar.set(
new wp.media.view.Toolbar( {
controller: this,
items: {
insert: {
style: 'primary',
text: wp.media.view.l10n.update,
priority: 80,
requires: { library: true },
/**
* @fires wp.media.controller.State#select
*/
click() {
const controller = this.controller,
state = controller.state();
controller.close();
state.trigger(
'select',
state.get( 'library' )
);
// Restore and reset the default state.
controller.setState( controller.options.state );
controller.reset();
},
},
},
} )
);
},
/** /**
* Create a media modal select frame, and store it so the instance can be reused when needed. * Create a media modal select frame, and store it so the instance can be reused when needed.
*/ */
@ -54,6 +15,9 @@ export default {
wp.media.model.settings.post.id = parseInt(this.params.relatedPostId); wp.media.model.settings.post.id = parseInt(this.params.relatedPostId);
this.frame = wp.media({ this.frame = wp.media({
button: {
text: this.params.button_labels.frame_button
},
states: [ states: [
new wp.media.controller.Library({ new wp.media.controller.Library({
title: this.params.button_labels.frame_title, title: this.params.button_labels.frame_title,
@ -64,7 +28,6 @@ export default {
posts_per_page: -1, posts_per_page: -1,
query: true query: true
}), }),
toolbar: 'main-gallery',
autoSelect: true, autoSelect: true,
sortable: true, sortable: true,
filterable: 'unattached', filterable: 'unattached',
@ -72,8 +35,6 @@ export default {
] ]
}).open(); }).open();
this.frame.on( 'toolbar:create:main-gallery', this.galleryToolbar, this.frame );
this.frame.$el.addClass( 'tainacan-item-attachments-modal' ); this.frame.$el.addClass( 'tainacan-item-attachments-modal' );
this.frame.$el['tainacan-document-id'] = this.params.document; this.frame.$el['tainacan-document-id'] = this.params.document;
this.frame.$el['tainacan-thumbnail-id'] = this.params.thumbnailId; this.frame.$el['tainacan-thumbnail-id'] = this.params.thumbnailId;

View File

@ -10,6 +10,10 @@
&.has-icons-left { &.has-icons-left {
.icon { .icon {
height: 100%; height: 100%;
&:has(+.help) { // Maxlenght count in autocomplete needs this
height: 32px;
}
.mdi::before { .mdi::before {
color: var(--tainacan-info-color); color: var(--tainacan-info-color);
display: inline-block; display: inline-block;

View File

@ -44,11 +44,28 @@
color: var(--tainacan-danger); color: var(--tainacan-danger);
} }
} }
.input, .textarea { .field.is-danger {
&.is-danger { .input,
background-color: var(--tainacan-red1); .textarea,
.select,
.dropdown,
.autocomplete {
--tainacan-input-border-color: var(--tainacan-danger);
--tainacan-input-background-color: var(--tainacan-red1);
} }
} }
.input,
.textarea,
.select {
&.is-danger {
--tainacan-input-border-color: var(--tainacan-danger);
--tainacan-input-background-color: var(--tainacan-red1);
}
}
input[data-is-danger="true"] {
--tainacan-input-border-color: var(--tainacan-danger);
--tainacan-input-background-color: var(--tainacan-red1);
}
a[disabled="true"] { a[disabled="true"] {
opacity: 0.5; opacity: 0.5;
user-select: none; user-select: none;

View File

@ -125,6 +125,10 @@
"collectionTextColor": { "collectionTextColor": {
"type": "string", "type": "string",
"default": "#ffffff" "default": "#ffffff"
},
"variableItemsWidth": {
"type": "boolean",
"default": false
} }
}, },
"supports": { "supports": {

View File

@ -1,6 +1,181 @@
const { useBlockProps } = (tainacan_blocks.wp_version < '5.2' ? wp.editor : wp.blockEditor ); const { useBlockProps } = (tainacan_blocks.wp_version < '5.2' ? wp.editor : wp.blockEditor );
export default [ export default [
/* Deprecated on version 0.21.7 to add variableItemsWidth feature */
{
"attributes": {
"content": {
"type": "array",
"source": "children",
"selector": "div"
},
"collectionId": {
"type": "string",
"default": ""
},
"items": {
"type": "array",
"default": []
},
"isModalOpen": {
"type": "boolean",
"default": false
},
"searchURL": {
"type": "string",
"default": ""
},
"selectedItems": {
"type": "array",
"default": []
},
"itemsRequestSource": {
"type": "string",
"default": ""
},
"maxItemsNumber": {
"type": "number",
"default": 12
},
"maxItemsPerScreen": {
"type": "number",
"default": 7
},
"spaceBetweenItems": {
"type": "number",
"default": 32
},
"spaceAroundCarousel": {
"type": "number",
"default": 50
},
"isLoading": {
"type": "boolean",
"default": false
},
"isLoadingCollection": {
"type": "boolean",
"default": false
},
"loadStrategy": {
"type": "string",
"default": "search"
},
"arrowsPosition": {
"type": "string",
"default": "around"
},
"largeArrows": {
"type": "boolean",
"default": false
},
"arrowsStyle": {
"type": "string",
"default": "type-1"
},
"autoPlay": {
"type": "boolean",
"default": false
},
"autoPlaySpeed": {
"type": "number",
"default": 3
},
"loopSlides": {
"type": "boolean",
"default": false
},
"hideTitle": {
"type": "boolean",
"default": true
},
"showCollectionHeader": {
"type": "boolean",
"default": false
},
"showCollectionLabel": {
"type": "boolean",
"default": false
},
"imageSize": {
"type": "string",
"default": "tainacan-medium"
},
"collection": {
"type": "object",
"default": {}
},
"blockId": {
"type": "string",
"default": ""
},
"collectionBackgroundColor": {
"type": "string",
"default": "#373839"
},
"collectionTextColor": {
"type": "string",
"default": "#ffffff"
}
},
save: function ({ attributes }) {
const {
content,
blockId,
collectionId,
searchURL,
selectedItems,
arrowsPosition,
largeArrows,
arrowsStyle,
loadStrategy,
maxItemsNumber,
maxItemsPerScreen,
spaceBetweenItems,
spaceAroundCarousel,
autoPlay,
autoPlaySpeed,
loopSlides,
hideTitle,
imageSize,
showCollectionHeader,
showCollectionLabel,
collectionBackgroundColor,
collectionTextColor
} = attributes;
// Gets attributes such as style, that are automatically added by the editor hook
const blockProps = useBlockProps.save();
return <div
{ ...blockProps }
data-module="carousel-items-list"
data-search-url={ searchURL }
data-selected-items={ JSON.stringify(selectedItems) }
data-arrows-position={ arrowsPosition }
data-load-strategy={ loadStrategy }
data-collection-id={ collectionId }
data-auto-play={ '' + autoPlay }
data-auto-play-speed={ autoPlaySpeed }
data-loop-slides={ '' + loopSlides }
data-hide-title={ '' + hideTitle }
data-large-arrows={ '' + largeArrows }
data-arrows-style={ arrowsStyle }
data-image-size={ imageSize }
data-show-collection-header={ '' + showCollectionHeader }
data-show-collection-label={ '' + showCollectionLabel }
data-collection-background-color={ collectionBackgroundColor }
data-collection-text-color={ collectionTextColor }
data-max-items-number={ maxItemsNumber }
data-max-items-per-screen={ maxItemsPerScreen }
data-space-between-items={ spaceBetweenItems }
data-space-around-carousel={ spaceAroundCarousel }
data-tainacan-api-root={ tainacan_blocks.root }
id={ 'wp-block-tainacan-carousel-items-list_' + blockId }>
{ content }
</div>
}
},
/* Deprecated during Vue 3 migration to prepend attributes with data- */ /* Deprecated during Vue 3 migration to prepend attributes with data- */
{ {
"attributes": { "attributes": {

View File

@ -43,7 +43,8 @@ export default function({ attributes, setAttributes, isSelected, clientId }){
isLoadingCollection, isLoadingCollection,
collection, collection,
collectionBackgroundColor, collectionBackgroundColor,
collectionTextColor collectionTextColor,
variableItemsWidth
} = attributes; } = attributes;
// Gets blocks props from hook // Gets blocks props from hook
@ -67,6 +68,10 @@ export default function({ attributes, setAttributes, isSelected, clientId }){
imageSize = 'tainacan-medium'; imageSize = 'tainacan-medium';
setAttributes({ imageSize: imageSize }); setAttributes({ imageSize: imageSize });
} }
if (variableItemsWidth === undefined) {
variableItemsWidth = false;
setAttributes({ variableItemsWidth: variableItemsWidth });
}
// Get available image sizes // Get available image sizes
const { imageSizes } = useSelect( const { imageSizes } = useSelect(
@ -89,7 +94,7 @@ export default function({ attributes, setAttributes, isSelected, clientId }){
return ( return (
<li <li
key={ item.id } key={ item.id }
className={ 'swiper-slide item-list-item ' + (maxItemsPerScreen ? ' max-items-per-screen-' + maxItemsPerScreen : '') + (['tainacan-medium', 'tainacan-small'].indexOf(imageSize) > -1 ? ' is-forced-square' : '') }> className={ 'swiper-slide item-list-item ' + ( variableItemsWidth ? ' variable-item-width' : '') + (!variableItemsWidth && maxItemsPerScreen ? ' max-items-per-screen-' + maxItemsPerScreen : '') + (['tainacan-medium', 'tainacan-small'].indexOf(imageSize) > -1 ? ' is-forced-square' : '') }>
{ loadStrategy == 'selection' ? { loadStrategy == 'selection' ?
<Button <Button
onClick={ () => removeItemOfId(item.id) } onClick={ () => removeItemOfId(item.id) }
@ -105,11 +110,13 @@ export default function({ attributes, setAttributes, isSelected, clientId }){
href={ item.url }> href={ item.url }>
<div className="items-list-item--image-wrap"> <div className="items-list-item--image-wrap">
<img <img
height={ thumbHelper.getHeight(item['thumbnail'], imageSize) }
width={ thumbHelper.getWidth(item['thumbnail'], imageSize) }
src={ thumbHelper.getSrc(item['thumbnail'], imageSize, item['document_mimetype']) } src={ thumbHelper.getSrc(item['thumbnail'], imageSize, item['document_mimetype']) }
srcSet={ thumbHelper.getSrcSet(item['thumbnail'], imageSize, item['document_mimetype']) } srcSet={ thumbHelper.getSrcSet(item['thumbnail'], imageSize, item['document_mimetype']) }
alt={ item.thumbnail_alt ? item.thumbnail_alt : (item && item.title ? item.title : __( 'Thumbnail', 'tainacan' )) }/> alt={ item.thumbnail_alt ? item.thumbnail_alt : (item && item.title ? item.title : __( 'Thumbnail', 'tainacan' )) }/>
</div> </div>
{ !hideTitle ? <span>{ item.title ? item.title : '' }</span> : null } { !hideTitle ? <span style={{ maxWidth: variableItemsWidth ? thumbHelper.getWidth(item['thumbnail'], imageSize) + 'px' : 'unset' }}>{ item.title ? item.title : '' }</span> : null }
</a> </a>
</li> </li>
); );
@ -375,8 +382,19 @@ export default function({ attributes, setAttributes, isSelected, clientId }){
initialOpen={ true } initialOpen={ true }
> >
<div> <div>
<ToggleControl
label={__('Variable items width', 'tainacan')}
help={ !variableItemsWidth ? __('Toggle to define each slide size based on its content natural width.', 'tainacan') : __('Toggle to define a fixed amount of items that should appear per screen size.', 'tainacan')}
checked={ variableItemsWidth }
onChange={ ( isChecked ) => {
variableItemsWidth = isChecked;
setAttributes({ variableItemsWidth: variableItemsWidth });
setContent();
}
}
/>
{ {
loadStrategy != 'parent' ? loadStrategy != 'parent' && variableItemsWidth !== true ?
<RangeControl <RangeControl
label={ __('Maximum items per slide on a wide screen', 'tainacan') } label={ __('Maximum items per slide on a wide screen', 'tainacan') }
help={ maxItemsPerScreen <= 4 ? __('Warning: with such a small number of items per slide, the image size is greater, thus the cropped version of the thumbnail won\'t be used.', 'tainacan') : null } help={ maxItemsPerScreen <= 4 ? __('Warning: with such a small number of items per slide, the image size is greater, thus the cropped version of the thumbnail won\'t be used.', 'tainacan') : null }

View File

@ -23,7 +23,8 @@ export default function ({ attributes }) {
showCollectionHeader, showCollectionHeader,
showCollectionLabel, showCollectionLabel,
collectionBackgroundColor, collectionBackgroundColor,
collectionTextColor collectionTextColor,
variableItemsWidth
} = attributes; } = attributes;
// Gets attributes such as style, that are automatically added by the editor hook // Gets attributes such as style, that are automatically added by the editor hook
@ -53,6 +54,7 @@ export default function ({ attributes }) {
data-space-between-items={ spaceBetweenItems } data-space-between-items={ spaceBetweenItems }
data-space-around-carousel={ spaceAroundCarousel } data-space-around-carousel={ spaceAroundCarousel }
data-tainacan-api-root={ tainacan_blocks.root } data-tainacan-api-root={ tainacan_blocks.root }
data-variable-items-width={ '' + variableItemsWidth }
id={ 'wp-block-tainacan-carousel-items-list_' + blockId }> id={ 'wp-block-tainacan-carousel-items-list_' + blockId }>
{ content } { content }
</div> </div>

View File

@ -302,10 +302,16 @@
scroll-snap-align: start; scroll-snap-align: start;
scroll-margin: 0 calc(var(--spaceBetweenItems, 32px) / 2 ); scroll-margin: 0 calc(var(--spaceBetweenItems, 32px) / 2 );
&.variable-item-width {
min-width: unset !important;
width: auto !important;
}
a { a {
color: inherit; color: inherit;
font-weight: bold; font-weight: bold;
line-height: normal; line-height: normal;
pointer-events: none;
&>span { &>span {
color: inherit; color: inherit;

View File

@ -44,7 +44,8 @@ export default (element) => {
showCollectionLabel: getDataAttribute(block, 'show-collection-label', false) == 'true', showCollectionLabel: getDataAttribute(block, 'show-collection-label', false) == 'true',
collectionBackgroundColor: getDataAttribute(block, 'collection-background-color', '#373839'), collectionBackgroundColor: getDataAttribute(block, 'collection-background-color', '#373839'),
collectionTextColor: getDataAttribute(block, 'collection-text-color', '#ffffff'), collectionTextColor: getDataAttribute(block, 'collection-text-color', '#ffffff'),
tainacanApiRoot: getDataAttribute(block, 'tainacan-api-root') tainacanApiRoot: getDataAttribute(block, 'tainacan-api-root'),
variableItemsWidth: getDataAttribute(block, 'variable-items-width', false) == 'true',
}); });
}, },
mounted() { mounted() {

View File

@ -62,6 +62,7 @@
v-for="index in 18" v-for="index in 18"
:key="index" :key="index"
role="listitem" role="listitem"
:style="variableItemsWidth ? 'width: auto;' : ''"
class="swiper-slide collection-list-item skeleton"> class="swiper-slide collection-list-item skeleton">
<a> <a>
<img> <img>
@ -80,6 +81,7 @@
v-for="(item, index) of items" v-for="(item, index) of items"
:key="index" :key="index"
role="listitem" role="listitem"
:style="variableItemsWidth ? 'width: auto;' : ''"
class="swiper-slide item-list-item" class="swiper-slide item-list-item"
:class="{ 'is-forced-square': ['tainacan-medium', 'tainacan-small'].indexOf(imageSize) > -1 }"> :class="{ 'is-forced-square': ['tainacan-medium', 'tainacan-small'].indexOf(imageSize) > -1 }">
<a <a
@ -93,7 +95,11 @@
:hash="$thumbHelper.getBlurhashString(item['thumbnail'], imageSize)" :hash="$thumbHelper.getBlurhashString(item['thumbnail'], imageSize)"
:alt="item.thumbnail_alt ? item.thumbnail_alt : (item && item.title ? item.title : wpI18n( 'Thumbnail', 'tainacan' ))" :alt="item.thumbnail_alt ? item.thumbnail_alt : (item && item.title ? item.title : wpI18n( 'Thumbnail', 'tainacan' ))"
:transition-duration="500" /> :transition-duration="500" />
<span v-if="!hideTitle">{{ item.title ? item.title : '' }}</span> <span
v-if="!hideTitle"
:style="variableItemsWidth ? ('max-width: ' + $thumbHelper.getWidth(item['thumbnail'], imageSize) + 'px') : ''">
{{ item.title ? item.title : '' }}
</span>
</a> </a>
</li> </li>
</ul> </ul>
@ -179,7 +185,8 @@ export default {
showCollectionLabel: Boolean, showCollectionLabel: Boolean,
collectionBackgroundColor: String, collectionBackgroundColor: String,
collectionTextColor: String, collectionTextColor: String,
tainacanApiRoot: String tainacanApiRoot: String,
variableItemsWidth: Boolean
}, },
data() { data() {
return { return {
@ -334,7 +341,7 @@ export default {
const self = this; const self = this;
const spaceBetween = Number(self.spaceBetweenItems); const spaceBetween = Number(self.spaceBetweenItems);
const slidesPerView = Number(self.maxItemsPerScreen); const slidesPerView = Number(self.maxItemsPerScreen);
this.swiper = new Swiper('#' + self.blockId + '-carousel', { let swiperOptions = {
watchOverflow: true, watchOverflow: true,
mousewheel: { mousewheel: {
forceToAxis: true forceToAxis: true
@ -343,7 +350,7 @@ export default {
preventInteractionOnTransition: true, preventInteractionOnTransition: true,
allowClick: true, allowClick: true,
allowTouchMove: true, allowTouchMove: true,
slidesPerView: 1, slidesPerView: self.variableItemsWidth ? 'auto' : 1,
slidesPerGroup: 1, slidesPerGroup: 1,
spaceBetween: spaceBetween, spaceBetween: spaceBetween,
slideToClickedSlide: true, slideToClickedSlide: true,
@ -351,7 +358,18 @@ export default {
nextEl: '#' + self.blockId + '-next', nextEl: '#' + self.blockId + '-next',
prevEl: '#' + self.blockId + '-prev', prevEl: '#' + self.blockId + '-prev',
}, },
breakpoints: (!isNaN(self.maxItemsPerScreen) && self.maxItemsPerScreen != 6) ? { autoplay: self.autoPlay ? { delay: self.autoPlaySpeed*1000 } : false,
loop: self.loopSlides ? self.loopSlides : false,
a11y: {
prevSlideMessage: wp.i18n.__( 'Previous slide', 'tainacan'),
nextSlideMessage: wp.i18n.__( 'Next slide', 'tainacan'),
firstSlideMessage: wp.i18n.__('This is the first slide', 'tainacan'),
lastSlideMessage: wp.i18n.__('This is the last slide', 'tainacan')
},
modules: [Autoplay, Navigation, A11y]
}
if ( !self.variableItemsWidth ) {
swiperOptions.breakpoints = (!isNaN(self.maxItemsPerScreen) && self.maxItemsPerScreen != 6) ? {
498: { slidesPerView: slidesPerView - 4 > 0 ? slidesPerView - 4 : 1, spaceBetween: spaceBetween }, 498: { slidesPerView: slidesPerView - 4 > 0 ? slidesPerView - 4 : 1, spaceBetween: spaceBetween },
768: { slidesPerView: slidesPerView - 3 > 0 ? slidesPerView - 3 : 1, spaceBetween: spaceBetween }, 768: { slidesPerView: slidesPerView - 3 > 0 ? slidesPerView - 3 : 1, spaceBetween: spaceBetween },
1024: { slidesPerView: slidesPerView - 2 > 0 ? slidesPerView - 2 : 1, spaceBetween: spaceBetween }, 1024: { slidesPerView: slidesPerView - 2 > 0 ? slidesPerView - 2 : 1, spaceBetween: spaceBetween },
@ -363,17 +381,9 @@ export default {
1024: { slidesPerView: 4, spaceBetween: spaceBetween }, 1024: { slidesPerView: 4, spaceBetween: spaceBetween },
1366: { slidesPerView: 5, spaceBetween: spaceBetween }, 1366: { slidesPerView: 5, spaceBetween: spaceBetween },
1600: { slidesPerView: 6, spaceBetween: spaceBetween } 1600: { slidesPerView: 6, spaceBetween: spaceBetween }
}, }
autoplay: self.autoPlay ? { delay: self.autoPlaySpeed*1000 } : false, }
loop: self.loopSlides ? self.loopSlides : false, this.swiper = new Swiper('#' + self.blockId + '-carousel', swiperOptions);
a11y: {
prevSlideMessage: wp.i18n.__( 'Previous slide', 'tainacan'),
nextSlideMessage: wp.i18n.__( 'Next slide', 'tainacan'),
firstSlideMessage: wp.i18n.__('This is the first slide', 'tainacan'),
lastSlideMessage: wp.i18n.__('This is the last slide', 'tainacan')
},
modules: [Autoplay, Navigation, A11y]
});
} }
} }
} }

View File

@ -490,7 +490,7 @@
<p <p
v-if="metadataSection.description && (!hideHelpButtons && helpInfoBellowLabel)" v-if="metadataSection.description && (!hideHelpButtons && helpInfoBellowLabel)"
class="metadatum-description-help-info"> class="metadata-section-description-help-info metadatum-description-help-info">
{{ metadataSection.description }} {{ metadataSection.description }}
</p> </p>
<template v-if="itemMetadata && Array.isArray(itemMetadata)"> <template v-if="itemMetadata && Array.isArray(itemMetadata)">
@ -1201,12 +1201,10 @@ export default {
return this.conditionalSections[sectionId] && this.conditionalSections[sectionId].hide; return this.conditionalSections[sectionId] && this.conditionalSections[sectionId].hide;
}, },
getMetadatumOrderInSection(sectionIndex, metadatum) { getMetadatumOrderInSection(sectionIndex, metadatum) {
if ( !this.collectionMetadataSectionOrder || !Array.isArray(this.collectionMetadataSectionOrder) || !this.collectionMetadataSectionOrder[sectionIndex] || !Array.isArray(this.collectionMetadataSectionOrder[sectionIndex]['metadata_order']) )
if ( !this.collection || !Array.isArray(this.collection['metadata_section_order']) || !this.collection['metadata_section_order'][sectionIndex] || !Array.isArray(this.collection['metadata_section_order'][sectionIndex]['metadata_order']) )
return -1; return -1;
let enabledMetadataInSection = []; let enabledMetadataInSection = [];
for (let metadatum of this.collection['metadata_section_order'][sectionIndex]['metadata_order']) { for (let metadatum of this.collectionMetadataSectionOrder[sectionIndex]['metadata_order']) {
if ( metadatum.enabled ) if ( metadatum.enabled )
enabledMetadataInSection.push(metadatum.id); enabledMetadataInSection.push(metadatum.id);
} }
@ -1498,6 +1496,11 @@ export default {
} }
} }
.metadata-section-description-help-info {
margin: 0.25em 0 0 1.125rem;
}
.metadatum-description-help-info { .metadatum-description-help-info {
font-size: 0.75em; font-size: 0.75em;
color: var(--tainacan-info-color); color: var(--tainacan-info-color);

View File

@ -114,7 +114,7 @@ class TAINACAN_REST_Items_Controller extends TAINACAN_UnitApiTestCase {
$data = $response->get_data(); $data = $response->get_data();
$this->assertEquals($item1->get_title(), $data['title']); $this->assertEquals($item1->get_id(), $data['id']);
$post_meta = get_post_meta($item1->get_id(), '_wp_trash_meta_status', true); $post_meta = get_post_meta($item1->get_id(), '_wp_trash_meta_status', true);
@ -147,7 +147,7 @@ class TAINACAN_REST_Items_Controller extends TAINACAN_UnitApiTestCase {
$data = $response->get_data(); $data = $response->get_data();
$this->assertEquals($item2->get_title(), $data['title']); $this->assertEquals($item2->get_id(), $data['id']);
$no_post = get_post($item2->get_id()); $no_post = get_post($item2->get_id());

View File

@ -643,6 +643,7 @@ class Taxonomies extends TAINACAN_UnitTestCase {
'metadata_type_options' => [ 'metadata_type_options' => [
'taxonomy_id' => $tax->get_id(), 'taxonomy_id' => $tax->get_id(),
'allow_new_terms' => 'no', 'allow_new_terms' => 'no',
'link_filtered_by_current_collection' => 'no',
'link_filtered_by_collections' => [$collectionOnly->get_id()] 'link_filtered_by_collections' => [$collectionOnly->get_id()]
] ]
), ),