diff --git a/package-lock.json b/package-lock.json
index 65afedac7..10ebe4637 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1647,7 +1647,7 @@
},
"@polka/url": {
"version": "1.0.0-next.24",
- "resolved": "https://registry.npmjs.org/@polka/url/-/url-1.0.0-next.24.tgz",
+ "resolved": "https://registry.npmjs.org/@polka/tainacan-url-plugin-metadata-type/-/url-1.0.0-next.24.tgz",
"integrity": "sha512-2LuNTFBIO0m7kKIQvvPHN6UE63VjpmL9rnEEaOOaiSPbZK+zUOYIzBAWcED+3XYzhYsd/0mD57VdxAEqqV52CQ==",
"dev": true
},
diff --git a/src/views/admin/components/metadata-types/metadata-type-helper/class-tainacan-metadata-type-helper.php b/src/views/admin/components/metadata-types/metadata-type-helper/class-tainacan-metadata-type-helper.php
index 5984643f7..318d36ff6 100644
--- a/src/views/admin/components/metadata-types/metadata-type-helper/class-tainacan-metadata-type-helper.php
+++ b/src/views/admin/components/metadata-types/metadata-type-helper/class-tainacan-metadata-type-helper.php
@@ -43,6 +43,7 @@ class Metadata_Type_Helper {
$this->Tainacan_Metadata->register_metadata_type('Tainacan\Metadata_Types\User');
$this->Tainacan_Metadata->register_metadata_type('Tainacan\Metadata_Types\Control');
$this->Tainacan_Metadata->register_metadata_type('Tainacan\Metadata_Types\GeoCoordinate');
+ $this->Tainacan_Metadata->register_metadata_type('Tainacan\Metadata_Types\URL');
// the priority should see less than on function
// `load_admin_page()` of class `Admin` in file /src/views/class-tainacan-admin.php
diff --git a/src/views/admin/components/metadata-types/url/FormURL.vue b/src/views/admin/components/metadata-types/url/FormURL.vue
new file mode 100644
index 000000000..e1a155805
--- /dev/null
+++ b/src/views/admin/components/metadata-types/url/FormURL.vue
@@ -0,0 +1,154 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ $i18n.getHelperTitle('tainacan-url', 'iframe-min-height') }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/views/admin/components/metadata-types/url/TainacanURL.vue b/src/views/admin/components/metadata-types/url/TainacanURL.vue
new file mode 100644
index 000000000..256a70d81
--- /dev/null
+++ b/src/views/admin/components/metadata-types/url/TainacanURL.vue
@@ -0,0 +1,87 @@
+
+
+
onInput($event)"
+ @blur="onBlur" />
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/views/admin/components/metadata-types/url/class-tainacan-url.php b/src/views/admin/components/metadata-types/url/class-tainacan-url.php
new file mode 100644
index 000000000..4b769cf06
--- /dev/null
+++ b/src/views/admin/components/metadata-types/url/class-tainacan-url.php
@@ -0,0 +1,209 @@
+set_name( __('URL', 'tainacan') );
+ $this->set_description( __('An URL link, possibly with embedded content.', 'tainacan') );
+ $this->set_primitive_type('string');
+ $this->set_component('tainacan-url');
+ $this->set_form_component('tainacan-form-url');
+ $this->set_default_options([
+ 'link-as-button' => 'no',
+ 'force-iframe' => 'no',
+ 'iframe-min-height' => '',
+ 'iframe-allowfullscreen' => 'no',
+ 'is-image' => 'no'
+ ]);
+ $this->set_preview_template('
+
+ ');
+ }
+
+ /**
+ * @inheritdoc
+ */
+ public function get_form_labels() {
+ return [
+ 'link-as-button' => [
+ 'title' => __( 'Display link as a button', 'tainacan' ),
+ 'description' => __( 'Style the link to be displayed as a button instead of a simple textual link.', 'tainacan' ),
+ ],
+ 'force-iframe' => [
+ 'title' => __( 'Force iframe', 'tainacan' ),
+ 'description' => __( 'Force the URL to be displayed in an iframe in case the content is not embeddable by WordPress.', 'tainacan' ),
+ ],
+ 'iframe-min-height' => [
+ 'title' => __( 'Forced iframe minimum height', 'tainacan' ),
+ 'description' => __( 'If forcing the use of an iframe, sets the height attribute, in pixels. Leave it empty to be 100% of the container.', 'tainacan' ),
+ ],
+ 'iframe-allowfullscreen' => [
+ 'title' => __( 'Allow fullscreen on forced iframe', 'tainacan' ),
+ 'description' => __( 'If forcing the use of an iframe, allows it to request fullscreen to the browser.', 'tainacan' ),
+ ],
+ 'is-image' => [
+ 'title' => __( 'Is link to external image', 'tainacan' ),
+ 'description' => __( 'If you are linking directly to an external image, use this option so it can be properly embedded.', 'tainacan' ),
+ ]
+ ];
+ }
+
+ /**
+ * Get the value as a HTML string with links
+ * @return string
+ */
+ public function get_value_as_html(\Tainacan\Entities\Item_Metadata_Entity $item_metadata) {
+
+ $value = $item_metadata->get_value();
+ $link_as_button = $this->get_option('link-as-button') == 'yes';
+ $return = '';
+
+ $return .= $link_as_button ? '' : '';
+
+ if ( is_array($value) && $item_metadata->is_multiple() ) {
+ $total = sizeof($value);
+ $count = 0;
+ $prefix = $item_metadata->get_multivalue_prefix();
+ $suffix = $item_metadata->get_multivalue_suffix();
+ $separator = $item_metadata->get_multivalue_separator();
+
+ foreach ( $value as $el ) {
+ if ( !empty($el) ) {
+ $return .= $prefix;
+
+ $return .= $this->get_single_value_as_html($el);
+
+ $return .= $suffix;
+
+ $count ++;
+
+ if ($count < $total && !$link_as_button)
+ $return .= $separator;
+ }
+ }
+
+ } else {
+ $return .= $this->get_single_value_as_html($value);
+ }
+ $return .= $link_as_button ? '
' : '';
+
+ return $return;
+ }
+
+ /**
+ * Get the a single value as a HTML string with links
+ * @return string
+ */
+ public function get_single_value_as_html($value) {
+ global $wp_embed;
+
+ $link_as_button = $this->get_option('link-as-button') == 'yes';
+ $return = '';
+
+ if ($link_as_button) {
+ $mkstr = preg_replace(
+ '/\[([^\]]+)\]\(([^\)]+)\)/',
+ '',
+ $value
+ );
+ $return = $this->make_clickable_links($mkstr);
+ } else {
+
+ // First, we try WordPress autoembed
+ $embed = $wp_embed->autoembed($value);
+
+ // If it didn't work, it will still ba a URL
+ if ( esc_url($embed) == esc_url($value) ) {
+
+ // Than we can force the usage of an iframe
+ if ( $this->get_option('force-iframe') == 'yes' ) {
+
+ // URL points to an image file
+ if ( $this->get_option('is-image') == 'yes' ) {
+ $return = sprintf(' ', $value, $value);
+
+ // URL points to a content that is not an image
+ } else {
+ $iframeMininumHeight = '100%';
+
+ if (!empty($this->get_option('iframe-min-height')))
+ $iframeMininumHeight = $this->get_option('iframe-min-height');
+
+ // Creates an embed with responsive wrapper
+ $tainacan_embed = \Tainacan\Embed::get_instance();
+ $return = $tainacan_embed->add_responsive_wrapper( '' );
+ }
+
+ // Or we can leave it as a link
+ } else {
+ $mkstr = preg_replace(
+ '/\[([^\]]+)\]\(([^\)]+)\)/',
+ '\1 ',
+ $value
+ );
+ $return = $this->make_clickable_links($mkstr);
+ }
+
+ // If the autoembed did work, we pass the responsive wrapper to it
+ } else {
+ $tainacan_embed = \Tainacan\Embed::get_instance();
+ $return = $tainacan_embed->add_responsive_wrapper($embed);
+ }
+ }
+
+ return $return;
+ }
+
+ /**
+ * Checks if the value passed is a valid URL or markdown link
+ * @return boolean
+ */
+ public function validate(\Tainacan\Entities\Item_Metadata_Entity $item_metadata) {
+ $value = $item_metadata->get_value();
+ //$reg_mrkd = '~\[(.+)\]\(([^ ]+)?\)~i';
+ $reg_url = '~^((www\.|http:\/\/www\.|http:\/\/|https:\/\/www\.|https:\/\/|ftp:\/\/www\.|ftp:\/\/|ftps:\/\/www\.|ftps:\/\/)[^"<\s]+)(?![^<>]*>|[^"]*?<\/a)$~i';
+ $reg_full = '~\[(.+)\]\((((www\.|http:\/\/www\.|http:\/\/|https:\/\/www\.|https:\/\/|ftp:\/\/www\.|ftp:\/\/|ftps:\/\/www\.|ftps:\/\/)[^"<\s]+)(?![^<>]*>|[^"]*?<\/a))?\)~i';
+
+ // Multivalued metadata --------------
+ if ( is_array($value) ) {
+ foreach ($value as $url_value) {
+
+ // Empty strings are valid
+ if ( !empty($url_value) ) {
+
+ // If this seems to be a markdown link, we check if the url inside it is ok as well
+ if ( !preg_match($reg_url, $url_value) && !preg_match($reg_full, $url_value) ) {
+ $this->add_error( sprintf( __('"%s" is invalid. Please provide a valid, full URL or a Markdown link in the form of [label](url).', 'tainacan'), $url_value ) );
+ return false;
+ }
+ }
+ }
+ return true;
+ }
+
+ // Single valued metadata --------------
+ // Empty strings are valid
+ if ( !empty($value) ) {
+
+ // If this seems to be a markdown link, we check if the url inside it is ok as well
+ if ( !preg_match($reg_url, $value) && !preg_match($reg_full, $value) ) {
+ $this->add_error( sprintf( __('"%s" is invalid. Please provide a valid, full URL or a Markdown link in the form of [label](url).', 'tainacan'), $value ) );
+ return false;
+ }
+ }
+ return true;
+ }
+
+}
+?>
diff --git a/src/views/admin/js/admin-main.js b/src/views/admin/js/admin-main.js
index cbbcf3bc0..b6191093d 100644
--- a/src/views/admin/js/admin-main.js
+++ b/src/views/admin/js/admin-main.js
@@ -44,6 +44,7 @@ import TainacanTaxonomy from '../components/metadata-types/taxonomy/TainacanTaxo
import TainacanCompound from '../components/metadata-types/compound/TainacanCompound.vue';
import TainacanUser from '../components/metadata-types/user/TainacanUser.vue';
import TainacanGeoCoordinate from '../components/metadata-types/geocoordinate/TainacanGeoCoordinate.vue'
+import TainacanURL from '../components/metadata-types/url/TainacanURL.vue';
import FormText from '../components/metadata-types/text/FormText.vue';
import FormTextarea from '../components/metadata-types/textarea/FormTextarea.vue';
@@ -53,6 +54,7 @@ import FormSelectbox from '../components/metadata-types/selectbox/FormSelectbox.
import FormNumeric from '../components/metadata-types/numeric/FormNumeric.vue';
import FormUser from '../components/metadata-types/user/FormUser.vue';
import FormGeoCoordinate from '../components/metadata-types/geocoordinate/FormGeoCoordinate.vue';
+import FormURL from '../components/metadata-types/url/FormURL.vue';
// Term edition form must be imported here so that it is not necessary on item-submission bundle
import TermEditionForm from '../components/edition/term-edition-form.vue';
@@ -231,6 +233,7 @@ export default (element) => {
app.component('tainacan-compound', TainacanCompound);
app.component('tainacan-user', TainacanUser);
app.component('tainacan-geocoordinate', TainacanGeoCoordinate);
+ app.component('tainacan-url', TainacanURL);
/* Metadata Option forms */
@@ -243,6 +246,7 @@ export default (element) => {
app.component('tainacan-form-user', FormUser);
app.component('term-edition-form', TermEditionForm);
app.component('tainacan-form-geocoordinate', FormGeoCoordinate);
+ app.component('tainacan-form-url', FormURL);
/* Filter Metadata Option forms */
app.component('tainacan-filter-form-numeric', FormFilterNumeric);
diff --git a/src/views/admin/scss/tainacan-admin.scss b/src/views/admin/scss/tainacan-admin.scss
index f41f24d7e..b3ff64015 100644
--- a/src/views/admin/scss/tainacan-admin.scss
+++ b/src/views/admin/scss/tainacan-admin.scss
@@ -304,6 +304,24 @@ html.is-clipped .page-container-small:not(.is-filters-menu-open) {
.taginput-container .input:hover {
border: none !important;
}
+.metadata-type-ainacan_url_plugin_metadata_type .multivalue-separator,
+.metadata-type-tainacan_url_plugin_metadata_type .multivalue-separator {
+ display: block;
+ max-height: 1px;
+ width: 80px;
+ background: var(--tainacan-gray3, #a5a5a5);
+ content: none;
+ color: transparent !important;
+ margin: 1em auto 1em 0 !important;
+}
+.metadata-type-ainacan_url_plugin_metadata_type .wp-block-buttons,
+.metadata-type-tainacan_url_plugin_metadata_type .wp-block-buttons {
+ display: flex !important;
+}
+.metadata-type-ainacan_url_plugin_metadata_type .wp-block-buttons>.wp-block-button,
+.metadata-type-tainacan_url_plugin_metadata_type .wp-block-buttons>.wp-block-button {
+ width: auto !important;
+}
// Buefy notices (toast, snackbar...)
.notices {
diff --git a/src/views/gutenberg-blocks/blocks/item-submission-form/theme.js b/src/views/gutenberg-blocks/blocks/item-submission-form/theme.js
index 91bde8082..c5e3301c4 100644
--- a/src/views/gutenberg-blocks/blocks/item-submission-form/theme.js
+++ b/src/views/gutenberg-blocks/blocks/item-submission-form/theme.js
@@ -38,6 +38,7 @@ import TainacanTaxonomy from '../../../admin/components/metadata-types/taxonomy/
import TainacanCompound from '../../../admin/components/metadata-types/compound/TainacanCompound.vue';
import TainacanUser from '../../../admin/components/metadata-types/user/TainacanUser.vue';
import TainacanGeoCoordinate from '../../../admin/components/metadata-types/geocoordinate/TainacanGeoCoordinate.vue';
+import TainacanURL from '../../../admin/components/metadata-types/url/TainacanURL.vue';
// Main components
import ItemSubmissionForm from './theme.vue';
@@ -176,6 +177,7 @@ export default (element) => {
VueItemSubmission.component('tainacan-compound', TainacanCompound);
VueItemSubmission.component('tainacan-user', TainacanUser);
VueItemSubmission.component('tainacan-geocoordinate', TainacanGeoCoordinate);
+ VueItemSubmission.component('tainacan-url', TainacanURL);
/* Others */
VueItemSubmission.component('tainacan-form-item', TainacanFormItem);
diff --git a/src/views/tainacan-i18n.php b/src/views/tainacan-i18n.php
index 0ebe2e342..33ff34bd8 100644
--- a/src/views/tainacan-i18n.php
+++ b/src/views/tainacan-i18n.php
@@ -706,6 +706,7 @@ return apply_filters( 'tainacan-i18n', [
'label_item_edition_form_options' => __( 'Item edition form options', 'tainacan' ),
'label_item_submission_options' => __( 'Item submission options', 'tainacan' ),
'label_metadata_related_features' => __( 'Metadata related features', 'tainacan' ),
+ 'label_preview' => __( 'Preview', 'tainacan' ),
// Instructions. More complex sentences to guide user and placeholders
'instruction_delete_selected_collections' => __( 'Delete selected collections', 'tainacan' ),