Merge remote-tracking branch 'origin/develop' into export

This commit is contained in:
Jacson Passold 2018-05-03 10:10:48 -03:00
commit 16d2b2a8ab
74 changed files with 3559 additions and 2142 deletions

107
package-lock.json generated
View File

@ -450,9 +450,9 @@
}
},
"babel-core": {
"version": "6.26.0",
"resolved": "https://registry.npmjs.org/babel-core/-/babel-core-6.26.0.tgz",
"integrity": "sha1-rzL3izGm/O8RnIew/Y2XU/A6C7g=",
"version": "6.26.2",
"resolved": "https://registry.npmjs.org/babel-core/-/babel-core-6.26.2.tgz",
"integrity": "sha512-rFKFnHY8sbRSqja2O5eTx0z0Na5hukdtsFt7X9xdBFXMurrJ5YoY78Y/2/EuNZIaDQKEJSfxSMePfsymxt0CZg==",
"dev": true,
"requires": {
"babel-code-frame": "^6.26.0",
@ -465,21 +465,21 @@
"babel-traverse": "^6.26.0",
"babel-types": "^6.26.0",
"babylon": "^6.18.0",
"convert-source-map": "^1.5.0",
"debug": "^2.6.8",
"convert-source-map": "^1.5.1",
"debug": "^2.6.9",
"json5": "^0.5.1",
"lodash": "^4.17.4",
"minimatch": "^3.0.4",
"path-is-absolute": "^1.0.1",
"private": "^0.1.7",
"private": "^0.1.8",
"slash": "^1.0.0",
"source-map": "^0.5.6"
"source-map": "^0.5.7"
}
},
"babel-generator": {
"version": "6.26.0",
"resolved": "https://registry.npmjs.org/babel-generator/-/babel-generator-6.26.0.tgz",
"integrity": "sha1-rBriAHC3n248odMmlhMFN3TyDcU=",
"version": "6.26.1",
"resolved": "https://registry.npmjs.org/babel-generator/-/babel-generator-6.26.1.tgz",
"integrity": "sha512-HyfwY6ApZj7BYTcJURpM5tznulaBvyio7/0d4zFOeMPUmfxkCjHocCuoLa2SAGzBI8AREcH3eP3758F672DppA==",
"dev": true,
"requires": {
"babel-messages": "^6.23.0",
@ -488,7 +488,7 @@
"detect-indent": "^4.0.0",
"jsesc": "^1.3.0",
"lodash": "^4.17.4",
"source-map": "^0.5.6",
"source-map": "^0.5.7",
"trim-right": "^1.0.1"
}
},
@ -7252,9 +7252,7 @@
"nan": {
"version": "2.10.0",
"resolved": "https://registry.npmjs.org/nan/-/nan-2.10.0.tgz",
"integrity": "sha512-bAdJv7fBLhWC+/Bls0Oza+mvTaNQtP+1RyhhhvD95pgUJz6XM5IzgmxOkItJ9tkoCiplvAnXI1tNmmUD/eScyA==",
"dev": true,
"optional": true
"integrity": "sha512-bAdJv7fBLhWC+/Bls0Oza+mvTaNQtP+1RyhhhvD95pgUJz6XM5IzgmxOkItJ9tkoCiplvAnXI1tNmmUD/eScyA=="
},
"nanomatch": {
"version": "1.2.9",
@ -7370,9 +7368,9 @@
}
},
"node-sass": {
"version": "4.8.3",
"resolved": "https://registry.npmjs.org/node-sass/-/node-sass-4.8.3.tgz",
"integrity": "sha512-tfFWhUsCk/Y19zarDcPo5xpj+IW3qCfOjVdHtYeG6S1CKbQOh1zqylnQK6cV3z9k80yxAnFX9Y+a9+XysDhhfg==",
"version": "4.9.0",
"resolved": "https://registry.npmjs.org/node-sass/-/node-sass-4.9.0.tgz",
"integrity": "sha512-QFHfrZl6lqRU3csypwviz2XLgGNOoWQbo2GOvtsfQqOfL4cy1BtWnhx/XUeAO9LT3ahBzSRXcEO6DdvAH9DzSg==",
"requires": {
"async-foreach": "^0.1.3",
"chalk": "^1.1.1",
@ -7403,11 +7401,6 @@
"lru-cache": "^4.0.1",
"which": "^1.2.9"
}
},
"nan": {
"version": "2.10.0",
"resolved": "https://registry.npmjs.org/nan/-/nan-2.10.0.tgz",
"integrity": "sha512-bAdJv7fBLhWC+/Bls0Oza+mvTaNQtP+1RyhhhvD95pgUJz6XM5IzgmxOkItJ9tkoCiplvAnXI1tNmmUD/eScyA=="
}
}
},
@ -11571,68 +11564,11 @@
"vue-template-es2015-compiler": "^1.6.0"
},
"dependencies": {
"ansi-styles": {
"version": "3.2.1",
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
"integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
"dev": true,
"requires": {
"color-convert": "^1.9.0"
}
},
"chalk": {
"version": "2.3.2",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-2.3.2.tgz",
"integrity": "sha512-ZM4j2/ld/YZDc3Ma8PgN7gyAk+kHMMMyzLNryCPGhWrsfAuDVeuid5bpRFTDgMH9JBK2lA4dyyAkkZYF/WcqDQ==",
"dev": true,
"requires": {
"ansi-styles": "^3.2.1",
"escape-string-regexp": "^1.0.5",
"supports-color": "^5.3.0"
}
},
"has-flag": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
"integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=",
"dev": true
},
"loader-utils": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.1.0.tgz",
"integrity": "sha1-yYrvSIvM7aL/teLeZG1qdUQp9c0=",
"dev": true,
"requires": {
"big.js": "^3.1.3",
"emojis-list": "^2.0.0",
"json5": "^0.5.0"
}
},
"postcss": {
"version": "6.0.21",
"resolved": "https://registry.npmjs.org/postcss/-/postcss-6.0.21.tgz",
"integrity": "sha512-y/bKfbQz2Nn/QBC08bwvYUxEFOVGfPIUOTsJ2CK5inzlXW9SdYR1x4pEsG9blRAF/PX+wRNdOah+gx/hv4q7dw==",
"dev": true,
"requires": {
"chalk": "^2.3.2",
"source-map": "^0.6.1",
"supports-color": "^5.3.0"
}
},
"source-map": {
"version": "0.6.1",
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
"integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
"dev": true
},
"supports-color": {
"version": "5.3.0",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.3.0.tgz",
"integrity": "sha512-0aP01LLIskjKs3lq52EC0aGBAJhLq7B2Rd8HC/DR/PtNNpcLilNmHC12O+hu0usQpo7wtHNRqtrhBwtDb0+dNg==",
"dev": true,
"requires": {
"has-flag": "^3.0.0"
}
}
}
},
@ -11649,19 +11585,6 @@
"requires": {
"hash-sum": "^1.0.2",
"loader-utils": "^1.0.2"
},
"dependencies": {
"loader-utils": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.1.0.tgz",
"integrity": "sha1-yYrvSIvM7aL/teLeZG1qdUQp9c0=",
"dev": true,
"requires": {
"big.js": "^3.1.3",
"emojis-list": "^2.0.0",
"json5": "^0.5.0"
}
}
}
},
"vue-template-compiler": {

View File

@ -2,8 +2,15 @@
<div
id="tainacan-admin-app"
class="columns is-fullheight">
<primary-menu/>
<tainacan-header/>
<primary-menu
:active-route="activeRoute"
:is-menu-compressed="isMenuCompressed"/>
<button
id="menu-compress-button"
@click="isMenuCompressed = !isMenuCompressed">
<b-icon :icon="isMenuCompressed ? 'menu-right' : 'menu-left'" />
</button>
<tainacan-header :is-menu-compressed="isMenuCompressed"/>
<div class="column is-main-content">
<router-view/>
</div>
@ -18,6 +25,8 @@
name: "AdminPage",
data(){
return {
isMenuCompressed: false,
activeRoute: '/collections'
}
},
components: {
@ -25,7 +34,15 @@
TainacanHeader
},
created() {
this.$userPrefs.init()
this.$userPrefs.init();
this.isMenuCompressed = (this.$route.params.collectionId != undefined);
this.activeRoute = this.$route.name;
},
watch: {
'$route' (to) {
this.isMenuCompressed = (to.params.collectionId != undefined);
this.activeRoute = to.name;
}
}
}
</script>
@ -63,7 +80,7 @@
margin: $header-height auto 0 auto;
position: relative;
overflow-y: auto;
height: calc(100% - 58px);
height: calc(100% - 53px);
@media screen and (max-width: 769px) {
& {
@ -77,5 +94,24 @@
}
}
#menu-compress-button {
position: absolute;
z-index: 99;
top: 70px;
max-width: 23px;
height: 21px;
width: 23px;
border: none;
background-color: $primary-light;
color: $secondary;
padding: 0px;
border-top-right-radius: 2px;
border-bottom-right-radius: 2px;
.icon {
margin-top: -1px;
}
}
</style>

View File

@ -56,46 +56,46 @@ class Admin {
wp_enqueue_style( 'tainacan-admin-page', $TAINACAN_BASE_URL . '/assets/css/tainacan-admin.css' );
$undesired_wp_styles = [
//'admin-menu',
//'admin-bar',
//'code-editor',
//'color-picker',
//'customize-controls',
//'customize-nav-menus',
//'customize-widgets',
//'dashboard',
//'dashicons',
//'deprecated-media',
//'edit',
//'wp-pointer',
//'farbtastic',
//'forms',
//'common',
//'install',
//'wp-auth-check',
//'site-icon',
//'buttons',
//'l10n',
//'list-tables',
//'login',
//'media',
//'nav-menus',
//'revisions',
//'themes',
//'widgets',
//'wp-admin'
];
//wp_dequeue_style( $undesired_wp_styles );
wp_deregister_style( $undesired_wp_styles );
// $undesired_wp_styles = [
// 'admin-menu',
// 'admin-bar',
// 'code-editor',
// 'color-picker',
// 'customize-controls',
// 'customize-nav-menus',
// 'customize-widgets',
// 'dashboard',
// 'dashicons',
// 'deprecated-media',
// 'edit',
// 'wp-pointer',
// 'farbtastic',
// 'forms',
// 'common',
// 'install',
// 'wp-auth-check',
// 'site-icon',
// 'buttons',
// 'l10n',
// 'list-tables',
// 'login',
// 'media',
// 'nav-menus',
// 'revisions',
// 'themes',
// 'widgets',
// 'wp-admin'
// ];
//
// wp_dequeue_style( $undesired_wp_styles );
// wp_deregister_style( $undesired_wp_styles );
}
function add_admin_js() {
global $TAINACAN_BASE_URL;
wp_enqueue_script( 'tainacan-user-admin', $TAINACAN_BASE_URL . '/assets/user_admin-components.js', [], null, true );
wp_enqueue_script( 'tainacan-user-admin', $TAINACAN_BASE_URL . '/assets/user_admin-components.js', ['underscore', 'media-editor', 'media-views', 'customize-controls'], null, true );
$settings = $this->get_admin_js_localization_params();
@ -103,6 +103,7 @@ class Admin {
wp_enqueue_media();
wp_enqueue_script('undescore', includes_url('js') . '/underscore.min.js' );
wp_enqueue_script('jcrop');
wp_enqueue_script( 'customize-controls' );
}
@ -157,7 +158,8 @@ class Admin {
'user_caps' => $user_caps,
'user_prefs' => $prefs,
'base_url' => $TAINACAN_BASE_URL,
'admin_url' => admin_url()
'admin_url' => admin_url(),
'custom_header_support' => get_theme_support('custom-header'),
];
$maps = [

View File

@ -1,36 +1,32 @@
<template>
<div
class="page-container"
:class="{'primary-page' : isNewCollection }">
<b-tag
v-if="collection != null && collection != undefined"
:type="'is-' + getStatusColor(collection.status)"
v-text="collection.status"/>
<div :class="{'primary-page' : isNewCollection }">
<tainacan-title />
<form
v-if="collection != null && collection != undefined"
class="tainacan-form"
label-width="120px">
<div class="columns">
<div class="columns is-variable is-8">
<div class="column is-narrow">
<!-- Thumbnail -------------------------------- -->
<b-field
:addons="false"
:label="$i18n.get('label_image')">
:label="$i18n.get('label_thumbnail')">
<div class="thumbnail-field">
<a
class="button is-rounred is-secondary"
id="button-edit-thumbnail"
:aria-label="$i18n.get('label_button_edit_thumb')"
@click="editImage($event, true)">
@click.prevent="thumbnailMediaFrame.openFrame($event)">
<b-icon icon="pencil" />
</a>
<figure class="image is-128x128">
<span
v-if="collection.featured_image == undefined || collection.featured_image == false"
class="image-placeholder">{{ $i18n.get('label_empty_thumbnail') }}</span>
<img
<img
id="thumbail-image"
:alt="$i18n.get('label_thumbnail')"
:src="(collection.featured_image == undefined || collection.featured_image == false) ? thumbPlaceholderPath : collection.featured_image">
</figure>
@ -44,7 +40,7 @@
</div>
</div>
</b-field>
<!-- Header Page -------------------------------- -->
<b-field
:addons="false"
@ -54,7 +50,7 @@
class="button is-rounred is-secondary"
id="button-edit-header-image"
:aria-label="$i18n.get('label_button_edit_header_image')"
@click="editImage($event, false)">
@click="headerImageMediaFrame.openFrame($event)">
<b-icon icon="pencil" />
</a>
<figure class="image is-128x128">
@ -63,7 +59,7 @@
class="image-placeholder">{{ $i18n.get('label_empty_header_image') }}</span>
<img
:alt="$i18n.get('label_thumbnail')"
:src="(collection.header_image == undefined || collection.header_image == false) ? thumbPlaceholderPath : collection.header_image">
:src="(collection.header_image == undefined || collection.header_image == false) ? headerPlaceholderPath : collection.header_image">
</figure>
<div class="thumbnail-buttons-row">
<a
@ -76,7 +72,30 @@
</div>
</b-field>
</div>
<div class="column">
<div class="column">
<!-- Status -------------------------------- -->
<b-field
:addons="false"
:label="$i18n.get('label_status')"
:type="editFormErrors['status'] != undefined ? 'is-danger' : ''"
:message="editFormErrors['status'] != undefined ? editFormErrors['status'] : ''">
<help-button
:title="$i18n.getHelperTitle('collections', 'status')"
:message="$i18n.getHelperMessage('collections', 'status')"/>
<b-select
id="tainacan-select-status"
v-model="form.status"
@focus="clearErrors('status')"
:placeholder="$i18n.get('instruction_select_a_status')">
<option
v-for="statusOption in statusOptions"
:key="statusOption.value"
:value="statusOption.value"
:disabled="statusOption.disabled">{{ statusOption.label }}
</option>
</b-select>
</b-field>
<!-- Name -------------------------------- -->
<b-field
:addons="false"
@ -108,29 +127,6 @@
@focus="clearErrors('description')"/>
</b-field>
<!-- Status -------------------------------- -->
<b-field
:addons="false"
:label="$i18n.get('label_status')"
:type="editFormErrors['status'] != undefined ? 'is-danger' : ''"
:message="editFormErrors['status'] != undefined ? editFormErrors['status'] : ''">
<help-button
:title="$i18n.getHelperTitle('collections', 'status')"
:message="$i18n.getHelperMessage('collections', 'status')"/>
<b-select
id="tainacan-select-status"
v-model="form.status"
@focus="clearErrors('status')"
:placeholder="$i18n.get('instruction_select_a_status')">
<option
v-for="statusOption in statusOptions"
:key="statusOption.value"
:value="statusOption.value"
:disabled="statusOption.disabled">{{ statusOption.label }}
</option>
</b-select>
</b-field>
<!-- Enable Cover Page -------------------------------- -->
<div class="field">
<b-checkbox
@ -158,6 +154,7 @@
:message="$i18n.getHelperMessage('collections', 'cover_page_id')"/>
<b-autocomplete
id="tainacan-text-cover-page"
:placeholder="$i18n.get('instruction_cover_page')"
:data="coverPages"
v-model="coverPageTitle"
@select="onSelectCoverPage($event)"
@ -168,7 +165,7 @@
<template slot-scope="props">
{{ props.option.title.rendered }}
</template>
<template slot="empty">No results found</template>
<template slot="empty">{{ $i18n.get('info_no_page_found') }}</template>
</b-autocomplete>
<div
@ -178,19 +175,60 @@
<span class="selected-cover-page-control">
<a
target="blank"
:href="coverPageEditPath">Edit</a>
:href="coverPageEditPath">{{ $i18n.get('edit') }}</a>
&nbsp;&nbsp;
<a
target="_blank"
:href="coverPage.link">View</a>
:href="coverPage.link">{{ $i18n.get('see') }}</a>
&nbsp;&nbsp;
<button
class="button is-secondary is-small"
@click.prevent="removeCoverPage()">Remove</button>
@click.prevent="removeCoverPage()">{{ $i18n.get('remove') }}</button>
</span>
</div>
</b-field>
<!-- Moderators List -------------------------------- -->
<b-field
:addons="false"
:label="$i18n.get('label_moderators')"
:type="editFormErrors['moderators'] != undefined ? 'is-danger' : ''"
:message="editFormErrors['moderators'] != undefined ? editFormErrors['moderators'] : ''">
<help-button
:title="$i18n.getHelperTitle('collections', 'moderators_ids')"
:message="$i18n.getHelperMessage('collections', 'moderators_ids')"/>
<b-autocomplete
id="tainacan-text-moderators-input"
:placeholder="$i18n.get('instruction_moderators')"
:data="users"
@select="onAddModerator($event)"
:loading="isFetchingModerators"
@input="fecthModerators($event)"
@focus="clearErrors('moderators')">
<template slot-scope="props">
{{ props.option.name }}
</template>
<template slot="empty">{{ $i18n.get('info_no_user_found') }}</template>
</b-autocomplete>
<ul
class="moderators-list"
v-if="moderators != undefined && moderators.length > 0">
<li
:key="index"
v-for="(moderator, index) of moderators">
<b-tag
attached
closable
@close="removeModerator(index)">
{{ moderator.name }}
</b-tag>
</li>
</ul>
<div
class="moderators-empty-list"
v-else>
{{ $i18n.get('info_no_moderator_on_collection') }}
</div>
</b-field>
<!-- Slug -------------------------------- -->
@ -207,6 +245,32 @@
v-model="form.slug"
@focus="clearErrors('slug')"/>
</b-field>
<!-- Parent Collection -------------------------------- -->
<b-field
:addons="false"
:label="$i18n.get('label_parent_collection')"
:type="editFormErrors['parent'] != undefined ? 'is-danger' : ''"
:message="editFormErrors['parent'] != undefined ? editFormErrors['parent'] : ''">
<help-button
:title="$i18n.getHelperTitle('collections', 'parent')"
:message="$i18n.getHelperMessage('collections', 'parent')"/>
<b-select
id="tainacan-select-parent"
v-model="form.parent"
@focus="clearErrors('parent')"
:loading="isFetchingCollections"
:placeholder="$i18n.get('instruction_select_a_parent_collection')">
<option value="0">{{ $i18n.get('label_no_parent_collection') }}</option>
<option
v-if="collection.id != anotherCollection.id"
v-for="anotherCollection of collections"
:key="anotherCollection.id"
:value="anotherCollection.id">{{ anotherCollection.name }}
</option>
</b-select>
</b-field>
</div>
</div>
@ -237,6 +301,7 @@
<script>
import { mapActions } from 'vuex';
import wpMediaFrames from '../../js/wp-media-frames';
export default {
name: 'CollectionEditionForm',
@ -253,7 +318,8 @@ export default {
enable_cover_page: '',
featured_image: '',
header_image: '',
files:[]
files:[],
moderators_ids: []
},
thumbnail: {},
cover: {},
@ -279,9 +345,15 @@ export default {
editFormErrors: {},
formErrorMessage: '',
isNewCollection: false,
// Fream Uploader variables
frameUploader: undefined,
thumbPlaceholderPath: tainacan_plugin.base_url + '/admin/images/placeholder.png'
thumbPlaceholderPath: tainacan_plugin.base_url + '/admin/images/placeholder_square.png',
headerPlaceholderPath: tainacan_plugin.base_url + '/admin/images/placeholder_rectangle.png',
isFetchingModerators: false,
users: [],
moderators: [],
collections: [],
isFetchingCollections: true,
thumbnailMediaFrame: undefined,
headerImageMediaFrame: undefined
}
},
methods: {
@ -293,7 +365,9 @@ export default {
'updateThumbnail',
'updateHeaderImage',
'fetchPages',
'fetchPage'
'fetchPage',
'fetchUsers',
'fetchCollectionsForParent'
]),
...mapActions('fields', [
'fetchFields'
@ -301,6 +375,10 @@ export default {
onSubmit() {
this.isLoading = true;
this.form.moderators_ids = [];
for (let moderator of this.moderators)
this.form.moderators_ids.push(moderator.id);
let data = {
collection_id: this.collectionId,
name: this.form.name,
@ -308,7 +386,9 @@ export default {
enable_cover_page: this.form.enable_cover_page,
cover_page_id: this.form.cover_page_id,
slug: this.form.slug,
status: this.form.status
status: this.form.status,
moderators_ids: this.form.moderators_ids,
parent: this.form.parent
};
this.updateCollection(data).then(updatedCollection => {
@ -338,20 +418,6 @@ export default {
this.isLoading = false;
});
},
getStatusColor(status) {
switch(status) {
case 'publish':
return 'success'
case 'draft':
return 'info'
case 'private':
return 'warning'
case 'trash':
return 'danger'
default:
return 'info'
}
},
createNewCollection() {
// Puts loading on Draft Collection creation
this.isLoading = true;
@ -363,16 +429,33 @@ export default {
this.collectionId = res.id;
this.collection = res;
// Initializes Media Frames now that collectonId exists
this.initializeMediaFrames();
// Fill this.form data with current data.
this.form.name = this.collection.name;
this.form.description = this.collection.description;
this.form.enable_cover_page = this.collection.enable_cover_page;
this.form.cover_page_id = this.collection.cover_page_id;
this.form.slug = this.collection.slug;
this.form.parent = this.collection.parent;
this.moderators = [];
// Pre-fill status with publish to incentivate it
this.form.status = 'publish';
// Generates options for parent collection
this.isFetchingCollections = true;
this.fetchCollectionsForParent()
.then((collections) => {
this.collections = collections;
this.isFetchingCollections = false;
})
.catch((error) => {
this.$console.error(error);
this.isFetchingCollections = false;
});
this.isLoading = false;
})
@ -402,67 +485,35 @@ export default {
this.coverPageTitle = this.coverPage.title.rendered;
this.coverPageEditPath = tainacan_plugin.admin_url + '/post.php?post=' + selectedPage.id + '&action=edit';
},
fecthModerators(search) {
this.isFetchingModerators = true;
let exceptions = [];
for (let user of this.moderators)
exceptions.push(parseInt(user.id));
exceptions.push(this.collection.author_id);
this.fetchUsers({ search: search, exceptions: exceptions})
.then((users) => {
this.users = users;
this.isFetchingModerators = false;
})
.catch((error) => {
this.$console.error(error);
this.isFetchingPages = false;
});
},
onAddModerator(user) {
this.moderators.push({'id': user.id, 'name': user.name});
},
removeModerator(moderatorIndex) {
this.moderators.splice(moderatorIndex, 1);
},
removeCoverPage() {
this.coverPage = {};
this.coverPageTitle = '';
this.form.cover_page_id = '';
},
editImage(event, isThumbnail) {
'use strict';
event.preventDefault();
// If the media frame already exists, reopen it.
if ( this.frameUploader ) {
this.frameUploader.open();
return;
}
// Create a new media frame
this.frameUploader = wp.media.frames.frame_uploader = wp.media({
frame: 'select',
title: 'Select or Upload and Image.',
button: {
text: 'Use this media'
},
multiple: false,
library: {
type: 'image',
uploadedTo: this.collectionId
},
uploader: true
});
wp.media.view.settings.post = {
id: this.collectionId
}
this.frameUploader.on('select', () => {
let media = this.frameUploader.state().get( 'selection' ).first().toJSON();
if (isThumbnail) {
this.updateThumbnail({collectionId: this.collectionId, thumbnailId: media.id})
.then((res) => {
this.collection.featured_image = res.featured_image;
})
.catch((error) => {
this.$console.error(error);
});
} else {
this.updateHeaderImage({collectionId: this.collectionId, headerImageId: media.id})
.then((res) => {
this.collection.header_image = res.header_image;
})
.catch((error) => {
this.$console.error(error);
});
}
});
this.frameUploader.open();
},
deleteThumbnail() {
this.updateThumbnail({collectionId: this.collectionId, thumbnailId: 0})
@ -483,6 +534,40 @@ export default {
this.$console.error(error);
});
},
initializeMediaFrames() {
this.thumbnailMediaFrame = new wpMediaFrames.thumbnailControl(
'my-thumbnail-media-frame', {
button_labels: {
frame_title: this.$i18n.get('instruction_select_collection_thumbnail'),
},
relatedPostId: this.collectionId,
onSave: (mediaId) => {
this.updateThumbnail({collectionId: this.collectionId, thumbnailId: mediaId})
.then((res) => {
this.collection.featured_image = res.featured_image;
})
.catch(error => this.$console.error(error));
}
}
);
this.headerImageMediaFrame = new wpMediaFrames.headerImageControl(
'my-header-image-media-frame', {
button_labels: {
frame_title: this.$i18n.get('instruction_select_collection_header_image'),
},
relatedPostId: this.collectionId,
onSave: (media) => {
this.updateHeaderImage({collectionId: this.collectionId, headerImageId: media.id})
.then((res) => {
this.collection.header_image = res.header_image;
})
.catch(error => this.$console.error(error));
}
}
);
}
},
created(){
@ -500,6 +585,9 @@ export default {
this.fetchCollection(this.collectionId).then(res => {
this.collection = res;
// Initializes Media Frames now that collectonId exists
this.initializeMediaFrames();
// Fill this.form data with current data.
this.form.name = this.collection.name;
this.form.description = this.collection.description;
@ -507,6 +595,9 @@ export default {
this.form.status = this.collection.status;
this.form.enable_cover_page = this.collection.enable_cover_page;
this.form.cover_page_id = this.collection.cover_page_id;
this.form.parent = this.collection.parent;
this.moderators = JSON.parse(JSON.stringify(this.collection.moderators));
// Generates CoverPage from current cover_page_id info
if (this.form.cover_page_id != undefined && this.form.cover_page_id != '') {
@ -524,7 +615,19 @@ export default {
this.$console.error(error);
this.isFetchingPages = false;
});
}
}
// Generates options for parent collection
this.isFetchingCollections = true;
this.fetchCollectionsForParent()
.then((collections) => {
this.collections = collections;
this.isFetchingCollections = false;
})
.catch((error) => {
this.$console.error(error);
this.isFetchingCollections = false;
});
this.isLoading = false;
});
@ -538,8 +641,13 @@ export default {
@import "../../scss/_variables.scss";
.tainacan-form>.columns>.column {
overflow: auto;
}
.thumbnail-field {
max-height: 128px;
margin-bottom: 96px;
margin-top: -20px;
.content {
padding: 10px;
@ -554,6 +662,7 @@ export default {
margin-right: 10px;
bottom: 50%;
font-size: 0.8rem;
font-weight: bold;
z-index: 99;
text-align: center;
color: gray;
@ -592,7 +701,7 @@ export default {
}
.selected-cover-page {
background-color: $tainacan-input-color;
padding: 6px;
padding: 8px;
font-size: .85rem;
.span { vertical-align: middle;}
@ -601,6 +710,18 @@ export default {
}
}
.moderators-list {
padding: 10px;
display: flex;
.tags {
margin-right: 5px;
}
}
.moderators-empty-list {
color: gray;
font-size: 0.85rem;
}
</style>

View File

@ -1,167 +1,178 @@
<template>
<form
id="fieldEditForm"
class="tainacan-form"
@submit.prevent="saveEdition(editForm)">
<b-field
<form
id="fieldEditForm"
class="tainacan-form"
@submit.prevent="saveEdition(editForm)">
<b-field
:addons="false"
:type="formErrors['name'] != undefined ? 'is-danger' : ''"
:message="formErrors['name'] != undefined ? formErrors['name'] : ''">
:type="formErrors['name'] != undefined ? 'is-danger' : ''"
:message="formErrors['name'] != undefined ? formErrors['name'] : ''">
<label class="label">
{{ $i18n.get('label_name') }}
<span
class="required-field-asterisk"
:class="formErrors['name'] != undefined ? 'is-danger' : ''">*</span>
<help-button
:title="$i18n.getHelperTitle('fields', 'name')"
{{ $i18n.get('label_name') }}
<span
class="required-field-asterisk"
:class="formErrors['name'] != undefined ? 'is-danger' : ''">*</span>
<help-button
:title="$i18n.getHelperTitle('fields', 'name')"
:message="$i18n.getHelperMessage('fields', 'name')"/>
</label>
<b-input
v-model="editForm.name"
name="name"
<b-input
v-model="editForm.name"
name="name"
@focus="clearErrors('name')"/>
</b-field>
<b-field
:addons="false"
:type="formErrors['description'] != undefined ? 'is-danger' : ''"
:type="formErrors['description'] != undefined ? 'is-danger' : ''"
:message="formErrors['description'] != undefined ? formErrors['description'] : ''">
<label class="label">
{{ $i18n.get('label_description') }}
<help-button
:title="$i18n.getHelperTitle('fields', 'description')"
<help-button
:title="$i18n.getHelperTitle('fields', 'description')"
:message="$i18n.getHelperMessage('fields', 'description')"/>
</label>
<b-input
type="textarea"
name="description"
v-model="editForm.description"
@focus="clearErrors('description')" />
<b-input
type="textarea"
name="description"
v-model="editForm.description"
@focus="clearErrors('description')"/>
</b-field>
<b-field
<b-field
:addons="false"
:type="formErrors['status'] != undefined ? 'is-danger' : ''"
:type="formErrors['status'] != undefined ? 'is-danger' : ''"
:message="formErrors['status'] != undefined ? formErrors['status'] : ''">
<label class="label">
{{ $i18n.get('label_status') }}
<help-button
:title="$i18n.getHelperTitle('fields', 'status')"
{{ $i18n.get('label_status') }}
<help-button
:title="$i18n.getHelperTitle('fields', 'status')"
:message="$i18n.getHelperMessage('fields', 'status')"/>
</label>
<div class="inline-block">
<b-radio
<b-radio
@focus="clearErrors('label_status')"
id="tainacan-select-status-publish"
name="status"
name="status"
v-model="editForm.status"
native-value="publish">
{{ $i18n.get('publish_visibility') }}
</b-radio>
<br>
<b-radio
<b-radio
@focus="clearErrors('label_status')"
id="tainacan-select-status-private"
name="status"
name="status"
v-model="editForm.status"
native-value="private">
{{ $i18n.get('private_visibility') }}
</b-radio>
</div>
</b-field>
<b-field
:addons="false"
:label="$i18n.get('label_display')">
<!-- Display on listing -->
<b-field
:addons="false">
<label class="label">
{{ $i18n.get('label_display') }}
<help-button
:title="$i18n.getHelperTitle('fields', 'display')"
:message="$i18n.getHelperMessage('fields', 'display')"/>
</label>
<b-field
:type="formErrors['required'] != undefined ? 'is-danger' : ''"
:message="formErrors['required'] != undefined ? formErrors['required'] : ''">
<b-checkbox
:type="formErrors['display'] != undefined ? 'is-danger' : ''"
:message="formErrors['display'] != undefined ? formErrors['display'] : ''">
<b-radio
size="is-small"
@input="clearErrors('required')"
v-model="editForm.required"
true-value="yes"
false-value="no"
name="required">
@input="clearErrors('display')"
v-model="editForm.display"
native-value="yes"
name="display">
{{ $i18n.get('label_display_default') }}
</b-checkbox>
<help-button
:title="$i18n.getHelperTitle('fields', 'required')"
:message="$i18n.getHelperMessage('fields', 'required')"/>
</b-radio>
</b-field>
<b-field>
<b-radio
size="is-small"
@input="clearErrors('display')"
v-model="editForm.display"
native-value="no"
name="display">
{{ $i18n.get('label_not_display') }}
</b-radio>
</b-field>
<b-field
:type="formErrors['multiple'] != undefined ? 'is-danger' : ''"
:message="formErrors['multiple'] != undefined ? formErrors['multiple'] : ''">
<b-checkbox
size="is-small"
@input="clearErrors('multiple')"
v-model="editForm.multiple"
true-value="yes"
false-value="no"
name="multiple">
:type="formErrors['display'] != undefined ? 'is-danger' : ''"
:message="formErrors['display'] != undefined ? formErrors['display'] : ''">
<b-radio
size="is-small"
v-model="editForm.display"
@input="clearErrors('display')"
native-value="never"
name="display">
{{ $i18n.get('label_display_never') }}
</b-checkbox>
<help-button
:title="$i18n.getHelperTitle('fields', 'multiple')"
:message="$i18n.getHelperMessage('fields', 'multiple')"/>
</b-radio>
</b-field>
</b-field>
<b-field
<b-field
:addons="false"
:label="$i18n.get('label_options')">
<b-field
:type="formErrors['required'] != undefined ? 'is-danger' : ''"
:type="formErrors['required'] != undefined ? 'is-danger' : ''"
:message="formErrors['required'] != undefined ? formErrors['required'] : ''">
<b-checkbox
size="is-small"
@input="clearErrors('required')"
v-model="editForm.required"
true-value="yes"
true-value="yes"
false-value="no"
name="required">
{{ $i18n.get('label_required') }}
</b-checkbox>
<help-button
:title="$i18n.getHelperTitle('fields', 'required')"
<help-button
:title="$i18n.getHelperTitle('fields', 'required')"
:message="$i18n.getHelperMessage('fields', 'required')"/>
</b-field>
<b-field
:type="formErrors['multiple'] != undefined ? 'is-danger' : ''"
:type="formErrors['multiple'] != undefined ? 'is-danger' : ''"
:message="formErrors['multiple'] != undefined ? formErrors['multiple'] : ''">
<b-checkbox
<b-checkbox
size="is-small"
@input="clearErrors('multiple')"
v-model="editForm.multiple"
true-value="yes"
true-value="yes"
false-value="no"
name="multiple">
{{ $i18n.get('label_allow_multiple') }}
</b-checkbox>
<help-button
:title="$i18n.getHelperTitle('fields', 'multiple')"
<help-button
:title="$i18n.getHelperTitle('fields', 'multiple')"
:message="$i18n.getHelperMessage('fields', 'multiple')"/>
</b-field>
<b-field
:type="formErrors['unique'] != undefined ? 'is-danger' : ''"
<b-field
:type="formErrors['unique'] != undefined ? 'is-danger' : ''"
:message="formErrors['unique'] != undefined ? formErrors['unique'] : ''">
<b-checkbox
size="is-small"
size="is-small"
@input="clearErrors('unique')"
v-model="editForm.unique"
true-value="yes"
true-value="yes"
false-value="no"
name="collecion_key">
{{ $i18n.get('label_unique_value') }}
</b-checkbox>
<help-button
:title="$i18n.getHelperTitle('fields', 'unique')"
:message="$i18n.getHelperMessage('fields', 'unique')"/>
<help-button
:title="$i18n.getHelperTitle('fields', 'unique')"
:message="$i18n.getHelperMessage('fields', 'unique')"/>
</b-field>
</b-field>
@ -171,21 +182,23 @@
:is="editForm.field_type_object.form_component"
:field="editForm"
v-model="editForm.field_type_options"/>
<div
v-html="editForm.edit_form"
<div
v-html="editForm.edit_form"
v-else/>
<div class="field is-grouped form-submit">
<div class="field is-grouped form-submit">
<div class="control">
<button
class="button is-outlined"
@click.prevent="cancelEdition()"
slot="trigger">{{ $i18n.get('cancel') }}</button>
<button
class="button is-outlined"
@click.prevent="cancelEdition()"
slot="trigger">{{ $i18n.get('cancel') }}
</button>
</div>
<div class="control">
<button
class="button is-success"
type="submit">{{ $i18n.get('save') }}</button>
<button
class="button is-success"
type="submit">{{ $i18n.get('save') }}
</button>
</div>
</div>
<p class="help is-danger">{{ formErrorMessage }}</p>
@ -193,117 +206,129 @@
</template>
<script>
import { mapActions } from 'vuex';
import {mapActions} from 'vuex';
export default {
name: 'FieldEditionForm',
data(){
return {
editForm: {},
oldForm: {},
formErrors: {},
formErrorMessage: '',
closedByForm: false
}
},
props: {
index: '',
editedField: Object,
originalField: Object,
isRepositoryLevel: false,
collectionId: ''
},
created() {
this.editForm = this.editedField;
this.formErrors = this.editForm.formErrors != undefined ? this.editForm.formErrors : {};
this.formErrorMessage = this.editForm.formErrors != undefined ? this.editForm.formErrorMessage : '';
this.oldForm = JSON.parse(JSON.stringify(this.originalField));
export default {
name: 'FieldEditionForm',
data() {
return {
editForm: {},
oldForm: {},
formErrors: {},
formErrorMessage: '',
closedByForm: false
}
},
props: {
index: '',
editedField: Object,
originalField: Object,
isRepositoryLevel: false,
collectionId: ''
},
created() {
},
beforeDestroy() {
if (this.closedByForm) {
this.editedField.saved = true;
} else {
this.oldForm.saved = this.editForm.saved;
if (JSON.stringify(this.editForm) != JSON.stringify(this.oldForm))
this.editedField.saved = false;
else
this.editForm = this.editedField;
this.formErrors = this.editForm.formErrors != undefined ? this.editForm.formErrors : {};
this.formErrorMessage = this.editForm.formErrors != undefined ? this.editForm.formErrorMessage : '';
this.oldForm = JSON.parse(JSON.stringify(this.originalField));
},
beforeDestroy() {
if (this.closedByForm) {
this.editedField.saved = true;
}
},
methods: {
...mapActions('fields', [
'updateField'
]),
saveEdition(field) {
if ((field.field_type_object && field.field_type_object.form_component) || field.edit_form == '') {
this.updateField({collectionId: this.collectionId, fieldId: field.id, isRepositoryLevel: this.isRepositoryLevel, index: this.index, options: this.editForm})
.then(() => {
this.editForm = {};
this.formErrors = {};
this.formErrorMessage = '';
this.closedByForm = true;
this.$emit('onEditionFinished');
})
.catch((errors) => {
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.editForm.formErrors = this.formErrors;
this.editForm.formErrorMessage = this.formErrorMessage;
});
} else {
let formElement = document.getElementById('fieldEditForm');
let formData = new FormData(formElement);
let formObj = {}
this.oldForm.saved = this.editForm.saved;
if (JSON.stringify(this.editForm) != JSON.stringify(this.oldForm))
this.editedField.saved = false;
else
this.editedField.saved = true;
}
},
methods: {
...mapActions('fields', [
'updateField'
]),
saveEdition(field) {
for (let [key, value] of formData.entries())
formObj[key] = value;
this.updateField({collectionId: this.collectionId, fieldId: field.id, isRepositoryLevel: this.isRepositoryLevel, index: this.index, options: formObj})
.then(() => {
this.editForm = {};
this.formErrors = {};
this.formErrorMessage = '';
this.closedByForm = true;
this.$emit('onEditionFinished');
if ((field.field_type_object && field.field_type_object.form_component) || field.edit_form == '') {
this.updateField({
collectionId: this.collectionId,
fieldId: field.id,
isRepositoryLevel: this.isRepositoryLevel,
index: this.index,
options: this.editForm
})
.catch((errors) => {
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');
.then(() => {
this.editForm = {};
this.formErrors = {};
this.formErrorMessage = '';
this.closedByForm = true;
this.$emit('onEditionFinished');
})
.catch((errors) => {
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.editForm.formErrors = this.formErrors;
this.editForm.formErrorMessage = this.formErrorMessage;
});
}
},
clearErrors(attribute) {
this.formErrors[attribute] = undefined;
},
cancelEdition() {
this.closedByForm = true;
this.$emit('onEditionCanceled');
},
this.editForm.formErrors = this.formErrors;
this.editForm.formErrorMessage = this.formErrorMessage;
});
} else {
let formElement = document.getElementById('fieldEditForm');
let formData = new FormData(formElement);
let formObj = {};
for (let [key, value] of formData.entries())
formObj[key] = value;
this.updateField({
collectionId: this.collectionId,
fieldId: field.id,
isRepositoryLevel: this.isRepositoryLevel,
index: this.index,
options: formObj
})
.then(() => {
this.editForm = {};
this.formErrors = {};
this.formErrorMessage = '';
this.closedByForm = true;
this.$emit('onEditionFinished');
})
.catch((errors) => {
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.editForm.formErrors = this.formErrors;
this.editForm.formErrorMessage = this.formErrorMessage;
});
}
},
clearErrors(attribute) {
this.formErrors[attribute] = undefined;
},
cancelEdition() {
this.closedByForm = true;
this.$emit('onEditionCanceled');
},
}
}
}
</script>
<style lang="scss" scoped>
@import "../../scss/_variables.scss";
form {
padding: 1.0em 2.0em;
border-top: 1px solid $draggable-border-color;

View File

@ -1,157 +1,286 @@
<template>
<div class="page-container">
<b-tag
v-if="!isLoading"
:type="'is-' + getStatusColor(item.status)"
v-text="item.status"/>
<div>
<tainacan-title />
<form
v-if="!isLoading"
class="tainacan-form"
label-width="120px">
<div class="columns">
<div class="column is-narrow">
<!-- Thumbnail -------------------------------- -->
<b-field :label="$i18n.get('label_image')">
<div class="thumbnail-field">
<button
v-if="item.featured_image == undefined || item.featured_image == false"
@click="editThumbnail($event)"
class="button is-primary">
<b-icon icon="upload" />
<span>{{ $i18n.get('label_choose_thumb') }}</span>
</button>
<div v-else>
<figure class="image is-128x128">
<img
:alt="$i18n.get('label_thumbnail')"
:src="item.featured_image">
</figure>
<div class="thumbnail-buttons-row">
<a
@click="editThumbnail($event)"
id="button-edit"
:aria-label="$i18n.get('label_button_edit_thumb')">
<b-icon icon="pencil"/>
</a>
<a
id="button-delete"
:aria-label="$i18n.get('label_button_delete_thumb')"
@click="deleteThumbnail()">
<b-icon icon="delete"/>
</a>
</div>
</div>
</div>
</b-field>
<div class="column is-4">
<!-- Attachments ------------------------------------------ -->
<b-field :label="$i18n.get('label_attachments')">
<div class="columns is-multiline">
<div class="column is-8">
<b-upload
v-model="form.files"
multiple
drag-drop
@input="uploadAttachment($event)">
<section class="section">
<div class="content has-text-centered">
<p>
<b-icon
icon="upload"
size="is-large"/>
</p>
<p>{{ $i18n.get('instruction_image_upload_box') }}</p>
</div>
</section>
</b-upload>
<div class="uploaded-files">
<div
v-for="(file, index) in form.files"
:key="index">
<span class="tag is-primary">
{{ file.name }}
<button
class="delete is-small"
type="button"
@click="deleteFile(index)"/>
</span>
<!-- <progress class="progress is-secondary" value="15" max="100">30%</progress> -->
</div>
</div>
</div>
<div
class="column is-narrow"
v-for="(attachment, index) of item.attachments"
:key="index">
<figure class="image is-128x128">
<img
:alt="attachment.title"
:src="attachment.url">
</figure>
<!-- Status -------------------------------- -->
<label class="section-label">{{ $i18n.get('label_status') }}</label>
<span class="required-field-asterisk">*</span>
<help-button
:title="$i18n.getHelperTitle('items', 'status')"
:message="$i18n.getHelperMessage('items', 'status')"/>
<div class="document-box">
<div class="field">
<b-select
v-model="form.status"
:placeholder="$i18n.get('instruction_select_a_status')">
<option
:id="`status-option-${statusOption.value}`"
v-for="statusOption in statusOptions"
:key="statusOption.value"
:value="statusOption.value"
:disabled="statusOption.disabled">{{ statusOption.label }}
</option>
</b-select>
<p
v-if="item.status == 'auto-draft'"
class="help is-danger">
{{ $i18n.get('info_item_not_saved') }}
</p>
</div>
<div class="field is-grouped">
<div class="control">
<button
id="button-submit-item-creation"
@click.prevent="onSubmit"
class="button is-success">
{{ $i18n.get('save') }}
</button>
</div>
</div>
</b-field>
</div>
<div class="column">
<!-- Status -------------------------------- -->
<b-field
:addons="false"
:label="$i18n.get('label_status')">
<help-button
:title="$i18n.getHelperTitle('items', 'status')"
:message="$i18n.getHelperMessage('items', 'status')"/>
<b-select
id="status-select"
v-model="form.status"
:placeholder="$i18n.get('instruction_select_a_status')">
<option
:id="`status-option-${statusOption.value}`"
v-for="statusOption in statusOptions"
:key="statusOption.value"
:value="statusOption.value"
:disabled="statusOption.disabled">{{ statusOption.label }}
</option>
</b-select>
</b-field>
<p class="help is-danger">{{ formErrorMessage }}</p>
</div>
<!-- Fields from Collection-------------------------------- -->
<tainacan-form-item
v-for="(field, index) in fieldList"
:key="index"
:field="field"/>
</div>
</div>
<!-- Document -------------------------------- -->
<label class="section-label">{{ form.document != undefined && form.document != null && form.document != '' ? $i18n.get('label_document') : $i18n.get('label_document_empty') }}</label>
<help-button
:title="$i18n.getHelperTitle('items', 'document')"
:message="$i18n.getHelperMessage('items', 'document')"/>
<div class="document-box">
<div
v-if="form.document != undefined && form.document != null &&
form.document_type != undefined && form.document_type != null &&
form.document != '' && form.document_type != 'empty'">
<div v-if="form.document_type == 'attachment'">
<div v-html="item.document_as_html" />
<button
class="button is-primary"
size="is-small"
@click.prevent="setFileDocument($event)">
{{ $i18n.get('edit') }}
</button>
<button
class="button is-primary"
size="is-small"
@click.prevent="removeDocument()">
{{ $i18n.get('remove') }}
</button>
</div>
<div v-if="form.document_type == 'text'">
<div v-html="item.document_as_html" />
<button
class="button is-primary"
size="is-small"
@click.prevent="setTextDocument()">
{{ $i18n.get('edit') }}
</button>
<button
class="button is-primary"
size="is-small"
@click.prevent="removeDocument()">
{{ $i18n.get('remove') }}
</button>
</div>
<div v-if="form.document_type == 'url'">
<div v-html="item.document_as_html" />
<button
class="button is-primary"
size="is-small"
@click.prevent="setURLDocument()">
{{ $i18n.get('edit') }}
</button>
<button
class="button is-primary"
size="is-small"
@click.prevent="removeDocument()">
{{ $i18n.get('remove') }}
</button>
</div>
</div>
<ul v-else>
<li>
<button @click.prevent="setFileDocument($event)">
<b-icon icon="upload"/>
</button>
<p>{{ $i18n.get('label_file') }}</p>
</li>
<li>
<button @click.prevent="setTextDocument()">
<b-icon icon="format-text"/>
</button>
<p>{{ $i18n.get('label_text') }}</p>
</li>
<li>
<button @click.prevent="setURLDocument()">
<b-icon icon="code-tags"/>
</button>
<p>{{ $i18n.get('label_url') }}</p>
</li>
</ul>
</div>
<!-- Text Insert Modal ----------------- -->
<b-modal
:can-cancel="false"
:active.sync="isTextModalActive"
:width="640"
scroll="keep">
<div class="tainacan-modal-content">
<div class="tainacan-modal-title">
<h2>{{ $i18n.get('instruction_write_text') }}</h2>
<hr>
</div>
<b-input
type="textarea"
v-model="textContent"/>
<div class="field is-grouped form-submit">
<div class="control">
<button
id="button-cancel-text-content-writing"
class="button is-outlined"
type="button"
@click="cancelTextWriting()">
{{ $i18n.get('cancel') }}</button>
</div>
<div class="control">
<button
id="button-submit-text-content-writing"
@click.prevent="confirmTextWriting()"
class="button is-success">
{{ $i18n.get('save') }}</button>
</div>
</div>
</div>
</b-modal>
<!-- URL Insert Modal ----------------- -->
<b-modal
:can-cancel="false"
:active.sync="isURLModalActive"
:width="640"
scroll="keep">
<div class="tainacan-modal-content">
<div class="tainacan-modal-title">
<h2>{{ $i18n.get('instruction_insert_url') }}</h2>
<hr>
</div>
<b-input v-model="urlLink"/>
<div class="field is-grouped form-submit">
<div class="control">
<button
id="button-cancel-url-link-selection"
class="button is-outlined"
type="button"
@click="cancelURLSelection()">
{{ $i18n.get('cancel') }}</button>
</div>
<div class="control">
<button
id="button-submit-url-link-selection"
@click.prevent="confirmURLSelection()"
class="button is-success">
{{ $i18n.get('save') }}</button>
</div>
</div>
</div>
</b-modal>
<!-- Thumbnail -------------------------------- -->
<label class="section-label">{{ $i18n.get('label_thumbnail') }}</label>
<help-button
:title="$i18n.getHelperTitle('items', 'featured_img_id')"
:message="$i18n.getHelperMessage('items', 'featured_img_id')"/>
<div class="document-box">
<div class="thumbnail-field">
<a
class="button is-rounred is-secondary"
id="button-edit-thumbnail"
:aria-label="$i18n.get('label_button_edit_thumb')"
@click.prevent="thumbnailMediaFrame.openFrame($event)">
<b-icon icon="pencil" />
</a>
<figure class="image">
<span
v-if="item.featured_image == undefined || item.featured_image == false"
class="image-placeholder">{{ $i18n.get('label_empty_thumbnail') }}</span>
<img
id="thumbail-image"
:alt="$i18n.get('label_thumbnail')"
:src="(item.featured_image == undefined || item.featured_image == false) ? thumbPlaceholderPath : item.featured_image">
</figure>
<div class="thumbnail-buttons-row">
<a
id="button-delete"
:aria-label="$i18n.get('label_button_delete_thumb')"
@click="deleteThumbnail()">
<b-icon icon="delete" />
</a>
</div>
</div>
</div>
<!-- Attachments ------------------------------------------ -->
<label class="section-label">{{ $i18n.get('label_attachments') }}</label>
<div class="document-box">
<button
class="button is-secondary"
@click.prevent="attachmentMediaFrame.openFrame($event)">
Attatchments (tests)
</button>
<div class="uploaded-files">
<div
v-for="(attachment, index) in attachmentsList"
:key="index">
<span class="tag is-primary">
{{ attachment.title.rendered }}
</span>
</div>
</div>
</div>
<div class="field is-grouped form-submit">
<div class="control">
<button
id="button-cancel-item-creation"
class="button is-outlined"
type="button"
@click="cancelBack">{{ $i18n.get('cancel') }}</button>
</div>
<div class="control">
<button
id="button-submit-item-creation"
@click.prevent="onSubmit"
class="button is-success"
>{{ $i18n.get('save') }}</button>
<div class="column is-1" />
<div class="column is-7">
<label class="section-label">{{ $i18n.get('fields') }}</label>
<br>
<a
class="collapse-all"
@click="toggleCollapseAll()">
{{ collapseAll ? $i18n.get('label_collapse_all') : $i18n.get('label_expand_all') }}
<b-icon
type="is-secondary"
:icon=" collapseAll ? 'menu-down' : 'menu-right'" />
</a>
<!-- Fields from Collection-------------------------------- -->
<tainacan-form-item
v-for="(field, index) of fieldList"
:key="index"
:field="field"
:is-collapsed="!fieldCollapses[index]" />
</div>
</div>
<p class="help is-danger">{{ formErrorMessage }}</p>
</form>
<b-loading
:active.sync="isLoading"
:can-cancel="false"/></div>
:can-cancel="false"/>
</div>
</template>
<script>
import { mapActions, mapGetters } from 'vuex';
import { eventBus } from '../../../js/event-bus-web-components.js'
import wpMediaFrames from '../../js/wp-media-frames';
export default {
name: 'ItemEditionForm',
@ -162,10 +291,13 @@ export default {
item: null,
collectionId: Number,
isLoading: false,
fieldCollapses: [],
collapseAll: false,
form: {
collectionId: Number,
status: '',
files:[],
document: '',
document_type: ''
},
thumbnail: {},
// Can be obtained from api later
@ -183,24 +315,32 @@ export default {
label: this.$i18n.get('trash')
}],
formErrorMessage: '',
// Frame Uploader variables
frameUploader: undefined
thumbPlaceholderPath: tainacan_plugin.base_url + '/admin/images/placeholder_square.png',
thumbnailMediaFrame: undefined,
attachmentMediaFrame: undefined,
fileMediaFrame: undefined,
isURLModalActive: false,
urlLink: '',
isTextModalActive: false,
textLink: ''
}
},
methods: {
...mapActions('item', [
'sendItem',
'updateItem',
'updateItemDocument',
'fetchFields',
'sendField',
'fetchItem',
'cleanFields',
'sendAttachment',
'updateThumbnail'
'sendAttachments',
'updateThumbnail',
'fetchAttachments'
]),
...mapGetters('item',[
'getFields',
'getItem',
'getAttachments'
]),
onSubmit() {
// Puts loading on Item edition
@ -214,6 +354,8 @@ export default {
// Fill this.form data with current data.
this.form.status = this.item.status;
this.form.document = this.item.document;
this.form.document_type = this.item.document_type;
this.isLoading = false;
@ -230,20 +372,6 @@ export default {
this.isLoading = false;
});
},
getStatusColor(status) {
switch(status) {
case 'publish':
return 'success'
case 'draft':
return 'info'
case 'private':
return 'warning'
case 'trash':
return 'danger'
default:
return 'info'
}
},
createNewItem() {
// Puts loading on Draft Item creation
this.isLoading = true;
@ -255,98 +383,63 @@ export default {
this.itemId = res.id;
this.item = res;
// Initializes Media Frames now that itemId exists
this.initializeMediaFrames();
// Pre-fill status with publish to incentivate it
this.form.status = 'publish';
this.form.document = this.item.document;
this.form.document_type = this.item.document_type;
this.loadMetadata();
})
.catch(error => this.$console.error(error));
},
loadMetadata() {
// Obtains Item Field
this.fetchFields(this.itemId).then(() => {
this.fetchFields(this.itemId).then((fields) => {
this.isLoading = false;
for (let field of fields) {
this.fieldCollapses.push(false);
}
});
},
cancelBack(){
this.$router.push(this.$routerHelper.getCollectionPath(this.collectionId));
setFileDocument(event) {
this.fileMediaFrame.openFrame(event);
},
uploadAttachment($event) {
for (let file of $event) {
this.sendAttachment({ item_id: this.itemId, file: file })
.then(() => {
})
.catch((error) => {
this.$console.error(error);
});
}
setTextDocument() {
this.isTextModalActive = true;
},
confirmTextWriting() {
this.isTextModalActive = false;
this.form.document_type = 'text';
this.form.document = this.textContent;
this.updateItemDocument({ item_id: this.itemId, document: this.form.document, document_type: this.form.document_type });
},
editThumbnail(event) {
'use strict';
event.preventDefault();
// If the media frame already exists, reopen it.
if ( this.frameUploader != undefined ) {
this.frameUploader.open();
return;
}
// Create a new media frame
this.frameUploader = wp.media({
frame: 'select',
title: 'Select or Upload Media Of Your Chosen Persuasion',
button: {
text: 'Use this media'
},
multiple: false,
library: {
type: 'image',
uploadedTo: this.itemId
},
uploader: true
});
wp.media.view.settings.post = {
id: this.itemId,
featuredImageId: this.item.featured_img_id
}
this.frameUploader.on('select', () => {
let media = this.frameUploader.state().get( 'selection' ).first().toJSON();
this.updateThumbnail({itemId: this.itemId, thumbnailId: media.id})
.then((res) => {
this.item.featured_image = res.featured_image;
})
.catch((error) => {
this.$console.error(error);
});
});
this.frameUploader.open();
cancelTextWriting() {
this.isTextModalActive = false;
this.textContent = '';
},
uploadThumbnail($event) {
this.sendAttachment({ item_id: this.itemId, file: $event[0] })
.then((res) => {
this.updateThumbnail({itemId: this.itemId, thumbnailId: res.id})
.then((res) => {
this.item.featured_image = res.featured_image;
})
.catch((error) => {
this.$console.error(error);
});
})
.catch((error) => {
this.$console.error(error);
});
setURLDocument() {
this.isURLModalActive = true;
},
confirmURLSelection() {
this.isURLModalActive = false;
this.form.document_type = 'url';
this.form.document = this.urlLink;
this.updateItemDocument({ item_id: this.itemId, document: this.form.document, document_type: this.form.document_type });
},
cancelURLSelection() {
this.isURLModalActive = false;
this.urlLink = '';
},
removeDocument() {
this.textContent = '';
this.urlLink = '';
this.form.document_type = 'empty';
this.form.document = '';
this.updateItemDocument({ item_id: this.itemId, document: this.form.document, document_type: this.form.document_type });
},
deleteThumbnail() {
this.updateThumbnail({itemId: this.itemId, thumbnailId: 0})
@ -357,25 +450,72 @@ export default {
this.$console.error(error);
});
},
deleteFile(index) {
this.$console.log("Delete:" + index);
initializeMediaFrames() {
this.fileMediaFrame = new wpMediaFrames.documentFileControl(
'my-file-media-frame', {
button_labels: {
frame_title: this.$i18n.get('instruction_select_document_file_for_item'),
frame_button: this.$i18n.get('label_select_file'),
},
relatedPostId: this.itemId,
onSave: (file) => {
this.form.document_type = 'attachment';
this.form.document = file.id + '';
this.updateItemDocument({ item_id: this.itemId, document: this.form.document, document_type: this.form.document_type })
.then((item) => {
this.item.document_as_html = item.document_as_html;
})
.catch(error => this.$console.error(error));
}
}
);
this.thumbnailMediaFrame = new wpMediaFrames.thumbnailControl(
'my-thumbnail-media-frame', {
button_labels: {
frame_title: this.$i18n.get('instruction_select_item_thumbnail'),
},
relatedPostId: this.itemId,
onSave: (mediaId) => {
this.updateThumbnail({itemId: this.itemId, thumbnailId: mediaId})
.then((res) => {
this.item.featured_image = res.featured_image;
})
.catch(error => this.$console.error(error));
}
}
);
this.attachmentMediaFrame = new wpMediaFrames.attachmentControl(
'my-attachment-media-frame', {
button_labels: {
frame_title: this.$i18n.get('instruction_select_files_to_attach_to_item'),
frame_button: this.$i18n.get('label_attach_to_item'),
},
relatedPostId: this.itemId,
onSave: (files) => {
// Fetch current existing attachments
this.fetchAttachments(this.itemId);
}
}
);
},
toggleCollapseAll() {
this.collapseAll = !this.collapseAll;
for (let i = 0; i < this.fieldCollapses.length; i++)
this.fieldCollapses[i] = this.collapseAll;
}
},
computed: {
fieldList(){
return this.getFields();
fieldList() {
return JSON.parse(JSON.stringify(this.getFields()));
},
formHasErrors(){
// for (let field of this.fieldList) {
// if (field.field.required == 'yes' &&
// (field.value == '' || field.value == undefined) &&
// this.form.status == 'publish') {
// return true;
// }
// }
return false;
}
attachmentsList(){
return this.getAttachments();
}
},
created(){
// Obtains collection ID
@ -391,14 +531,26 @@ export default {
// Obtains current Item ID from URL
this.itemId = this.$route.params.itemId;
// Initializes Media Frames now that itemId exists
this.initializeMediaFrames();
this.fetchItem(this.itemId).then(res => {
this.item = res;
// Fill this.form data with current data.
this.form.status = this.item.status;
this.form.document = this.item.document;
this.form.document_type = this.item.document_type;
if (this.form.document_type != undefined && this.form.document_type == 'url')
this.urlLink = this.form.document;
if (this.form.document_type != undefined && this.form.document_type == 'text')
this.textContent = this.form.document;
this.loadMetadata();
});
// Fetch current existing attachments
this.fetchAttachments(this.itemId);
}
@ -409,21 +561,100 @@ export default {
<style lang="scss" scoped>
@import '../../scss/_variables.scss';
.page-container{
height: calc(100% - 82px);
}
form>.columns>.column{
padding: 0px;
}
.section-label {
font-size: 16px !important;
font-weight: 500 !important;
color: $tertiary !important;
line-height: 1.2em;
}
.collapse-all {
font-size: 12px;
.icon { vertical-align: bottom; }
}
.document-box {
border: 1px solid $draggable-border-color;
padding: 30px;
margin-top: 16px;
margin-bottom: 38px;
ul {
display: flex;
justify-content: space-evenly;
li {
text-align: center;
button {
border-radius: 50px;
height: 72px;
width: 72px;
border: none;
background-color: $tainacan-input-color;
color: $secondary;
margin-bottom: 6px;
&:hover {
background-color: $primary-lighter;
cursor: pointer;
}
}
p { color: $secondary; }
}
}
}
.thumbnail-field {
width: 128px;
height: 128px;
max-width: 128px;
max-height: 128px;
margin-bottom: 96px;
margin-top: -20px;
.content {
padding: 10px;
font-size: 0.8em;
}
img {
bottom: 0;
position: absolute;
height: 105px;
width: 105px;
}
.image-placeholder {
position: absolute;
margin-left: 10px;
margin-right: 10px;
font-size: 0.8rem;
font-weight: bold;
z-index: 99;
text-align: center;
color: gray;
top: 38px;
max-width: 90px;
}
#button-edit-thumbnail {
border-radius: 100px !important;
height: 40px !important;
width: 40px !important;
bottom: -20px;
left: -20px;
z-index: 99;
.icon {
display: inherit;
padding: 0;
margin: 0;
margin-left: -8px;
margin-top: 3px;
}
}
.thumbnail-buttons-row {
display: none;
}
@ -431,12 +662,14 @@ export default {
.thumbnail-buttons-row {
display: inline-block;
position: relative;
bottom: 31px;
background-color: rgba(255,255,255,0.8);
top: 0px;
background-color: rgba(255, 255, 255, 0.9);
padding: 2px 8px;
border-radius: 0px 4px 0px 0px;
border-radius: 0px 0px 0px 4px;
left: 65px;
}
}
}

View File

@ -1,53 +1,92 @@
<template>
<form
id="termEditForm"
class="tainacan-form"
@submit.prevent="saveEdition(editForm)">
<b-field
:addons="false"
:type="(formErrors.name != '' && formErrors.name != undefined) ? 'is-danger' : ''"
:message="formErrors.name">
<label class="label">
{{ $i18n.get('label_name') }}
<span class="required-term-asterisk">*</span>
<help-button
:title="$i18n.getHelperTitle('terms', 'name')"
:message="$i18n.getHelperMessage('terms', 'name')"/>
</label>
<b-input
v-model="editForm.name"
name="name"/>
</b-field>
<form
id="termEditForm"
class="tainacan-form"
@submit.prevent="saveEdition(editForm)">
<b-field
:addons="false"
:type="formErrors['description'] != '' && formErrors['description'] != undefined ? 'is-danger' : ''"
:message="formErrors['description']">
<label class="label">
{{ $i18n.get('label_description') }}
<help-button
:title="$i18n.getHelperTitle('terms', 'description')"
:message="$i18n.getHelperMessage('terms', 'description')"/>
</label>
<b-input
type="textarea"
name="description"
v-model="editForm.description"/>
</b-field>
<div class="columns">
<div class="column is-narrow">
<!-- Header Image -------------------------------- -->
<b-field
:addons="false"
:label="$i18n.get('label_header_image')">
<div class="thumbnail-field">
<a
class="button is-rounred is-secondary"
id="button-edit-thumbnail"
:aria-label="$i18n.get('label_button_edit_header_image')"
@click="headerImageMediaFrame.openFrame($event)">
<b-icon icon="pencil"/>
</a>
<figure class="image is-128x128">
<span
v-if="editForm.header_image === undefined || editForm.header_image === false"
class="image-placeholder">{{ $i18n.get('label_empty_header_image') }}</span>
<img
:alt="$i18n.get('label_header_image')"
:src="(editForm.header_image === undefined || editForm.header_image === false) ? headerPlaceholderPath : editForm.header_image">
</figure>
<div class="thumbnail-buttons-row">
<a
id="button-delete"
:aria-label="$i18n.get('label_button_delete_header_image')"
@click="deleteHeaderImage()">
<b-icon icon="delete"/>
</a>
</div>
</div>
</b-field>
</div>
<div class="field is-grouped form-submit">
<div class="column">
<b-field
:addons="false"
:type="((formErrors.name !== '' || formErrors.repeated !== '') && (formErrors.name !== undefined || formErrors.repeated !== undefined )) ? 'is-danger' : ''"
:message="formErrors.name ? formErrors : formErrors.repeated">
<label class="label">
{{ $i18n.get('label_name') }}
<span class="required-term-asterisk">*</span>
<help-button
:title="$i18n.getHelperTitle('terms', 'name')"
:message="$i18n.getHelperMessage('terms', 'name')"/>
</label>
<b-input
v-model="editForm.name"
name="name"
@focus="clearErrors({ name: 'name', repeated: 'repeated' })"/>
</b-field>
<b-field
:addons="false"
:type="formErrors['description'] !== '' && formErrors['description'] !== undefined ? 'is-danger' : ''"
:message="formErrors['description']">
<label class="label">
{{ $i18n.get('label_description') }}
<help-button
:title="$i18n.getHelperTitle('terms', 'description')"
:message="$i18n.getHelperMessage('terms', 'description')"/>
</label>
<b-input
type="textarea"
name="description"
v-model="editForm.description"
@focus="clearErrors('description')"/>
</b-field>
</div>
</div>
<div class="field is-grouped form-submit">
<div class="control">
<button
class="button is-outlined"
@click.prevent="cancelEdition()"
<button
class="button is-outlined"
@click.prevent="cancelEdition()"
slot="trigger">
{{ $i18n.get('cancel') }}
</button>
</div>
<div class="control">
<button
class="button is-success"
<button
class="button is-success"
type="submit">
{{ $i18n.get('save') }}
</button>
@ -57,89 +96,189 @@
</template>
<script>
import { mapActions, mapGetters } from 'vuex';
import {mapActions, mapGetters} from 'vuex';
import wpMediaFrames from '../../js/wp-media-frames';
export default {
name: 'TermEditionForm',
data () {
return {
formErrors: {}
}
},
props: {
editForm: Object,
categoryId: ''
},
methods: {
...mapActions('category', [
'sendTerm',
'updateTerm'
]),
...mapGetters('category', [
'getTerms'
]),
saveEdition(term) {
if (term.id == 'new') {
this.sendTerm({
categoryId: this.categoryId,
name: this.editForm.name,
description: this.editForm.description,
parent: this.editForm.parent
})
.then(() => {
this.editForm = {};
this.formErrors = {};
this.$emit('onEditionFinished');
})
.catch((errors) => {
for (let error of errors.errors) {
for (let field of Object.keys(error)){
this.$set(this.formErrors, field, (this.formErrors[field] != undefined ? this.formErrors[field] : '') + error[field] + '\n');
}
}
this.$emit('onErrorFound');
});
} else {
this.updateTerm({
categoryId: this.categoryId,
termId: this.editForm.id,
name: this.editForm.name,
description: this.editForm.description,
parent: this.editForm.parent
})
.then(() => {
this.editForm.saved = true;
this.formErrors = {};
this.$emit('onEditionFinished');
})
.catch((errors) => {
for (let error of errors.errors) {
for (let field of Object.keys(error)){
this.$set(this.formErrors, field, (this.formErrors[field] != undefined ? this.formErrors[field] : '') + error[field] + '\n');
}
}
this.$emit('onErrorFound');
});
export default {
name: 'TermEditionForm',
data() {
return {
formErrors: {},
headerPlaceholderPath: tainacan_plugin.base_url + '/admin/images/placeholder_rectangle.png',
headerImageMediaFrame: undefined
}
},
cancelEdition() {
this.$emit('onEditionCanceled', this.editForm);
props: {
editForm: Object,
categoryId: ''
},
methods: {
...mapActions('category', [
'sendTerm',
'updateTerm',
]),
...mapGetters('category', [
'getTerms'
]),
saveEdition(term) {
if (term.id === 'new') {
this.sendTerm({
categoryId: this.categoryId,
name: this.editForm.name,
description: this.editForm.description,
parent: this.editForm.parent,
headerImageId: this.editForm.header_image_id,
})
.then(() => {
this.editForm = {};
this.formErrors = {};
this.$emit('onEditionFinished');
})
.catch((errors) => {
for (let error of errors.errors) {
for (let field of Object.keys(error)) {
this.$set(this.formErrors, field, (this.formErrors[field] !== undefined ? this.formErrors[field] : '') + error[field] + '\n');
}
}
this.$emit('onErrorFound');
});
} else {
this.updateTerm({
categoryId: this.categoryId,
termId: this.editForm.id,
name: this.editForm.name,
description: this.editForm.description,
parent: this.editForm.parent,
headerImageId: this.editForm.header_image_id,
})
.then(() => {
this.editForm.saved = true;
this.formErrors = {};
this.$emit('onEditionFinished');
})
.catch((errors) => {
for (let error of errors.errors) {
for (let field of Object.keys(error)) {
this.$set(this.formErrors, field, (this.formErrors[field] !== undefined ? this.formErrors[field] : '') + error[field] + '\n');
}
}
this.$emit('onErrorFound');
});
}
},
cancelEdition() {
this.$emit('onEditionCanceled', this.editForm);
},
deleteHeaderImage() {
this.editForm = Object.assign({},
this.editForm,
{
header_image_id: "0",
header_image: false
}
);
},
initializeMediaFrames() {
this.headerImageMediaFrame = new wpMediaFrames.headerImageControl(
'my-header-image-media-frame', {
button_labels: {
frame_title: this.$i18n.get('instruction_select_term_header_image'),
},
relatedPostId: this.editForm.id,
onSave: (croppedImage) => {
this.editForm = Object.assign({},
this.editForm,
{
header_image_id: croppedImage.id.toString(),
header_image: croppedImage.url
}
);
}
}
);
},
clearErrors(attributes) {
if(attributes instanceof Object){
for(let attribute in attributes){
this.formErrors[attribute] = undefined;
}
} else {
this.formErrors[attributes] = undefined;
}
},
},
created() {
this.initializeMediaFrames();
}
}
}
</script>
<style lang="scss" scoped>
@import "../../scss/_variables.scss";
form {
padding: 1.0em 2.0em;
border-top: 1px solid $draggable-border-color;
border-bottom: 1px solid $draggable-border-color;
margin-top: 1.0em;
.thumbnail-field {
max-height: 128px;
margin-bottom: 96px;
margin-top: -20px;
.content {
padding: 10px;
font-size: 0.8em;
}
img {
position: absolute;
}
.image-placeholder {
position: absolute;
margin-left: 10px;
margin-right: 10px;
bottom: 50%;
font-size: 0.8rem;
font-weight: bold;
z-index: 99;
text-align: center;
color: gray;
}
#button-edit-thumbnail, #button-edit-header-image {
border-radius: 100px !important;
height: 40px !important;
width: 40px !important;
bottom: -20px;
left: -20px;
z-index: 99;
.icon {
display: inherit;
padding: 0;
margin: 3px 0 0 -8px;
}
}
.thumbnail-buttons-row {
display: none;
}
&:hover {
.thumbnail-buttons-row {
display: inline-block;
position: relative;
top: -128px;
background-color: rgba(255, 255, 255, 0.9);
padding: 2px 8px;
border-radius: 0px 0px 0px 4px;
left: 88px;
}
}
}
}
</style>

View File

@ -6,7 +6,10 @@
<button
v-if="selectedCollections.length > 0"
class="button field is-danger"
@click="deleteSelectedCollections()"><span>{{ $i18n.get('instruction_delete_selected_collections') }} </span><b-icon icon="delete"/></button>
@click="deleteSelectedCollections()">
<span>{{ $i18n.get('instruction_delete_selected_collections') }} </span>
<b-icon icon="delete"/>
</button>
</b-field>
<b-table
v-if="totalCollections > 0"

View File

@ -1,9 +1,7 @@
<template>
<div>
<b-loading :active.sync="isLoadingFieldTypes"/>
<div class="page-title">
<h2>{{ isRepositoryLevel ? $i18n.get('instruction_dragndrop_fields_repository') : $i18n.get('instruction_dragndrop_fields_collection') }}</h2>
</div>
<tainacan-title v-if="!isRepositoryLevel"/>
<div class="columns">
<div class="column">
<draggable

View File

@ -1,9 +1,7 @@
<template>
<div>
<b-loading :active.sync="isLoadingFieldTypes"/>
<div class="page-title">
<h2>{{ isRepositoryLevel ? $i18n.get('instruction_dragndrop_filters_collection') : $i18n.get('instruction_dragndrop_filters_collection') }}</h2>
</div>
<tainacan-title v-if="!isRepositoryLevel"/>
<div class="columns">
<div class="column">
<draggable

View File

@ -27,7 +27,7 @@
:key="index"
:custom-key="column.slug"
:label="column.name"
:visible="column.visible"
:visible="column.display"
:class="column.field == 'row_creation' ? 'row-creation' : ''"
:width="column.field == 'row_actions' ? 78 : column.field == 'row_thumbnail' ? 55 : undefined ">

View File

@ -274,12 +274,12 @@ export default {
}
term.depth = termDepth;
if (this.orderedTermsList[term.id] == undefined ) {
if (this.orderedTermsList[term.id] === undefined ) {
term.opened = false;
term.saved = true ;
} else {
term.opened = (this.orderedTermsList[term.id].opened == undefined ? false : this.orderedTermsList[term.id].opened);
term.saved = (this.orderedTermsList[term.id].saved == undefined ? true : this.orderedTermsList[term.id].saved);
term.opened = (this.orderedTermsList[term.id].opened === undefined ? false : this.orderedTermsList[term.id].opened);
term.saved = (this.orderedTermsList[term.id].saved === undefined ? true : this.orderedTermsList[term.id].saved);
}
if (term.taxonomy != null)
this.orderedTermsList.push(JSON.parse(JSON.stringify(term)));

View File

@ -1,44 +1,29 @@
<template>
<nav
id="primary-menu"
:class="isCompressed ? 'is-compressed' : ''"
:class="isMenuCompressed ? 'is-compressed' : ''"
role="navigation"
:aria-label="$i18n.get('label_main_menu')"
class="column is-sidebar-menu">
<aside class="menu">
<div class="menu-header">
<ul class="menu-list"><li>
<router-link
tag="a"
to="/">
<b-icon
size="is-medium"
icon="chevron-left"/>
<img
class="tainacan-logo"
alt="Tainacan Logo"
:src="logoHeader">
</router-link>
</li></ul>
</div>
<ul class="menu-list">
<li><router-link
tag="a"
to="/collections"
:class="activeRoute == 'CollectionsPage' || isCompressed ? 'is-active':''">
:class="activeRoute == 'CollectionsPage' || isMenuCompressed ? 'is-active':''">
<b-icon
size="is-small"
icon="folder-multiple"/> <span class="menu-text">{{ $i18n.getFrom('collections', 'name') }}</span>
</router-link></li>
<li><router-link
<!-- <li><router-link
tag="a"
to="/items"
:class="activeRoute == 'ItemsPage' ? 'is-active':''">
<b-icon
size="is-small"
icon="file-multiple"/> <span class="menu-text">{{ $i18n.getFrom('items', 'name') }}</span>
</router-link></li>
</router-link></li> -->
<li class="separator"/>
<li><router-link
tag="a"
@ -46,7 +31,7 @@
:class="activeRoute == 'FieldsPage' ? 'is-active':''">
<b-icon
size="is-small"
icon="format-list-checks"/> <span class="menu-text">{{ $i18n.getFrom('fields', 'name') }}</span>
icon="format-list-bulleted-type"/> <span class="menu-text">{{ $i18n.getFrom('fields', 'name') }}</span>
</router-link></li>
<li><router-link
tag="a"
@ -70,7 +55,7 @@
:class="activeRoute == 'EventsPage' ? 'is-active':''">
<b-icon
size="is-small"
icon="bell"/> <span class="menu-text">{{ $i18n.get('events') }}</span>
icon="calendar-range"/> <span class="menu-text">{{ $i18n.get('events') }}</span>
</router-link></li>
</ul>
</aside>
@ -80,22 +65,9 @@
<script>
export default {
name: 'PrimaryMenu',
data(){
return {
logoHeader: tainacan_plugin.base_url + '/admin/images/tainacan_logo_header.png',
isCompressed: false,
activeRoute: '/collections'
}
},
watch: {
'$route' (to) {
this.isCompressed = (to.params.collectionId != undefined);
this.activeRoute = to.name;
}
},
created () {
this.isCompressed = (this.$route.params.collectionId != undefined);
this.activeRoute = this.$route.name;
props: {
isMenuCompressed: false,
activeRoute: '/collections'
}
}
</script>
@ -105,46 +77,21 @@ export default {
@import "../../scss/_variables.scss";
#primary-menu {
background-color: $secondary;
padding: 0px;
background-color: $primary;
padding: 100px 0px 0px 0px;
-webkit-transition: max-width 0.2s linear; /* Safari */
transition: max-width 0.2s linear;
max-width: $side-menu-width;
box-shadow: -3px 0px 8px #111;
z-index: 99;
.menu { padding-top: 0px; }
.menu-header {
background-color: rgba(0,0,0,0.1);
height: $header-height;
a { padding: 1.45em 2.5em }
.icon {
position: absolute;
opacity: 0;
visibility: hidden;
transition: opacity 0.2s linear, visibility 0.2s linear;
-webkit-transition: opacity 0.2s linear, visibility 0.2s linear;
}
.tainacan-logo {
max-height: 28px;
opacity: 1;
visibility: visible;
transition: opacity 0.15s linear, visibility 0.15s linear;
-webkit-transition: opacity 0.15s linear, visibility 0.15s linear;
}
}
.separator {
height: 2px;
background-color: $separator-color;
width: 100%;
margin: 1.75em 0;
margin: 24px 0;
}
li{
&.search-area {
visibility: visible;
opacity: 1;
padding-top: 1.8em;
.field { padding: 0 1.8em 0.5em; }
.menu-text { font-size: 0.85em; }
}
a {
color: white;
white-space: nowrap;
@ -155,14 +102,13 @@ export default {
-webkit-transition: padding 0.2s linear; /* Safari */
transition: padding 0.2s linear;
}
a:hover {
background-color: $primary;
color: $tertiary
}
a.is-active {
a:hover, a.is-active {
background-color: $primary;
color: $tertiary;
}
a:focus{
box-shadow: none;
}
.menu-text {
padding-left: 0.7em;
opacity: 1;
@ -173,34 +119,15 @@ export default {
}
&.is-compressed {
max-width: 42px;
.menu-header {
a { padding: 1.67em 0.3em }
.icon {
visibility: visible;
opacity: 1;
}
.tainacan-logo {
visibility: hidden;
opacity: 0;
}
}
.search-area {
visibility: hidden;
opacity: 0;
}
max-width: 45px;
a {
padding-left: 0.8em;
padding-right: 0.8em;
color: rgba(255,255,255,0.4);
}
.menu-text {
visibility: hidden;
opacity: 0;
}
box-shadow: -3px 0px 10px #111;
z-index: 10;
}
@media screen and (max-width: 769px) {
@ -217,41 +144,10 @@ export default {
align-items: stretch;
justify-content: space-evenly;
.separator { display: none; }
.search-area {
flex-basis: 100%;
padding-top: 1.0em !important;
.field {
padding: 0 0.8em !important;
margin-bottom: 0px;
}
}
a{
padding: 0.8em !important;
text-align: center;
}
li + li {
.menu-text{
display: none !important;
}
}
}
&.is-compressed {
.menu-header {
.icon {
visibility: hidden !important;
opacity: 0 !important;
}
.tainacan-logo {
visibility: visible !important;
opacity: 1 !important;
}
}
.search-area {
display: none;
}
}
}
}

View File

@ -1,211 +0,0 @@
<template>
<nav
id="secondary-menu"
role="navigation"
:aria-label="$i18n.get('label_collection_menu')"
class="column is-sidebar-menu">
<aside class="menu">
<div class="menu-header">
<ul class="menu-list"><li>
<router-link
tag="a"
to="/"
target='_blank'>
<b-icon
size="is-medium"
icon="chevron-left"/>
<img
class="tainacan-logo"
alt="Tainacan Logo"
:src="logoHeader">
</router-link>
</li></ul>
</div>
<ul class="menu-list">
<li class="search-area">
<b-field>
<b-input
:placeholder="$i18n.getFrom('items', 'search_items')"
type="search"
size="is-small"
icon="magnify"/>
</b-field>
<router-link
tag="a"
to="">
<b-icon
size="is-small"
icon="magnify"/> <span class="menu-text">{{ $i18n.get('advanced_search') }}</span>
</router-link>
</li>
<li class="separator"/>
<li><router-link
tag="a"
:to="{ path: $routerHelper.getCollectionItemsPath(id, '') }"
:class="activeRoute == 'ItemPage' || activeRoute == 'CollectionItemsPage' || activeRoute == 'ItemEditionForm' || activeRoute == 'ItemCreatePage' ? 'is-active':''"
:aria-label="$i18n.get('label_collection_fields')">
<b-icon
size="is-small"
icon="file-multiple"/> <span class="menu-text">{{ $i18n.getFrom('items', 'name') }}</span>
</router-link></li>
<li><router-link
tag="a"
:to="{ path: $routerHelper.getCollectionEditPath(id) }"
:class="activeRoute == 'CollectionEditionForm' ? 'is-active':''"
:aria-label="$i18n.getFrom('collections','edit_item')">
<b-icon
size="is-small"
icon="pencil"/> <span class="menu-text">{{ $i18n.get('edit') }}</span>
</router-link></li>
<li><router-link
tag="a"
:to="{ path: $routerHelper.getCollectionFieldsPath(id) }"
:class="activeRoute == 'FieldsList' ? 'is-active':''"
:aria-label="$i18n.get('label_collection_fields')">
<b-icon
size="is-small"
icon="format-list-checks"/> <span class="menu-text">{{ $i18n.getFrom('fields', 'name') }}</span>
</router-link></li>
<li><router-link
tag="a"
:to="{ path: $routerHelper.getCollectionFiltersPath(id) }"
:class="activeRoute == 'FiltersList' ? 'is-active':''"
:aria-label="$i18n.get('label_collection_filters')">
<b-icon
size="is-small"
icon="filter"/> <span class="menu-text">{{ $i18n.getFrom('filters', 'name') }}</span>
</router-link></li>
</ul>
</aside>
</nav>
</template>
<script>
export default {
name: 'SecondaryMenu',
data(){
return {
logoHeader: tainacan_plugin.base_url + '/admin/images/tainacan_logo_header.png',
activeRoute: 'ItemsList'
}
},
props: {
id: Number
},
watch: {
'$route' (to) {
this.activeRoute = to.name;
}
},
created () {
this.activeRoute = this.$route.name;
}
}
</script>
<style lang="scss" scoped>
@import "../../scss/_variables.scss";
#secondary-menu {
background-color: $primary;
padding: 0px;
-webkit-transition: max-width 0.3s linear; /* Safari */
transition: max-width 0.3s linear;
max-width: $side-menu-width;
z-index: 9;
.menu-header {
background-color: rgba(0,0,0,0.1);
height: $header-height;
a { padding: 1.45em 2.5em }
.icon {
position: absolute;
opacity: 0;
visibility: hidden;
transition: opacity 0.2s linear, visibility 0.2s linear;
-webkit-transition: opacity 0.2s linear, visibility 0.2s linear;
}
.tainacan-logo {
max-height: 28px;
opacity: 1;
visibility: visible;
transition: opacity 0.15s linear, visibility 0.15s linear;
-webkit-transition: opacity 0.15s linear, visibility 0.15s linear;
}
}
.separator {
height: 2px;
background-color: rgba(0,0,0,0.15);
width: 100%;
margin: 1.75em 0;
}
li{
&.search-area {
visibility: visible;
opacity: 1;
padding-top: 1.8em;
.field { padding: 0 1.8em 0.5em; }
.menu-text { font-size: 0.85em; }
}
a {
color: white;
white-space: nowrap;
overflow: hidden;
padding: 0.75em 1.8em;
line-height: 1.5em;
border-radius: 0px;
-webkit-transition: padding 0.3s linear; /* Safari */
transition: padding 0.3s linear;
}
a:hover {
background-color: rgba(255,255,255,0.4);
color: $tertiary;
}
a.is-active {
background-color: rgba(255,255,255,0.4);
color: $tertiary;
}
.menu-text {
padding-left: 0.7em;
opacity: 1;
visibility: visible;
transition: opacity 0.3s linear, visibility 0.3s linear;
-webkit-transition: opacity 0.3s linear, visibility 0.3s linear;
}
}
@media screen and (max-width: 769px) {
width: 100% !important;
max-width: 100% !important;
.separator, .menu-header { display: none; }
ul {
flex-flow: wrap;
display: flex;
align-items: stretch;
justify-content: space-evenly;
a {
padding: 0.5em 0.7em !important;
text-align: center;
}
.menu-text {
padding-left: 0.3em !important;
}
.search-area {
flex-basis: 100%;
padding-top: 1.0em !important;
.field {
padding: 0 0.8em !important;
margin-bottom: 0px;
}
}
}
}
}
</style>

View File

@ -2,33 +2,34 @@
<div
id="tainacan-header"
class="level"
:class="{'secondary-page': onSecondaryPage}">
:class="{'menu-compressed': isMenuCompressed}">
<div class="level-left">
<div class="level-item">
<h1 class="has-text-weight-bold is-uppercase"><b-icon
size="is-small"
:icon="currentIcon"/>{{ pageTitle }}</h1>
<nav class="breadcrumbs">
<router-link
tag="a"
:to="$routerHelper.getCollectionsPath()">{{ $i18n.get('repository') }}</router-link> >
<span
v-for="(pathItem, index) in arrayRealPath"
:key="index">
<router-link
tag="a"
:to="'/' + arrayRealPath.slice(0, index + 1).join('/')">
{{ arrayViewPath[index] }}
</router-link>
<span v-if="index != arrayRealPath.length - 1"> > </span>
</span>
</nav>
<router-link
tag="a"
to="/">
<img
class="tainacan-logo"
alt="Tainacan Logo"
:src="logoHeader">
</router-link>
</div>
</div>
<div class="level-right">
<a class="level-item">
<b-icon icon="magnify"/>
</a>
<span class="search-area">
<div class="control has-icons-right is-small is-clearfix">
<input
autocomplete="on"
:placeholder="$i18n.get('instruction_search_repository')"
class="input is-small"
type="search"
v-model="searchTerm">
<span class="icon is-right">
<i class="mdi mdi-magnify" />
</span>
</div>
<a href="">{{ $i18n.get('advanced_search') }}</a>
</span>
<a
class="level-item"
:href="wordpressAdmin">
@ -39,102 +40,18 @@
</template>
<script>
import { mapActions, mapGetters } from 'vuex';
export default {
name: 'TainacanHeader',
data(){
return {
logoHeader: tainacan_plugin.base_url + '/admin/images/tainacan_logo_header.png',
wordpressAdmin: window.location.origin + window.location.pathname.replace('admin.php', ''),
onSecondaryPage: false,
pageTitle: '',
arrayRealPath: [],
arrayViewPath: [],
activeRouteName: '',
currentIcon: ''
searchTerm: ''
}
},
methods: {
...mapActions('collection', [
'fetchCollectionName'
]),
...mapGetters('collection', [
'getCollectionName'
]),
...mapActions('item', [
'fetchItemTitle'
]),
...mapGetters('item', [
'getItemTitle'
]),
...mapActions('category', [
'fetchCategoryName'
]),
...mapGetters('category', [
'getCategoryName'
]),
...mapActions('event', [
'fetchEventTitle'
]),
generateViewPath() {
for (let i = 0; i < this.arrayRealPath.length; i++) {
this.arrayViewPath.push('');
if (!isNaN(this.arrayRealPath[i]) && i > 0) {
switch(this.arrayRealPath[i-1]) {
case 'collections':
this.fetchCollectionName(this.arrayRealPath[i])
.then(collectionName => this.arrayViewPath.splice(i, 1, collectionName))
.catch((error) => this.$console.error(error));
break;
case 'items':
this.fetchItemTitle(this.arrayRealPath[i])
.then(itemTitle => this.arrayViewPath.splice(i, 1, itemTitle))
.catch((error) => this.$console.error(error));
break;
case 'categories':
this.fetchCategoryName(this.arrayRealPath[i])
.then(categoryName => this.arrayViewPath.splice(i, 1, categoryName))
.catch((error) => this.$console.error(error));
break;
case 'events':
this.fetchEventTitle(this.arrayRealPath[i])
.then(eventName => this.arrayViewPath.splice(i, 1, eventName))
.catch((error) => this.$console.error(error));
break;
}
} else {
this.arrayViewPath.splice(i, 1, this.$i18n.get(this.arrayRealPath[i]));
}
}
}
},
watch: {
'$route' (to) {
this.onSecondaryPage = (to.params.collectionId != undefined);
this.pageTitle = this.$route.meta.title;
this.currentIcon = this.$route.meta.icon;
this.arrayRealPath = to.path.split("/");
this.arrayRealPath = this.arrayRealPath.filter((item) => item.length != 0);
this.generateViewPath();
}
},
created() {
this.onSecondaryPage = (this.$route.params.collectionId != undefined);
this.pageTitle = this.$route.meta.title;
this.currentIcon = this.$route.meta.icon;
this.arrayRealPath = this.$route.path.split("/");
this.arrayRealPath = this.arrayRealPath.filter((item) => item.length != 0);
this.generateViewPath();
props: {
isMenuCompressed: false
}
}
</script>
@ -148,35 +65,85 @@ export default {
background-color: $secondary;
height: $header-height;
max-height: $header-height;
box-shadow: 0px -2px 7px #000;
width: 100%;
border-bottom: 0.5px solid #ddd;
padding: 1.0em;
padding: 12px;
vertical-align: middle;
left: 0;
right: 0;
position: absolute;
z-index: 9;
z-index: 999;
color: white;
.icon {
padding-right: 1.3em;
margin-left: -1.3em;
}
.breadcrumbs {
font-size: 0.85em;
}
.level-left {
.level-item {
display: inline-block;
margin-left: 268px;
}
margin-left: -12px;
.level-item{
height: $header-height;
width: 180px;
transition: width 0.15s, background-color 0.2s;
-webkit-transition: width 0.15s background-color 0.2s;
cursor: pointer;
background-color: #257787;
&:focus {
box-shadow: none;
}
.tainacan-logo {
max-height: 22px;
padding: 0px 24px;
transition: padding 0.15s;
-webkit-transition: padding linear 0.15s;
}
}
}
&.secondary-page {
.level-item {
margin-left: 87px;
.level-right {
padding-right: 12px;
a{
color: white;
}
a:hover {
color: $tertiary;
}
.search-area {
display: none;//display: flex;
align-items: center;
margin-right: 36px;
.control {
input {
height: 27px;
font-size: 11px;
color: $gray-light;
transition: width linear 0.15s;
-webkit-transition: width linear 0.15s;
width: 160px;
}
input:focus, input:active {
width: 220px !important;
}
.icon {
color: $tertiary;
height: 27px;
font-size: 18px;
}
}
a {
margin: 0px 12px;
font-size: 12px;
}
}
}
&.menu-compressed {
.level-left .level-item {
width: 220px;
background-color: $secondary;
.tainacan-logo {
padding: 0px 42px;
}
}
}
@media screen and (max-width: 769px) {
@ -191,7 +158,7 @@ export default {
}
top: 206px;
&.secondary-page {
&.menu-compressed {
top: 237px !important;
}
margin-bottom: 0px !important;

View File

@ -1,13 +1,10 @@
<template>
<div
id="tainacan-subheader"
class="level"
:class="{'secondary-page': onSecondaryPage}">
class="level secondary-page">
<div class="level-left">
<div class="level-item">
<h1 class="has-text-weight-bold is-uppercase has-text-tertiary"><b-icon
size="is-small"
:icon="currentIcon"/>{{ pageTitle }}</h1>
<h1>{{ getCollectionName() }}</h1>
<nav class="breadcrumbs">
<router-link
tag="a"
@ -15,11 +12,13 @@
<span
v-for="(pathItem, index) in arrayRealPath"
:key="index">
<router-link
<router-link
v-if="index < arrayRealPath.length - 1"
tag="a"
:to="'/' + arrayRealPath.slice(0, index + 1).join('/')">
{{ arrayViewPath[index] }}
</router-link>
<span v-if="index == arrayRealPath.length - 1">{{ arrayViewPath[index] }}</span>
<span v-if="index != arrayRealPath.length - 1"> > </span>
</span>
</nav>
@ -30,12 +29,12 @@
tag="a"
:to="{ path: $routerHelper.getCollectionItemsPath(id, '') }"
:class="activeRoute == 'ItemPage' || activeRoute == 'CollectionItemsPage' || activeRoute == 'ItemEditionForm' || activeRoute == 'ItemCreatePage' ? 'is-active':''"
:aria-label="$i18n.get('label_collection_fields')">
:aria-label="$i18n.get('label_collection_items')">
<b-icon
size="is-small"
icon="file-multiple"/>
icon="folder-outline"/>
<br>
<span class="menu-text">{{ $i18n.getFrom('items', 'name') }}</span>
<span class="menu-text">{{ $i18n.getFrom('collections', 'singular_name') }}</span>
</router-link></li>
<li class="level-item"><router-link
tag="a"
@ -55,7 +54,7 @@
:aria-label="$i18n.get('label_collection_fields')">
<b-icon
size="is-small"
icon="format-list-checks"/>
icon="format-list-bulleted-type"/>
<br>
<span class="menu-text">{{ $i18n.getFrom('fields', 'name') }}</span>
</router-link></li>
@ -70,6 +69,17 @@
<br>
<span class="menu-text">{{ $i18n.getFrom('filters', 'name') }}</span>
</router-link></li>
<li class="level-item"><router-link
tag="a"
:to="{ path: $routerHelper.getCollectionEventsPath(id) }"
:class="activeRoute == 'EventsList' ? 'is-active':''"
:aria-label="$i18n.get('label_collection_events')">
<b-icon
size="is-small"
icon="calendar-range"/>
<br>
<span class="menu-text">{{ $i18n.get('events') }}</span>
</router-link></li>
</ul>
</div>
@ -87,7 +97,6 @@ export default {
arrayRealPath: [],
arrayViewPath: [],
activeRouteName: '',
currentIcon: ''
}
},
props: {
@ -97,9 +106,7 @@ export default {
'$route' (to) {
this.activeRoute = to.name;
this.onSecondaryPage = (to.params.collectionId != undefined);
this.pageTitle = this.$route.meta.title;
this.currentIcon = this.$route.meta.icon;
this.arrayRealPath = to.path.split("/");
this.arrayRealPath = this.arrayRealPath.filter((item) => item.length != 0);
@ -170,9 +177,7 @@ export default {
created() {
this.activeRoute = this.$route.name;
this.onSecondaryPage = (this.$route.params.collectionId != undefined);
this.pageTitle = this.$route.meta.title;
this.currentIcon = this.$route.meta.icon;
this.arrayRealPath = this.$route.path.split("/");
this.arrayRealPath = this.arrayRealPath.filter((item) => item.length != 0);
@ -192,7 +197,6 @@ export default {
height: $subheader-height;
max-height: $subheader-height;
width: 100%;
border-bottom: 0.5px solid #ddd;
padding: 1.0em;
margin: 0px;
vertical-align: middle;
@ -200,47 +204,73 @@ export default {
right: 0;
z-index: 9;
.icon {
padding-right: 1.3em;
margin-left: -1.3em;
h1 {
font-size: 18px;
font-weight: 500;
color: $tertiary;
line-height: 22px;
margin-bottom: 12px;
max-width: 450px;
text-overflow: ellipsis;
white-space: nowrap;
overflow: hidden;
}
.breadcrumbs {
font-size: 0.85em;
font-size: 12px;
line-height: 12px;
color: #1d1d1d;
}
.level-left {
margin-left: 5px;
.level-item {
display: inline-block;
margin-left: 45px;
}
}
li{
margin-right: 0px;
a {
color: $secondary;
color: $tertiary;
text-align: center;
white-space: nowrap;
overflow: hidden;
padding: 1.0em 1.8em;
padding: 1.0em 10px;
min-width: 75px;
line-height: 1.5em;
border-radius: 0px;
-webkit-transition: padding 0.3s linear; /* Safari */
transition: padding 0.3s linear;
position: relative;
overflow: inherit;
}
a:hover {
background-color: rgba(255,255,255,0.4);
color: $tertiary;
}
a:hover,
a.is-active {
background-color: rgba(255,255,255,0.6);
color: $tertiary;
background-color: #d1f1f2;
}
a:focus{
box-shadow: none;
}
a.is-active:after {
position: absolute;
content: '';
width: 0;
height: 0;
bottom: -1px;
border-left: 10px solid transparent;
border-right: 10px solid transparent;
border-bottom: 11px solid white;
left: calc(50% - 10px);
-moz-transform: scale(0.999);
-webkit-backface-visibility: hidden;
}
.icon {
margin: 0;
padding: 0;
}
.menu-text {
font-size: 14px;
opacity: 1;
visibility: visible;
transition: opacity 0.3s linear, visibility 0.3s linear;

View File

@ -0,0 +1,167 @@
<template>
<div
class="tainacan-page-title"
id="title-row">
<h1>{{ pageTitle }} <span class="has-text-weight-bold">{{ isRepositoryLevel ? '' : entityName }}</span></h1>
<a
@click="$router.go(-1)"
class="back-link is-secondary">
{{ $i18n.get('return') }}
</a>
<hr>
<nav
v-show="isRepositoryLevel"
class="breadcrumbs">
<router-link
tag="a"
:to="$routerHelper.getCollectionsPath()">{{ $i18n.get('repository') }}</router-link> >
<span
v-for="(pathItem, index) in arrayRealPath"
:key="index">
<router-link
tag="a"
:to="'/' + arrayRealPath.slice(0, index + 1).join('/')">
{{ arrayViewPath[index] }}
</router-link>
<span v-if="index != arrayRealPath.length - 1"> > </span>
</span>
</nav>
</div>
</template>
<script>
import { mapActions, mapGetters } from 'vuex';
export default {
name: 'TainacanTitle',
data(){
return {
wordpressAdmin: window.location.origin + window.location.pathname.replace('admin.php', ''),
isRepositoryLevel: true,
pageTitle: '',
arrayRealPath: [],
arrayViewPath: [],
activeRouteName: '',
entityName: ''
}
},
methods: {
...mapActions('collection', [
'fetchCollectionName'
]),
...mapGetters('collection', [
'getCollectionName'
]),
...mapActions('item', [
'fetchItemTitle'
]),
...mapGetters('item', [
'getItemTitle'
]),
...mapActions('category', [
'fetchCategoryName'
]),
...mapGetters('category', [
'getCategoryName'
]),
...mapActions('event', [
'fetchEventTitle'
]),
generateViewPath() {
for (let i = 0; i < this.arrayRealPath.length; i++) {
this.arrayViewPath.push('');
if (!isNaN(this.arrayRealPath[i]) && i > 0) {
switch(this.arrayRealPath[i-1]) {
case 'collections':
this.fetchCollectionName(this.arrayRealPath[i])
.then(collectionName => { this.arrayViewPath.splice(i, 1, collectionName); this.entityName = collectionName; })
.catch((error) => this.$console.error(error));
break;
case 'items':
this.fetchItemTitle(this.arrayRealPath[i])
.then(itemName => { this.arrayViewPath.splice(i, 1, itemName); this.entityName = itemName; })
.catch((error) => this.$console.error(error));
break;
case 'categories':
this.fetchCategoryName(this.arrayRealPath[i])
.then(categoryName => this.arrayViewPath.splice(i, 1, categoryName))
.catch((error) => this.$console.error(error));
break;
case 'events':
this.fetchEventTitle(this.arrayRealPath[i])
.then(eventName => this.arrayViewPath.splice(i, 1, eventName))
.catch((error) => this.$console.error(error));
break;
}
} else {
this.arrayViewPath.splice(i, 1, this.$i18n.get(this.arrayRealPath[i]));
}
}
}
},
watch: {
'$route' (to) {
this.isRepositoryLevel = (to.params.collectionId == undefined);
this.pageTitle = this.$route.meta.title;
this.arrayRealPath = to.path.split("/");
this.arrayRealPath = this.arrayRealPath.filter((item) => item.length != 0);
this.generateViewPath();
}
},
created() {
this.isRepositoryLevel = (this.$route.params.collectionId == undefined);
this.pageTitle = this.$route.meta.title;
this.arrayRealPath = this.$route.path.split("/");
this.arrayRealPath = this.arrayRealPath.filter((item) => item.length != 0);
this.generateViewPath();
}
}
</script>
<style lang="scss" scoped>
@import "../../scss/_variables.scss";
// Tainacan Header
#title-row {
.breadcrumbs {
font-size: 0.85em;
}
.level-left {
.level-item {
display: inline-block;
margin-left: 268px;
}
}
@media screen and (max-width: 769px) {
.level-left {
margin-left: 0px !important;
.level-item {
margin-left: 30px;
}
}
.level-right {
display: none;
}
top: 206px;
margin-bottom: 0px !important;
}
}
</style>

View File

@ -1,10 +1,12 @@
<template>
<div>
<tainacan-filter-item
<tainacan-filter-item
v-show="!isMenuCompressed"
:query="getQuery"
v-for="(filter, index) in filters"
:key="index"
:filter="filter"/>
:filter="filter"
:opened="collapsed"/>
</div>
</template>
@ -13,7 +15,8 @@
export default {
props: {
filters: Array
filters: Array,
collapsed: Boolean,
},
methods: {
...mapGetters('search',[

View File

@ -1,20 +1,44 @@
<template>
<span>
<div class="header-item">
<b-dropdown id="item-creation-options-dropdown">
<button
class="button is-secondary"
slot="trigger">
<span>{{ `${$i18n.get('add')} ${$i18n.get('item')}` }}</span>
<b-icon icon="menu-down"/>
</button>
<b-dropdown-item>
<router-link
id="a-create-item"
tag="div"
:to="{ path: $routerHelper.getNewItemPath(collectionId) }">
{{ $i18n.get('add_one_item') }}
</router-link>
</b-dropdown-item>
<b-dropdown-item disabled>{{ $i18n.get('add_items_bulk') + ' (Not ready)' }}
</b-dropdown-item>
<b-dropdown-item disabled>{{ $i18n.get('add_items_external_source') + ' (Not ready)' }}<br><small class="is-small">{{ $i18n.get() }}</small></b-dropdown-item>
</b-dropdown>
</div>
<div class="header-item">
<b-dropdown>
<button
class="button"
<button
class="button"
slot="trigger">
<span>{{ $i18n.get('label_table_fields') }}</span>
<b-icon icon="menu-down"/>
</button>
<b-dropdown-item
v-for="(column, index) in tableFields"
<b-dropdown-item
v-for="(column, index) in tableFields"
:key="index"
class="control"
class="control"
custom>
<b-checkbox
v-model="column.visible"
v-model="column.display"
:native-value="column.field">
{{ column.name }}
</b-checkbox>
@ -24,23 +48,24 @@
<div class="header-item">
<b-field>
<b-select
@input="onChangeOrderBy($event)"
@input="onChangeOrderBy($event)"
:placeholder="$i18n.get('label_sorting')">
<option
v-for="field in tableFields"
v-if="
v-for="field in tableFields"
v-if="
field.id == 'date' || (
field.id != undefined &&
field.field_type_object.related_mapped_prop != 'description' &&
field.field_type_object.primitive_type != 'term' &&
field.field_type_object.primitive_type != 'item' &&
field.field_type_object.primitive_type != 'compound')"
:value="field"
:key="field.id">
field.id != undefined &&
field.field_type_object.related_mapped_prop != 'description' &&
field.field_type_object.primitive_type != 'term' &&
field.field_type_object.primitive_type != 'item' &&
field.field_type_object.primitive_type != 'compound'
)"
:value="field"
:key="field.id">
{{ field.name }}
</option>
</b-select>
<button
<button
class="button is-small"
@click="onChangeOrder()">
<b-icon :icon="order == 'ASC' ? 'sort-ascending' : 'sort-descending'"/>
@ -51,47 +76,49 @@
</template>
<script>
import { mapGetters } from 'vuex';
import {mapGetters} from 'vuex';
export default {
name: 'SearchControl',
data() {
return {
prefTableFields: []
}
},
props: {
collectionId: Number,
isRepositoryLevel: false,
tableFields: Array
},
computed: {
orderBy() {
return this.getOrderBy();
export default {
name: 'SearchControl',
data() {
return {
prefTableFields: []
}
},
order() {
return this.getOrder();
}
},
methods: {
...mapGetters('search', [
'getOrderBy',
'getOrder'
]),
onChangeOrderBy(field) {
this.$eventBusSearch.setOrderBy(field);
props: {
collectionId: Number,
isRepositoryLevel: false,
tableFields: Array
},
onChangeOrder() {
this.order == 'DESC' ? this.$eventBusSearch.setOrder('ASC') : this.$eventBusSearch.setOrder('DESC');
computed: {
orderBy() {
return this.getOrderBy();
},
order() {
return this.getOrder();
}
},
methods: {
...mapGetters('search', [
'getOrderBy',
'getOrder'
]),
onChangeOrderBy(field) {
this.$eventBusSearch.setOrderBy(field);
},
onChangeOrder() {
this.order == 'DESC' ? this.$eventBusSearch.setOrder('ASC') : this.$eventBusSearch.setOrder('DESC');
}
}
}
}
</script>
<style>
.header-item {
display: inline-block;
padding-right: 8em;
}
#item-creation-options-dropdown {
margin-right: 80px;
}
</style>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 217 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 161 KiB

View File

@ -35,9 +35,10 @@ import TaincanFiltersList from '../../classes/filter-types/tainacan-filter-item.
// Remaining imports
import AdminPage from '../admin.vue'
import HelpButton from '../components/other/help-button.vue';
import TainacanTitle from '../components/navigation/tainacan-title.vue';
import draggable from 'vuedraggable'
import store from '../../js/store/store'
import { router} from './router'
import router from './router'
import eventBusSearch from '../../js/event-bus-search';
import { I18NPlugin, UserPrefsPlugin, RouterHelperPlugin, ConsolePlugin } from './utilities';
@ -76,6 +77,7 @@ Vue.component('tainacan-filter-category-selectbox', FilterCategorySelectbox);
/* Others */
Vue.component('help-button', HelpButton);
Vue.component('draggable', draggable);
Vue.component('tainacan-title', TainacanTitle);
Vue.use(eventBusSearch, { store: store, router: router});

View File

@ -22,6 +22,7 @@ import CategoryEditionForm from '../components/edition/category-edition-form.vue
// Listing components
import FiltersList from '../components/lists/filters-list.vue'
import FieldsList from '../components/lists/fields-list.vue'
import EventsList from '../components/lists/events-list.vue'
Vue.use(VueRouter);
@ -30,44 +31,45 @@ const i18nGet = function (key) {
return (string != undefined && string != null && string != '' ) ? string : "ERROR: Invalid i18n key!";
};
const routes = [
const routes = [
{ path: '/', redirect:'/collections' },
{ path: '/collections', name: 'CollectionsPage', component: CollectionsPage, meta: {title: i18nGet('title_collections_page'), icon: 'folder-multiple'} },
{ path: '/collections', name: 'CollectionsPage', component: CollectionsPage, meta: {title: i18nGet('title_repository_collections_page'), icon: 'folder-multiple'} },
{ path: '/collections/new', name: 'CollectionCreationForm', component: CollectionEditionForm, meta: {title: i18nGet('title_create_collection'), icon: 'folder-multiple'} },
{ path: '/collections/:collectionId', component: CollectionPage, meta: {title: i18nGet('title_collection_page'), icon: 'folder-multiple'},
children: [
{ path: '', redirect: 'items'},
{ path: 'items', component: ItemsPage, name: 'CollectionItemsPage', meta: {title: i18nGet('title_collection_page'), icon: 'folder-multiple'} },
{ path: 'items/:itemId/edit', name: 'ItemEditionForm', component: ItemEditionForm, meta: {title: i18nGet('title_item_edition'), icon: 'folder-multiple'} },
{ path: 'items/new', name: 'CollectionItemCreatePage', component: ItemEditionForm, meta: {title: i18nGet('title_create_item'), icon: 'folder-multiple'} },
{ path: 'items/:itemId/edit', name: 'ItemEditionForm', component: ItemEditionForm, meta: {title: i18nGet('title_edit_item'), icon: 'folder-multiple'} },
{ path: 'items/new', name: 'CollectionItemCreatePage', component: ItemEditionForm, meta: {title: i18nGet('title_create_item_collection'), icon: 'folder-multiple'} },
{ path: 'items/:itemId', name: 'ItemPage', component: ItemPage, meta: {title: i18nGet('title_item_page'), icon: 'folder-multiple'} },
{ path: 'edit', component: CollectionEditionForm, name: 'CollectionEditionForm', meta: {title: i18nGet('title_collection_edition'), icon: 'folder-multiple'} },
{ path: 'edit', component: CollectionEditionForm, name: 'CollectionEditionForm', meta: {title: i18nGet('title_edit_collection'), icon: 'folder-multiple'} },
{ path: 'fields', component: FieldsList, name: 'FieldsList', meta: {title: i18nGet('title_collection_fields_edition'), icon: 'folder-multiple'} },
{ path: 'filters', component: FiltersList, name: 'FiltersList', meta: {title: i18nGet('title_collection_page'), icon: 'folder-multiple'} }
]
{ path: 'filters', component: FiltersList, name: 'FiltersList', meta: {title: i18nGet('title_collection_filters_edition'), icon: 'folder-multiple'} },
{ path: 'events', component: EventsList, name: 'EventsList', meta: {title: i18nGet('title_collection_events'), icon: 'calendar'} }
]
},
{ path: '/items', name: 'ItemsPage', component: ItemsPage, meta: {title: i18nGet('title_items_page'), icon: 'file-multiple'} },
{ path: '/items/new', name: 'ItemCreationForm', component: ItemEditionForm, meta: {title: i18nGet('title_create_item'), icon: 'file-multiple'} },
// { path: '/items', name: 'ItemsPage', component: ItemsPage, meta: {title: i18nGet('title_items_page'), icon: 'file-multiple'} },
// { path: '/items/new', name: 'ItemCreationForm', component: ItemEditionForm, meta: {title: i18nGet('title_create_item'), icon: 'file-multiple'} },
{ path: '/filters', name: 'FiltersPage', component: FiltersPage, meta: {title: i18nGet('title_filters_page'), icon: 'filter'} },
{ path: '/filters', name: 'FiltersPage', component: FiltersPage, meta: {title: i18nGet('title_repository_filters_page'), icon: 'filter'} },
{ path: '/fields', name: 'FieldsPage', component: FieldsPage, meta: {title: i18nGet('title_fields_page'), icon: 'format-list-checks'} },
{ path: '/fields', name: 'FieldsPage', component: FieldsPage, meta: {title: i18nGet('title_repository_fields_page'), icon: 'format-list-checks'} },
{ path: '/categories', name: 'CategoriesPage', component: CategoriesPage, meta: {title: i18nGet('title_categories_page'), icon: 'shape'} },
{ path: '/categories/new', name: 'CategoryCreationForm', component: CategoryEditionForm, meta: {title: i18nGet('title_create_category_page'), icon: 'shape'} },
{ path: '/categories/:categoryId/edit', name: 'CategoryEditionForm', component: CategoryEditionForm, meta: {title: i18nGet('title_category_edition_page'), icon: 'shape'} },
{ path: '/categories/:categoryId', name: 'CategoryPage', component: CategoryPage, meta: {title: i18nGet('title_category_page'), icon: 'shape'} },
{ path: '/events', name: 'EventsPage', component: EventsPage, meta: {title: i18nGet('title_events_page'), icon: 'bell'} },
{ path: '/events/:eventId', name: 'EventPage', component: EventPage, meta: {title: i18nGet('title_event_page'), icon: 'bell'} },
{ path: '/events', name: 'EventsPage', component: EventsPage, meta: {title: i18nGet('title_repository_events_page'), icon: 'calendar'} },
{ path: '/events/:eventId', name: 'EventPage', component: EventPage, meta: {title: i18nGet('title_event_page'), icon: 'calendar'} },
{ path: '*', redirect: '/'}
];
export const router = new VueRouter ({
export default new VueRouter ({
routes,
// set custom query resolver
parseQuery(query) {
@ -79,18 +81,3 @@ export const router = new VueRouter ({
return result ? ('?' + result) : '';
}
});
const themeRoutes = [];
export const routerTheme = new VueRouter ({
themeRoutes,
// set custom query resolver
parseQuery(query) {
return qs.parse(query);
},
stringifyQuery(query) {
let result = qs.stringify(query);
return result ? ('?' + result) : '';
}
});

View File

@ -33,7 +33,7 @@ import ItemsPage from '../pages/lists/items-page.vue';
import HelpButton from '../components/other/help-button.vue';
import draggable from 'vuedraggable'
import store from '../../js/store/store'
import { routerTheme } from './router.js'
import routerTheme from './theme-router.js'
import eventBusSearch from '../../js/event-bus-search';
import { I18NPlugin, UserPrefsPlugin, RouterHelperPlugin, ConsolePlugin } from './utilities';

View File

@ -0,0 +1,20 @@
import Vue from 'vue';
import VueRouter from 'vue-router'
import qs from 'qs';
Vue.use(VueRouter);
const themeRoutes = [];
export default new VueRouter ({
themeRoutes,
// set custom query resolver
parseQuery(query) {
return qs.parse(query);
},
stringifyQuery(query) {
let result = qs.stringify(query);
return result ? ('?' + result) : '';
}
});

View File

@ -168,6 +168,9 @@ RouterHelperPlugin.install = function (Vue, options = {}) {
getCollectionFiltersPath(collectionId) {
return '/collections/'+ collectionId + '/filters/';
},
getCollectionEventsPath(collectionId) {
return '/collections/'+ collectionId + '/events/';
},
getItemsPath(query) {
return '/items/?' + qs.stringify(query);
},

View File

@ -0,0 +1,218 @@
export default {
attachmentControl: wp.customize.MediaControl.extend({
/**
* Create a media modal select frame, and store it so the instance can be reused when needed.
*/
initFrame: function() {
wp.media.view.settings.post = {
id: this.params.relatedPostId
}
this.frame = wp.media({
button: {
text: this.params.button_labels.frame_button
},
states: [
new wp.media.controller.Library({
title: this.params.button_labels.frame_title,
library: wp.media.query({
uploadedTo: this.params.relatedPostId
}),
multiple: true,
date: false
})
]
});
// When a file is selected, run a callback.
this.frame.on( 'select', () => {
// Get the attachment from the modal frame.
var node,
attachments,
mejsSettings = window._wpmejsSettings || {};
attachments = this.frame.state().get( 'selection' ).toJSON();
this.params.attachments = attachments;
this.params.onSave(attachments);
// Set the Customizer setting; the callback takes care of rendering.
//this.setting( attachment.id );
node = this.container.find( 'audio, video' ).get(0);
// Initialize audio/video previews.
if ( node ) {
this.player = new MediaElementPlayer( node, mejsSettings );
} else {
this.cleanupPlayer();
}
});
}
}),
// CroppedImageControl, with presets for thumbnail dimensions
thumbnailControl: wp.customize.CroppedImageControl.extend({
initFrame: function() {
var l10n = _wpMediaViewsL10n;
wp.media.view.settings.post = {
id: this.params.relatedPostId
}
this.params.flex_width = 0;
this.params.flex_height = 0;
this.params.width = 220;
this.params.height = 220;
this.frame = wp.media({
frame: 'select',
button: {
text: l10n.select,
close: false
},
uploader: true,
states: [
new wp.media.controller.Library({
title: this.params.button_labels.frame_title,
library: wp.media.query({
type: 'image',
uploadedTo: this.params.relatedPostId
}),
multiple: false,
date: false,
filterable: true,
priority: 20,
suggestedWidth: this.params.width,
suggestedHeight: this.params.height,
uploadedTo: this.params.relatedPostId
}),
new wp.media.controller.CustomizeImageCropper({
imgSelectOptions: this.calculateImageSelectOptions,
control: this
})
]
});
this.frame.on( 'select', this.onSelect, this );
this.frame.on( 'cropped', this.onCropped, this );
this.frame.on( 'skippedcrop', this.onSkippedCrop, this );
},
// Called on both skippedcrop and cropped states
setImageFromAttachment: function( attachment ) {
wp.media.view.settings.post = {
id: this.params.relatedPostId
}
this.params.attachment = attachment;
this.params.onSave(attachment.id);
}
}),
// CroppedImageControl, with presets for thumbnail dimensions
headerImageControl: wp.customize.CroppedImageControl.extend({
initFrame: function() {
var l10n = _wpMediaViewsL10n;
wp.media.view.settings.post = {
id: this.params.relatedPostId
}
this.params.flex_width = tainacan_plugin.custom_header_support[0].flex_width ? 1 : 0;
this.params.flex_height = tainacan_plugin.custom_header_support[0].flex_height ? 1 : 0;
this.params.width = tainacan_plugin.custom_header_support[0].width;
this.params.height = tainacan_plugin.custom_header_support[0].height;
this.frame = wp.media({
frame: 'select',
button: {
text: l10n.select,
close: false
},
uploader: true,
states: [
new wp.media.controller.Library({
title: this.params.button_labels.frame_title,
library: wp.media.query({
type: 'image',
uploadedTo: this.params.relatedPostId
}),
multiple: false,
date: false,
priority: 20,
suggestedWidth: this.params.width,
suggestedHeight: this.params.height
}),
new wp.media.controller.CustomizeImageCropper({
imgSelectOptions: this.calculateImageSelectOptions,
control: this
})
]
});
//this.frame.state('cropper').set( 'canSkipCrop', true );
this.frame.on( 'select', this.onSelect, this );
this.frame.on( 'cropped', this.onCropped, this );
this.frame.on( 'skippedcrop', this.onSkippedCrop, this );
},
// Called on both skippedcrop and cropped states
setImageFromAttachment: function( attachment ) {
this.params.attachment = attachment;
this.params.onSave(attachment);
}
}),
// DocumentFileControl: similar to attachment, but used for items where the documents is of type file
documentFileControl: wp.customize.MediaControl.extend({
/**
* Create a media modal select frame, and store it so the instance can be reused when needed.
*/
initFrame: function() {
// We don't want document file to be listed as an attachment of item
wp.media.view.settings.post = {
id: null
}
this.frame = wp.media({
button: {
text: this.params.button_labels.frame_button
},
states: [
new wp.media.controller.Library({
title: this.params.button_labels.frame_title,
library: wp.media.query({
uploadedTo: this.params.relatedPostId
}),
multiple: false,
date: false,
uploadedTo: this.params.relatedPostId
})
]
});
// When a file is selected, run a callback.
this.frame.on( 'select', () => {
// Get the attachment from the modal frame.
var node,
attachment,
mejsSettings = window._wpmejsSettings || {};
attachment = this.frame.state().get( 'selection' ).first().toJSON();
this.params.attachment = attachment;
this.params.onSave(attachment);
// Set the Customizer setting; the callback takes care of rendering.
//this.setting( attachment.id );
node = this.container.find( 'audio, video' ).get(0);
// Initialize audio/video previews.
if ( node ) {
this.player = new MediaElementPlayer( node, mejsSettings );
} else {
this.cleanupPlayer();
}
});
}
}),
}

View File

@ -1,6 +1,7 @@
<template>
<div>
<div class="primary-page page-container-small">
<div class="primary-page page-container">
<tainacan-title />
<div
class="sub-header"
v-if="totalCategories > 0">
@ -147,8 +148,8 @@
@import '../../scss/_variables.scss';
.sub-header {
max-height: $header-height;
height: $header-height;
max-height: $subheader-height;
height: $subheader-height;
margin-left: -$page-small-side-padding;
margin-right: -$page-small-side-padding;
margin-top: -$page-small-top-padding;
@ -179,14 +180,6 @@
min-height: 100%;
height: auto;
.filters-menu {
min-width: $side-menu-width;
max-width: $side-menu-width;
background-color: $primary-lighter;
margin-left: -$page-small-side-padding;
padding-left: $page-small-side-padding
}
.table-container {
margin-right: -$page-small-side-padding;
padding: 3em 2.5em;

View File

@ -1,5 +1,6 @@
<template>
<div class="primary-page page-container-small">
<div class="primary-page page-container">
<tainacan-title />
<div
class="sub-header"
v-if="totalCollections > 0">
@ -13,13 +14,8 @@
</router-link>
</div>
</div>
<div class="columns above-subheader">
<aside
class="column filters-menu"
v-if="totalCollections > 0">
<h3>{{ $i18n.getFrom('filters', 'name') }}</h3>
</aside>
<div class="column table-container">
<div class="above-subheader">
<div class="table-container">
<collections-list
:is-loading="isLoading"
:total-collections="totalCollections"
@ -182,14 +178,6 @@ export default {
min-height: 100%;
height: auto;
.filters-menu {
min-width: $side-menu-width;
max-width: $side-menu-width;
background-color: $primary-lighter;
margin-left: -$page-small-side-padding;
padding-left: $page-small-side-padding
}
.table-container {
margin-right: -$page-small-side-padding;
padding: 3em 2.5em;

View File

@ -1,7 +1,7 @@
<template>
<div>
<div class="primary-page page-container-small">
<div class="primary-page page-container">
<tainacan-title />
<div class="columns above-subheader">
<div class="column table-container">
<events-list
@ -66,7 +66,8 @@
isLoading: false,
totalEvents: 0,
page: 1,
eventsPerPage: 12
eventsPerPage: 12,
isRepositoryLevel: false
}
},
components: {
@ -119,6 +120,7 @@
}
},
created() {
this.isRepositoryLevel = (this.$route.params.collectionId == undefined);
this.$userPrefs.get('events_per_page')
.then((value) => {
this.eventsPerPage = value;

View File

@ -1,5 +1,6 @@
<template>
<div class="primary-page page-container">
<tainacan-title />
<fields-list/>
</div>
</template>

View File

@ -1,5 +1,6 @@
<template>
<div class="primary-page page-container">
<tainacan-title />
<filters-list/>
</div>
</template>

View File

@ -1,225 +1,317 @@
<template>
<div
class="page-container-small"
:class="{'primary-page': isRepositoryLevel}">
<div class="sub-header">
<b-loading
:is-full-page="false"
:active.sync="isLoadingFields"/>
<div class="header-item">
<router-link
id="button-create-item"
tag="button"
class="button is-secondary"
:to="{ path: $routerHelper.getNewItemPath(collectionId) }">
{{ $i18n.getFrom('items', 'new_item') }}
</router-link>
<template>
<div :class="{'primary-page': isRepositoryLevel, 'page-container': isRepositoryLevel, 'page-container-small' :!isRepositoryLevel }">
<!-- SEARCH AND FILTERS --------------------- -->
<button
id="filter-menu-compress-button"
@click="isFiltersMenuCompressed = !isFiltersMenuCompressed">
<b-icon :icon="isFiltersMenuCompressed ? 'menu-right' : 'menu-left'" />
</button>
<aside
v-show="!isFiltersMenuCompressed"
class="filters-menu">
<b-loading
:is-full-page="false"
:active.sync="isLoadingFilters"/>
<b-field class="margin-1">
<b-input
placeholder="Search..."
type="search"
size="is-small"
icon="magnify" />
</b-field>
<a class="is-size-7 is-secondary is-pulled-right">Busca avançada</a>
<br>
<br>
<h3 class="has-text-weight-semibold">{{ $i18n.get('filters') }}</h3>
<a
class="collapse-all is-size-7"
@click="toggleCollapseAll">
{{ collapseAll ? $i18n.get('label_collapse_all') : $i18n.get('label_expand_all') }}
<b-icon
type="is-secondary"
size="is-small"
:icon=" collapseAll ? 'menu-down' : 'menu-right'" />
</a>
<br>
<filters-items-list
v-if="!isLoadingFilters && filters.length > 0"
:filters="filters"
:collapsed="collapseAll"/>
<section
v-else
class="is-grouped-centered section">
<div class="content has-text-gray has-text-centered">
<p>
<b-icon
icon="filter-outline"
size="is-large"/>
</p>
<p>{{ $i18n.get('info_there_is_no_filter' ) }}</p>
<router-link
id="button-create-filter"
:to="isRepositoryLevel ? $routerHelper.getNewFilterPath() : $routerHelper.getNewCollectionFilterPath(collectionId)"
tag="button"
class="button is-secondary is-centered">
{{ $i18n.getFrom('filters', 'new_item') }}
</router-link>
</div>
</section>
</aside>
<div
class="items-list-area"
:class="{ 'spaced-to-right': !isFiltersMenuCompressed }">
<!-- SEARCH CONTROL ------------------------- -->
<div class="sub-header">
<b-loading
:is-full-page="false"
:active.sync="isLoadingFields"/>
<search-control
v-if="fields.length > 0 && (items.length > 0 || isLoadingItems)"
:is-repository-level="isRepositoryLevel"
:collection-id="collectionId"
:table-fields="tableFields"
:pref-table-fields="prefTableFields"/>
</div>
<search-control
v-if="fields.length > 0 && (items.length != 0 || isLoadingItems)"
:is-repository-level="isRepositoryLevel"
:collection-id="collectionId"
:table-fields="tableFields"
:pref-table-fields="prefTableFields"/>
</div>
<div class="columns">
<aside class="column filters-menu">
<b-loading
:is-full-page="false"
:active.sync="isLoadingFilters"/>
<h3>{{ $i18n.get('filters') }}</h3>
<filters-items-list
v-if="!isLoadingFilters && filters.length > 0"
:filters="filters"/>
<section
v-else
class="is-grouped-centered section">
<div class="content has-text-gray has-text-centered">
<!-- LISTING RESULTS ------------------------- -->
<div class="table-container above-subheader">
<b-loading
:is-full-page="false"
:active.sync="isLoadingItems"/>
<items-list
v-if="!isLoadingItems && items.length > 0"
:collection-id="collectionId"
:table-fields="tableFields"
:items="items"
:is-loading="isLoading"/>
<section
v-if="!isLoadingItems && items.length <= 0"
class="section">
<div class="content has-text-grey has-text-centered">
<p>
<b-icon
icon="filter-outline"
icon="inbox"
size="is-large"/>
</p>
<p>{{ $i18n.get('info_there_is_no_filter' ) }}</p>
<p>{{ hasFiltered ? $i18n.get('info_no_item_found') : $i18n.get('info_no_item_created')
}}</p>
<router-link
id="button-create-filter"
:to="isRepositoryLevel ? $routerHelper.getNewFilterPath() : $routerHelper.getNewCollectionFilterPath(collectionId)"
tag="button"
class="button is-secondary is-centered">
{{ $i18n.getFrom('filters', 'new_item') }}</router-link>
v-if="!hasFiltered"
id="button-create-item"
tag="button"
class="button is-primary"
:to="{ path: $routerHelper.getNewItemPath(collectionId) }">
{{ $i18n.getFrom('items', 'new_item') }}
</router-link>
</div>
</section>
</aside>
<div class="column">
<div class="table-container above-subheader">
<b-loading
:is-full-page="false"
:active.sync="isLoadingItems"/>
<items-list
v-if="!isLoadingItems && items.length > 0"
:collection-id="collectionId"
:table-fields="tableFields"
:items="items"
:is-loading="isLoading"/>
<section
v-if="!isLoadingItems && items.length <= 0"
class="section">
<div class="content has-text-grey has-text-centered">
<p>
<b-icon
icon="inbox"
size="is-large"/>
</p>
<p>{{ hasFiltered ? $i18n.get('info_no_item_found') : $i18n.get('info_no_item_created') }}</p>
<router-link
v-if="!hasFiltered"
id="button-create-item"
tag="button"
class="button is-primary"
:to="{ path: $routerHelper.getNewItemPath(collectionId) }">
{{ $i18n.getFrom('items', 'new_item') }}
</router-link>
</div>
</section>
<!-- Pagination Footer -->
<pagination v-if="items.length > 0"/>
</div>
<!-- Pagination Footer -->
<pagination v-if="items.length > 0"/>
</div>
</div>
</div>
</div>
</template>
<script>
import SearchControl from '../../components/search/search-control.vue'
import ItemsList from '../../components/lists/items-list.vue';
import FiltersItemsList from '../../components/search/filters-items-list.vue';
import Pagination from '../../components/search/pagination.vue'
import { mapActions, mapGetters } from 'vuex';
import SearchControl from '../../components/search/search-control.vue'
import ItemsList from '../../components/lists/items-list.vue';
import FiltersItemsList from '../../components/search/filters-items-list.vue';
import Pagination from '../../components/search/pagination.vue'
import {mapActions, mapGetters} from 'vuex';
export default {
name: 'ItemsPage',
data(){
return {
isRepositoryLevel: false,
tableFields: [],
prefTableFields: [],
isLoadingItems: false,
isLoadingFilters: false,
isLoadingFields: false,
hasFiltered: false
}
},
props: {
collectionId: Number
},
components: {
SearchControl,
ItemsList,
FiltersItemsList,
Pagination
},
methods: {
...mapGetters('collection', [
'getItems'
]),
...mapActions('fields', [
'fetchFields'
]),
...mapGetters('fields', [
'getFields'
]),
...mapActions('filter',[
'fetchFilters'
]),
...mapGetters('filter', [
'getFilters'
])
},
computed: {
items(){
return this.getItems();
},
filters(){
return this.getFilters();
},
fields() {
return this.getFields();
}
},
created() {
this.isRepositoryLevel = (this.collectionId == undefined);
this.$eventBusSearch.$on('isLoadingItems', isLoadingItems => {
this.isLoadingItems = isLoadingItems;
});
this.$eventBusSearch.$on('hasFiltered', hasFiltered => {
this.hasFiltered = hasFiltered;
});
this.isLoadingFilters = true;
this.fetchFilters( { collectionId: this.collectionId, isRepositoryLevel: this.isRepositoryLevel, isContextEdit: true })
.then(() => this.isLoadingFilters = false)
.catch(() => this.isLoadingFilters = false);
this.isLoadingFields = true;
this.fetchFields({ collectionId: this.collectionId, isRepositoryLevel: this.isRepositoryLevel, isContextEdit: false }).then(() => {
this.tableFields.push({ name: this.$i18n.get('label_thumbnail'), field: 'row_thumbnail', field_type: undefined, slug: 'featured_image', id: undefined, visible: true });
for (let field of this.fields) {
this.tableFields.push(
{name: field.name, field: field.description, slug: field.slug, field_type: field.field_type, field_type_object: field.field_type_object, id: field.id, visible: true }
);
export default {
name: 'ItemsPage',
data() {
return {
isRepositoryLevel: false,
tableFields: [],
prefTableFields: [],
isLoadingItems: false,
isLoadingFilters: false,
isLoadingFields: false,
hasFiltered: false,
isFiltersMenuCompressed: false,
collapseAll: false,
}
this.tableFields.push({ name: this.$i18n.get('label_creation'), field: 'row_creation', field_type: undefined, slug: 'creation', id: 'date', visible: true});
this.tableFields.push({ name: this.$i18n.get('label_actions'), field: 'row_actions', field_type: undefined, slug: 'actions', id: undefined, visible: true });
//this.prefTableFields = this.tableFields;
// this.$userPrefs.get('table_columns_' + this.collectionId)
// .then((value) => {
// this.prefTableFields = value;
// })
// .catch((error) => {
// this.$userPrefs.set('table_columns_' + this.collectionId, this.prefTableFields, null);
// });
this.isLoadingFields = false;
},
props: {
collectionId: Number
},
components: {
SearchControl,
ItemsList,
FiltersItemsList,
Pagination
},
methods: {
...mapGetters('collection', [
'getItems'
]),
...mapActions('fields', [
'fetchFields'
]),
...mapGetters('fields', [
'getFields'
]),
...mapActions('filter', [
'fetchFilters'
]),
...mapGetters('filter', [
'getFilters'
]),
toggleCollapseAll() {
this.collapseAll = !this.collapseAll;
}).catch(() => {
this.isLoadingFields = false;
});
},
mounted(){
this.$eventBusSearch.setCollectionId(this.collectionId);
this.$eventBusSearch.updateStoreFromURL();
this.$eventBusSearch.loadItems();
for (let i = 0; i < this.fieldCollapses.length; i++)
this.fieldCollapses[i] = this.collapseAll;
},
},
computed: {
items() {
return this.getItems();
},
filters() {
return this.getFilters();
},
fields() {
return this.getFields();
}
},
created() {
this.isRepositoryLevel = (this.collectionId == undefined);
this.$eventBusSearch.$on('isLoadingItems', isLoadingItems => {
this.isLoadingItems = isLoadingItems;
});
this.$eventBusSearch.$on('hasFiltered', hasFiltered => {
this.hasFiltered = hasFiltered;
});
this.isLoadingFilters = true;
this.fetchFilters({
collectionId: this.collectionId,
isRepositoryLevel: this.isRepositoryLevel,
isContextEdit: true
})
.then(() => this.isLoadingFilters = false)
.catch(() => this.isLoadingFilters = false);
this.isLoadingFields = true;
this.fetchFields({
collectionId: this.collectionId,
isRepositoryLevel: this.isRepositoryLevel,
isContextEdit: false
})
.then(() => {
this.tableFields.push({
name: this.$i18n.get('label_thumbnail'),
field: 'row_thumbnail',
field_type: undefined,
slug: 'featured_image',
id: undefined,
display: true
});
for (let field of this.fields) {
if (field.display !== 'never') {
// Will be pushed on array
let display = true;
if (field.display === 'no') {
display = false;
}
this.tableFields.push(
{
name: field.name,
field: field.description,
slug: field.slug,
field_type: field.field_type,
field_type_object: field.field_type_object,
id: field.id,
display: display
}
);
}
}
this.tableFields.push({
name: this.$i18n.get('label_creation'),
field: 'row_creation',
field_type: undefined,
slug: 'creation',
id: 'date',
display: true
});
this.tableFields.push({
name: this.$i18n.get('label_actions'),
field: 'row_actions',
field_type: undefined,
slug: 'actions',
id: undefined,
display: true
});
// this.prefTableFields = this.tableFields;
// this.$userPrefs.get('table_columns_' + this.collectionId)
// .then((value) => {
// this.prefTableFields = value;
// })
// .catch((error) => {
// this.$userPrefs.set('table_columns_' + this.collectionId, this.prefTableFields, null);
// });
this.isLoadingFields = false;
})
.catch(() => {
this.isLoadingFields = false;
});
},
mounted() {
this.$eventBusSearch.setCollectionId(this.collectionId);
this.$eventBusSearch.updateStoreFromURL();
this.$eventBusSearch.loadItems();
}
}
}
</script>
<style lang="scss" scoped>
@import '../../scss/_variables.scss';
.page-container-small>.columns {
margin-top: 0;
.margin-1 {
margin-bottom: 0.1rem;
}
.page-container, .page-container-small {
padding: 0px;
}
.sub-header {
min-height: $subheader-height;
height: $subheader-height;
margin-left: -$page-small-side-padding;
margin-right: -$page-small-side-padding;
margin-top: -$page-small-top-padding;
padding-top: $page-small-top-padding;
padding-left: $page-small-side-padding;
padding-right: $page-small-side-padding;
border-bottom: 0.5px solid #ddd;
position: relative;
@media screen and (max-width: 769px) {
height: 60px;
margin-top: -0.5em;
@ -235,32 +327,46 @@ export default {
margin-bottom: 0;
margin-top: 0;
min-height: 100%;
height: auto;
height: auto;
}
.filters-menu {
position: relative;
min-width: $side-menu-width;
max-width: $side-menu-width;
width: $filter-menu-width;
max-width: $filter-menu-width;
min-height: 100%;
background-color: $tainacan-input-color;
margin-left: -$page-small-side-padding;
padding: $page-small-side-padding;
float: left;
height: 100%;
max-height: 100%;
overflow-y: auto;
visibility: visible;
display: block;
transition: visibility ease 0.5s, display ease 0.5s;
.label {
font-size: 12px;
font-weight: normal;
}
}
.items-list-area {
margin-left: 0;
transition: margin-left ease 0.5s ;
}
.spaced-to-right {
margin-left: $filter-menu-width;
}
.table-container {
margin-right: -$page-small-side-padding;
padding: 3em 2.5em;
padding: 3em 55px;
position: relative;
}
@media screen and (max-width: 769px) {
.filters-menu {
.filters-menu {
display: none;
}
.table-container {
@ -269,7 +375,25 @@ export default {
}
}
#filter-menu-compress-button {
position: absolute;
z-index: 9;
top: 152px;
left: 0px;
max-width: 23px;
height: 21px;
width: 23px;
border: none;
background-color: $primary-light;
color: $tertiary;
padding: 0px;
border-top-right-radius: 2px;
border-bottom-right-radius: 2px;
.icon {
margin-top: -1px;
}
}
</style>

View File

@ -4,7 +4,7 @@
<tainacan-subheader :id="collectionId"/>
<router-view
:collection-id="collectionId"
class="page-container"/>
class="page-container page-container-small"/>
</section>
</div>
</template>

View File

@ -1,37 +1,71 @@
<template>
<div>
<b-loading
:active.sync="isLoading"
<b-loading
:active.sync="isLoading"
:can-cancel="false"/>
<div class="card">
<div
class="card-image"
v-if="item.featured_image">
<figure class="image">
<img
:src="item.featured_image"
:alt="item.title">
</figure>
</div>
<div class="card-content">
<div class="media">
<div
class="card-image"
v-if="item.document">
<figure
class="image"
v-html="item.document_as_html" />
</div>
<br>
<div
v-if="item.featured_image"
class="media">
<figure
class="media-left" >
<p class="image is-128x128">
<img :src="item.featured_image">
</p>
</figure>
<div class="media-content">
<p class="title is-4">{{ item.title }}</p>
{{ $i18n.get('label_thumbnail') }}
</div>
</div>
<div class="content">
{{ item.description }}
<div
v-for="(metadata, index) in item.metadata"
:key="index"
class="box">
<p
v-if="metadata.value_as_html"
class="is-size-3"
v-html="metadata.value_as_html"/>
<p
v-else>--</p>
<p>
<i>
{{ metadata.name }}
</i>
</p>
</div>
<div
class="box">
<p>--</p>
<p>
<i>
{{ $i18n.get('label_attachments') }}
</i>
</p>
</div>
</div>
<footer class="card-footer">
<router-link
class="card-footer-item"
class="card-footer-item"
:to="{ path: $routerHelper.getCollectionPath(collectionId)}">
{{ $i18n.get('see') + ' ' + $i18n.get('collection') }}
</router-link>
<router-link
class="card-footer-item"
class="card-footer-item"
:to="{ path: $routerHelper.getItemEditPath(collectionId, itemId)}">
{{ $i18n.get('edit') + ' ' + $i18n.get('item') }}
</router-link>
@ -67,14 +101,14 @@ export default {
},
created(){
// Obtains item and collection ID
this.collectionId = this.$route.params.collectionId;
this.collectionId = this.$route.params.collectionId;
this.itemId = this.$route.params.itemId;
// Puts loading on Item Loading
this.isLoading = true;
let loadingInstance = this;
// Obtains Item
// Obtains Item
this.fetchItem(this.itemId).then(() => {
loadingInstance.isLoading = false;
});
@ -82,5 +116,3 @@ export default {
}
</script>

View File

@ -3,22 +3,22 @@
@import "../../../node_modules/bulma/sass/utilities/functions";
// Tainacan custom colors
$primary: #2cb4c1;// #25a189;
$primary: #2cb4c1;
$primary-invert: findColorInvert($primary);
$secondary: #298596;
$secondary-invert: findColorInvert($secondary);
$tertiary: #1f2f56;
$tertiary: #01295c;
$tertiary-invert: findColorInvert($tertiary);
$primary-light:#A5CDD7;
$primary-lighter: lighten($primary-light, 15%);
$primary-lighter: #e6f6f8;
$primary-dark: #55A0AF;
$primary-darker: darken($primary-dark, 5%);
$success: #25a088;
$success-invert: findColorInvert($success);
$separator-color: #2a6e77;
$separator-color: #2b98a4;
$tainacan-input-color: #f0f0f0;
$draggable-border-color: #d8d8d8;
@ -53,24 +53,25 @@ $colors: map-merge($colors, $addColors);
$size-small: 0.85em; // 0.75em on Bulma.
// Tainacan Header and side menus
$header-height: 58px;
$subheader-height: 78px;
$side-menu-width: 222px;
$page-height: calc(100% - 58px);
$header-height: 53px;
$subheader-height: 82px;
$side-menu-width: 180px;
$filter-menu-width: 200px;
$page-height: calc(100% - 53px);
// Overall Pages padding:
$page-side-padding: 4em;
$page-top-padding: 1.5em;
$page-small-side-padding: 2em;
$page-small-top-padding: 1.5em;
$page-side-padding: 82px;
$page-top-padding: 27px;
$page-small-side-padding: 22px;
$page-small-top-padding: 22px;
$page-mobile-side-padding: 1em;
$page-mobile-top-padding: 0.5em;
// Links
$link: $primary;
$link-invert: $primary-invert;
$link-focus-border: $primary;
$link: $secondary;
$link-invert: $secondary-invert;
$link-focus-border: $secondary;
// Table
$table-head-cell-color: $gray-light;

View File

@ -31,15 +31,8 @@ html {
font-family: $family-sans-serif;
}
.media-modal {
left: 130px;
top: 130px;
bottom: 130px;
right: 130px;
z-index: 99999999999 !important;
}
.media-modal-backdrop {
z-index: 9999999 !important;
.modal-background {
background-color: rgba(0, 0, 0, 0.70);
}
/* Rules for sizing the icon. */
@ -60,7 +53,7 @@ html {
margin-top: $header-height;
height: $page-height !important;
}
.page-container{
.page-container {
padding: $page-top-padding $page-side-padding;
height: 100%;
overflow-y: auto;
@ -71,15 +64,66 @@ html {
}
}
.page-container-small{
padding: $page-small-top-padding $page-small-side-padding;
height: calc(100% - 78px);
height: calc(100% - 82px);
overflow-y: auto;
}
.page-container-narrow{
padding: $page-small-top-padding $page-small-side-padding;
@media screen and (max-width: 769px) {
padding: $page-mobile-top-padding $page-mobile-side-padding;
}
}
.tainacan-page-title, .tainacan-modal-title {
h1, h2 {
font-size: 20px;
font-weight: 500;
color: $tertiary;
display: inline-block;
}
a.back-link{
font-weight: 500;
float: right;
}
hr{
margin: 3px 0px 4px 0px;
height: 1px;
background-color: $secondary;
}
margin-bottom: 60px;
}
.tainacan-modal-content {
background-color: white;
padding: 52px 80px;
.form-submit {
padding: 80px 0em 0.4em 0em !important;
}
}
// Custom ScrollBar (will work only in Chrome, Opera, Safari, webkit browsers)
/* width */
::-webkit-scrollbar {
width: 10px;
}
/* Track */
::-webkit-scrollbar-track {
background: #f1f1f1;
opacity: 0.5;
}
/* Handle */
::-webkit-scrollbar-thumb {
background: #888;
opacity: 0.5;
}
/* Handle on hover */
::-webkit-scrollbar-thumb:hover {
background: #555;
}
// Buefy notices (toast, snackbar...)
.notices {
z-index: 99999999999999 !important;
@ -99,6 +143,9 @@ html {
.button.is-outlined {
color: $tertiary;
}
&.has-only-save {
justify-content: end !important;
}
}
.label {
font-weight: normal;
@ -219,6 +266,9 @@ html {
&:hover {
background-color: $primary-lighter;
}
.is-small {
color: gray;
}
}
}
}
@ -327,4 +377,143 @@ html {
.pagination {
flex-grow: 1;
}
}
}
// WordPress Media Modal customization
.wp-core-ui {
a:focus{ box-shadow: none; }
}
.media-modal-backdrop {
z-index: 9999999 !important;
}
.media-modal {
left: 100px;
top: 100px;
bottom: 100px;
right: 100px;
background-color: white;
z-index: 99999999999 !important;
}
// .media-modal {
// left: 100px;
// top: 100px;
// bottom: 100px;
// right: 100px;
// background-color: white;
// z-index: 99999999999 !important;
// .media-modal-content {
// background-color: white;
// margin: 42px 60px;
// box-shadow: none;
// }
// .media-frame-title {
// border-bottom: 1px solid #298596 !important;
// margin-bottom: 60px !important;
// box-shadow: none !important;
// h1, h2 {
// font-size: 20px;
// font-weight: 500;
// color: $tertiary;
// display: inline-block;
// margin-bottom: 3px;
// }
// }
// .media-frame-router {
// margin-top: 28px;
// a.media-menu-item.active {
// border-top: none;
// border-left: none;
// border-right: none;
// border-bottom: 3px solid $primary;
// }
// }
// .media-frame-content {
// margin-top: 30px;
// .attachments-browser .attachments, .media-toolbar {
// padding: 12px;
// right: 270px;
// .media-toolbar-primary {
// max-width: 30%;
// }
// .media-toolbar-secondary {
// max-width: 70%;
// }
// .attachment {
// padding: 12px;
// }
// .attachment.details {
// box-shadow: inset 0 0 0 3px #fff, inset 0 0 0 14px $primary-light;
// }
// .attachment.details .check, .wp-core-ui .attachment.selected .check:focus, .wp-core-ui .media-frame.mode-grid .attachment.selected .check {
// background-color: $primary-light;
// box-shadow: 0 0 0 1px #fff,0 0 0 2px $primary-light;
// }
// }
// .media-sidebar {
// background-color: white;
// width: 238px;
// }
// .crop-content{
// .imgareaselect-outer {
// background-color: white;
// opacity: 0.6;
// }
// }
// }
// .media-frame-toolbar .media-toolbar {
// bottom: initial;
// margin-top: -1px;
// padding: 28px 0px;
// }
// .media-button.button-primary {
// background-color: $success;
// border: none;
// box-shadow: none;
// color: #fff;
// text-decoration: none;
// text-shadow: none;
// }
// .media-button.button-primary[disabled] {
// color: #66c6e4 !important;
// background: $success !important;
// border-color: none!important;
// box-shadow: none !important;
// text-shadow: none !important;
// cursor: default;
// }
// .media-button.button-secondary {
// color: $tertiary;
// border-color: #999;
// background: white;
// box-shadow: none;
// vertical-align: middle;
// }
// .button-hero {
// background-color: $secondary;
// border: none;
// box-shadow: none;
// color: #fff;
// text-decoration: none;
// text-shadow: none;
// font-size: 14px;
// height: 46px;
// line-height: 20px !important;
// padding: 0 36px;
// &:hover{
// background-color: #339daf;
// color: white;
// }
// }
// }

View File

@ -21,17 +21,23 @@ return [
// Actions
'edit' => __( 'Edit', 'tainacan' ),
'new' => __( 'New', 'tainacan' ),
'add' => __( 'Add', 'tainacan' ),
'import' => __( 'Import', 'tainacan' ),
'export' => __( 'Export', 'tainacan' ),
'cancel' => __( 'Cancel', 'tainacan' ),
'remove' => __( 'Remove', 'tainacan' ),
'save' => __( 'Save', 'tainacan' ),
'next' => __( 'Next', 'tainacan' ),
'return' => __( 'Return', 'tainacan' ),
'see' => __( 'See', 'tainacan' ),
'search' => __( 'Search', 'tainacan' ),
'advanced_search' => __( 'Advanced Search', 'tainacan' ),
'continue' => __( 'Continue', 'tainacan' ),
'approve_item' => __( 'Approve', 'tainacan' ),
'not_approve_item' => __( 'Not approve', 'tainacan' ),
'add_one_item' => __( 'Add one item', 'tainacan' ),
'add_items_bulk' => __( 'Add items in bulk', 'tainacan' ),
'add_items_external_source' => __( 'Add items from an external source', 'tainacan' ),
// Wordpress Status
'publish' => __( 'Publish', 'tainacan' ),
@ -43,38 +49,39 @@ return [
'private_visibility' => __( 'Visible only for editors', 'tainacan' ),
// Page Titles (used mainly on Router)
'title_collections_page' => __( 'Collections Page', 'tainacan' ),
'title_repository_collections_page' => __( 'Repository Collections Page', 'tainacan' ),
'title_items_page' => __( 'Items Page', 'tainacan' ),
'title_fields_page' => __( 'Fields Page', 'tainacan' ),
'title_filters_page' => __( 'Filters Page', 'tainacan' ),
'title_repository_fields_page' => __( 'Repository Fields Page', 'tainacan' ),
'title_repository_filters_page' => __( 'Repository Filters Page', 'tainacan' ),
'title_categories_page' => __( 'Categories Page', 'tainacan' ),
'title_terms_page' => __( 'Terms Page', 'tainacan' ),
'title_events_page' => __( 'Events Page', 'tainacan' ),
'title_repository_events_page' => __( 'Repository Events Page', 'tainacan' ),
'title_collection_page' => __( 'Collection Page', 'tainacan' ),
'title_item_page' => __( 'Item Page', 'tainacan' ),
'title_field_page' => __( 'Field Page', 'tainacan' ),
/* translators: alkdjklasdj laksjd klsadj */
'title_collection_page' => __( 'Collection Page', 'tainacan' ),
/* translators: alkdjklasdj laksjd klsadj */
'title_filter_page' => __( 'Filter Page', 'tainacan' ),
'title_category_page' => __( 'Category Page', 'tainacan' ),
'title_term_page' => __( 'Term Page', 'tainacan' ),
'title_event_page' => __( 'Event Page', 'tainacan' ),
'title_create_collection' => __( 'Collection Creation Page', 'tainacan' ),
'title_create_category_page' => __( 'Category Creation Page', 'tainacan' ),
'title_create_item' => __( 'Item Creation Page', 'tainacan' ),
'title_create_item_collection' => __( 'Create Item on Collection', 'tainacan' ),
'title_create_filter' => __( 'Filter Creation Page', 'tainacan' ),
'title_collection_edition' => __( 'Collection Edition Page', 'tainacan' ),
'title_item_edition' => __( 'Item Edition Page', 'tainacan' ),
'title_edit_collection' => __( 'Edit Collection', 'tainacan' ),
'title_edit_item' => __( 'Edit Item', 'tainacan' ),
'title_category_edition_page' => __( 'Category Edition Page', 'tainacan' ),
'title_filter_edition' => __( 'Filter Edition Page', 'tainacan' ),
'title_collection_fields_edition' => __( 'Collection Fields Edition Page', 'tainacan' ),
'title_field_edition' => __( 'Field Edition Page', 'tainacan' ),
'title_collection_fields_edition' => __( 'Edit Fields of', 'tainacan' ),
'title_collection_filters_edition' => __( 'Edit Filters of', 'tainacan' ),
// Labels (used mainly on Aria Labels and Inputs)
'label_clean' => __( 'Clear', 'tainacan' ),
'label_selected' => __( 'Selected', 'tainacan' ),
'label_relationship_new_search' => __( 'New Search', 'tainacan' ),
'label_relationship_items_found' => __( 'Items found', 'tainacan' ),
'label_clean' => __( 'Clear', 'tainacan' ),
'label_selected' => __( 'Selected', 'tainacan' ),
'label_relationship_new_search' => __( 'New Search', 'tainacan' ),
'label_relationship_items_found' => __( 'Items found', 'tainacan' ),
'label_menu' => __( 'Menu', 'tainacan' ),
'label_main_menu' => __( 'Main Menu', 'tainacan' ),
'label_collection_menu' => __( 'Collection Menu', 'tainacan' ),
@ -87,12 +94,16 @@ return [
'label_image' => __( 'Image', 'tainacan' ),
'label_thumbnail' => __( 'Thumbnail', 'tainacan' ),
'label_empty_thumbnail' => __( 'Empty Thumbnail', 'tainacan' ),
'label_moderators' => __( 'Moderators', 'tainacan' ),
'label_parent_collection' => __( 'Parent collection', 'tainacan' ),
'label_no_parent_collection' => __( 'No parent collection', 'tainacan' ),
'label_button_view' => __( 'Button View', 'tainacan' ),
'label_button_edit' => __( 'Button Edit', 'tainacan' ),
'label_button_delete' => __( 'Button Delete', 'tainacan' ),
'label_button_delete_header_image' => __( 'Button Delete Header Image', 'tainacan' ),
'label_button_edit_thumb' => __( 'Button Edit Thumbnail', 'tainacan' ),
'label_button_edit_header_image' => __( 'Button Edit Header Image', 'tainacan' ),
'label_choose_thumb' => __( 'Choose Thumbnail', 'tainacan' ),
'label_choose_thumb' => __( 'Choose Thumbnail', 'tainacan' ),
'label_button_delete_thumb' => __( 'Button Delete Thumbnail', 'tainacan' ),
'label_collections_per_page' => __( 'Collections per Page:', 'tainacan' ),
'label_categories_per_page' => __( 'Categories per Page:', 'tainacan' ),
@ -129,43 +140,65 @@ return [
'label_collection_fields' => __( 'Collection Fields', 'tainacan' ),
'label_collection_filters' => __( 'Collection Filters', 'tainacan' ),
'label_parent_term' => __( 'Parent Term', 'tainacan' ),
'label_new_term' => __( 'New Term', 'tainacan' ),
'label_new_term' => __( 'New Term', 'tainacan' ),
'label_new_child' => __( 'New Child', 'tainacan' ),
'label_category_terms' => __( 'Category Terms', 'tainacan' ),
'label_no_parent_term' => __( 'No parent term', 'tainacan' ),
'label_term_without_name' => __( 'Term without name', 'tainacan' ),
'label_inherited' => __( 'Inherited', 'tainacan' ),
'label_sorting' => __( 'Sorting', 'tainacan' ),
'label_inherited' => __( 'Inherited', 'tainacan' ),
'label_sorting' => __( 'Sorting', 'tainacan' ),
'label_who_when' => __( 'Who and when', 'tainacan' ),
'label_event_title' => __( 'Event', 'tainacan' ),
'label_header_image' => __( 'Header Image', 'tainacan' ),
'label_empty_header_image' => __( 'Empty Header Image', 'tainacan' ),
'label_enable_cover_page' => __( 'Enable Cover Page', 'tainacan' ),
'label_cover_page' => __( 'Cover Page', 'tainacan' ),
'label_default_displayed_fields' => __( 'Default Displayed Fields', 'tainacan' ),
'label_display' => __( 'Display on Listing', 'tainacan' ),
'label_display_default' => __( 'Display by default', 'tainacan' ),
'label_display_never' => __( 'Never displayed', 'tainacan' ),
'label_cover_page' => __( 'Cover Page', 'tainacan' ),
'label_default_displayed_fields' => __( 'Default Displayed Fields', 'tainacan' ),
'label_display' => __( 'Display on Listing', 'tainacan' ),
'label_display_default' => __( 'Display by default', 'tainacan' ),
'label_display_never' => __( 'Never displayed', 'tainacan' ),
'label_not_display' => __( 'Not display by default', 'tainacan' ),
'label_attach_to_item' => __( 'Attach to item', 'tainacan' ),
'label_document' => __( 'Document', 'tainacan' ),
'label_document_empty' => __( 'Empty document', 'tainacan' ),
'label_file' => __( 'File', 'tainacan' ),
'label_text' => __( 'Texto', 'tainacan' ),
'label_url' => __( 'URL', 'tainacan' ),
'label_select_file' => __( 'Select File', 'tainacan' ),
'label_expand_all' => __( 'Expand all', 'tainacan' ),
'label_collapse_all' => __( 'Collapse all', 'tainacan' ),
// Instructions. More complex sentences to guide user and placeholders
'instruction_dragndrop_fields_collection' => __( 'Drag and drop Fields here to Collection.', 'tainacan' ),
'instruction_dragndrop_filters_collection' => __( 'Drag and drop Fields to create Filters on Collection.', 'tainacan' ),
'instruction_dragndrop_fields_repository' => __( 'Drag and drop Fields here to Repository.', 'tainacan' ),
'instruction_dragndrop_filters_repository' => __( 'Drag and drop Fields to create Filters on Repository.', 'tainacan' ),
'instruction_delete_selected_collections' => __( 'Delete selected collections', 'tainacan' ),
'instruction_delete_selected_items' => __( 'Delete selected items', 'tainacan' ),
'instruction_delete_selected_categories' => __( 'Delete selected categories', 'tainacan' ),
'instruction_image_upload_box' => __( 'Drop an image here or click to upload.', 'tainacan' ),
'instruction_select_a_status' => __( 'Select a status:', 'tainacan' ),
'instruction_select_a_filter_type' => __( 'Select a filter type:', 'tainacan' ),
'instruction_select_a_parent_term' => __( 'Select a parent term:', 'tainacan' ),
'instruction_delete_selected_collections' => __( 'Delete selected collections', 'tainacan' ),
'instruction_delete_selected_items' => __( 'Delete selected items', 'tainacan' ),
'instruction_delete_selected_categories' => __( 'Delete selected categories', 'tainacan' ),
'instruction_image_upload_box' => __( 'Drop an image here or click to upload.', 'tainacan' ),
'instruction_select_a_status' => __( 'Select a status:', 'tainacan' ),
'instruction_select_a_filter_type' => __( 'Select a filter type:', 'tainacan' ),
'instruction_select_a_parent_term' => __( 'Select a parent term:', 'tainacan' ),
'instruction_cover_page' => __( 'Type to search a Page to choose.', 'tainacan'),
'instruction_moderators' => __( 'Type to search a User to add.', 'tainacan'),
'instruction_select_a_parent_collection' => __( 'Select a parent colection.', 'tainacan' ),
'instruction_select_collection_thumbnail' => __( 'Select a thumbnail image for collection', 'tainacan' ),
'instruction_select_item_thumbnail' => __( 'Select a thumbnail image for item', 'tainacan' ),
'instruction_select_collection_header_image' => __( 'Select a header image for collection', 'tainacan' ),
'instruction_select_term_header_image' => __( 'Select a header image for term', 'tainacan' ),
'instruction_select_files_to_attach_to_item' => __( 'Select files to attach to item', 'tainacan' ),
'instruction_select_document_file_for_item' => __( 'Select a document file for item', 'tainacan' ),
'instruction_insert_url' => __( 'Insert URL', 'tainacan' ),
'instruction_write_text' => __( 'Write Text', 'tainacan' ),
'instruction_search_repository' => __( 'Search on repository', 'tainacan' ),
// Info. Other feedback to user.
'info_name_is_required' => __( 'Name is required.', 'tainacan' ),
'info_no_collection_created' => __( 'No collection was created in this repository.', 'tainacan' ),
'info_no_category_created' => __( 'No category was created in this repository.', 'tainacan' ),
'info_no_item_created' => __( 'No item was created in this collection.', 'tainacan' ),
'info_no_page_found' => __( 'No page was found with this name.', 'tainacan' ),
'info_no_user_found' => __( 'No user was found with this name.', 'tainacan' ),
'info_no_item_found' => __( 'No item was found here with these filters.', 'tainacan' ),
'info_item_not_saved' => __( 'Warning: Item not saved.', 'tainacan' ),
'info_no_moderator_on_collection' => __( "This collection doesn't have any moderator yet.", 'tainacan' ),
'info_error_deleting_collection' => __( 'Error on deleting collection.', 'tainacan' ),
'info_error_deleting_category' => __( 'Error on deleting category', 'tainacan' ),
'info_collection_deleted' => __( 'Collection deleted.', 'tainacan' ),
@ -188,19 +221,19 @@ return [
'info_by' => __( 'By: ', 'tainacan' ),
'info_date' => __( 'Date: ', 'tainacan' ),
'info_not_saved' => __( 'Not saved ', 'tainacan' ),
'info_warning_fields_not_saved' => __('Are you sure? There are fields not saved, changes will be lost.', 'tainacan'),
'info_warning_filters_not_saved' => __('Are you sure? There are filters not saved, changes will be lost.', 'tainacan'),
'info_no_description_provided' => __('No description provided.', 'tainacan'),
'info_warning_category_not_saved' => __('Are you sure? The category is not saved, changes will be lost.', 'tainacan'),
'info_warning_terms_not_saved' => __('Are you sure? There are terms not saved, changes will be lost.', 'tainacan'),
'info_warning_orphan_terms' => __('Are you sure? This term is parent of other terms. These will be converted to root terms.', 'tainacan'),
'info_there_is_no_field' => __('There is no field here.', 'tainacan' ),
'info_warning_fields_not_saved' => __( 'Are you sure? There are fields not saved, changes will be lost.', 'tainacan' ),
'info_warning_filters_not_saved' => __( 'Are you sure? There are filters not saved, changes will be lost.', 'tainacan' ),
'info_no_description_provided' => __( 'No description provided.', 'tainacan' ),
'info_warning_category_not_saved' => __( 'Are you sure? The category is not saved, changes will be lost.', 'tainacan' ),
'info_warning_terms_not_saved' => __( 'Are you sure? There are terms not saved, changes will be lost.', 'tainacan' ),
'info_warning_orphan_terms' => __( 'Are you sure? This term is parent of other terms. These will be converted to root terms.', 'tainacan' ),
'info_no_events' => __( 'No events', 'tainacan' ),
'info_logs_before' => __( 'Before updating', 'tainacan' ),
'info_logs_after' => __( 'What was updated', 'tainacan' ),
'info_there_is_no_field' => __('There is no field here yet.', 'tainacan' ),
'info_there_is_no_filter' => __('There is no filter here yet.', 'tainacan' ),
'info_there_is_no_field' => __( 'There is no field here yet.', 'tainacan' ),
'info_there_is_no_filter' => __( 'There is no filter here yet.', 'tainacan' ),
'info_changes' => __( 'Changes', 'tainacan' ),
'info_possible_external_sources' => __( 'Possible external sources: CSV, Instagram, Youtube, etc.', 'tainacan' ),
// Tainacan Field Types
'tainacan-text' => __( 'Text', 'tainacan' ),

View File

@ -148,9 +148,28 @@ class REST_Collections_Controller extends REST_Controller {
$item_arr = $item->__toArray();
if ( $request['context'] === 'edit' ) {
$moderators_ids = $item_arr['moderators_ids'];
$moderators = [];
foreach ($moderators_ids as $id){
$user_data = get_userdata($id);
if($user_data){
$user['name'] = $user_data->display_name;
//$user['roles'] = $user_data->roles;
$user['id'] = $user_data->ID;
$moderators[] = $user;
}
}
$item_arr['moderators'] = $moderators;
$item_arr['current_user_can_edit'] = $item->can_edit();
}
#unset($item_arr['moderators_ids']);
} else {
$attributes_to_filter = $request['fetch_only'];

View File

@ -128,6 +128,14 @@ class REST_Items_Controller extends REST_Controller {
$item_arr['current_user_can_edit'] = $item->can_edit();
}
$img_size = 'large';
if($request['doc_img_size']){
$img_size = $request['doc_img_size'];
}
$item_arr['document_as_html'] = $item->get_document_html($img_size);
return $this->add_metadata_to_item( $item, $item_arr );
}

View File

@ -62,18 +62,18 @@ class Embed {
//$viewer_url = $TAINACAN_BASE_URL . '/assets/pdfjs-dist/web/viewer.html?file=' . $url;
$defaults = array(
'width' => 800,
'height' => 1000
'width' => '100%',
'height' => '640px'
);
$args = array_merge($defaults, $attr);
$args = array_merge($attr, $defaults);
$dimensions = '';
if ( ! empty( $args['width'] ) && ! empty( $args['height'] ) ) {
$dimensions .= sprintf( "width='%d' ", (int) $args['width'] );
$dimensions .= sprintf( "height='%d' ", (int) $args['height'] );
$dimensions .= sprintf( "width='%s' ", $args['width'] );
$dimensions .= sprintf( "height='%s' ", $args['height'] );
}
$pdf = "<iframe id='iframePDF' name='iframePDF' src='$viewer_url' $dimensions allowfullscreen webkitallowfullscreen></iframe>";
return $pdf;
}

View File

@ -31,6 +31,7 @@ class Collection extends Entity {
$enable_cover_page,
$cover_page_id,
$header_image_id,
$header_image,
$moderators_ids;
/**
@ -68,7 +69,7 @@ class Collection extends Entity {
$array_collection = parent::__toArray();
$array_collection['featured_image'] = $this->get_featured_image();
$array_collection['featured_img_id'] = $this->get_featured_img_id();
$array_collection['header_image'] = $this->get_header_image();
$array_collection['author_name'] = $this->get_author_name();
return $array_collection;
@ -207,6 +208,13 @@ class Collection extends Entity {
return get_the_post_thumbnail_url( $this->get_id(), 'full' );
}
/**
* @return false|string
*/
function get_header_image(){
return wp_get_attachment_url( $this->get_header_image_id() );
}
/**
* @param $id
*/
@ -593,7 +601,11 @@ class Collection extends Entity {
*
* @return void
*/
function set_moderators_ids( $value ) {
function set_moderators_ids( array $value ) {
// make sure you never have duplicated moderators
$value = array_unique($value);
$this->set_mapped_property( 'moderators_ids', $value );
}

View File

@ -150,13 +150,33 @@ class Entity {
/**
* set the value of a mapped property
*
* This is a protected method. If you want to set an entity prop
* using the prop name dynamically, use the set() method
*
* @param string $prop id of the property
* @param mixed $value the value to be setted
*/
public function set_mapped_property($prop, $value) {
protected function set_mapped_property($prop, $value) {
$this->set_validated(false);
$this->$prop = $value;
}
/**
* set the value property
*
*
* @param string $prop id of the property
* @param mixed $value the value to be setted
* @return null|mixed Null on failure, the value that was set on success
*/
public function set($prop, $value) {
$method = 'set_' . $prop;
if ( method_exists($this, $method) ) {
return $this->$method($value);
}
return null;
}
/**
* set the status of the entity

View File

@ -16,6 +16,7 @@ class Field extends Entity {
$description,
$required,
$multiple,
$display,
$cardinality,
$collection_key,
$mask,
@ -40,6 +41,21 @@ class Field extends Entity {
return 'Hello, my name is '. $this->get_name();
}
/**
* @param $display
*/
function set_display( $display ){
$this->set_mapped_property('display', $display);
}
/**
* @return mixed
*/
function get_display(){
return $this->get_mapped_property('display');
}
/**
* Return the field name
*
@ -315,7 +331,7 @@ class Field extends Entity {
* @param bool $value
*/
function set_accept_suggestion( $value ) {
return $this->set_mapped_property('accept_suggestion', $value);
$this->set_mapped_property('accept_suggestion', $value);
}
/**

View File

@ -64,6 +64,7 @@ class Term extends Entity {
unset($term_array['status']);
$term_array['id'] = $term_id;
$term_array['header_image'] = $this->get_header_image();
return $term_array;
}
@ -136,6 +137,13 @@ class Term extends Entity {
return $this->get_mapped_property( 'header_image_id' );
}
/**
* @return false|string
*/
function get_header_image(){
return wp_get_attachment_url( $this->get_header_image_id() );
}
// Setters
/**
@ -218,9 +226,10 @@ class Term extends Entity {
* unless a unique slug has been explicitly provided.
*/
$name_matches = get_terms( $taxonomy, array(
'name' => $name,
'name' => $name,
'hide_empty' => false,
'parent' => $parent,
'parent' => $parent,
'exclude' => $this->get_id()
) );
/*
@ -239,7 +248,7 @@ class Term extends Entity {
if ($name_match) {
$this->add_error( 'repeated', __('You can not have two terms with the same name at the same level', 'tainacan') );
return false;
return false;
}
$this->set_as_valid();

View File

@ -1,52 +1,64 @@
<template>
<b-field
:addons="false"
:label="field.field.name"
:message="getErrorMessage"
:type="fieldTypeMessage">
<span
v-if="field.field.required == 'yes'"
class="required-field-asterisk"
:class="fieldTypeMessage">*</span>
<help-button
:title="field.field.name"
:message="field.field.description"/>
<div v-if="isTextInputComponent( field.field.field_type_object.component )">
<component
:id="field.field.field_type_object.component + '-' + field.field.slug"
:is="field.field.field_type_object.component"
v-model="inputs[0]"
:field="field"
@blur="changeValue()"/>
<div v-if="field.field.multiple == 'yes'">
<div
v-if="index > 0"
v-for="(input, index) in inputsList "
:key="index"
class="multiple-inputs">
<component
:id="field.field.field_type_object.component + '-' + field.field.slug"
:is="field.field.field_type_object.component"
v-model="inputs[index]"
:field="field"
@blur="changeValue()"/><a
class="button"
<b-collapse >
<span @click="isCollapsed = !isCollapsed">
<b-icon
type="is-secondary"
:icon="!isCollapsed ? 'menu-down' : 'menu-right'" />
<label class="label">{{ field.field.name }}</label>
<span
v-if="field.field.required == 'yes'"
class="required-field-asterisk"
:class="fieldTypeMessage">*</span>
<span class="field-type">({{ $i18n.get(field.field.field_type_object.component) }})</span>
</span>
<help-button
:title="field.field.name"
:message="field.field.description"/>
<div
v-show="!isCollapsed"
v-if="isTextInputComponent( field.field.field_type_object.component )">
<component
:id="field.field.field_type_object.component + '-' + field.field.slug"
:is="field.field.field_type_object.component"
v-model="inputs[0]"
:field="field"
@blur="changeValue()"/>
<div v-if="field.field.multiple == 'yes'">
<div
v-if="index > 0"
@click="removeInput(index)">-</a>
v-for="(input, index) in inputsList "
:key="index"
class="multiple-inputs">
<component
:id="field.field.field_type_object.component + '-' + field.field.slug"
:is="field.field.field_type_object.component"
v-model="inputs[index]"
:field="field"
@blur="changeValue()"/><a
class="button"
v-if="index > 0"
@click="removeInput(index)">-</a>
</div>
<a
class="button"
@click="addInput">+</a>
</div>
<a
class="button"
@click="addInput">+</a>
</div>
</div>
<div v-else>
<component
:id="field.field.field_type_object.component + '-' + field.field.slug"
:is="field.field.field_type_object.component"
v-model="inputs"
:field="field"
@blur="changeValue()"/>
</div>
<div
v-show="!isCollapsed"
v-else>
<component
:id="field.field.field_type_object.component + '-' + field.field.slug"
:is="field.field.field_type_object.component"
v-model="inputs"
:field="field"
@blur="changeValue()"/>
</div>
</b-collapse>
</b-field>
</template>
@ -58,7 +70,8 @@
props: {
field: {
type: Object
}
},
isCollapsed: Boolean
},
data(){
return {
@ -125,8 +138,36 @@
}
</script>
<style scoped>
<style lang="scss" scoped>
@import '../../admin/scss/_variables.scss';
.multiple-inputs {
display: flex;
}
.field {
border-bottom: 1px solid $draggable-border-color;
padding: 10px 25px;
.label {
font-size: 14px;
font-weight: 500;
margin-left: 18px;
margin-bottom: 0.5em;
}
.field-type {
font-size: 13px;
font-weight: 400;
color: $gray;
top: -0.2em;
position: relative;
}
.help-wrapper {
top: -0.2em;
}
.collapse .collapse-content>div {
margin-left: 45px;
}
}
</style>

View File

@ -5,6 +5,7 @@
size="is-small"
v-model="date_init"
@input="validate_values()"
:readonly="false"
icon="calendar-today"/>
<br>
<b-datepicker
@ -12,6 +13,7 @@
v-model="date_end"
@input="validate_values()"
@focus="isTouched = true"
:readonly="false"
icon="calendar-today"/>
<br>
</div>

View File

@ -1,16 +1,24 @@
<template>
<b-field
:label="filter.name"
<b-field
:message="getErrorMessage"
:type="filterTypeMessage">
<div>
<component
:id="filter.filter_type_object.component + '-' + filter.slug"
:is="filter.filter_type_object.component"
:filter="filter"
:query="query"
@input="listen( $event )"/>
</div>
<b-collapse :open="opened">
<label slot="trigger">
<b-icon
icon="menu-down"
size="is-small" />
{{ filter.name }}
</label>
<div>
<component
:id="filter.filter_type_object.component + '-' + filter.slug"
:is="filter.filter_type_object.component"
:filter="filter"
:query="query"
@input="listen( $event )"/>
</div>
</b-collapse>
</b-field>
</template>
@ -21,7 +29,8 @@
name: 'TainacanFilterItem',
props: {
filter: Object,
query: Object
query: Object,
opened: false,
},
data(){
return {

View File

@ -181,7 +181,8 @@ class Collections extends Repository {
'moderators_ids' => [
'map' => 'meta_multi',
'title' => __( 'Moderators', 'tainacan' ),
'type' => 'array',
'type' => 'array/object/string',
'items' => [ 'type' => 'array/string/integer/object' ],
'description' => __( 'The IDs of users assigned as moderators of this collection', 'tainacan' ),
'validation' => ''
],
@ -320,7 +321,7 @@ class Collections extends Repository {
} elseif ( is_array( $args ) ) {
$args = array_merge( [
'posts_per_page' => - 1,
'posts_per_page' => -1,
], $args );
$args = $this->parse_fetch_args( $args );

View File

@ -41,138 +41,148 @@ class Fields extends Repository {
public function get_map() {
return apply_filters('tainacan-get-map-'.$this->get_name(), [
'name' => [
'map' => 'post_title',
'title' => __('Name', 'tainacan'),
'type' => 'string',
'description'=> __('Name of the field', 'tainacan'),
'on_error' => __('The name should be a text value and not empty', 'tainacan'),
'validation' => v::stringType()->notEmpty(),
],
'slug' => [
'map' => 'post_name',
'title' => __('Slug', 'tainacan'),
'type' => 'string',
'description'=> __('A unique and santized string representation of the field', 'tainacan'),
//'validation' => v::stringType(),
],
'order' => [
'map' => 'menu_order',
'title' => __('Order', 'tainacan'),
'type' => 'string/integer',
'description'=> __('Field order. Field used if collections are manually ordered', 'tainacan'),
'on_error' => __('The menu order should be a numeric value', 'tainacan'),
//'validation' => v::numeric(),
],
'parent' => [
'map' => 'post_parent',
'title' => __('Parent', 'tainacan'),
'type' => 'integer',
'description'=> __('Parent field', 'tainacan'),
'default' => 0
//'on_error' => __('The Parent should be numeric value', 'tainacan'),
//'validation' => v::numeric(),
],
'description' => [
'map' => 'post_content',
'title' => __('Description', 'tainacan'),
'type' => 'string',
'description'=> __('The field description', 'tainacan'),
'default' => '',
//'on_error' => __('The description should be a text value', 'tainacan'),
//'validation' => v::stringType()->notEmpty(),
],
'field_type' => [
'map' => 'meta',
'title' => __('Type', 'tainacan'),
'type' => 'string',
'description'=> __('The field type', 'tainacan'),
'on_error' => __('Field type is empty', 'tainacan'),
'validation' => v::stringType()->notEmpty(),
],
'required' => [
'map' => 'meta',
'title' => __('Required', 'tainacan'),
'type' => 'string',
'description'=> __('The field is required', 'tainacan'),
'on_error' => __('Field required field is invalid', 'tainacan'),
'validation' => v::stringType()->in(['yes', 'no']), // yes or no
'default' => 'no'
],
'collection_key' => [
'map' => 'meta',
'title' => __('Collection key', 'tainacan'),
'type' => 'string',
'description'=> __('Field value should not be repeated', 'tainacan'),
'on_error' => __('Collection key is invalid', 'tainacan'),
'validation' => v::stringType()->in(['yes', 'no']), // yes or no
'default' => 'no'
],
'multiple' => [
'map' => 'meta',
'title' => __('Multiple', 'tainacan'),
'type' => 'string',
'description'=> __('Allow multiple fields for the field', 'tainacan'),
'on_error' => __('Multiple fields is invalid', 'tainacan'),
'validation' => v::stringType()->in(['yes', 'no']), // yes or no. It cant be multiple if its collection_key
'default' => 'no'
],
'cardinality' => [
'map' => 'meta',
'title' => __('Cardinality', 'tainacan'),
'type' => 'string/number',
'description'=> __('Number of multiples possible fields', 'tainacan'),
'on_error' => __('The number of fields not allowed', 'tainacan'),
'validation' => v::numeric()->positive(),
'default' => 1
],
'mask' => [
'map' => 'meta',
'title' => __('Mask', 'tainacan'),
'type' => 'string',
'description'=> __('The mask to be used in the field', 'tainacan'),
//'on_error' => __('Mask is invalid', 'tainacan'),
//'validation' => ''
],
'default_value' => [
'map' => 'meta',
'title' => __('Default value', 'tainacan'),
'type' => 'string',
'description'=> __('The value default fot the field', 'tainacan'),
],
'field_type_options' => [ // not showed in form
'map' => 'meta',
'title' => __('Field Type options', 'tainacan'),
'type' => 'array/object/string',
'items' => ['type' => 'array/string/integer/object'],
'description'=> __('Options specific for field type', 'tainacan'),
// 'validation' => ''
],
'collection_id' => [ // not showed in form
'map' => 'meta',
'title' => __('Collection', 'tainacan'),
'type' => 'integer/string',
'description'=> __('The collection ID', 'tainacan'),
//'validation' => ''
],
'accept_suggestion' => [
'map' => 'meta',
'title' => __('Field Value Accepts Suggestions', 'tainacan'),
'type' => 'bool',
'description'=> __('Allow the community suggest a different values for that field', 'tainacan'),
'default' => false,
'validation' => v::boolType()
],
'exposer_mapping' => [
'map' => 'meta',
'title' => __('exposer_mapping', 'tainacan'),
'type' => 'array',
'description'=> __('The field mapping options', 'tainacan'),
'on_error' => __('Invalid Field Mapping', 'tainacan'),
//'validation' => v::arrayType(),
'default' => []
],
]);
'name' => [
'map' => 'post_title',
'title' => __( 'Name', 'tainacan' ),
'type' => 'string',
'description' => __( 'Name of the field', 'tainacan' ),
'on_error' => __( 'The name should be a text value and not empty', 'tainacan' ),
'validation' => v::stringType()->notEmpty(),
],
'slug' => [
'map' => 'post_name',
'title' => __( 'Slug', 'tainacan' ),
'type' => 'string',
'description' => __( 'A unique and santized string representation of the field', 'tainacan' ),
//'validation' => v::stringType(),
],
'order' => [
'map' => 'menu_order',
'title' => __( 'Order', 'tainacan' ),
'type' => 'string/integer',
'description' => __( 'Field order. Field used if collections are manually ordered', 'tainacan' ),
'on_error' => __( 'The menu order should be a numeric value', 'tainacan' ),
//'validation' => v::numeric(),
],
'parent' => [
'map' => 'post_parent',
'title' => __( 'Parent', 'tainacan' ),
'type' => 'integer',
'description' => __( 'Parent field', 'tainacan' ),
'default' => 0
//'on_error' => __('The Parent should be numeric value', 'tainacan'),
//'validation' => v::numeric(),
],
'description' => [
'map' => 'post_content',
'title' => __( 'Description', 'tainacan' ),
'type' => 'string',
'description' => __( 'The field description', 'tainacan' ),
'default' => '',
//'on_error' => __('The description should be a text value', 'tainacan'),
//'validation' => v::stringType()->notEmpty(),
],
'field_type' => [
'map' => 'meta',
'title' => __( 'Type', 'tainacan' ),
'type' => 'string',
'description' => __( 'The field type', 'tainacan' ),
'on_error' => __( 'Field type is empty', 'tainacan' ),
'validation' => v::stringType()->notEmpty(),
],
'required' => [
'map' => 'meta',
'title' => __( 'Required', 'tainacan' ),
'type' => 'string',
'description' => __( 'The field is required', 'tainacan' ),
'on_error' => __( 'Field required field is invalid', 'tainacan' ),
'validation' => v::stringType()->in( [ 'yes', 'no' ] ), // yes or no
'default' => 'no'
],
'collection_key' => [
'map' => 'meta',
'title' => __( 'Collection key', 'tainacan' ),
'type' => 'string',
'description' => __( 'Field value should not be repeated', 'tainacan' ),
'on_error' => __( 'Collection key is invalid', 'tainacan' ),
'validation' => v::stringType()->in( [ 'yes', 'no' ] ), // yes or no
'default' => 'no'
],
'multiple' => [
'map' => 'meta',
'title' => __( 'Multiple', 'tainacan' ),
'type' => 'string',
'description' => __( 'Allow multiple fields for the field', 'tainacan' ),
'on_error' => __( 'Multiple fields is invalid', 'tainacan' ),
'validation' => v::stringType()->in( [ 'yes', 'no' ] ),
// yes or no. It cant be multiple if its collection_key
'default' => 'no'
],
'cardinality' => [
'map' => 'meta',
'title' => __( 'Cardinality', 'tainacan' ),
'type' => 'string/number',
'description' => __( 'Number of multiples possible fields', 'tainacan' ),
'on_error' => __( 'The number of fields not allowed', 'tainacan' ),
'validation' => v::numeric()->positive(),
'default' => 1
],
'mask' => [
'map' => 'meta',
'title' => __( 'Mask', 'tainacan' ),
'type' => 'string',
'description' => __( 'The mask to be used in the field', 'tainacan' ),
//'on_error' => __('Mask is invalid', 'tainacan'),
//'validation' => ''
],
'default_value' => [
'map' => 'meta',
'title' => __( 'Default value', 'tainacan' ),
'type' => 'string',
'description' => __( 'The value default fot the field', 'tainacan' ),
],
'field_type_options' => [ // not showed in form
'map' => 'meta',
'title' => __( 'Field Type options', 'tainacan' ),
'type' => 'array/object/string',
'items' => [ 'type' => 'array/string/integer/object' ],
'description' => __( 'Options specific for field type', 'tainacan' ),
// 'validation' => ''
],
'collection_id' => [ // not showed in form
'map' => 'meta',
'title' => __( 'Collection', 'tainacan' ),
'type' => 'integer/string',
'description' => __( 'The collection ID', 'tainacan' ),
//'validation' => ''
],
'accept_suggestion' => [
'map' => 'meta',
'title' => __( 'Field Value Accepts Suggestions', 'tainacan' ),
'type' => 'bool',
'description' => __( 'Allow the community suggest a different values for that field', 'tainacan' ),
'default' => false,
'validation' => v::boolType()
],
'exposer_mapping' => [
'map' => 'meta',
'title' => __( 'exposer_mapping', 'tainacan' ),
'type' => 'array/object/string',
'items' => [ 'type' => 'array/string/integer/object' ],
'description' => __( 'The field mapping options', 'tainacan' ),
'on_error' => __( 'Invalid Field Mapping', 'tainacan' ),
//'validation' => v::arrayType(),
'default' => []
],
'display' => [
'map' => 'meta',
'title' => __( 'Display', 'tainacan' ),
'type' => __( 'string' ),
'validation' => v::stringType()->in( [ 'yes', 'no', 'never' ] ),
'description' => __( 'Display by default on listing or not display or never display.', 'tainacan' ),
'default' => 'yes'
]
] );
}
/**

View File

@ -95,8 +95,8 @@ class Items extends Repository {
'type' => 'string',
'description' => __( 'The document type, can be a local attachment, an external URL or a text', 'tainacan' ),
'on_error' => __( 'Invalid document type', 'tainacan' ),
'validation' => v::stringType()->in( [ 'attachment', 'url', 'text' ] ),
'default' => 'attachment'
'validation' => v::stringType()->in( [ 'attachment', 'url', 'text', 'empty'] ),
'default' => 'empty'
],
'document' => [
'map' => 'meta',

View File

@ -117,7 +117,6 @@ abstract class Repository {
}
$post_t = $collection->get_db_identifier();
$obj->WP_Post->post_type = $post_t;
}
@ -134,50 +133,9 @@ abstract class Repository {
}
}
if ( method_exists( $obj, 'get_featured_img_id' ) ) {
if ( ! get_post_thumbnail_id( $obj->WP_Post->ID ) ) {
// was added a thumbnail
$diffs = $this->insert_thumbnail( $obj, $diffs );
$settled = set_post_thumbnail( $obj->WP_Post, (int) $obj->get_featured_img_id() );
if ( $settled ) {
$thumbnail_url = get_the_post_thumbnail_url( $obj->WP_Post->ID );
$diffs['featured_image'] = [
'new' => $thumbnail_url,
'old' => '',
'diff_with_index' => 0,
];
}
} else {
// was update a thumbnail
$old_thumbnail = get_the_post_thumbnail_url( $obj->WP_Post->ID );
$fid = $obj->get_featured_img_id();
if(!$fid){
$settled = delete_post_thumbnail($obj->WP_Post);
} else {
$settled = set_post_thumbnail( $obj->WP_Post, (int) $fid );
}
if ( $settled ) {
$thumbnail_url = get_the_post_thumbnail_url( $obj->WP_Post->ID );
$diffs['featured_image'] = [
'new' => $thumbnail_url,
'old' => $old_thumbnail,
'diff_with_index' => 0,
];
}
}
}
// TODO: Logs for header image insert and update
do_action( 'tainacan-insert', $obj, $diffs, $is_update );
do_action( 'tainacan-insert-' . $obj->get_post_type(), $obj );
@ -205,14 +163,25 @@ abstract class Repository {
update_post_meta( $obj->get_id(), $prop, $this->maybe_add_slashes( $obj->get_mapped_property( $prop ) ) );
} elseif ( $map[ $prop ]['map'] == 'meta_multi' ) {
$values = $obj->get_mapped_property( $prop );
delete_post_meta( $obj->get_id(), $prop );
if ( is_array( $values ) ) {
foreach ( $values as $value ) {
add_post_meta( $obj->get_id(), $prop, $this->maybe_add_slashes( $value ) );
}
$current_values = get_post_meta( $obj->get_id(), $prop );
if (empty($values) || !is_array($values))
$values = [];
if (empty($current_values) || !is_array($current_values))
$current_values = [];
$deleted = array_diff( $current_values, $values );
$added = array_diff( $values, $current_values );
foreach ($deleted as $del) {
delete_post_meta( $obj->get_id(), $prop, $del );
}
foreach ($added as $add) {
add_post_meta( $obj->get_id(), $prop, $this->maybe_add_slashes( $add ) );
}
}
}
@ -741,12 +710,66 @@ abstract class Repository {
}
}
unset($diff['id'], $diff['collection_id'], $diff['author_id'], $diff['creation_date']);
unset($diff['id'], $diff['collection_id'], $diff['author_id'], $diff['creation_date'], $diff['featured_img_id']);
$diff = apply_filters( 'tainacan-entity-diff', $diff, $new, $old );
return $diff;
}
/**
* @param $obj
* @param $diffs
*
* @return mixed
*/
protected function insert_thumbnail( $obj, $diffs ) {
if ( method_exists( $obj, 'get_featured_img_id' ) ) {
if ( ! get_post_thumbnail_id( $obj->WP_Post->ID ) ) {
// was added a thumbnail
$settled = set_post_thumbnail( $obj->WP_Post, (int) $obj->get_featured_img_id() );
if ( $settled ) {
$thumbnail_url = get_the_post_thumbnail_url( $obj->WP_Post->ID );
$diffs['featured_image'] = [
'new' => $thumbnail_url,
'old' => '',
'diff_with_index' => 0,
];
}
} else {
// was update a thumbnail
$old_thumbnail = get_the_post_thumbnail_url( $obj->WP_Post->ID );
$fid = $obj->get_featured_img_id();
if ( ! $fid ) {
$settled = delete_post_thumbnail( $obj->WP_Post );
} else {
$settled = set_post_thumbnail( $obj->WP_Post, (int) $fid );
}
if ( $settled ) {
$thumbnail_url = get_the_post_thumbnail_url( $obj->WP_Post->ID );
$diffs['featured_image'] = [
'new' => $thumbnail_url,
'old' => $old_thumbnail,
'diff_with_index' => 0,
];
}
}
}
return $diffs;
}
}
?>

View File

@ -157,7 +157,9 @@ class Terms extends Repository {
update_term_meta($term_saved['term_id'], $prop, wp_slash( $term->get_mapped_property($prop) ));
}
}
// TODO: Log header image updates
do_action('tainacan-insert', $term, $diffs, $is_update);
do_action('tainacan-insert-Term', $term);

View File

@ -27,8 +27,6 @@ class DevInterface {
add_action('save_post', array(&$this, 'save_post'), 10, 2);
add_action('admin_enqueue_scripts', array(&$this, 'add_admin_js'));
add_filter('post_type_link', array(&$this, 'permalink_filter'), 10, 3);
$Tainacan_Collections = \Tainacan\Repositories\Collections::get_instance();
$Tainacan_Filters = \Tainacan\Repositories\Filters::get_instance();
$Tainacan_Logs = \Tainacan\Repositories\Logs::get_instance();
@ -62,34 +60,6 @@ class DevInterface {
wp_localize_script( 'tainacan-dev-admin', 'tainacan_plugin', $settings );
}
/**
* Filters the permalink for posts to:
*
* * Replace Collectino single permalink with the link to the post type archive for items of that collection
*
* @return string new permalink
*/
function permalink_filter($permalink, $post, $leavename) {
$collection_post_type = \Tainacan\Entities\Collection::get_post_type();
if (!is_admin() && $post->post_type == $collection_post_type) {
$collection = new \Tainacan\Entities\Collection($post);
$items_post_type = $collection->get_db_identifier();
$post_type_object = get_post_type_object($items_post_type);
if (isset($post_type_object->rewrite) && is_array($post_type_object->rewrite) && isset($post_type_object->rewrite['slug']))
return site_url($post_type_object->rewrite['slug']);
}
return $permalink;
}
/**
* Run through all post types attributes and add metaboxes for them.

View File

@ -10,8 +10,8 @@ defined( 'ABSPATH' ) or die( 'No script kiddies please!' );
*/
class Exposers {
private $types = [];
private $mappers = [];
protected $types = [];
protected $mappers = [];
private static $instance = null;
const MAPPER_CLASS_PREFIX = 'Tainacan\Exposers\Mappers\\';
@ -118,7 +118,7 @@ class Exposers {
$ret = $item_arr;
$field_mapping = $item_arr['field']['exposer_mapping'];
if(array_key_exists($mapper->slug, $field_mapping)) {
if(is_array($mapper->options) && !array_key_exists( $field_mapping[$mapper->slug], $mapper->options) ) {
if(is_array($mapper->metadata) && !array_key_exists( $field_mapping[$mapper->slug], $mapper->metadata) ) {
throw new \Exception('Invalid Mapper Option');
}
$ret = [$mapper->prefix.$field_mapping[$mapper->slug].$mapper->sufix => $item_arr['value']]; //TODO Validate option
@ -220,13 +220,13 @@ class Exposers {
) {
if(
$type === false || // do not have a exposer type
$type->mappers === true || // the type accept all mappers
( is_array($type->mappers) && in_array($body['exposer-map'], $type->mappers) ) ) { // the current mapper is accepted by type
$type->get_mappers() === true || // the type accept all mappers
( is_array($type->mappers) && in_array($body['exposer-map'], $type->get_mappers()) ) ) { // the current mapper is accepted by type
$mapper = $Tainacan_Exposers->check_class_name($body['exposer-map'], true, self::MAPPER_CLASS_PREFIX);
return new $mapper;
}
} elseif( is_object($type) && is_array($type->mappers) && count($type->mappers) > 0 ) { //there are no defined mapper, let use the first one o list if has a list
$mapper = $Tainacan_Exposers->check_class_name($type->mappers[0], true, self::MAPPER_CLASS_PREFIX);
} elseif( is_object($type) && is_array($type->get_mappers()) && count($type->get_mappers()) > 0 ) { //there are no defined mapper, let use the first one o list if has a list
$mapper = $Tainacan_Exposers->check_class_name($type->get_mappers()[0], true, self::MAPPER_CLASS_PREFIX);
return new $mapper;
}
return false; // No mapper need, using Tainacan defautls

View File

@ -2,6 +2,11 @@
namespace Tainacan\Exposers\Mappers;
/**
* Support Dublin Core Mapping
* http://purl.org/dc/elements/1.1/
*
*/
class Dublin_Core extends Mapper {
public $slug = 'dublin-core';
public $name = 'Dublin Core';
@ -9,7 +14,7 @@ class Dublin_Core extends Mapper {
public $context_url = 'http://dublincore.org/documents/dcmi-terms/';
public $header = '<?xml version="1.0"?><!DOCTYPE rdf:RDF SYSTEM "http://dublincore.org/2000/12/01-dcmes-xml-dtd.dtd"><rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:dc="http://purl.org/dc/elements/1.1/" ></rdf:RDF>';
public $prefix = 'dc:';
public $options = [
public $metadata = [
'contributor' => [
'URI' => 'http://purl.org/dc/elements/1.1/contributor',
'label' => 'Contributor'

View File

@ -3,12 +3,12 @@
namespace Tainacan\Exposers\Mappers;
abstract class Mapper {
public $slug = null;
public $name = null;
public $allow_extra_fields = true;
public $context_url = null;
public $options = false;
public $prefix = '';
public $sufix = '';
public $header = false;
public $slug = null; // Slug of Mapper, used as option on api call
public $name = null; // Public name do mapper
public $allow_extra_fields = true; // Allow more field to be register
public $context_url = null; // URL of mapper documentation
public $metadata = false; // array of supported metadata, false for not validade the list
public $prefix = ''; // Tag prefix like "dc:"
public $sufix = ''; // Tag sufix
public $header = false; // API response header or file header to be used with
}

View File

@ -2,12 +2,16 @@
namespace Tainacan\Exposers\Mappers;
/**
* Mapper class for export fields in key => value format where key can be defined
*
*/
class Value extends Mapper {
public $slug = 'value';
public $name = 'Value';
public $allow_extra_fields = true;
public $context_url = '';
public $header = '';
public $options = [];
public $metadata = false;
}

View File

@ -2,8 +2,16 @@
namespace Tainacan\Exposers\Types;
/**
* Generate a Csv formated response
*
*/
class Csv extends Type {
/**
* List of supported mappers
* @var array
*/
public $mappers = ['Value'];
/**

View File

@ -2,6 +2,10 @@
namespace Tainacan\Exposers\Types;
/**
* Generate a Html formated response
*
*/
class Html extends Type {
public $mappers = ['Value'];

View File

@ -2,6 +2,10 @@
namespace Tainacan\Exposers\Types;
/**
* Generate a OAI_PMH/oai_dc formated response
*
*/
class OAI_PMH extends Xml {
public $mappers = ['Dublin Core'];

View File

@ -2,6 +2,10 @@
namespace Tainacan\Exposers\Types;
/**
* Generate a text formated response
*
*/
class Txt extends Type {
public $mappers = ['Value'];

View File

@ -2,15 +2,24 @@
namespace Tainacan\Exposers\Types;
/**
* abstract class for implement exposer types
*
*/
abstract class Type {
public $mappers = true; // List of supported mapper, leave true for all
protected $mappers = true; // List of supported mapper, leave true for all
/**
* Change response after api callbacks
* @param \WP_REST_Response $response
* @param \WP_REST_Server $handler
* @param \WP_REST_Request $request
* @return \WP_REST_Response
*/
public abstract function rest_request_after_callbacks( $response, $handler, $request );
public function get_mappers() {
return apply_filters('tainacan-exporser-type-mappers', $this->mappers, $this);
}
}

View File

@ -2,6 +2,10 @@
namespace Tainacan\Exposers\Types;
/**
* Generate a Csv formated response
*
*/
class Xml extends Type {
/**
*

View File

@ -78,6 +78,14 @@ abstract class Importer {
'url' => false,
];
private $is_repository = false;
private $steps = [];
private $current_step = 0;
private $url = '';
public function __construct() {
if (!session_id()) {
@session_start();
@ -94,6 +102,34 @@ abstract class Importer {
return $this->id;
}
/**
* Set URL
* @param $url string
* @return bool
*/
public function set_url($url)
{
if(!empty($url) && !is_array($url))
{
$this->url = rtrim(trim($url), "/");
return true;
}
return false;
}
/**
* @return string or bool
*/
public function get_url()
{
if(!empty($this->url))
{
return $this->url;
}
return false;
}
/**
* @return array Mapping
@ -294,7 +330,20 @@ abstract class Importer {
}
return false;
}
/**
* Sets importer as repository importer
*/
public function set_repository()
{
$this->is_repository = true;
}
public function set_steps($steps)
{
$this->steps =$steps;
}
/**
* Removes method accepeted by the importer
*
@ -424,25 +473,40 @@ abstract class Importer {
* run the process
*/
public function run(){
if ( ( !isset($this->collection) || ! $this->collection instanceof Entities\Collection ) && $this->import_structure_and_mapping ) {
$new_collection = new Entities\Collection();
$new_collection->set_name('New Imported Collection');
$new_collection->set_status('publish');
$new_collection->validate();
$new_collection = Tainacan\Repositories\Collections::get_instance()->insert($new_collection);
$this->set_collection($new_collection);
if (!method_exists($this, 'create_fields_and_mapping')) {
throw new Exception('Importers with import_structure_and_mapping true must implement create_fields_and_mapping method');
}
if($this->is_repository && $this->current_step < count($this->steps))
{
$process_name = key($this->steps);
$function_name = current($this->steps);
$continue = $this->{$function_name}();//If true still there is stuff to process
$this->create_fields_and_mapping();
}
$this->process( $this->start );
return sizeof($this->get_processed_items());
if(!$continue)
{
//Move on to the next step
next($this->steps);
$this->current_step++;
}
}
else
{
if ( ( !isset($this->collection) || ! $this->collection instanceof Entities\Collection ) && $this->import_structure_and_mapping ) {
$new_collection = new Entities\Collection();
$new_collection->set_name('New Imported Collection');
$new_collection->set_status('publish');
$new_collection->validate();
$new_collection = Tainacan\Repositories\Collections::get_instance()->insert($new_collection);
$this->set_collection($new_collection);
if (!method_exists($this, 'create_fields_and_mapping')) {
throw new Exception('Importers with import_structure_and_mapping true must implement create_fields_and_mapping method');
}
$this->create_fields_and_mapping();
}
$this->process( $this->start );
return sizeof($this->get_processed_items());
}
}
}

View File

@ -8,9 +8,9 @@
namespace Tainacan\Importer;
class Old_Tainacan extends Importer
{
public $avoid = [
'ID',
'post_author',
@ -32,16 +32,146 @@ class Old_Tainacan extends Importer
'filter',
'link',
'thumbnail'
];
],
$steps = [
'Creating all categories' => 'create_categories',
'Create empty collections' => 'create_collections',
'Create repository metadata' => 'create_repo_meta',
'Create collections metadata' => 'create_collection_metas',
'Create collections items' => 'create_collection_items',
'Setting relationships' => 'set_relationships'
], $tainacan_api_address, $wordpress_api_address;
public function __construct($import_structure_and_mapping = false)
public function __construct()
{
parent::__construct();
$this->set_repository();
$this->set_steps($this->steps);
$this->remove_import_method('file');
$this->add_import_method('url');
$this->tainacan_api_address = "/wp-json/tainacan/v1";
$this->wordpress_api_address = "/wp-json/wp/v2";
}
$this->import_structure_and_mapping = $import_structure_and_mapping;
public function verify_process_result($result)
{
if(is_wp_error($result))
{
$this->add_log('error', $result->get_error_message());
return false;
}else if(isset($result['body']))
{
return json_decode($result['body']);
}
return false;
}
public function create_categories()
{
$categories_link = $this->get_url() . $this->tainacan_api_address . "/categories";
$categories = wp_remote_get($categories_link);
$categories_array = $this->verify_process_result($categories);
if($categories_array)
{
$Tainacan_Taxonomies = \Tainacan\Repositories\Taxonomies::get_instance();
$categories_array = $this->remove_same_name($categories_array);
foreach ($categories_array as $category)
{
$taxonomy = new \Tainacan\Entities\Taxonomy();
$taxonomy->set_name($category->name);
$taxonomy->set_description($category->description);
$taxonomy->set_allow_insert(true);
$Tainacan_Taxonomies->insert($taxonomy);
$inserted_taxonomy = $Tainacan_Taxonomies->fetch($taxonomy->get_id());
if(isset($category->children) && $inserted_taxonomy)
{
$this->add_all_terms($inserted_taxonomy, $category->children);
}
}
}
return false;
}
private function add_all_terms($taxonomy_father, $children, $term_father = null)
{
$Tainacan_Terms = \Tainacan\Repositories\Terms::get_instance();
$children = $this->remove_same_name($children);
foreach ($children as $term)
{
$new_term = new \Tainacan\Entities\Term();
$new_term->set_taxonomy($taxonomy_father->get_db_identifier());
if($term_father)
{
$new_term->set_parent($term_father->get_id());
}
$new_term->set_name($term->name);
$new_term->set_description($term->description);
$inserted_term = $Tainacan_Terms->insert($new_term);
if(isset($term->children))
{
$this->add_all_terms($taxonomy_father, $term->children, $inserted_term);
}
}
}
public function remove_same_name($terms)
{
$unique = [];
$unique_terms = [];
foreach($terms as $term)
{
$unique[$term->name] = $term->term_id;
}
foreach($terms as $index => $term)
{
if(in_array($term->term_id, $unique))
{
array_push($unique_terms, $term);
}
}
return $unique_terms;
}
public function create_collections()
{
return false;
}
public function create_repo_meta()
{
return false;
}
public function create_collection_metas()
{
return false;
}
public function create_collection_items()
{
return false;
}
public function set_relationships()
{
return false;
}
public function fetch_from_remote( $url ){
@ -70,7 +200,6 @@ class Old_Tainacan extends Importer
}
}
if(!empty($link))
{
$info = wp_remote_get( $link."/items/?includeMetadata=1" );
@ -116,7 +245,6 @@ class Old_Tainacan extends Importer
$file_content = unserialize($file->fread($file->getSize()));
$item = $file_content->items[0];
$fields = [];
//Default meta

View File

@ -112,12 +112,13 @@ export const fetchCategoryName = ({ commit }, categoryId) => {
};
// CATEGORY TERMS
export const sendTerm = ({commit}, { categoryId, name, description, parent }) => {
export const sendTerm = ({commit}, { categoryId, name, description, parent, headerImageId }) => {
return new Promise(( resolve, reject ) => {
axios.tainacan.post('/taxonomy/' + categoryId + '/terms/', {
name: name,
description: description,
parent: parent
parent: parent,
header_image_id: headerImageId,
})
.then( res => {
let term = res.data;
@ -144,12 +145,13 @@ export const deleteTerm = ({ commit }, { categoryId, termId }) => {
});
};
export const updateTerm = ({ commit }, { categoryId, termId, name, description, parent }) => {
export const updateTerm = ({ commit }, { categoryId, termId, name, description, parent, headerImageId }) => {
return new Promise(( resolve, reject ) => {
axios.tainacan.patch('/taxonomy/' + categoryId + '/terms/' + termId, {
axios.tainacan.patch(`/taxonomy/${categoryId}/terms/${termId}`, {
name: name,
description: description,
parent: parent
parent: parent,
header_image_id: headerImageId,
})
.then( res => {
let term = res.data;

View File

@ -62,7 +62,7 @@ export const fetchCollections = ({commit} , { page, collectionsPerPage }) => {
export const fetchCollection = ({ commit }, id) => {
commit('cleanCollection');
return new Promise((resolve, reject) =>{
axios.tainacan.get('/collections/' + id)
axios.tainacan.get('/collections/' + id + '?context=edit')
.then(res => {
let collection = res.data;
commit('setCollection', collection);
@ -103,7 +103,7 @@ export const deleteCollection = ({ commit }, id) => {
});
}
export const updateCollection = ({ commit }, { collection_id, name, description, slug, status, enable_cover_page, cover_page_id }) => {
export const updateCollection = ({ commit }, { collection_id, name, description, slug, status, enable_cover_page, cover_page_id, moderators_ids, parent }) => {
return new Promise((resolve, reject) => {
axios.tainacan.patch('/collections/' + collection_id, {
name: name,
@ -111,7 +111,9 @@ export const updateCollection = ({ commit }, { collection_id, name, description,
status: status,
slug: slug,
cover_page_id: "" + cover_page_id,
enable_cover_page: enable_cover_page
enable_cover_page: enable_cover_page,
moderators_ids: moderators_ids,
parent: parent
}).then( res => {
commit('setCollection', {
id: collection_id,
@ -120,7 +122,9 @@ export const updateCollection = ({ commit }, { collection_id, name, description,
slug: slug,
status: status,
enable_cover_page: enable_cover_page,
cover_page_id: cover_page_id
cover_page_id: cover_page_id,
moderators_ids: moderators_ids,
parent: parent
});
resolve( res.data );
}).catch( error => {
@ -205,7 +209,7 @@ export const updateThumbnail = ({ commit }, { collectionId, thumbnailId }) => {
export const updateHeaderImage = ({ commit }, { collectionId, headerImageId }) => {
return new Promise((resolve, reject) => {
axios.tainacan.patch('/collections/' + collectionId, {
header_img_id: headerImageId
header_image_id: headerImageId + ''
}).then( res => {
let collection = res.data
commit('setCollection', collection);
@ -242,4 +246,38 @@ export const fetchPage = ({ commit }, pageId ) => {
reject( error );
});
});
};
};
// Users for moderators configuration
export const fetchUsers = ({ commit }, { search, exceptions }) => {
let endpoint = '/users?search=' + search;
if (exceptions.length > 0)
endpoint += '&exclude=' + exceptions.toString();
return new Promise((resolve, reject) => {
axios.wp.get(endpoint)
.then(res => {
let users = res.data;
resolve( users );
})
.catch(error => {
reject( error );
});
});
};
// Fetch Collections for choosing Parent Collection
export const fetchCollectionsForParent = ({ commit }) => {
return new Promise((resolve, reject) =>{
axios.tainacan.get('/collections/?fetch_only[0]=name&fetch_only[1]=id')
.then(res => {
let collections = res.data;
resolve( collections );
})
.catch(error => {
reject(error);
})
});
}

View File

@ -1,11 +1,11 @@
import axios from '../../../axios/axios';
import qs from 'qs';
export const fetchFields = ({ commit }, {collectionId, isRepositoryLevel, isContextEdit }) => {
export const fetchFields = ({commit}, {collectionId, isRepositoryLevel, isContextEdit}) => {
return new Promise((resolve, reject) => {
let endpoint = '';
if (!isRepositoryLevel)
endpoint = '/collection/' + collectionId + '/fields/';
if (!isRepositoryLevel)
endpoint = '/collection/' + collectionId + '/fields/';
else
endpoint = '/fields/';
@ -14,116 +14,119 @@ export const fetchFields = ({ commit }, {collectionId, isRepositoryLevel, isCont
endpoint += '&context=edit';
axios.tainacan.get(endpoint)
.then((res) => {
let fields= res.data;
commit('setFields', fields);
resolve (fields);
})
.catch((error) => {
console.log(error);
reject(error);
});
.then((res) => {
let fields = res.data;
commit('setFields', fields);
resolve(fields);
})
.catch((error) => {
console.log(error);
reject(error);
});
});
}
};
export const sendField = ( { commit }, { collectionId, name, fieldType, status, isRepositoryLevel, newIndex }) => {
return new Promise(( resolve, reject ) => {
export const sendField = ({commit}, {collectionId, name, fieldType, status, isRepositoryLevel, newIndex}) => {
return new Promise((resolve, reject) => {
let endpoint = '';
if (!isRepositoryLevel)
endpoint = '/collection/' + collectionId + '/fields/';
if (!isRepositoryLevel)
endpoint = '/collection/' + collectionId + '/fields/';
else
endpoint = '/fields/';
axios.tainacan.post(endpoint + '?context=edit', {
name: name,
field_type: fieldType,
field_type: fieldType,
status: status
})
.then( res => {
.then(res => {
let field = res.data;
commit('setSingleField', { field: field, index: newIndex});
resolve( res.data );
commit('setSingleField', {field: field, index: newIndex});
resolve(res.data);
})
.catch(error => {
reject( error.response );
reject(error.response);
});
});
};
export const updateField = ( { commit }, { collectionId, fieldId, isRepositoryLevel, index, options }) => {
return new Promise(( resolve, reject ) => {
export const updateField = ({commit}, {collectionId, fieldId, isRepositoryLevel, index, options}) => {
return new Promise((resolve, reject) => {
let endpoint = '';
if (!isRepositoryLevel)
endpoint = '/collection/' + collectionId + '/fields/' + fieldId;
if (!isRepositoryLevel)
endpoint = '/collection/' + collectionId + '/fields/' + fieldId;
else
endpoint = '/fields/' + fieldId;
axios.tainacan.put(endpoint + '?context=edit', options)
.then( res => {
let field = res.data
commit('setSingleField', { field: field, index: index });
resolve( field );
.then(res => {
let field = res.data;
commit('setSingleField', {field: field, index: index});
resolve(field);
})
.catch(error => {
reject({ error_message: error['response']['data'].error_message, errors: error['response']['data'].errors });
reject({
error_message: error['response']['data'].error_message,
errors: error['response']['data'].errors
});
});
});
};
export const updateFields = ( { commit }, fields) => {
export const updateFields = ({commit}, fields) => {
commit('setFields', fields);
};
export const deleteField = ({ commit }, { collectionId, fieldId, isRepositoryLevel }) => {
export const deleteField = ({commit}, {collectionId, fieldId, isRepositoryLevel}) => {
let endpoint = '';
if (!isRepositoryLevel)
endpoint = '/collection/' + collectionId + '/fields/' + fieldId;
if (!isRepositoryLevel)
endpoint = '/collection/' + collectionId + '/fields/' + fieldId;
else
endpoint = '/fields/' + fieldId;
return new Promise((resolve, reject) => {
axios.tainacan.delete(endpoint)
.then( res => {
commit('deleteField', res.data );
resolve( res.data );
}).catch((error) => {
console.log(error);
reject( error );
});
});
};
export const updateCollectionFieldsOrder = ({ commit, dispatch }, { collectionId, fieldsOrder }) => {
return new Promise((resolve, reject) => {
axios.tainacan.patch('/collections/' + collectionId, {
fields_order: fieldsOrder
}).then( res => {
// dispatch('collection/setCollection', res.data, {root: true});
resolve( res.data );
}).catch( error => {
reject( error.response );
});
});
}
export const fetchFieldTypes = ({ commit} ) => {
return new Promise((resolve, reject) => {
axios.tainacan.get('/field-types')
.then((res) => {
let fieldTypes = res.data;
commit('setFieldTypes', fieldTypes);
resolve (fieldTypes);
})
.catch((error) => {
.then(res => {
commit('deleteField', res.data);
resolve(res.data);
}).catch((error) => {
console.log(error);
reject(error);
});
});
};
export const updateCollectionFieldsOrder = ({ dispatch }, {collectionId, fieldsOrder}) => {
return new Promise((resolve, reject) => {
axios.tainacan.patch('/collections/' + collectionId, {
fields_order: fieldsOrder
}).then(res => {
//dispatch('collection/setCollection', res.data, {root: true});
resolve(res.data);
}).catch(error => {
reject(error.response);
});
});
}
export const fetchFieldTypes = ({commit}) => {
return new Promise((resolve, reject) => {
axios.tainacan.get('/field-types')
.then((res) => {
let fieldTypes = res.data;
commit('setFieldTypes', fieldTypes);
resolve(fieldTypes);
})
.catch((error) => {
console.log(error);
reject(error);
});
});
}
export const updateFieldTypes = ( { commit }, fieldTypes) => {
export const updateFieldTypes = ({commit}, fieldTypes) => {
commit('setFieldTypes', fieldTypes);
};

View File

@ -89,7 +89,7 @@ export const sendItem = ( { commit }, { collection_id, status }) => {
status: status
})
.then( res => {
commit('setItem', { collection_id: collection_id, status: status });
commit('setItem', res.data);
resolve( res.data );
})
.catch(error => {
@ -103,7 +103,22 @@ export const updateItem = ({ commit }, { item_id, status }) => {
axios.tainacan.patch('/items/' + item_id, {
status: status
}).then( res => {
commit('setItem', { id: item_id, status: status });
commit('setItem', res.data);
resolve( res.data );
}).catch( error => {
reject({ error_message: error['response']['data'].error_message, errors: error['response']['data'].errors });
});
});
};
export const updateItemDocument = ({ commit }, { item_id, document, document_type }) => {
return new Promise((resolve, reject) => {
axios.tainacan.patch('/items/' + item_id, {
document: document,
document_type: document_type
}).then( res => {
commit('setItem', res.data);
resolve( res.data );
}).catch( error => {
reject({ error_message: error['response']['data'].error_message, errors: error['response']['data'].errors });
@ -118,9 +133,6 @@ export const sendAttachment = ( { commit }, { item_id, file }) => {
return new Promise(( resolve, reject ) => {
axios.wp.post('/media/?post=' + item_id, file, {
headers: { 'Content-Disposition': 'attachment; filename=' + file.name },
onUploadProgress: progressEvent => {
console.log(progressEvent.loaded + '/' + progressEvent.total);
}
})
.then( res => {
let attachment = res.data;
@ -136,7 +148,7 @@ export const sendAttachment = ( { commit }, { item_id, file }) => {
export const fetchAttachments = ({ commit }, item_id) => {
commit('cleanAttachments');
return new Promise((resolve, reject) => {
axios.wp.get('/media/?post=' + item_id)
axios.wp.get('/media/?parent=' + item_id + '&per_page=100&paged=1')
.then(res => {
let attachments = res.data;
commit('setAttachments', attachments);

View File

@ -131,8 +131,9 @@ class Theme_Helper {
$term = get_queried_object();
if ($this->is_term_a_tainacan_term($term)) {
// TODO: Why post_type = any does not work?
if ($term instanceof \WP_Term && $this->is_term_a_tainacan_term($term)) {
// TODO: Why post_type = any does not work?
// ANSWER because post types are registered with exclude_from_search. Should we change it?
$wp_query->set( 'post_type', \Tainacan\Repositories\Repository::get_collections_db_identifiers() );
}

View File

@ -117,6 +117,39 @@ class Collections extends TAINACAN_UnitTestCase {
// now he can edit
$this->assertFalse(user_can($new_user, $collection_test_moderator->cap->edit_post, $collection_test_moderator->WP_Post->ID));
}
function test_avoid_duplicated_moderator () {
$collection_test = $this->tainacan_entity_factory->create_entity(
'collection',
array(
'name' => 'testeCaps',
'description' => 'adasdasdsa',
'default_order' => 'DESC'
),
true
);
$new_user = $this->factory()->user->create(array( 'role' => 'subscriber' ));
wp_set_current_user($new_user);
$user_id = get_current_user_id();
$this->assertEquals($new_user, $user_id);
$autor1 = $this->factory()->user->create(array( 'role' => 'author' ));
wp_set_current_user($autor1);
$autor1_id = get_current_user_id();
$moderators_ids = [
$user_id,
$autor1_id,
$user_id,
$autor1_id,
];
$collection_test->set('moderators_ids', $moderators_ids);
$this->assertEquals(2, sizeof( $collection_test->get_moderators_ids() ));
}
function debug_meta($user = false)

View File

@ -31,21 +31,22 @@ class ImporterTests extends TAINACAN_UnitTestCase {
/*public function test_automapping_old_tainacan()
{
$Tainacan_Items = \Tainacan\Repositories\Items::get_instance();
$Tainacan_Fields = \Tainacan\Repositories\Fields::get_instance();
//$Tainacan_Items = \Tainacan\Repositories\Items::get_instance();
//$Tainacan_Fields = \Tainacan\Repositories\Fields::get_instance();
$old_tainacan = new Importer\Old_Tainacan(true);
$old_tainacan = new Importer\Old_Tainacan();
$id = $old_tainacan->get_id();
$_SESSION['tainacan_importer'][$id]->set_items_per_step(50);
/*if(!copy('./tests/attachment/json_old_tainacan_base.txt', './tests/attachment/json_old_tainacan.txt'))
{
return false;
}
// if(!copy('./tests/attachment/json_old_tainacan_base.txt', './tests/attachment/json_old_tainacan.txt'))
// {
// return false;
// }
$_SESSION['tainacan_importer'][$id]->set_file( './tests/attachment/json_old_tainacan.txt' );
$_SESSION['tainacan_importer'][$id]->fetch_from_remote( 'http://localhost/colecao/colecao-to-import/' );
//$_SESSION['tainacan_importer'][$id]->set_file( './tests/attachment/json_old_tainacan.txt' );
$url = 'http://localhost/';
$_SESSION['tainacan_importer'][$id]->set_url($url);
$_SESSION['tainacan_importer'][$id]->run();
}*/
@ -241,7 +242,7 @@ class ImporterTests extends TAINACAN_UnitTestCase {
public function test_fetch_file(){
$csv_importer = new Importer\CSV();
$id = $csv_importer->get_id();
$_SESSION['tainacan_importer'][$id]->fetch_from_remote( 'http://localhost/wordpress-test/wp-json' );
$this->assertTrue( isset( $_SESSION['tainacan_importer'][$id]->tmp_file ) );
/*$_SESSION['tainacan_importer'][$id]->fetch_from_remote( 'http://localhost/wordpress-test/wp-json' );
$this->assertTrue( isset( $_SESSION['tainacan_importer'][$id]->tmp_file ) );*/
}
}