Merge branch 'release/0.20.0' of github.com:tainacan/tainacan into release/0.20.0

This commit is contained in:
vnmedeiros 2023-02-23 09:12:18 -03:00
commit 5f0ba3d234
19 changed files with 905 additions and 226 deletions

View File

@ -240,12 +240,29 @@
display: flex; display: flex;
flex-wrap: wrap; flex-wrap: wrap;
justify-content: flex-start; } justify-content: flex-start; }
.wp-block-tainacan-item-submission-form .item-submission-form-placeholder .fake-steps {
margin-top: 1em;
display: flex;
justify-content: space-evenly;
align-items: center; }
.wp-block-tainacan-item-submission-form .item-submission-form-placeholder .fake-steps .fake-step {
width: 1em;
height: 1em;
border-radius: 100%;
background-color: var(--tainacan-input-border-color, rgba(200, 200, 200, 0.3)); }
.wp-block-tainacan-item-submission-form .item-submission-form-placeholder .fake-steps .fake-step:first-of-type {
background-color: var(--tainacan-secondary, rgba(200, 200, 200, 0.3)); }
.wp-block-tainacan-item-submission-form .item-submission-form-placeholder .fake-steps + .metadata-section {
margin-top: -0.5em;
border: 1px solid var(--tainacan-input-border-color, rgba(200, 200, 200, 0.3));
padding: 2em; }
.wp-block-tainacan-item-submission-form .item-submission-form-placeholder .metadata-section { .wp-block-tainacan-item-submission-form .item-submission-form-placeholder .metadata-section {
width: 100%; width: 100%;
padding: 0.5em 1em; padding: 0.5em 1em;
display: flex; display: flex;
flex-wrap: nowrap; flex-wrap: nowrap;
flex-direction: column; } flex-direction: column;
box-sizing: border-box; }
.wp-block-tainacan-item-submission-form .item-submission-form-placeholder .metadata-section .fake-metadata { .wp-block-tainacan-item-submission-form .item-submission-form-placeholder .metadata-section .fake-metadata {
display: flex; display: flex;
flex-direction: column; flex-direction: column;

File diff suppressed because one or more lines are too long

View File

@ -1130,7 +1130,7 @@ class Item extends Entity {
* *
* This function expects a $metadata_section object. For a more generic approach, check the get_metadata_sections_as_html function * This function expects a $metadata_section object. For a more generic approach, check the get_metadata_sections_as_html function
* *
* @param object $metadata_section The Metadata Section object * @param \Tainacan\Entities\Metadata_Section $metadata_section The Metadata Section object
* @param array|string $args { * @param array|string $args {
* Optional. Array or string of arguments. * Optional. Array or string of arguments.
* *
@ -1169,6 +1169,17 @@ class Item extends Entity {
$return = ''; $return = '';
if($metadata_section->is_conditional_section()) {
$rules = $metadata_section->get_conditional_section_rules();
$item_id = $this->get_id();
foreach($rules as $meta_id => $meta_values_conditional) {
$meta_values = get_post_meta( $item_id, $meta_id );
if (!array_intersect($meta_values, $meta_values_conditional)) {
return $return;
}
}
}
$defaults = array( $defaults = array(
'hide_name' => false, 'hide_name' => false,
'hide_description' => true, 'hide_description' => true,

View File

@ -18,7 +18,9 @@ class Metadata_Section extends Entity {
$name, $name,
$slug, $slug,
$description, $description,
$description_bellow_name; $description_bellow_name,
$is_conditional_section,
$conditional_section_rules;
/** /**
* {@inheritDoc} * {@inheritDoc}
@ -94,6 +96,34 @@ class Metadata_Section extends Entity {
return $tainacan_metadata_sections->get_metadata_object_list($this->get_id(), $args); return $tainacan_metadata_sections->get_metadata_object_list($this->get_id(), $args);
} }
/**
* Get if section is conditional.
*
* @return string "yes"|"no"
*/
function get_is_conditional_section() {
return $this->get_mapped_property( 'is_conditional_section' );
}
/**
* get the rules of the conditional section
*
* @return array|object
*/
function get_conditional_section_rules() {
return $this->get_mapped_property( 'conditional_section_rules' );
}
/**
* Checks if section is conditional
*
* @return boolean
*/
function is_conditional_section() {
return $this->get_is_conditional_section() == 'yes';
}
/** /**
* Set the metadata section name * Set the metadata section name
* *
@ -141,6 +171,25 @@ class Metadata_Section extends Entity {
} }
/**
* set if section is conditional.
*
* @return void
*/
function set_is_conditional_section($value) {
return $this->set_mapped_property( 'is_conditional_section', $value );
}
/**
* set the rules of the conditional section
*
* @return void
*/
function set_conditional_section_rules($value) {
return $this->set_mapped_property( 'conditional_section_rules', $value );
}
/** /**
* Transient property used to store the status of the metadatum section for a particular collection * Transient property used to store the status of the metadatum section for a particular collection
* *
@ -168,6 +217,25 @@ class Metadata_Section extends Entity {
$name = $this->get_name(); $name = $this->get_name();
$collection = $this->get_collection(); $collection = $this->get_collection();
if ($this->is_conditional_section()) {
$metadata_section_id = $this->get_id();
if ($metadata_section_id == static::$default_section_slug) {
$this->add_error($this->get_id(), __("conditional section cannot be enabled in default section", 'tainacan'));
$no_errors = false;
} else {
$metadata_list = $this->get_metadata_object_list();
$required_metadata_list = array_filter($metadata_list, function($m) {
return $m->is_required();
});
if ( count($required_metadata_list) ) {
$no_errors = false;
foreach($required_metadata_list as $metadata) {
$this->add_error($metadata->get_id(), __("metadata cannot be required", 'tainacan'));
}
}
}
}
if( empty($collection) ) { if( empty($collection) ) {
$this->add_error($this->get_id(), __("collection is required", 'tainacan')); $this->add_error($this->get_id(), __("collection is required", 'tainacan'));
$no_errors = false; $no_errors = false;

View File

@ -534,6 +534,17 @@ class Metadatum extends Entity {
} }
} }
if( $this->is_required() ) {
$meta_section_id = $this->get_metadata_section_id();
if($meta_section_id != \Tainacan\Entities\Metadata_Section::$default_section_slug) {
$meta_section = new \Tainacan\Entities\Metadata_Section($meta_section_id);
if($meta_section->is_conditional_section()) {
$this->add_error($this->get_id(), __('Metadata cannot be required into conditional section', 'tainacan'));
return false;
}
}
}
// You cant have a taxonomy metadatum inside a multiple compound metadatum // You cant have a taxonomy metadatum inside a multiple compound metadatum
if ( $this->get_parent() > 0 && $this->get_metadata_type_object()->get_primitive_type() == 'term' ) { if ( $this->get_parent() > 0 && $this->get_metadata_type_object()->get_primitive_type() == 'term' ) {
$parent_metadatum = new \Tainacan\Entities\Metadatum($this->get_parent()); $parent_metadatum = new \Tainacan\Entities\Metadatum($this->get_parent());

View File

@ -76,6 +76,21 @@ class Metadata_Sections extends Repository {
'title' => __( 'Collection', 'tainacan' ), 'title' => __( 'Collection', 'tainacan' ),
'type' => ['integer', 'string'], 'type' => ['integer', 'string'],
'description' => __( 'The collection ID', 'tainacan' ), 'description' => __( 'The collection ID', 'tainacan' ),
],
'is_conditional_section' => [
'map' => 'meta',
'title' => __( 'Enable conditional section', 'tainacan' ),
'type' => 'string',
'description' => __( 'Binds this section visibility to a set of rules related to some metadata values.', 'tainacan' ),
'on_error' => __( 'Value should be "yes" or "no"', 'tainacan' ),
'validation' => v::stringType()->in( [ 'yes', 'no' ] ),
'default' => 'no'
],
'conditional_section_rules' => [
'map' => 'meta',
'title' => __( 'Conditional section rules', 'tainacan' ),
'type' => ['object', 'array'],
'description' => __( 'The conditions that will allow this section to be displayed, based on metadata values.', 'tainacan' ),
] ]
] ); ] );
} }

View File

@ -372,19 +372,25 @@
<div <div
v-for="(metadataSection, sectionIndex) of metadataSections" v-for="(metadataSection, sectionIndex) of metadataSections"
:key="sectionIndex" :key="sectionIndex"
:class="'metadata-section-slug-' + metadataSection.slug" :class="'metadata-section-slug-' + metadataSection.slug + (isSectionHidden(metadataSection.id) ? ' metadata-section-hidden' : '')"
:id="'metadata-section-id-' + metadataSection.id"> :id="'metadata-section-id-' + metadataSection.id"
v-tooltip="{
content: isSectionHidden(metadataSection.id) ? $i18n.get('info_metadata_section_hidden_conditional') : false,
autoHide: true,
placement: 'auto',
popperClass: ['tainacan-tooltip', 'tooltip']
}">
<div class="metadata-section-header section-label"> <div class="metadata-section-header section-label">
<span <span
class="collapse-handle" class="collapse-handle"
@click="(isMetadataNavigation || $adminOptions.hideItemEditionCollapses) ? null : toggleMetadataSectionCollapse(sectionIndex)"> @click="(isMetadataNavigation || $adminOptions.hideItemEditionCollapses || isSectionHidden(metadataSection.id)) ? null : toggleMetadataSectionCollapse(sectionIndex)">
<span <span
v-if="!$adminOptions.hideItemEditionCollapses && !isMetadataNavigation" v-if="!$adminOptions.hideItemEditionCollapses && !isMetadataNavigation"
class="icon"> class="icon">
<i <i
:class="{ :class="{
'tainacan-icon-arrowdown' : metadataSectionCollapses[sectionIndex] || errorMessage, 'tainacan-icon-arrowdown' : (metadataSectionCollapses[sectionIndex] || errorMessage) && !isSectionHidden(metadataSection.id),
'tainacan-icon-arrowright' : !(metadataSectionCollapses[sectionIndex] || errorMessage) 'tainacan-icon-arrowright' : !(metadataSectionCollapses[sectionIndex] || errorMessage) || isSectionHidden(metadataSection.id)
}" }"
class="has-text-secondary tainacan-icon tainacan-icon-1-25em"/> class="has-text-secondary tainacan-icon tainacan-icon-1-25em"/>
</span> </span>
@ -409,7 +415,7 @@
<transition name="filter-item"> <transition name="filter-item">
<div <div
class="metadata-section-metadata-list" class="metadata-section-metadata-list"
v-show="metadataSectionCollapses[sectionIndex] || isMetadataNavigation"> v-show="(metadataSectionCollapses[sectionIndex] || isMetadataNavigation) && !isSectionHidden(metadataSection.id)">
<p <p
class="metadatum-description-help-info" class="metadatum-description-help-info"
v-if="metadataSection.description && metadataSection.description_bellow_name == 'yes'"> v-if="metadataSection.description && metadataSection.description_bellow_name == 'yes'">
@ -852,6 +858,9 @@ export default {
formErrors() { formErrors() {
return eventBusItemMetadata && eventBusItemMetadata.errors && eventBusItemMetadata.errors.length ? eventBusItemMetadata.errors : [] return eventBusItemMetadata && eventBusItemMetadata.errors && eventBusItemMetadata.errors.length ? eventBusItemMetadata.errors : []
}, },
conditionalSections() {
return eventBusItemMetadata && eventBusItemMetadata.conditionalSections ? eventBusItemMetadata.conditionalSections : [];
},
isEditingItemMetadataInsideIframe() { isEditingItemMetadataInsideIframe() {
return this.$route.query && this.$route.query.editingmetadata; return this.$route.query && this.$route.query.editingmetadata;
}, },
@ -1016,6 +1025,25 @@ export default {
.then((metadataSections) => { .then((metadataSections) => {
this.metadataSectionCollapses = Array(metadataSections.length).fill(true) this.metadataSectionCollapses = Array(metadataSections.length).fill(true)
this.isLoadingMetadataSections = false; this.isLoadingMetadataSections = false;
/**
* Creates the conditional metadata set to later watch values
* of certain metadata that control sections visibility.
*/
eventBusItemMetadata.conditionalSections = {};
for (let metadataSection of metadataSections) {
if ( metadataSection.is_conditional_section == 'yes') {
const conditionalSectionId = Object.keys(metadataSection.conditional_section_rules);
if ( conditionalSectionId.length ) {
eventBusItemMetadata.conditionalSections[metadataSection.id] = {
sectionId: metadataSection.id,
metadatumId: conditionalSectionId[0],
metadatumValues: metadataSection.conditional_section_rules[conditionalSectionId[0]],
hide: true
};
}
}
}
}) })
.catch((error) => { .catch((error) => {
this.isLoadingMetadataSections = false; this.isLoadingMetadataSections = false;
@ -1285,6 +1313,16 @@ export default {
} }
} }
/* Sets initial state for conditional sections based on metadatum values */
for (let conditionalSectionId in this.conditionalSections) {
const currentItemMetadatum = metadata.find(anItemMetadatum => anItemMetadatum.metadatum.id == this.conditionalSections[conditionalSectionId].metadatumId);
if (currentItemMetadatum) {
const itemMetadatumValues = Array.isArray(currentItemMetadatum.value) ? currentItemMetadatum.value : [ currentItemMetadatum.value ];
const conditionalValues = Array.isArray(eventBusItemMetadata.conditionalSections[conditionalSectionId].metadatumValues) ? eventBusItemMetadata.conditionalSections[conditionalSectionId].metadatumValues : [eventBusItemMetadata.conditionalSections[conditionalSectionId].metadatumValues];
eventBusItemMetadata.conditionalSections[conditionalSectionId].hide = itemMetadatumValues.every(aValue => conditionalValues.indexOf(aValue) < 0);
}
}
this.isLoading = false; this.isLoading = false;
}); });
}, },
@ -1803,6 +1841,9 @@ export default {
webkit.messageHandlers.cordova_iab webkit.messageHandlers.cordova_iab
) )
webkit.messageHandlers.cordova_iab.postMessage(JSON.stringify({ 'type': 'exited_from_navigation', 'item': this.item })); webkit.messageHandlers.cordova_iab.postMessage(JSON.stringify({ 'type': 'exited_from_navigation', 'item': this.item }));
},
isSectionHidden(sectionId) {
return this.conditionalSections[sectionId] && this.conditionalSections[sectionId].hide;
} }
} }
} }
@ -2125,6 +2166,15 @@ export default {
border-bottom: 1px solid var(--tainacan-input-border-color); border-bottom: 1px solid var(--tainacan-input-border-color);
} }
.metadata-section-hidden {
opacity: 0.5;
filter: grayscale(1.0);
& > {
pointer-events: none;
}
}
.item-edition-tab-content { .item-edition-tab-content {
border-top: 1px solid var(--tainacan-input-border-color); border-top: 1px solid var(--tainacan-input-border-color);
} }

View File

@ -123,6 +123,93 @@
</div> </div>
<div
@click="hideConditionalSectionSettings = !hideConditionalSectionSettings;"
class="metadata-form-section">
<span class="icon">
<i
class="tainacan-icon"
:class="!hideConditionalSectionSettings ? 'tainacan-icon-arrowdown' : 'tainacan-icon-arrowright'" />
</span>
<strong>{{ $i18n.get('label_advanced_metadata_options') }}</strong>
<hr>
</div>
<transition name="filter-item">
<div
v-show="!hideConditionalSectionSettings"
class="options-columns">
<b-field
:addons="false"
:label="$i18n.getHelperTitle('metadata-sections', 'is_conditional_section')"
:type="formErrors['is_conditional_section'] != undefined ? 'is-danger' : ''"
:message="formErrors['is_conditional_section'] != undefined ? formErrors['is_conditional_section'] : ''">
&nbsp;
<b-switch
size="is-small"
@input="clearErrors('is_conditional_section')"
v-model="form.is_conditional_section"
true-value="yes"
false-value="no"
name="is_conditional_section">
<help-button
:title="$i18n.getHelperTitle('metadata-sections', 'is_conditional_section')"
:message="$i18n.getHelperMessage('metadata-sections', 'is_conditional_section')"
:extra-classes="isRepositoryLevel ? 'tainacan-repository-tooltip' : ''" />
</b-switch>
</b-field>
<div v-if="isConditionalSection && !availableConditionalMetadata.length">
<p style="break-inside: avoid;">{{ $i18n.get('info_create_select_metadatum_for_conditional_section') }}</p>
</div>
<transition name="filter-item">
<b-field
v-if="isConditionalSection && availableConditionalMetadata.length"
:addons="false"
:type="formErrors['conditional_section_rules'] != undefined ? 'is-danger' : ''"
:message="formErrors['conditional_section_rules'] != undefined ? formErrors['conditional_section_rules'] : ''">
<label class="label is-inline">
{{ $i18n.getHelperTitle('metadata-sections', 'conditional_section_rules') }}
<help-button
:title="$i18n.getHelperTitle('metadata-sections', 'conditional_section_rules')"
:message="$i18n.getHelperMessage('metadata-sections', 'conditional_section_rules')"
:extra-classes="isRepositoryLevel ? 'tainacan-repository-tooltip' : ''" />
</label>
<b-select
v-model="selectedConditionalMetadatum"
:placeholder="$i18n.get('label_select_metadatum')">
<option
v-for="conditionalMetadatum of availableConditionalMetadata"
:key="conditionalMetadatum.id"
:value="conditionalMetadatum.id">
{{ conditionalMetadatum.name }}
</option>
</b-select>
</b-field>
</transition>
<transition name="filter-item">
<b-field
v-if="isConditionalSection && selectedConditionalMetadatum"
:addons="false"
:type="formErrors['conditional_section_rules'] != undefined ? 'is-danger' : ''"
:message="formErrors['conditional_section_rules'] != undefined ? formErrors['conditional_section_rules'] : ''">
<label class="label is-inline">
{{ availableConditionalMetadata.find((availableMetadatum) => availableMetadatum.id == selectedConditionalMetadatum).name }}
</label>
<div style="overflow-y: auto; overflow-x: hidden; max-height: 100px;">
<b-checkbox
v-model="selectedConditionalValue"
v-for="(conditionalValue, conditionalValueIndex) of availableConditionalMetadata.find((availableMetadatum) => availableMetadatum.id == selectedConditionalMetadatum).metadata_type_object.options.options.split('\n')"
:key="conditionalValueIndex"
:native-value="conditionalValue">
{{ conditionalValue }}
</b-checkbox>
</div>
</b-field>
</transition>
</div>
</transition>
<!-- Hook for extra Form options --> <!-- Hook for extra Form options -->
<template v-if="hasEndLeftForm" > <template v-if="hasEndLeftForm" >
<form <form
@ -156,7 +243,7 @@
</template> </template>
<script> <script>
import { mapActions } from 'vuex'; import { mapActions, mapGetters } from 'vuex';
import { formHooks } from "../../js/mixins"; import { formHooks } from "../../js/mixins";
export default { export default {
@ -166,7 +253,7 @@
index: '', index: '',
originalMetadataSection: Object, originalMetadataSection: Object,
collectionId: '', collectionId: '',
isInsideImporterFlow: false isInsideImporterFlow: false,
}, },
data() { data() {
return { return {
@ -175,7 +262,28 @@
formErrorMessage: '', formErrorMessage: '',
closedByForm: false, closedByForm: false,
entityName: 'metadataSection', entityName: 'metadataSection',
isUpdating: false isUpdating: false,
selectedConditionalMetadatum: undefined,
selectedConditionalValue: [],
hideConditionalSectionSettings: false
}
},
computed: {
...mapGetters('metadata', [
'getMetadataSections'
]),
availableConditionalMetadata() {
if (this.getMetadataSections.length) {
const otherMetadataSections = this.getMetadataSections.filter(aMetadataSection => aMetadataSection.id != this.form.id);
const availableMetadata = [];
for (let aMetadataSection of otherMetadataSections)
availableMetadata.push.apply(availableMetadata, aMetadataSection.metadata_object_list);
return availableMetadata.filter(aMetadatum => aMetadatum.metadata_type === 'Tainacan\\Metadata_Types\\Selectbox');
}
return {};
},
isConditionalSection() {
return this.form.is_conditional_section == 'yes';
} }
}, },
created() { created() {
@ -184,6 +292,12 @@
if (this.form.status == 'auto-draft') if (this.form.status == 'auto-draft')
this.form.status = 'publish'; this.form.status = 'publish';
if ( this.form.is_conditional_section == 'yes' && Object.keys(this.form.conditional_section_rules).length ) {
const conditionalMetadatum = Object.keys(this.form.conditional_section_rules)[0];
this.selectedConditionalMetadatum = conditionalMetadatum;
this.selectedConditionalValue = this.form.conditional_section_rules[conditionalMetadatum];
}
this.formErrors = this.form.formErrors != undefined ? this.form.formErrors : {}; this.formErrors = this.form.formErrors != undefined ? this.form.formErrors : {};
this.formErrorMessage = this.form.formErrors != undefined ? this.form.formErrorMessage : ''; this.formErrorMessage = this.form.formErrors != undefined ? this.form.formErrorMessage : '';
}, },
@ -200,79 +314,40 @@
]), ]),
saveEdition(metadataSection) { saveEdition(metadataSection) {
if ( (metadataSection.metadata_type_object && metadataSection.metadata_type_object.form_component) || metadataSection.edit_form == '') { if ( this.form.is_conditional_section == 'yes' && this.selectedConditionalMetadatum && this.selectedConditionalValue ) {
this.form.conditional_section_rules = {}
this.form.conditional_section_rules[this.selectedConditionalMetadatum] = this.selectedConditionalValue;
} else
this.form.conditional_section_rules = null;
this.fillExtraFormData(this.form); this.fillExtraFormData(this.form);
this.isUpdating = true; this.isUpdating = true;
this.updateMetadataSection({ this.updateMetadataSection({
collectionId: this.collectionId, collectionId: this.collectionId,
metadataSectionId: metadataSection.id, metadataSectionId: metadataSection.id,
index: this.index, index: this.index,
options: this.form options: this.form
})
.then(() => {
this.form = {};
this.formErrors = {};
this.formErrorMessage = '';
this.isUpdating = false;
this.closedByForm = true;
this.$emit('onEditionFinished');
}) })
.then(() => { .catch((errors) => {
this.form = {}; this.isUpdating = false;
this.formErrors = {}; for (let error of errors.errors) {
this.formErrorMessage = ''; for (let attribute of Object.keys(error))
this.isUpdating = false; this.formErrors[attribute] = error[attribute];
this.closedByForm = true; }
this.formErrorMessage = errors.error_message;
this.$emit('onEditionFinished'); this.form.formErrors = this.formErrors;
}) this.form.formErrorMessage = this.formErrorMessage;
.catch((errors) => { });
this.isUpdating = false;
for (let error of errors.errors) {
for (let attribute of Object.keys(error))
this.formErrors[attribute] = error[attribute];
}
this.formErrorMessage = errors.error_message;
this.form.formErrors = this.formErrors;
this.form.formErrorMessage = this.formErrorMessage;
});
} else {
let formElement = document.getElementById('metadataSectionEditForm');
let formData = new FormData(formElement);
let formObj = {};
for (let [key, value] of formData.entries()) {
if (key === 'description_bellow_name')
formObj[key] = value ? 'yes' : 'no';
else
formObj[key] = value;
}
this.fillExtraFormData(formObj);
this.isUpdating = true;
this.updateMetadataSection({
collectionId: this.collectionId,
metadataSectionId: metadataSection.id,
index: this.index,
options: formObj
})
.then(() => {
this.form = {};
this.formErrors = {};
this.formErrorMessage = '';
this.isUpdating = false;
this.closedByForm = true;
this.$emit('onEditionFinished');
})
.catch((errors) => {
this.isUpdating = false;
for (let error of errors.errors) {
for (let attribute of Object.keys(error))
this.formErrors[attribute] = error[attribute];
}
this.formErrorMessage = errors.error_message;
this.$emit('onErrorFound');
this.form.formErrors = this.formErrors;
this.form.formErrorMessage = this.formErrorMessage;
});
}
}, },
clearErrors(attribute) { clearErrors(attribute) {
this.formErrors[attribute] = undefined; this.formErrors[attribute] = undefined;

View File

@ -4,7 +4,8 @@ import store from './store/store'
export const eventBusItemMetadata = new Vue({ export const eventBusItemMetadata = new Vue({
store, store,
data: { data: {
errors : [] errors : [],
conditionalSections: {}
}, },
watch: { watch: {
errors() { errors() {
@ -88,6 +89,19 @@ export const eventBusItemMetadata = new Vue({
this.$emit('isUpdatingValue', false); this.$emit('isUpdatingValue', false);
} }
/**
* Updates conditionalSections set values if this is one of the
* metadata with values that affect the sections visibility.
*/
let updatedConditionalSections = JSON.parse(JSON.stringify(this.conditionalSections));
for (let conditionalSectionId in updatedConditionalSections) {
if ( updatedConditionalSections[conditionalSectionId].metadatumId == metadatumId ) {
const conditionalValues = Array.isArray(updatedConditionalSections[conditionalSectionId].metadatumValues) ? updatedConditionalSections[conditionalSectionId].metadatumValues : [ this.conditionalSections[conditionalSectionId].metadatumValues ];
updatedConditionalSections[conditionalSectionId].hide = values.every(aValue => conditionalValues.indexOf(aValue) < 0);
}
}
this.conditionalSections = updatedConditionalSections;
}, },
removeItemMetadataGroup({ itemId, metadatumId, parentMetaId, parentMetadatum }) { removeItemMetadataGroup({ itemId, metadatumId, parentMetaId, parentMetadatum }) {

View File

@ -13,140 +13,144 @@
}, },
"attributes": { "attributes": {
"collectionId": { "collectionId": {
"type": "String", "type": "string",
"default": "" "default": ""
}, },
"isCollectionModalOpen": { "isCollectionModalOpen": {
"type": "Boolean", "type": "boolean",
"default": false "default": false
}, },
"hideFileModalButton": { "hideFileModalButton": {
"type": "Boolean", "type": "boolean",
"default": false "default": false
}, },
"hideTextModalButton": { "hideTextModalButton": {
"type": "Boolean", "type": "boolean",
"default": false "default": false
}, },
"hideLinkModalButton": { "hideLinkModalButton": {
"type": "Boolean", "type": "boolean",
"default": false "default": false
}, },
"hideThumbnailSection": { "hideThumbnailSection": {
"type": "Boolean", "type": "boolean",
"default": false "default": false
}, },
"hideAttachmentsSection": { "hideAttachmentsSection": {
"type": "Boolean", "type": "boolean",
"default": false "default": false
}, },
"hideHelpButtons": { "hideHelpButtons": {
"type": "Boolean", "type": "boolean",
"default": false "default": false
}, },
"hideMetadataTypes": { "hideMetadataTypes": {
"type": "Boolean", "type": "boolean",
"default": false "default": false
}, },
"showAllowCommentsSection": { "showAllowCommentsSection": {
"type": "Boolean", "type": "boolean",
"default": false "default": false
}, },
"hideCollapses": { "hideCollapses": {
"type": "Boolean", "type": "boolean",
"default": false "default": false
}, },
"backgroundColor": { "backgroundColor": {
"type": "String", "type": "string",
"default": "rgba(255,255,255,0)" "default": "rgba(255,255,255,0)"
}, },
"baseFontSize": { "baseFontSize": {
"type": "Number", "type": "number",
"default": 16 "default": 16
}, },
"inputColor": { "inputColor": {
"type": "String", "type": "string",
"default": "#1d1d1d" "default": "#1d1d1d"
}, },
"inputBackgroundColor": { "inputBackgroundColor": {
"type": "String", "type": "string",
"default": "#ffffff" "default": "#ffffff"
}, },
"inputBorderColor": { "inputBorderColor": {
"type": "String", "type": "string",
"default": "#dbdbdb" "default": "#dbdbdb"
}, },
"labelColor": { "labelColor": {
"type": "String", "type": "string",
"default": "#454647" "default": "#454647"
}, },
"infoColor": { "infoColor": {
"type": "String", "type": "string",
"default": "#555758" "default": "#555758"
}, },
"primaryColor": { "primaryColor": {
"type": "String", "type": "string",
"default": "#d9eced" "default": "#d9eced"
}, },
"secondaryColor": { "secondaryColor": {
"type": "String", "type": "string",
"default": "#298596" "default": "#298596"
}, },
"enabledMetadata": { "enabledMetadata": {
"type": "Array", "type": "array",
"default": [] "default": []
}, },
"collectionMetadata": { "collectionMetadata": {
"type": "Array", "type": "array",
"default": [] "default": []
}, },
"isLoadingCollectionMetadata": { "isLoadingCollectionMetadata": {
"type": "Boolean", "type": "boolean",
"default": false "default": false
}, },
"sentFormHeading": { "sentFormHeading": {
"type": "String", "type": "string",
"default": "Form submitted!" "default": "Form submitted!"
}, },
"sentFormMessage": { "sentFormMessage": {
"type": "String", "type": "string",
"default": "Thank you. Your item was submitted to the collection." "default": "Thank you. Your item was submitted to the collection."
}, },
"documentSectionLabel": { "documentSectionLabel": {
"type": "String", "type": "string",
"default": "Document" "default": "Document"
}, },
"attachmentsSectionLabel": { "attachmentsSectionLabel": {
"type": "String", "type": "string",
"default": "Attachments" "default": "Attachments"
}, },
"thumbnailSectionLabel": { "thumbnailSectionLabel": {
"type": "String", "type": "string",
"default": "Thumbnail" "default": "Thumbnail"
}, },
"metadataSectionLabel": { "metadataSectionLabel": {
"type": "String", "type": "string",
"default": "Metadata" "default": "Metadata"
}, },
"showItemLinkButton": { "showItemLinkButton": {
"type": "Boolean", "type": "boolean",
"default": false "default": false
}, },
"itemLinkButtonLabel": { "itemLinkButtonLabel": {
"type": "String", "type": "string",
"default": "Go to the item page" "default": "Go to the item page"
}, },
"helpInfoBellowLabel": { "helpInfoBellowLabel": {
"type": "Boolean", "type": "boolean",
"default": false "default": false
}, },
"showTermsAgreementCheckbox": { "showTermsAgreementCheckbox": {
"type": "Boolean", "type": "boolean",
"default": false "default": false
}, },
"termsAgreementMessage": { "termsAgreementMessage": {
"type": "String", "type": "string",
"default": "I agree to submit this item information." "default": "I agree to submit this item information."
},
"isLayoutSteps": {
"type": "boolean",
"default": false
} }
}, },
"supports": { "supports": {

View File

@ -2,6 +2,235 @@ const { __ } = wp.i18n;
const { RichText, useBlockProps } = (tainacan_blocks.wp_version < '5.2' ? wp.editor : wp.blockEditor ); const { RichText, useBlockProps } = (tainacan_blocks.wp_version < '5.2' ? wp.editor : wp.blockEditor );
export default [ export default [
/* Deprecated on Tainacan 0.20.0, due to isLayoutSteps */
{
"attributes": {
"collectionId": {
"type": "String",
"default": ""
},
"isCollectionModalOpen": {
"type": "Boolean",
"default": false
},
"hideFileModalButton": {
"type": "Boolean",
"default": false
},
"hideTextModalButton": {
"type": "Boolean",
"default": false
},
"hideLinkModalButton": {
"type": "Boolean",
"default": false
},
"hideThumbnailSection": {
"type": "Boolean",
"default": false
},
"hideAttachmentsSection": {
"type": "Boolean",
"default": false
},
"hideHelpButtons": {
"type": "Boolean",
"default": false
},
"hideMetadataTypes": {
"type": "Boolean",
"default": false
},
"showAllowCommentsSection": {
"type": "Boolean",
"default": false
},
"hideCollapses": {
"type": "Boolean",
"default": false
},
"backgroundColor": {
"type": "String",
"default": "rgba(255,255,255,0)"
},
"baseFontSize": {
"type": "Number",
"default": 16
},
"inputColor": {
"type": "String",
"default": "#1d1d1d"
},
"inputBackgroundColor": {
"type": "String",
"default": "#ffffff"
},
"inputBorderColor": {
"type": "String",
"default": "#dbdbdb"
},
"labelColor": {
"type": "String",
"default": "#454647"
},
"infoColor": {
"type": "String",
"default": "#555758"
},
"primaryColor": {
"type": "String",
"default": "#d9eced"
},
"secondaryColor": {
"type": "String",
"default": "#298596"
},
"enabledMetadata": {
"type": "Array",
"default": []
},
"collectionMetadata": {
"type": "Array",
"default": []
},
"isLoadingCollectionMetadata": {
"type": "Boolean",
"default": false
},
"sentFormHeading": {
"type": "String",
"default": "Form submitted!"
},
"sentFormMessage": {
"type": "String",
"default": "Thank you. Your item was submitted to the collection."
},
"documentSectionLabel": {
"type": "String",
"default": "Document"
},
"attachmentsSectionLabel": {
"type": "String",
"default": "Attachments"
},
"thumbnailSectionLabel": {
"type": "String",
"default": "Thumbnail"
},
"metadataSectionLabel": {
"type": "String",
"default": "Metadata"
},
"showItemLinkButton": {
"type": "Boolean",
"default": false
},
"itemLinkButtonLabel": {
"type": "String",
"default": "Go to the item page"
},
"helpInfoBellowLabel": {
"type": "Boolean",
"default": false
},
"showTermsAgreementCheckbox": {
"type": "Boolean",
"default": false
},
"termsAgreementMessage": {
"type": "String",
"default": "I agree to submit this item information."
}
},
save({ attributes, className }) {
const {
collectionId,
backgroundColor,
hideFileModalButton,
hideTextModalButton,
hideLinkModalButton,
hideThumbnailSection,
hideAttachmentsSection,
showAllowCommentsSection,
hideHelpButtons,
hideMetadataTypes,
hideCollapses,
documentSectionLabel,
thumbnailSectionLabel,
attachmentsSectionLabel,
metadataSectionLabel,
baseFontSize,
inputColor,
inputBackgroundColor,
inputBorderColor,
labelColor,
infoColor,
primaryColor,
secondaryColor,
enabledMetadata,
sentFormHeading,
sentFormMessage,
showItemLinkButton,
itemLinkButtonLabel,
helpInfoBellowLabel,
showTermsAgreementCheckbox,
termsAgreementMessage
} = attributes;
const blockProps = useBlockProps.save();
let termsAgreementMessageHTML = <RichText.Content { ...blockProps } tagName="p" value={ termsAgreementMessage } />;
termsAgreementMessageHTML = (termsAgreementMessageHTML && termsAgreementMessageHTML.props && termsAgreementMessageHTML.props.value) ? termsAgreementMessageHTML.props.value : '';
if (backgroundColor.rgb != undefined) {
if (backgroundColor.rgb.a)
backgroundColor = 'rgba(' + backgroundColor.rgb.r + ',' + backgroundColor.rgb.g + ',' + backgroundColor.rgb.b + ',' + backgroundColor.rgb.a + ')';
else
backgroundColor = 'rgb(' + backgroundColor.rgb.r + ',' + backgroundColor.rgb.g + ',' + backgroundColor.rgb.b + ')';
}
return <div
style={{
'font-size': baseFontSize + 'px',
'--tainacan-base-font-size': baseFontSize + 'px',
'--tainacan-background-color': backgroundColor,
'--tainacan-input-color': inputColor,
'--tainacan-input-background-color': inputBackgroundColor,
'--tainacan-input-border-color': inputBorderColor,
'--tainacan-label-color': labelColor,
'--tainacan-info-color': infoColor,
'--tainacan-primary': primaryColor,
'--tainacan-secondary': secondaryColor
}}
className={ className }>
<div
id="tainacan-item-submission-form"
data-module="item-submission-form"
collection-id={ collectionId }
hide-file-modal-button={ hideFileModalButton.toString() }
hide-text-modal-button={ hideTextModalButton.toString() }
hide-link-modal-button={ hideLinkModalButton.toString() }
hide-thumbnail-section={ hideThumbnailSection.toString() }
hide-attachments-section={ hideAttachmentsSection.toString() }
show-allow-comments-section={ showAllowCommentsSection.toString() }
hide-help-buttons={ hideHelpButtons.toString() }
hide-metadata-types={ hideMetadataTypes.toString() }
hide-collapses={ hideCollapses.toString() }
enabled-metadata={ enabledMetadata.toString() }
sent-form-heading={ sentFormHeading }
sent-form-message={ sentFormMessage }
document-section-label={ documentSectionLabel }
thumbnail-section-label={ thumbnailSectionLabel }
attachments-section-label={ attachmentsSectionLabel }
metadata-section-label={ metadataSectionLabel }
show-item-link-button={ showItemLinkButton ? showItemLinkButton.toString() : 'false' }
show-terms-agreement-checkbox={ showTermsAgreementCheckbox ? showTermsAgreementCheckbox.toString() : 'false' }
terms-agreement-message={ termsAgreementMessageHTML }
item-link-button-label={ itemLinkButtonLabel ? itemLinkButtonLabel : __( 'Go to the item page', 'tainacan' ) }
help-info-bellow-label={ helpInfoBellowLabel ? helpInfoBellowLabel.toString() : 'false' } >
</div>
</div>
}
},
/* Deprecated on Tainacan 0.18.8, due to the backgroundColor being a string instead of object */ /* Deprecated on Tainacan 0.18.8, due to the backgroundColor being a string instead of object */
{ {
"attributes": { "attributes": {

View File

@ -56,7 +56,8 @@ export default function ({ attributes, setAttributes, className }) {
itemLinkButtonLabel, itemLinkButtonLabel,
helpInfoBellowLabel, helpInfoBellowLabel,
showTermsAgreementCheckbox, showTermsAgreementCheckbox,
termsAgreementMessage termsAgreementMessage,
isLayoutSteps
} = attributes; } = attributes;
const blockProps = useBlockProps(); const blockProps = useBlockProps();
@ -280,6 +281,16 @@ export default function ({ attributes, setAttributes, className }) {
<PanelBody <PanelBody
title={__('Form elements', 'tainacan')} title={__('Form elements', 'tainacan')}
initialOpen={ true } > initialOpen={ true } >
<ToggleControl
label={__('Show metadata sections as steps on the form.', 'tainacan')}
help={ isLayoutSteps ? __('Do not show the metadata sections as separate steps of the form.', 'tainacan') : __('Toggle to show the metadata sections as steps of the form.', 'tainacan')}
checked={ isLayoutSteps }
onChange={ ( isChecked ) => {
isLayoutSteps = isChecked;
setAttributes({ isLayoutSteps: isChecked });
}
}
/>
<ToggleControl <ToggleControl
label={__('Hide the Document type file button', 'tainacan')} label={__('Hide the Document type file button', 'tainacan')}
help={ hideFileModalButton ? __('Do not show the button for uploading a file document', 'tainacan') : __('Toggle to show the button to upload a file document.', 'tainacan')} help={ hideFileModalButton ? __('Do not show the button for uploading a file document', 'tainacan') : __('Toggle to show the button to upload a file document.', 'tainacan')}
@ -646,8 +657,17 @@ export default function ({ attributes, setAttributes, className }) {
<div style={{ flexGrow: '1' }}> <div style={{ flexGrow: '1' }}>
{ metadataSectionLabel ? { metadataSectionLabel ?
<div class="fake-text section-label"></div> <div class="fake-text section-label"></div>
:null } : null }
{ !hideCollapses ? <div class="fake-link"></div> : null } { !hideCollapses && !isLayoutSteps ?
<div class="fake-link"></div>
: null }
{ isLayoutSteps ?
<div class="fake-steps">
<div class="fake-step"/>
<div class="fake-step"/>
<div class="fake-step"/>
</div>
: null }
<div class="metadata-section"> <div class="metadata-section">
{ enabledMetadata.length ? { enabledMetadata.length ?
enabledMetadata.map( (isEnabled) => { enabledMetadata.map( (isEnabled) => {

View File

@ -4,6 +4,7 @@
:is-full-page="false" :is-full-page="false"
:active.sync="isLoading" :active.sync="isLoading"
:can-cancel="false"/> :can-cancel="false"/>
<template v-if="couldLoadCollection && collecionAllowsItemSubmission"> <template v-if="couldLoadCollection && collecionAllowsItemSubmission">
<form <form
v-if="!hasSentForm" v-if="!hasSentForm"
@ -400,7 +401,7 @@
</div> </div>
<a <a
v-if="!hideCollapses" v-if="!showSteppedLayout && !hideCollapses"
class="collapse-all" class="collapse-all"
@click="toggleCollapseAll()"> @click="toggleCollapseAll()">
{{ collapseAll ? $i18n.get('label_collapse_all') : $i18n.get('label_expand_all') }} {{ collapseAll ? $i18n.get('label_collapse_all') : $i18n.get('label_expand_all') }}
@ -416,89 +417,111 @@
v-if="hasBeforeHook('metadata')" v-if="hasBeforeHook('metadata')"
class="item-submission-hook item-submission-hook-metadata-before" class="item-submission-hook item-submission-hook-metadata-before"
v-html="getBeforeHook('metadata')" /> v-html="getBeforeHook('metadata')" />
<component
<div v-if="metadataSections.length"
v-for="(metadataSection, sectionIndex) of metadataSections" :is="showSteppedLayout ? 'b-steps' : 'div'"
:key="sectionIndex" v-model="activeSectionStep"
:class="'metadata-section-slug-' + metadataSection.slug" :has-navigation="false"
:id="'metadata-section-id-' + metadataSection.id"> type="is-secondary"
mobile-mode="compact"
<div class="metadata-section-header section-label"> size="is-small"
<span ref="item-submission-steps-layout">
class="collapse-handle" <component
@click="!hideCollapses ? toggleMetadataSectionCollapse(sectionIndex) : ''"> :is="showSteppedLayout ? 'b-step-item' : 'div'"
<span v-for="(metadataSection, sectionIndex) of metadataSections"
v-if="!hideCollapses" :key="sectionIndex"
class="icon" :step="sectionIndex + 1"
@click="toggleMetadataSectionCollapse(sectionIndex)"> :label="metadataSection.name"
<i :label-position="'right'"
:class="{ :clickable="true"
'tainacan-icon-arrowdown' : metadataSectionCollapses[sectionIndex] || formErrorMessage, :class="'metadata-section-slug-' + metadataSection.slug + (!showSteppedLayout && isSectionHidden(metadataSection.id) ? ' metadata-section-hidden' : '')"
'tainacan-icon-arrowright' : !(metadataSectionCollapses[sectionIndex] || formErrorMessage) :id="'metadata-section-id-' + metadataSection.id"
}" v-tooltip="{
class="has-text-secondary tainacan-icon tainacan-icon-1-25em"/> content: !showSteppedLayout && isSectionHidden(metadataSection.id) ? $i18n.get('info_metadata_section_hidden_conditional') : false,
</span> autoHide: true,
<label>{{ metadataSection.name }}</label> placement: 'auto',
<help-button popperClass: ['tainacan-tooltip', 'tooltip']
v-if="!hideHelpButtons && }">
!helpInfoBellowLabel &&
metadataSection.description"
:title="metadataSection.name"
:message="metadataSection.description" />
</span>
</div>
<transition name="filter-item">
<div <div
class="metadata-section-metadata-list" v-if="!showSteppedLayout"
v-show="metadataSectionCollapses[sectionIndex]"> class="metadata-section-header section-label">
<span
<!-- JS-side hook for extra content --> class="collapse-handle"
<div @click="!hideCollapses && !isSectionHidden(metadataSection.id) ? toggleMetadataSectionCollapse(sectionIndex) : ''">
v-if="hasBeforeHook('metadata_section')" <span
class="item-submission-hook item-submission-hook-metadata-section-before" v-if="!hideCollapses"
v-html="getBeforeHook('metadata_section', { metadataSection: metadataSection, sectionIndex: sectionIndex })" /> class="icon"
@click="toggleMetadataSectionCollapse(sectionIndex)">
<p <i
class="metadatum-description-help-info" :class="{
v-if="metadataSection.description && (!hideHelpButtons && helpInfoBellowLabel)"> 'tainacan-icon-arrowdown' : (metadataSectionCollapses[sectionIndex] || formErrorMessage) && !isSectionHidden(metadataSection.id),
{{ metadataSection.description }} 'tainacan-icon-arrowright' : !(metadataSectionCollapses[sectionIndex] || formErrorMessage) || isSectionHidden(metadataSection.id)
</p> }"
<template v-if="itemMetadata && Array.isArray(itemMetadata)"> class="has-text-secondary tainacan-icon tainacan-icon-1-25em"/>
<template v-for="(itemMetadatum, index) of itemMetadata.filter(anItemMetadatum => anItemMetadatum.metadatum.metadata_section_id == metadataSection.id)"> </span>
<label>{{ metadataSection.name }}</label>
<!-- JS-side hook for extra content --> <help-button
<div v-if="!hideHelpButtons &&
:key="index" !helpInfoBellowLabel &&
v-if="hasBeforeHook('metadatum')" metadataSection.description"
class="item-submission-hook item-submission-hook-metadatum-before" :title="metadataSection.name"
v-html="getBeforeHook('metadatum', { metadatum: itemMetadatum.metadatum, index: index, metadataSection: metadataSection, sectionIndex: sectionIndex })" /> :message="metadataSection.description" />
</span>
<tainacan-form-item
:key="index"
v-if="enabledMetadata[index] == 'true'"
:item-metadatum="itemMetadatum"
:hide-collapses="hideCollapses"
:is-collapsed="metadataCollapses[index]"
@changeCollapse="onChangeCollapse($event, index)"/>
<!-- JS-side hook for extra content -->
<div
:key="index"
v-if="hasAfterHook('metadatum')"
class="item-submission-hook item-submission-hook-metadatum-after"
v-html="getAfterHook('metadatum', { metadatum: itemMetadatum.metadatum, index: index, metadataSection: metadataSection, sectionIndex: sectionIndex })" />
</template>
</template>
<!-- JS-side hook for extra content -->
<div
v-if="hasAfterHook('metadata_section')"
class="item-submission-hook item-submission-hook-metadata-section-after"
v-html="getAfterHook('metadata_section', { metadataSection: metadataSection, sectionIndex: sectionIndex })" />
</div> </div>
</transition> <transition name="filter-item">
<div
class="metadata-section-metadata-list"
v-show="metadataSectionCollapses[sectionIndex] && (showSteppedLayout || !isSectionHidden(metadataSection.id))">
</div> <!-- JS-side hook for extra content -->
<div
v-if="hasBeforeHook('metadata_section')"
class="item-submission-hook item-submission-hook-metadata-section-before"
v-html="getBeforeHook('metadata_section', { metadataSection: metadataSection, sectionIndex: sectionIndex })" />
<p
class="metadatum-description-help-info"
v-if="metadataSection.description && (!hideHelpButtons && helpInfoBellowLabel)">
{{ metadataSection.description }}
</p>
<template v-if="itemMetadata && Array.isArray(itemMetadata)">
<template v-for="(itemMetadatum, index) of itemMetadata.filter(anItemMetadatum => anItemMetadatum.metadatum.metadata_section_id == metadataSection.id)">
<!-- JS-side hook for extra content -->
<div
:key="index"
v-if="hasBeforeHook('metadatum')"
class="item-submission-hook item-submission-hook-metadatum-before"
v-html="getBeforeHook('metadatum', { metadatum: itemMetadatum.metadatum, index: index, metadataSection: metadataSection, sectionIndex: sectionIndex })" />
<tainacan-form-item
:key="index"
v-if="enabledMetadata[index] == 'true'"
:item-metadatum="itemMetadatum"
:hide-collapses="hideCollapses"
:is-collapsed="metadataCollapses[index]"
@changeCollapse="onChangeCollapse($event, index)"/>
<!-- JS-side hook for extra content -->
<div
:key="index"
v-if="hasAfterHook('metadatum')"
class="item-submission-hook item-submission-hook-metadatum-after"
v-html="getAfterHook('metadatum', { metadatum: itemMetadatum.metadatum, index: index, metadataSection: metadataSection, sectionIndex: sectionIndex })" />
</template>
</template>
<!-- JS-side hook for extra content -->
<div
v-if="hasAfterHook('metadata_section')"
class="item-submission-hook item-submission-hook-metadata-section-after"
v-html="getAfterHook('metadata_section', { metadataSection: metadataSection, sectionIndex: sectionIndex })" />
</div>
</transition>
</component>
</component>
<!-- JS-side hook for extra content --> <!-- JS-side hook for extra content -->
<div <div
@ -536,15 +559,9 @@
v-if="error.errors.length" v-if="error.errors.length"
:key="index"> :key="index">
<a <a
v-if="['thumbnail', 'attachments', 'document'].includes(error.metadatum_id)" v-if="['thumbnail', 'attachments', 'document'].includes(error.metadatum_id) || metadataElements[error.metadatum_id + (error.parent_meta_id ? ('_parent_meta_id-' + error.parent_meta_id) : '')]"
class="has-text-danger" class="has-text-danger"
@click="metadataElements[error.metadatum_id].scrollIntoView({ behavior: 'smooth', block: 'center' })"> @click="goToErrorMetadatum(error)">
{{ getErrorMessage(error.errors) }}
</a>
<a
v-else-if="metadataElements[error.metadatum_id + (error.parent_meta_id ? ('_parent_meta_id-' + error.parent_meta_id) : '')]"
class="has-text-danger"
@click="metadataElements[error.metadatum_id + (error.parent_meta_id ? ('_parent_meta_id-' + error.parent_meta_id) : '')].scrollIntoView({ behavior: 'smooth', block: 'center' })">
{{ getErrorMessage(error.errors) }} {{ getErrorMessage(error.errors) }}
</a> </a>
<p v-else>{{ getErrorMessage(error.errors) }}</p> <p v-else>{{ getErrorMessage(error.errors) }}</p>
@ -584,8 +601,12 @@
class="item-submission-hook item-submission-hook-footer-before" class="item-submission-hook item-submission-hook-footer-before"
v-html="getBeforeHook('footer')" /> v-html="getBeforeHook('footer')" />
<div class="wp-block-buttons"> <div
<div class="wp-block-button is-style-outline"> class="wp-block-buttons"
style="gap: 1rem;">
<div
class="wp-block-button is-style-outline"
style="margin-right: auto;">
<button <button
@click="onDiscard()" @click="onDiscard()"
type="button" type="button"
@ -593,7 +614,29 @@
{{ $i18n.get('cancel') }} {{ $i18n.get('cancel') }}
</button> </button>
</div> </div>
<div class="wp-block-button"> <div
v-if="showSteppedLayout && activeSectionStep > 0"
class="wp-block-button">
<button
@click="onPreviousStep()"
type="button"
class="wp-block-button__link wp-element-button">
{{ $i18n.get('previous') }}
</button>
</div>
<div
v-if="showSteppedLayout && activeSectionStep < metadataSections.length - 1"
class="wp-block-button">
<button
@click="onNextStep()"
type="button"
class="wp-block-button__link wp-element-button">
{{ $i18n.get('next') }}
</button>
</div>
<div
v-if="!showSteppedLayout || activeSectionStep == metadataSections.length - 1"
class="wp-block-button">
<button <button
:disabled="showTermsAgreementCheckbox && !userHasAgreedToTerms" :disabled="showTermsAgreementCheckbox && !userHasAgreedToTerms"
@click="onSubmit()" @click="onSubmit()"
@ -724,7 +767,8 @@ export default {
itemLinkButtonLabel: String, itemLinkButtonLabel: String,
helpInfoBellowLabel: Boolean, helpInfoBellowLabel: Boolean,
showTermsAgreementCheckbox: Boolean, showTermsAgreementCheckbox: Boolean,
termsAgreementMessage: String termsAgreementMessage: String,
isLayoutSteps: Boolean
}, },
data(){ data(){
return { return {
@ -758,10 +802,14 @@ export default {
captchaSiteKey: tainacan_plugin['item_submission_captcha_site_key'], captchaSiteKey: tainacan_plugin['item_submission_captcha_site_key'],
linkToCreatedItem: '', linkToCreatedItem: '',
userHasAgreedToTerms: false, userHasAgreedToTerms: false,
metadataElements: {} metadataElements: {},
activeSectionStep: 0,
} }
}, },
computed: { computed: {
showSteppedLayout() {
return this.isLayoutSteps;
},
itemSubmission() { itemSubmission() {
return this.getItemSubmission(); return this.getItemSubmission();
}, },
@ -791,9 +839,9 @@ export default {
// To find which is the section of this metadatum, we look for an intersection of the existeing sections // To find which is the section of this metadatum, we look for an intersection of the existeing sections
// in this collection and the list of section ids in the repository metadata // in this collection and the list of section ids in the repository metadata
const intersectionOfSections = this.metadataSections.filter( const intersectionOfSections = this.getMetadataSections() // We do not use the computed metadataSections here as we want to include every section, even those hidden by stepped layout
(aMetadataSection) => metadatumSectionId.includes("" + aMetadataSection.id) && aMetadataSection.id !== 'default_section' .filter((aMetadataSection) => metadatumSectionId.includes("" + aMetadataSection.id) && aMetadataSection.id !== 'default_section');
);
if (intersectionOfSections.length === 1) if (intersectionOfSections.length === 1)
metadatum.metadata_section_id = intersectionOfSections[0].id; metadatum.metadata_section_id = intersectionOfSections[0].id;
@ -807,7 +855,7 @@ export default {
return tweakedItemMetadata; return tweakedItemMetadata;
}, },
metadataSections() { metadataSections() {
return this.getMetadataSections(); return this.showSteppedLayout ? this.getMetadataSections().filter(aMetadataSection => !this.isSectionHidden(aMetadataSection.id)) : this.getMetadataSections();
}, },
formErrors() { formErrors() {
return eventBusItemMetadata && eventBusItemMetadata.errors && eventBusItemMetadata.errors.length ? eventBusItemMetadata.errors : [] return eventBusItemMetadata && eventBusItemMetadata.errors && eventBusItemMetadata.errors.length ? eventBusItemMetadata.errors : []
@ -826,7 +874,10 @@ export default {
thumbnailErrorMessage() { thumbnailErrorMessage() {
const existingError = this.formErrors.find(error => error.metadatum_id == 'thumbnail'); const existingError = this.formErrors.find(error => error.metadatum_id == 'thumbnail');
return existingError ? existingError.errors : ''; return existingError ? existingError.errors : '';
} },
conditionalSections() {
return eventBusItemMetadata && eventBusItemMetadata.conditionalSections ? eventBusItemMetadata.conditionalSections : [];
},
}, },
created() { created() {
@ -876,6 +927,25 @@ export default {
.then((metadataSections) => { .then((metadataSections) => {
this.metadataSectionCollapses = Array(metadataSections.length).fill(true); this.metadataSectionCollapses = Array(metadataSections.length).fill(true);
this.isLoadingMetadataSections = false; this.isLoadingMetadataSections = false;
/**
* Creates the conditional metadata set to later watch values
* of certain metadata that control sections visibility.
*/
eventBusItemMetadata.conditionalSections = {};
for (let metadataSection of metadataSections) {
if ( metadataSection.is_conditional_section == 'yes') {
const conditionalSectionId = Object.keys(metadataSection.conditional_section_rules);
if ( conditionalSectionId.length ) {
eventBusItemMetadata.conditionalSections[metadataSection.id] = {
sectionId: metadataSection.id,
metadatumId: conditionalSectionId[0],
metadatumValues: metadataSection.conditional_section_rules[conditionalSectionId[0]],
hide: true
};
}
}
}
}) })
.catch((error) => { .catch((error) => {
this.isLoadingMetadataSections = false; this.isLoadingMetadataSections = false;
@ -1046,7 +1116,6 @@ export default {
} }
this.setItemSubmissionMetadata( metadata.map((metadatum) => { return { metadatum_id: metadatum.id, value: null } }) ); this.setItemSubmissionMetadata( metadata.map((metadatum) => { return { metadatum_id: metadatum.id, value: null } }) );
this.couldLoadCollection = true; this.couldLoadCollection = true;
this.isLoading = false;
// Mounts grecapcha // Mounts grecapcha
if (this.useCaptcha == 'yes') { if (this.useCaptcha == 'yes') {
@ -1056,6 +1125,8 @@ export default {
this.$console.log(error); this.$console.log(error);
} }
} }
this.isLoading = false;
}) })
.catch(() => { .catch(() => {
this.couldLoadCollection = false; this.couldLoadCollection = false;
@ -1095,6 +1166,30 @@ export default {
this.formErrors.map((error) => { this.formErrors.map((error) => {
this.metadataElements[error.metadatum_id + (error.parent_meta_id ? ('_parent_meta_id-' + error.parent_meta_id) : '')] = document.getElementById('tainacan-item-metadatum_id-' + error.metadatum_id + (error.parent_meta_id ? ('_parent_meta_id-' + error.parent_meta_id) : '')); this.metadataElements[error.metadatum_id + (error.parent_meta_id ? ('_parent_meta_id-' + error.parent_meta_id) : '')] = document.getElementById('tainacan-item-metadatum_id-' + error.metadatum_id + (error.parent_meta_id ? ('_parent_meta_id-' + error.parent_meta_id) : ''));
}); });
},
goToErrorMetadatum(error) {
if ( ['thumbnail', 'attachments', 'document'].includes(error.metadatum_id) )
this.metadataElements[error.metadatum_id].scrollIntoView({ behavior: 'smooth', block: 'center' });
else if ( this.metadataElements[error.metadatum_id + (error.parent_meta_id ? ('_parent_meta_id-' + error.parent_meta_id) : '')] ) {
if ( this.showSteppedLayout ) {
const stepWhereTheErrorIs = this.metadataSections.findIndex((aMetadataSection) => aMetadataSection.metadata_object_list.findIndex((aMetadatatum) => aMetadatatum.id == error.metadatum_id || aMetadatatum.id == error.parent_meta_id) >= 0);
if (stepWhereTheErrorIs >= 0)
this.activeSectionStep = stepWhereTheErrorIs;
}
this.metadataElements[error.metadatum_id + (error.parent_meta_id ? ('_parent_meta_id-' + error.parent_meta_id) : '')].scrollIntoView({ behavior: 'smooth', block: 'center' });
}
},
onPreviousStep() {
if ( this.$refs['item-submission-steps-layout'] && typeof this.$refs['item-submission-steps-layout'].prev == 'function' )
this.$refs['item-submission-steps-layout'].prev();
},
onNextStep() {
if ( this.$refs['item-submission-steps-layout'] && typeof this.$refs['item-submission-steps-layout'].next == 'function' )
this.$refs['item-submission-steps-layout'].next();
},
isSectionHidden(sectionId) {
return this.conditionalSections[sectionId] && this.conditionalSections[sectionId].hide;
} }
} }
} }
@ -1141,6 +1236,14 @@ export default {
} }
} }
.metadata-section-hidden {
opacity: 0.5;
filter: grayscale(1.0);
& > {
pointer-events: none;
}
}
.section-label { .section-label {
position: relative; position: relative;
@ -1223,6 +1326,8 @@ export default {
margin-top: 1.25em; margin-top: 1.25em;
.field { .field {
margin-left: 0;
margin-right: 0;
padding: 0.75em 0.75em 0.5em 0.75em; padding: 0.75em 0.75em 0.5em 0.75em;
border: 1px dashed var(--tainacan-input-border-color); border: 1px dashed var(--tainacan-input-border-color);
@ -1313,5 +1418,29 @@ export default {
} }
} }
.b-steps {
border: 1px solid var(--tainacan-input-border-color);
border-radius: 2px;
margin-top: 1em;
/deep/ .steps {
.step-items {
margin-top: -1em;
padding-right: 0px;
margin-right: 0px;
padding-left: 0px;
margin-left: 0px;
.step-item.is-active .step-title {
color: var(--tainacan-secondary);
}
.step-item:not(.is-active) .step-title {
color: var(--tainacan-label-color);
}
}
}
}
} }
</style> </style>

View File

@ -12,3 +12,6 @@
@import "../../../../../../../node_modules/bulma/sass/grid/columns.sass" @import "../../../../../../../node_modules/bulma/sass/grid/columns.sass"
@import "../../../../../../../node_modules/bulma/sass/components/dropdown.sass" @import "../../../../../../../node_modules/bulma/sass/components/dropdown.sass"
@import "../../../../../../../node_modules/bulma/sass/components/modal.sass" @import "../../../../../../../node_modules/bulma/sass/components/modal.sass"
$body-background-color: #fff;
$body-color: #fff;

View File

@ -32,7 +32,8 @@ export default function({ attributes, className }) {
itemLinkButtonLabel, itemLinkButtonLabel,
helpInfoBellowLabel, helpInfoBellowLabel,
showTermsAgreementCheckbox, showTermsAgreementCheckbox,
termsAgreementMessage termsAgreementMessage,
isLayoutSteps
} = attributes; } = attributes;
const blockProps = useBlockProps.save(); const blockProps = useBlockProps.save();
@ -84,7 +85,8 @@ export default function({ attributes, className }) {
show-terms-agreement-checkbox={ showTermsAgreementCheckbox ? showTermsAgreementCheckbox.toString() : 'false' } show-terms-agreement-checkbox={ showTermsAgreementCheckbox ? showTermsAgreementCheckbox.toString() : 'false' }
terms-agreement-message={ termsAgreementMessageHTML } terms-agreement-message={ termsAgreementMessageHTML }
item-link-button-label={ itemLinkButtonLabel ? itemLinkButtonLabel : __( 'Go to the item page', 'tainacan' ) } item-link-button-label={ itemLinkButtonLabel ? itemLinkButtonLabel : __( 'Go to the item page', 'tainacan' ) }
help-info-bellow-label={ helpInfoBellowLabel ? helpInfoBellowLabel.toString() : 'false' } > help-info-bellow-label={ helpInfoBellowLabel ? helpInfoBellowLabel.toString() : 'false' }
is-layout-steps={ isLayoutSteps.toString() } >
</div> </div>
</div> </div>
}; };

View File

@ -303,12 +303,34 @@
flex-wrap: wrap; flex-wrap: wrap;
justify-content: flex-start; justify-content: flex-start;
} }
.fake-steps {
margin-top: 1em;
display: flex;
justify-content: space-evenly;
align-items: center;
.fake-step {
width: 1em;
height: 1em;
border-radius: 100%;
background-color: var(--tainacan-input-border-color, rgba(200,200,200, 0.3));
}
.fake-step:first-of-type {
background-color: var(--tainacan-secondary, rgba(200,200,200, 0.3));
}
}
.fake-steps+.metadata-section {
margin-top: -0.5em;
border: 1px solid var(--tainacan-input-border-color, rgba(200,200,200, 0.3));
padding: 2em;
}
.metadata-section { .metadata-section {
width: 100%; width: 100%;
padding: 0.5em 1em; padding: 0.5em 1em;
display: flex; display: flex;
flex-wrap: nowrap; flex-wrap: nowrap;
flex-direction: column; flex-direction: column;
box-sizing: border-box;
.fake-metadata { .fake-metadata {
display: flex; display: flex;

View File

@ -19,7 +19,8 @@ import {
Input, Input,
Select, Select,
Taginput, Taginput,
Snackbar Snackbar,
Steps
} from 'buefy'; } from 'buefy';
import VTooltip from 'floating-vue'; import VTooltip from 'floating-vue';
import cssVars from 'css-vars-ponyfill'; import cssVars from 'css-vars-ponyfill';
@ -87,6 +88,7 @@ export default (element) => {
Vue.use(Snackbar); Vue.use(Snackbar);
Vue.use(Modal); Vue.use(Modal);
Vue.use(Input); Vue.use(Input);
Vue.use(Steps);
Vue.use(VTooltip, { Vue.use(VTooltip, {
popperTriggers: ['hover'], popperTriggers: ['hover'],
themes: { themes: {
@ -166,7 +168,8 @@ export default (element) => {
itemLinkButtonLabel: '', itemLinkButtonLabel: '',
helpInfoBellowLabel: false, helpInfoBellowLabel: false,
showItemLinkButton: false, showItemLinkButton: false,
termsAgreementMessage: '' termsAgreementMessage: '',
isLayoutSteps: false
}, },
beforeMount () { beforeMount () {
// Collection source settings // Collection source settings
@ -194,6 +197,8 @@ export default (element) => {
this.hideMetadataTypes = this.isParameterTrue('hide-metadata-types'); this.hideMetadataTypes = this.isParameterTrue('hide-metadata-types');
if (this.$el.attributes['help-info-bellow-label'] != undefined) if (this.$el.attributes['help-info-bellow-label'] != undefined)
this.helpInfoBellowLabel = this.isParameterTrue('help-info-bellow-label'); this.helpInfoBellowLabel = this.isParameterTrue('help-info-bellow-label');
if (this.$el.attributes['is-layout-steps'] != undefined)
this.isLayoutSteps = this.isParameterTrue('is-layout-steps');
// Form sections labels // Form sections labels
if (this.$el.attributes['document-section-label'] != undefined) if (this.$el.attributes['document-section-label'] != undefined)

View File

@ -23,6 +23,7 @@
:help-info-bellow-label="$root.helpInfoBellowLabel ? $root.helpInfoBellowLabel : false" :help-info-bellow-label="$root.helpInfoBellowLabel ? $root.helpInfoBellowLabel : false"
:show-terms-agreement-checkbox="$root.showTermsAgreementCheckbox ? $root.showTermsAgreementCheckbox : false" :show-terms-agreement-checkbox="$root.showTermsAgreementCheckbox ? $root.showTermsAgreementCheckbox : false"
:terms-agreement-message="$root.termsAgreementMessage" :terms-agreement-message="$root.termsAgreementMessage"
:is-layout-steps="$root.isLayoutSteps"
/> />
</template> </template>
@ -55,6 +56,7 @@ export default {
@import "../../../../../node_modules/buefy/src/scss/components/_dialog.scss"; @import "../../../../../node_modules/buefy/src/scss/components/_dialog.scss";
@import "../../../../../node_modules/buefy/src/scss/components/_notices.scss"; @import "../../../../../node_modules/buefy/src/scss/components/_notices.scss";
@import "../../../../../node_modules/buefy/src/scss/components/_numberinput.scss"; @import "../../../../../node_modules/buefy/src/scss/components/_numberinput.scss";
@import "../../../../../node_modules/buefy/src/scss/components/_steps.scss";
// Block level custom variables // Block level custom variables
@import "../../../admin/scss/_custom_variables.scss"; @import "../../../admin/scss/_custom_variables.scss";

View File

@ -1004,6 +1004,8 @@ return apply_filters( 'tainacan-i18n', [
'info_item_submission_draft_status' => __( 'Warning: draft items may be submitted even without filling all required metadata.', 'tainacan' ), 'info_item_submission_draft_status' => __( 'Warning: draft items may be submitted even without filling all required metadata.', 'tainacan' ),
'info_empty_geocoordinate_metadata_list' => __( 'No geocoordinate metadata was found. Try enabling it in the "displayed metadata" dropdown.', 'tainacan' ), 'info_empty_geocoordinate_metadata_list' => __( 'No geocoordinate metadata was found. Try enabling it in the "displayed metadata" dropdown.', 'tainacan' ),
'info_non_located_item' => __( 'This item does not have any location based on this metadata.', 'tainacan' ), 'info_non_located_item' => __( 'This item does not have any location based on this metadata.', 'tainacan' ),
'info_metadata_section_hidden_conditional' => __( 'Section disabled due to a conditional metadatum value.', 'tainacan' ),
'info_create_select_metadatum_for_conditional_section' => __( 'For configuring conditional sections, first create one select type metadatum to use its values as rules for displaing this section. The metadatum should be inside another metadatum section.', 'tainacan' ),
/* Activity actions */ /* Activity actions */
'action_update-metadata-value' => __( 'Item Metadata Value Updates', 'tainacan'), 'action_update-metadata-value' => __( 'Item Metadata Value Updates', 'tainacan'),