Merge branch 'develop' of https://github.com/tainacan/tainacan into develop

This commit is contained in:
weryques 2018-06-10 10:19:18 -03:00
commit 90ed143980
79 changed files with 3374 additions and 970 deletions

View File

@ -1,3 +1,5 @@
[![Waffle.io - Columns and their card count](https://badge.waffle.io/tainacan/tainacan.svg?columns=In%20Progress)](https://waffle.io/tainacan/tainacan)
# Tainacan
** This is the development repository of a new version of Tainacan. If you are looking for the current stable version, please visit http://github.com/medialab-ufg/tainacan. **

View File

@ -140,6 +140,7 @@
import { mapActions, mapGetters } from 'vuex';
import TermsList from '../lists/terms-list.vue'
import htmlToJSON from 'html-to-json';
import CustomDialog from '../other/custom-dialog.vue';
export default {
name: 'CategoryEditionForm',
@ -194,20 +195,20 @@
formNotSaved = true;
if (formNotSaved) {
this.$dialog.confirm({
title: this.$i18n.get('label_warning'),
message: this.$i18n.get('info_warning_category_not_saved'),
onConfirm: () => {
next();
},
icon: 'alert-circle',
hasIcon: true,
cancelText: this.$i18n.get('cancel'),
confirmText: this.$i18n.get('continue'),
type: 'is-success'
this.$modal.open({
parent: this,
component: CustomDialog,
props: {
icon: 'alert',
title: this.$i18n.get('label_warning'),
message: this.$i18n.get('info_warning_category_not_saved'),
onConfirm: () => {
next();
}
}
});
} else {
next()
next();
}
},
methods: {

View File

@ -410,6 +410,8 @@ export default {
editFormErrors: {},
formErrorMessage: '',
isNewCollection: false,
isMapped: false,
mapper: false,
thumbPlaceholderPath: tainacan_plugin.base_url + '/admin/images/placeholder_square.png',
headerPlaceholderPath: tainacan_plugin.base_url + '/admin/images/placeholder_rectangle.png',
isFetchingModerators: false,
@ -494,7 +496,7 @@ export default {
this.isLoading = true;
// Creates draft Collection
let data = { name: '', description: '', status: 'auto-draft'};
let data = { name: '', description: '', status: 'auto-draft', mapper: (this.isMapped && this.mapper != false ? this.mapper : false ) };
this.sendCollection(data).then(res => {
this.collectionId = res.id;
@ -717,6 +719,15 @@ export default {
this.isLoading = false;
});
} else {
var tmppath = this.$route.fullPath.split("/");
var mapper = tmppath.pop();
if(tmppath.pop() == 'new') {
this.isNewCollection = true;
this.isMapped = true;
this.mapper = mapper;
this.createNewCollection();
}
}
},
mounted() {

View File

@ -21,7 +21,7 @@
:title="$i18n.getHelperTitle('items', 'status')"
:message="$i18n.getHelperMessage('items', 'status')"/>
</div>
<div class="section-box section-status">
<div class="section-status">
<div class="field">
<!-- <div class="block">
<b-radio
@ -336,6 +336,7 @@ import { mapActions, mapGetters } from 'vuex';
import { eventBus } from '../../../js/event-bus-web-components.js'
import wpMediaFrames from '../../js/wp-media-frames';
import FileItem from '../other/file-item.vue';
import CustomDialog from '../other/custom-dialog.vue';
export default {
name: 'ItemEditionForm',
@ -675,17 +676,17 @@ export default {
},
beforeRouteLeave ( to, from, next ) {
if (this.item.status == 'auto-draft') {
this.$dialog.confirm({
title: this.$i18n.get('label_warning'),
message: this.$i18n.get('info_warning_item_not_saved'),
onConfirm: () => {
next();
},
icon: 'alert-circle',
hasIcon: true,
cancelText: this.$i18n.get('cancel'),
confirmText: this.$i18n.get('continue'),
type: 'is-success'
this.$modal.open({
parent: this,
component: CustomDialog,
props: {
icon: 'alert',
title: this.$i18n.get('label_warning'),
message: this.$i18n.get('info_warning_item_not_saved'),
onConfirm: () => {
next();
},
}
});
} else {
next()
@ -797,7 +798,7 @@ export default {
}
}
.section-status{
width: 174px;
padding: 16px 0;
}
.section-thumbnail {
width: 174px;

View File

@ -18,7 +18,7 @@
@click="headerImageMediaFrame.openFrame($event)">
<b-icon icon="pencil"/>
</a>
<figure class="image is-128x128">
<figure class="image">
<span
v-if="editForm.header_image === undefined || editForm.header_image === false"
class="image-placeholder">{{ $i18n.get('label_empty_header_image') }}</span>
@ -29,18 +29,22 @@
<div class="thumbnail-buttons-row">
<a
id="button-delete"
:aria-label="$i18n.get('label_button_delete_header_image')"
@click="deleteHeaderImage()">
<b-icon icon="delete"/>
:aria-label="$i18n.get('label_button_delete_thumb')"
@click="deleteThumbnail()">
<b-icon
type="is-gray"
icon="delete" />
<span class="text">{{ $i18n.get('remove') }} </span>
</a>
</div>
<a
v-if="editForm.url != undefined && editForm.url!= ''"
class="button is-secondary"
:href="editForm.url">
{{ $i18n.get('see') + ' ' + $i18n.get('term') }}
</a>
<br>
</div>
<a
v-if="editForm.url != undefined && editForm.url!= ''"
class="button is-secondary"
:href="editForm.url">
{{ $i18n.get('see') + ' ' + $i18n.get('term') }}
</a>
</b-field>
</div>
@ -229,15 +233,22 @@
@import "../../scss/_variables.scss";
.column {
padding: 0;
&.is-narrow {
padding-right: 42px;
}
}
form {
padding: 1.0em 2.0em;
padding: 2.0rem 2rem 1rem 2rem;
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-bottom: 66px;
margin-top: -20px;
.content {
@ -245,12 +256,14 @@
font-size: 0.8em;
}
img {
position: absolute;
position: relative;
width: 128px;
}
.image-placeholder {
position: absolute;
margin-left: 10px;
margin-right: 10px;
top: 24px;
bottom: 50%;
font-size: 0.8rem;
font-weight: bold;
@ -273,18 +286,19 @@
margin-top: 1px;
}
}
.thumbnail-buttons-row {
display: none;
}
&:hover {
.thumbnail-buttons-row {
display: inline-block;
display: inline-block;
padding: 8px 0px;
border-radius: 0px 0px 0px 4px;
font-size: 14px;
a { color: $tainacan-input-color; }
.text {
top: -3px;
position: relative;
top: -128px;
background-color: rgba(255, 255, 255, 0.9);
padding: 2px 8px;
border-radius: 0px 0px 0px 4px;
left: 88px;
}
i.mdi-24px.mdi-set, i.mdi-24px.mdi::before {
font-size: 20px;
}
}
}

View File

@ -132,7 +132,8 @@
</template>
<script>
import { mapActions } from 'vuex'
import { mapActions } from 'vuex';
import CustomDialog from '../other/custom-dialog.vue';
export default {
name: 'CategoriesList',
@ -179,73 +180,77 @@
this.selectedCategories.splice(i, 1, !this.allCategoriesOnPageSelected);
},
deleteOneCategory(categoryId) {
this.$dialog.confirm({
title: this.$i18n.get('label_warning'),
message: this.$i18n.get('info_warning_category_delete'),
onConfirm: () => {
this.deleteCategory(categoryId)
.then(() => {
// this.$toast.open({
// duration: 3000,
// message: this.$i18n.get('info_category_deleted'),
// position: 'is-bottom',
// type: 'is-secondary',
// queue: true
// });
for (let i = 0; i < this.selectedCategories.length; i++) {
if (this.selectedCategories[i].id === this.categoryId)
this.selectedCategories.splice(i, 1);
}
})
.catch(() => {
// this.$toast.open({
// duration: 3000,
// message: this.$i18n.get('info_error_deleting_category'),
// position: 'is-bottom',
// type: 'is-danger',
// queue: true
// });
});
},
icon: 'alert-circle',
hasIcon: true,
type: 'is-success'
});
},
deleteSelectedCategories() {
this.$dialog.confirm({
title: this.$i18n.get('label_warning'),
message: this.$i18n.get('info_warning_selected_categories_delete'),
onConfirm: () => {
for (let i = 0; i < this.categories.length; i++) {
if (this.selectedCategories[i]) {
this.deleteCategory(this.categories[i].id)
.then(() => {
// this.loadCategories();
// this.$toast.open({
// duration: 3000,
// message: this.$i18n.get('info_category_deleted'),
// position: 'is-bottom',
// type: 'is-secondary',
// queue: false
// })
}).catch(() => {
this.$modal.open({
parent: this,
component: CustomDialog,
props: {
icon: 'alert',
title: this.$i18n.get('label_warning'),
message: this.$i18n.get('info_warning_category_delete'),
onConfirm: () => {
this.deleteCategory(categoryId)
.then(() => {
// this.$toast.open({
// duration: 3000,
// message: this.$i18n.get('info_category_deleted'),
// position: 'is-bottom',
// type: 'is-secondary',
// queue: true
// });
for (let i = 0; i < this.selectedCategories.length; i++) {
if (this.selectedCategories[i].id === this.categoryId)
this.selectedCategories.splice(i, 1);
}
})
.catch(() => {
// this.$toast.open({
// duration: 3000,
// message: this.$i18n.get('info_error_deleting_category'),
// position: 'is-bottom',
// type: 'is-danger',
// queue: false
// queue: true
// });
});
}
}
this.allCategoriesOnPageSelected = false;
},
icon: 'alert-circle',
hasIcon: true,
type: 'is-success'
}
});
},
deleteSelectedCategories() {
this.$modal.open({
parent: this,
component: CustomDialog,
props: {
icon: 'alert',
title: this.$i18n.get('label_warning'),
message: this.$i18n.get('info_warning_selected_categories_delete'),
onConfirm: () => {
for (let i = 0; i < this.categories.length; i++) {
if (this.selectedCategories[i]) {
this.deleteCategory(this.categories[i].id)
.then(() => {
// this.loadCategories();
// this.$toast.open({
// duration: 3000,
// message: this.$i18n.get('info_category_deleted'),
// position: 'is-bottom',
// type: 'is-secondary',
// queue: false
// })
}).catch(() => {
// this.$toast.open({
// duration: 3000,
// message: this.$i18n.get('info_error_deleting_category'),
// position: 'is-bottom',
// type: 'is-danger',
// queue: false
// });
});
}
}
this.allCategoriesOnPageSelected = false;
}
}
});
},
goToCategoryPage(categoryId) {

View File

@ -181,7 +181,8 @@
</template>
<script>
import { mapActions } from 'vuex'
import { mapActions } from 'vuex';
import CustomDialog from '../other/custom-dialog.vue';
export default {
name: 'CollectionsList',
@ -229,72 +230,77 @@ export default {
this.selectedCollections.splice(i, 1, !this.allCollectionsOnPageSelected);
},
deleteOneCollection(collectionId) {
this.$dialog.confirm({
title: this.$i18n.get('label_warning'),
message: this.isOnTrash ? this.$i18n.get('info_warning_collection_delete') : this.$i18n.get('info_warning_collection_trash'),
onConfirm: () => {
this.deleteCollection({ collectionId: collectionId, isPermanently: this.isOnTrash })
.then(() => {
// this.$toast.open({
// duration: 3000,
// message: this.$i18n.get('info_collection_deleted'),
// position: 'is-bottom',
// type: 'is-secondary',
// queue: true
// });
for (let i = 0; i < this.selectedCollections.length; i++) {
if (this.selectedCollections[i].id == collectionId)
this.selectedCollections.splice(i, 1);
}
}).catch(() => {
// this.$toast.open({
// duration: 3000,
// message: this.$i18n.get('info_error_deleting_collection'),
// position: 'is-bottom',
// type: 'is-danger',
// queue: true
// })
});
},
icon: 'alert-circle',
hasIcon: true,
type: 'is-success'
this.$modal.open({
parent: this,
component: CustomDialog,
props: {
icon: 'alert',
title: this.$i18n.get('label_warning'),
message: this.isOnTrash ? this.$i18n.get('info_warning_collection_delete') : this.$i18n.get('info_warning_collection_trash'),
onConfirm: () => {
this.deleteCollection({ collectionId: collectionId, isPermanently: this.isOnTrash })
.then(() => {
// this.$toast.open({
// duration: 3000,
// message: this.$i18n.get('info_collection_deleted'),
// position: 'is-bottom',
// type: 'is-secondary',
// queue: true
// });
for (let i = 0; i < this.selectedCollections.length; i++) {
if (this.selectedCollections[i].id == collectionId)
this.selectedCollections.splice(i, 1);
}
}).catch(() => {
// this.$toast.open({
// duration: 3000,
// message: this.$i18n.get('info_error_deleting_collection'),
// position: 'is-bottom',
// type: 'is-danger',
// queue: true
// })
});
}
}
});
},
deleteSelectedCollections() {
this.$dialog.confirm({
title: this.$i18n.get('label_warning'),
message: this.isOnTrash ? this.$i18n.get('info_warning_selected_collections_delete') : this.$i18n.get('info_warning_selected_collections_trash'),
onConfirm: () => {
this.$modal.open({
parent: this,
component: CustomDialog,
props: {
icon: 'alert',
title: this.$i18n.get('label_warning'),
message: this.isOnTrash ? this.$i18n.get('info_warning_selected_collections_delete') : this.$i18n.get('info_warning_selected_collections_trash'),
onConfirm: () => {
for (let i = 0; i < this.collections.length; i++) {
if (this.selectedCollections[i]) {
this.deleteCollection({ collectionId: this.collections[i].id, isPermanently: this.isOnTrash })
.then(() => {
// this.loadCollections();
// this.$toast.open({
// duration: 3000,
// message: this.$i18n.get('info_collection_deleted'),
// position: 'is-bottom',
// type: 'is-secondary',
// queue: false
// })
}).catch(() => {
// this.$toast.open({
// duration: 3000,
// message: this.$i18n.get('info_error_deleting_collection'),
// position: 'is-bottom',
// type: 'is-danger',
// queue: false
// });
});
for (let i = 0; i < this.collections.length; i++) {
if (this.selectedCollections[i]) {
this.deleteCollection({ collectionId: this.collections[i].id, isPermanently: this.isOnTrash })
.then(() => {
// this.loadCollections();
// this.$toast.open({
// duration: 3000,
// message: this.$i18n.get('info_collection_deleted'),
// position: 'is-bottom',
// type: 'is-secondary',
// queue: false
// })
}).catch(() => {
// this.$toast.open({
// duration: 3000,
// message: this.$i18n.get('info_error_deleting_collection'),
// position: 'is-bottom',
// type: 'is-danger',
// queue: false
// });
});
}
}
}
this.allCollectionsOnPageSelected = false;
},
icon: 'alert-circle',
hasIcon: true,
type: 'is-success'
this.allCollectionsOnPageSelected = false;
},
}
});
},
goToCollectionPage(collectionId) {
@ -367,6 +373,10 @@ export default {
}
}
}
img.table-thumb {
border-radius: 50px !important;
}
</style>

View File

@ -149,6 +149,7 @@
import { mapActions, mapGetters } from 'vuex';
import GripIcon from '../other/grip-icon.vue';
import FieldEditionForm from './../edition/field-edition-form.vue';
import CustomDialog from '../other/custom-dialog.vue';
export default {
name: 'FieldsList',
@ -195,18 +196,18 @@ export default {
hasUnsavedForms = true;
}
if ((this.openedFieldId != '' && this.openedFieldId != undefined) || hasUnsavedForms ) {
this.$dialog.confirm({
title: this.$i18n.get('label_warning'),
message: this.$i18n.get('info_warning_fields_not_saved'),
onConfirm: () => {
this.onEditionCanceled();
next();
},
icon: 'alert-circle',
hasIcon: true,
cancelText: this.$i18n.get('cancel'),
confirmText: this.$i18n.get('continue'),
type: 'is-success'
this.$modal.open({
parent: this,
component: CustomDialog,
props: {
icon: 'alert',
title: this.$i18n.get('label_warning'),
message: this.$i18n.get('info_warning_fields_not_saved'),
onConfirm: () => {
this.onEditionCanceled();
next();
},
}
});
} else {
next()

View File

@ -141,6 +141,7 @@
}">
<div
class="available-field-item"
v-if="field.enabled"
v-for="(field, index) in availableFieldList"
:key="index"
@click.prevent="addFieldViaButton(field, index)">
@ -177,6 +178,7 @@
import { mapActions, mapGetters } from 'vuex';
import GripIcon from '../other/grip-icon.vue';
import FilterEditionForm from './../edition/filter-edition-form.vue';
import CustomDialog from '../other/custom-dialog.vue';
export default {
name: 'FiltersList',
@ -221,18 +223,18 @@ export default {
hasUnsavedForms = true;
}
if ((this.openedFilterId != '' && this.openedFilterId != undefined) || hasUnsavedForms ) {
this.$dialog.confirm({
title: this.$i18n.get('label_warning'),
message: this.$i18n.get('info_warning_filters_not_saved'),
onConfirm: () => {
this.onEditionCanceled();
next();
},
icon: 'alert-circle',
hasIcon: true,
cancelText: this.$i18n.get('cancel'),
confirmText: this.$i18n.get('continue'),
type: 'is-success'
this.$modal.open({
parent: this,
component: CustomDialog,
props: {
icon: 'alert',
title: this.$i18n.get('label_warning'),
message: this.$i18n.get('info_warning_filters_not_saved'),
onConfirm: () => {
this.onEditionCanceled();
next();
},
}
});
} else {
next()

View File

@ -60,17 +60,15 @@
v-for="(column, index) in tableFields"
:key="index"
v-if="column.display && column.field_type_object != undefined && (column.field_type_object.related_mapped_prop == 'title')"
class="metadata-title">
<a
v-html="item.metadata != undefined ? renderMetadata(item.metadata, column) : ''"
@click="goToItemPage(item)"/>
</p>
class="metadata-title"
@click="goToItemPage(item)"
v-html="item.metadata != undefined ? renderMetadata(item.metadata, column) : ''"/>
<!-- Thumbnail -->
<a
v-if="item.thumbnail != undefined"
@click="goToItemPage(item)">
<img :src="item['thumbnail'].medium">
v-if="item.thumbnail != undefined"
@click="goToItemPage(item)">
<img :src="item['thumbnail'].medium ? item['thumbnail'].medium : thumbPlaceholderPath">
</a>
<!-- Actions -->
@ -103,82 +101,76 @@
<div
class="tainacan-cards-container"
v-if="viewMode == 'cards'">
<div class="columns is-multiline is-gapless no-gutters">
<div
:key="index"
v-for="(item, index) of items"
:class="{ 'selected-card': selectedItems[index] }"
class="tainacan-card">
<!-- Checkbox -->
<div
:class="{ 'is-selecting': isSelectingItems }"
class="card-checkbox">
<b-checkbox
size="is-small"
v-model="selectedItems[index]"/>
</div>
<!-- Title -->
<p
v-for="(column, index) in tableFields"
:key="index"
v-for="(item, index) of items"
class="column is-12-tablet is-half-desktop is-half-widescreen is-one-third-fullhd">
<div
:class="{ 'selected-card': selectedItems[index] }"
class="tainacan-card">
<!-- Checkbox -->
<div
:class="{ 'is-selecting': isSelectingItems }"
class="card-checkbox">
<b-checkbox
size="is-small"
v-model="selectedItems[index]"/>
</div>
<!-- Title -->
<p
v-if="column.display && column.field_type_object != undefined && (column.field_type_object.related_mapped_prop == 'title')"
class="metadata-title"
@click="goToItemPage(item)"
v-html="item.metadata != undefined ? renderMetadata(item.metadata, column) : ''" />
<!-- Actions -->
<div
v-if="item.current_user_can_edit"
class="actions-area"
:label="$i18n.get('label_actions')">
<a
id="button-edit"
:aria-label="$i18n.getFrom('items','edit_item')"
@click.prevent.stop="goToItemEditPage(item.id)">
<b-icon
type="is-secondary"
icon="pencil"/>
</a>
<a
id="button-delete"
:aria-label="$i18n.get('label_button_delete')"
@click.prevent.stop="deleteOneItem(item.id)">
<b-icon
type="is-secondary"
:icon="!isOnTrash ? 'delete' : 'delete-forever'"/>
</a>
</div>
<!-- Remaining metadata -->
<div
class="media"
@click="goToItemPage(item)">
<a
v-if="item.thumbnail != undefined"
@click="goToItemPage(item)">
<img :src="item['thumbnail'].medium_large ? item['thumbnail'].medium_large : thumbPlaceholderPath">
</a>
<div class="list-metadata media-body">
<span
v-for="(column, index) in tableFields"
:key="index"
v-if="column.display && column.field_type_object != undefined && (column.field_type_object.related_mapped_prop == 'title')"
class="metadata-title">
<a
v-if="column.display && column.slug != 'thumbnail' && column.field_type_object != undefined && (column.field_type_object.related_mapped_prop != 'title')">
<h3 class="metadata-label">{{ column.name }}</h3>
<p
v-html="item.metadata != undefined ? renderMetadata(item.metadata, column) : ''"
@click="goToItemPage(item)"/>
</p>
<!-- Actions -->
<div
v-if="item.current_user_can_edit"
class="actions-area"
:label="$i18n.get('label_actions')">
<a
id="button-edit"
:aria-label="$i18n.getFrom('items','edit_item')"
@click.prevent.stop="goToItemEditPage(item.id)">
<b-icon
type="is-secondary"
icon="pencil"/>
</a>
<a
id="button-delete"
:aria-label="$i18n.get('label_button_delete')"
@click.prevent.stop="deleteOneItem(item.id)">
<b-icon
type="is-secondary"
:icon="!isOnTrash ? 'delete' : 'delete-forever'"/>
</a>
</div>
<div class="card-line"/>
<!-- Remaining metadata -->
<div class="media">
<a
v-if="item.thumbnail != undefined"
@click="goToItemPage(item)">
<img :src="item['thumbnail'].thumb">
</a>
<div class="list-metadata media-body">
<span
v-for="(column, index) in tableFields"
:key="index"
v-if="column.display && column.slug != 'thumbnail' && column.field_type_object != undefined && (column.field_type_object.related_mapped_prop != 'title')">
<h3 class="metadata-label">{{ column.name }}</h3>
<p
v-html="item.metadata != undefined ? renderMetadata(item.metadata, column) : ''"
class="metadata-value"/>
</span>
</div>
</div>
class="metadata-value"/>
</span>
</div>
</div>
</div>
</div>
</div>
@ -267,7 +259,7 @@
<span v-if="column.field == 'row_thumbnail'">
<img
class="table-thumb"
:src="item[column.slug].thumb">
:src="item['thumbnail'].thumb ? item['thumbnail'].thumb : thumbPlaceholderPath">
</span>
<p
v-tooltip="{
@ -315,6 +307,7 @@
<script>
import { mapActions } from 'vuex';
import CustomDialog from '../other/custom-dialog.vue';
export default {
name: 'ItemsList',
@ -323,6 +316,7 @@ export default {
allItemsOnPageSelected: false,
isSelectingItems: false,
selectedItems: [],
thumbPlaceholderPath: tainacan_plugin.base_url + '/admin/images/placeholder_square.png',
}
},
props: {
@ -336,7 +330,7 @@ export default {
mounted() {
this.selectedItems = [];
for (let i = 0; i < this.items.length; i++)
this.selectedItems.push(false);
this.selectedItems.push(false);
},
watch: {
selectedItems() {
@ -362,77 +356,81 @@ export default {
this.selectedItems.splice(i, 1, !this.allItemsOnPageSelected);
},
deleteOneItem(itemId) {
this.$dialog.confirm({
title: this.$i18n.get('label_warning'),
message: this.isOnTrash ? this.$i18n.get('info_warning_item_delete') : this.$i18n.get('info_warning_item_trash'),
onConfirm: () => {
this.deleteItem({ itemId: itemId, isPermanently: this.isOnTrash })
.then(() => {
// this.$toast.open({
// duration: 3000,
// message: this.$i18n.get('info_item_deleted'),
// position: 'is-bottom',
// type: 'is-secondary',
// queue: true
// });
for (let i = 0; i < this.selectedItems.length; i++) {
if (this.selectedItems[i].id == itemId)
this.selectedItems.splice(i, 1);
}
}).catch(() => {
this.$modal.open({
parent: this,
component: CustomDialog,
props: {
icon: 'alert',
title: this.$i18n.get('label_warning'),
message: this.isOnTrash ? this.$i18n.get('info_warning_item_delete') : this.$i18n.get('info_warning_item_trash'),
onConfirm: () => {
this.deleteItem({ itemId: itemId, isPermanently: this.isOnTrash })
// .then(() => {
// // this.$toast.open({
// // duration: 3000,
// // message: this.$i18n.get('info_item_deleted'),
// // position: 'is-bottom',
// // type: 'is-secondary',
// // queue: true
// // });
// for (let i = 0; i < this.selectedItems.length; i++) {
// if (this.selectedItems[i].id == itemId)
// this.selectedItems.splice(i, 1);
// }
// }).catch(() => {
// this.$toast.open({
// duration: 3000,
// message: this.$i18n.get('info_error_deleting_item'),
// position: 'is-bottom',
// type: 'is-danger',
// queue: true
// })
});
},
icon: 'alert-circle',
hasIcon: true,
type: 'is-success'
// // this.$toast.open({
// // duration: 3000,
// // message: this.$i18n.get('info_error_deleting_item'),
// // position: 'is-bottom',
// // type: 'is-danger',
// // queue: true
// // })
// });
}
}
});
},
deleteSelectedItems() {
this.$dialog.confirm({
title: this.$i18n.get('label_warning'),
message: this.isOnTrash ? this.$i18n.get('info_warning_selected_items_delete') : this.$i18n.get('info_warning_selected_items_trash'),
onConfirm: () => {
this.$modal.open({
parent: this,
component: CustomDialog,
props: {
icon: 'alert',
title: this.$i18n.get('label_warning'),
message: this.isOnTrash ? this.$i18n.get('info_warning_selected_items_delete') : this.$i18n.get('info_warning_selected_items_trash'),
onConfirm: () => {
for (let i = 0; i < this.selectedItems.length; i++) {
if (this.selectedItems[i]) {
this.deleteItem({ itemId: this.items[i].id, isPermanently: this.isOnTrash })
.then(() => {
// this.$toast.open({
// duration: 3000,
// message: this.$i18n.get('info_item_deleted'),
// position: 'is-bottom',
// type: 'is-secondary',
// queue: false
// });
for (let i = 0; i < this.selectedItems.length; i++) {
if (this.selectedItems[i].id == this.itemId)
this.selectedItems.splice(i, 1);
}
}).catch(() => {
// this.$toast.open({
// duration: 3000,
// message: this.$i18n.get('info_error_deleting_item'),
// position: 'is-bottom',
// type: 'is-danger',
// queue: false
// });
});
for (let i = 0; i < this.selectedItems.length; i++) {
if (this.selectedItems[i]) {
this.deleteItem({ itemId: this.items[i].id, isPermanently: this.isOnTrash })
.then(() => {
// this.$toast.open({
// duration: 3000,
// message: this.$i18n.get('info_item_deleted'),
// position: 'is-bottom',
// type: 'is-secondary',
// queue: false
// });
for (let i = 0; i < this.selectedItems.length; i++) {
if (this.selectedItems[i].id == this.itemId)
this.selectedItems.splice(i, 1);
}
}).catch(() => {
// this.$toast.open({
// duration: 3000,
// message: this.$i18n.get('info_error_deleting_item'),
// position: 'is-bottom',
// type: 'is-danger',
// queue: false
// });
});
}
}
this.allItemsOnPageSelected = false;
}
this.allItemsOnPageSelected = false;
},
icon: 'alert-circle',
hasIcon: true,
type: 'is-success'
}
});
},
goToItemPage(item) {

View File

@ -85,7 +85,8 @@
<script>
import { mapActions, mapGetters } from 'vuex';
import TermEditionForm from '../edition/term-edition-form.vue'
import TermEditionForm from '../edition/term-edition-form.vue';
import CustomDialog from '../other/custom-dialog.vue';
export default {
name: 'TermsList',
@ -212,16 +213,15 @@ export default {
// Checks if user is deleting a term with unsaved info.
if (term.id == 'new' || !term.saved || term.opened) {
this.$dialog.confirm({
title: this.$i18n.get('label_warning'),
message: this.$i18n.get('info_warning_terms_not_saved'),
onCancel: () => { return },
onConfirm: () => { this.removeTerm(term); },
icon: 'alert-circle',
hasIcon: true,
cancelText: this.$i18n.get('cancel'),
confirmText: this.$i18n.get('continue'),
type: 'is-success'
this.$modal.open({
parent: this,
component: CustomDialog,
props: {
icon: 'alert',
title: this.$i18n.get('label_warning'),
message: this.$i18n.get('info_warning_terms_not_saved'),
onConfirm: () => { this.removeTerm(term); },
}
});
} else{
this.removeTerm(term);

View File

@ -121,6 +121,7 @@ export default {
.control {
input {
border-width: 0 !important;
height: 27px;
font-size: 11px;
color: $gray-light;

View File

@ -0,0 +1,25 @@
<template>
<svg
xmlns="http://www.w3.org/2000/svg"
x="0px"
y="0px"
width="6.44px"
height="32.202px"
viewBox="0 0 6.44 32.202"
enable-background="new 0 0 6.44 32.202"
xml:space="preserve">
<path
fill="#BB3636"
d="M0,25.761h6.44v6.44H0V25.761 M0,0h6.44v19.321H0V0"/>
</svg>
</template>
<script>
export default {
name: 'AlertIcon'
}
</script>

View File

@ -0,0 +1,25 @@
<template>
<svg
xmlns="http://www.w3.org/2000/svg"
x="0px"
y="0px"
width="38.819px"
height="29.746px"
viewBox="0 0 38.819 29.746"
enable-background="new 0 0 38.819 29.746"
xml:space="preserve">
<path
fill="#259F87"
d="M38.819,3.128L12.2,29.746L0,17.546l3.128-3.128l9.073,9.05L35.691,0L38.819,3.128z"/>
</svg>
</template>
<script>
export default {
name: 'CheckIcon'
}
</script>

View File

@ -0,0 +1,57 @@
<template>
<div class="tainacan-form dialog">
<div
class="modal-card"
style="width: auto">
<div
v-if="icon != undefined && icon != ''"
class="modal-custom-icon">
<component :is="icon + '-icon'"/>
</div>
<section
class="modal-card-body">
<header
class="modal-card-head">
<h1 class="modal-card-title">{{ title }}</h1>
</header>
{{ message }}
</section>
<footer class="modal-card-foot form-submit">
<button
class="button is-outline"
type="button"
@click="$parent.close()">
{{ $i18n.get('cancel') }}
</button>
<button
class="button is-success"
@click="onConfirm(); $parent.close();">
{{ $i18n.get('continue') }}
</button>
</footer>
</div>
</div>
</template>
<script>
import AlertIcon from './alert-icon.vue';
import CheckIcon from './check-icon.vue';
import QuestionIcon from './question-icon.vue';
export default {
name: 'CustomDialog',
props: {
title: String,
message: String,
icon: String,
onConfirm: {
type: Function,
default: () => {}
}
},
components: {
AlertIcon,
CheckIcon,
QuestionIcon
}
}
</script>

View File

@ -0,0 +1,28 @@
<template>
<svg
xmlns="http://www.w3.org/2000/svg"
x="0px"
y="0px"
width="19.749px"
height="33.073px"
viewBox="0 0 19.749 33.073"
enable-background="new 0 0 19.749 33.073"
xml:space="preserve">
<path
fill="#BB3636"
d="M6.615,28.112h4.961v4.961H6.615V28.112 M9.922,0c8.847,0.364,12.7,9.293,7.441,15.991
c-1.373,1.654-3.588,2.745-4.68,4.134c-1.108,1.373-1.108,3.026-1.108,4.68H6.615c0-2.762,0-5.093,1.108-6.747
c1.091-1.654,3.307-2.629,4.68-3.721c4.002-3.704,3.01-8.946-2.48-9.376c-2.74,0-4.961,2.221-4.961,4.961H0C0,4.442,4.442,0,9.922,0
z"/>
</svg>
</template>
<script>
export default {
name: 'QuestionIcon'
}
</script>

View File

@ -35,6 +35,7 @@ const routes = [
{ 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/new/:mapper', name: 'MappedCollectionCreationForm', 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: [

View File

@ -212,6 +212,9 @@ RouterHelperPlugin.install = function (Vue, options = {}) {
getNewCollectionPath() {
return '/collections/new';
},
getNewMappedCollectionPath(mapperSlug) {
return '/collections/new/' + mapperSlug;
},
getNewItemPath(collectionId) {
return '/collections/' + collectionId + '/items/new';
},

View File

@ -1,5 +1,6 @@
<template>
<div class="primary-page page-container">
<b-loading :active.sync="isLoadingFieldMappers"/>
<tainacan-title />
<div
class="sub-header"
@ -9,22 +10,29 @@
<button
class="button is-secondary"
slot="trigger">
<span>{{ $i18n.getFrom('collections','new_item') }}</span>
<div>{{ $i18n.getFrom('collections', 'new_item') }}</div>
<b-icon icon="menu-down"/>
</button>
<b-dropdown-item>
<router-link
id="a-create-collection"
tag="div"
:to="{ path: $routerHelper.getNewCollectionPath() }">
{{ $i18n.get('label_blank_collection') }}
{{ $i18n.get('new_blank_collection') }}
<br>
<small class="is-small">{{ $i18n.get('info_choose_your_metadata') }}</small>
</router-link>
</b-dropdown-item>
<b-dropdown-item disabled>
{{ $i18n.get('label_dublin_core') + ' (Not ready)' }}
<b-dropdown-item
:key="field_mapper.slug"
v-for="field_mapper in field_mappers"
v-if="field_mapper.metadata != false">
<router-link
:id="'a-create-collection-' + field_mapper.slug"
tag="div"
:to="{ path: $routerHelper.getNewMappedCollectionPath(field_mapper.slug) }">
{{ $i18n.get(field_mapper.name) }}
</router-link>
</b-dropdown-item>
</b-dropdown>
</div>
@ -132,6 +140,7 @@ export default {
totalCollections: 0,
page: 1,
collectionsPerPage: 12,
isLoadingFieldMappers: true,
status: ''
}
},
@ -142,9 +151,15 @@ export default {
...mapActions('collection', [
'fetchCollections',
]),
...mapActions('fields', [
'fetchFieldMappers'
]),
...mapGetters('collection', [
'getCollections'
]),
...mapGetters('fields', [
'getFieldMappers'
]),
onChangeTab(status) {
this.status = status;
this.loadCollections();
@ -177,11 +192,17 @@ export default {
}
},
computed: {
field_mappers: {
get() {
return this.getFieldMappers();
}
},
collections() {
return this.getCollections();
}
},
created() {
this.isLoadingFieldTypes = true;
this.$userPrefs.get('collections_per_page')
.then((value) => {
this.collectionsPerPage = value;
@ -189,6 +210,13 @@ export default {
.catch(() => {
this.$userPrefs.set('collections_per_page', 12, null);
});
this.fetchFieldMappers()
.then(() => {
this.isLoadingFieldMappers = false;
})
.catch(() => {
this.isLoadingFieldMappers = false;
});
},
mounted(){
this.loadCollections();

View File

@ -148,7 +148,7 @@
<button
class="button is-white"
slot="trigger">
<span>{{ $i18n.get('label_table_fields') }}</span>
<span>{{ $i18n.get('label_displayed_metadata') }}</span>
<b-icon icon="menu-down"/>
</button>
<div class="metadata-options-container">
@ -222,6 +222,7 @@
<span
v-if="registeredViewModes[viewMode] != undefined"
v-html="registeredViewModes[viewMode].icon"/>
&nbsp;&nbsp;&nbsp;{{ $i18n.get('label_visualization') }}
<b-icon icon="menu-down" />
</button>
<b-dropdown-item
@ -253,6 +254,7 @@
'table' : (adminViewMode == 'cards' ?
'view-list' : 'view-grid')"/>
</span>
&nbsp;&nbsp;&nbsp;{{ $i18n.get('label_visualization') }}
<b-icon icon="menu-down" />
</button>
<b-dropdown-item :value="'table'">
@ -763,21 +765,22 @@
.search-area {
display: flex;
align-items: center;
margin-right: 36px;
width: 100%;
.control {
width: 100%;
input {
height: 27px;
font-size: 11px;
color: $gray-light;
width: 148px;
}
.icon {
pointer-events: all;
cursor: pointer;
color: $tertiary;
height: 27px;
font-size: 18px;
font-size: 18px !important;
}
margin-bottom: 5px;
}
@ -788,6 +791,11 @@
font-weight: normal;
}
.checkbox {
margin-bottom: 5px;
align-items: baseline;
}
}
.filter-menu-compress-button-top-repo {
top: 123px !important;

View File

@ -1,5 +1,5 @@
.b-checkbox.checkbox {
input[type="checkbox"] {
box-shadow: none !important;
}

View File

@ -16,11 +16,14 @@
.dropdown-menu {
padding: 0;
border-radius: 0px;
min-width: 6rem;
.dropdown-content {
padding: 0;
border-radius: 0px !important;
.dropdown-item {
padding: 0.375rem 1rem;
font-size: 13px;
color: $tainacan-input-color !important;
label { margin-bottom: 0; }
&.control { font-size: 13px !important; }
.b-checkbox { width: 100% };

View File

@ -43,32 +43,42 @@
background-color: $modal-backgound-color;
color: $secondary;
border-radius: 10px;
flex-wrap: wrap;
flex-direction: row;
padding: 35px;
margin: 0 auto !important;
.modal-card-head, .modal-card-body, .modal-card-foot {
background-color: $modal-backgound-color;
color: $secondary;
border: none;
padding-bottom: 12px;
}
.modal-custom-icon {
background-color: white;
border-radius: 50px;
height: 80px;
width: 80px;
display: flex;
align-items: center;
justify-content: center;
}
.modal-card-head {
p { color: $secondary; }
h1 {
color: $secondary;
}
font-weight: normal;
padding: 30px 35px 0px 35px;
padding: 0;
margin-bottom: 12px;
}
.modal-card-body {
padding: 16px 35px;
i {
color: white !important;
&::before {
background-color: $danger;
border-radius: 55px;
display: initial;
}
}
padding: 0px 0px 0px 16px;
width: 50%;
}
.modal-card-foot {
justify-content: space-between;
padding: 0px 35px 30px 35px;
padding: 24px 0px 0px 0px;
width: 100%;
.button {
border-radius: 6px !important;

View File

@ -5,6 +5,7 @@
border: 2px solid $gray-light;
width: 2.7em;
height: 1.7em;
transition: none;
&::before {
background-color: white;

View File

@ -23,17 +23,6 @@
top: auto;
display: table-cell;
&::before {
box-shadow: inset 50px 0 10px -12px #222;
content: " ";
width: 50px;
height: 100%;
position: absolute;
left: 0;
top: 0;
visibility: hidden;
}
label.checkbox {
border-radius: 0px;
background-color: white;
@ -141,7 +130,7 @@
&.selected-row {
background-color: $primary-lighter;
.checkbox-cell .checkbox, .actions-cell .actions-container {
background-color: $primary-lighter;
background-color: $primary-lighter-hover;
}
}
td {
@ -185,6 +174,10 @@
a {
margin: auto;
font-size: 18px !important;
.mdi-settings, .mdi-settings::before {
font-size: 23px;
}
}
}
@ -196,26 +189,15 @@
.checkbox-cell {
position: sticky !important;
position: -webkit-sticky !important;
&::before { visibility: visible; }
background-color: $gray-hover;
.checkbox {
background-color: $tainacan-input-background !important;
background-color: $gray-hover !important;
}
}
.actions-cell {
.actions-container {
background: $tainacan-input-background !important;
}
&::after {
box-shadow: inset -97px 0 17px -21px #222;
content: " ";
width: 100px;
height: 100%;
position: absolute;
right: 0px;
top: 0;
background: $gray-hover !important;
}
}

View File

@ -4,7 +4,7 @@
.form-submit {
justify-content: space-between !important;
padding: 12px 30px;
padding: 12px 0px ;
margin-bottom: 0px;
.button {
border-width: 1px;
@ -18,6 +18,7 @@
font-weight: bold;
font-size: 14px;
display: inline-block;
white-space: nowrap;
}
.required-field-asterisk {
color: $gray;
@ -26,8 +27,8 @@
}
}
.input, .textarea {
background-color: $tainacan-input-background;
background-color: white;
border: 1px solid $tainacan-input-background;
color: $tainacan-input-color;
transition: background-color 0.1s;
@ -54,7 +55,9 @@
}
}
.radio, .checkbox {
margin-bottom: 0.2em;
margin-bottom: 5px;
align-items: baseline;
&.is-danger {
border-color: $danger;
}
@ -75,7 +78,7 @@
}
}
&.is-empty select{
background-color: $tainacan-input-background !important;
//background-color: $tainacan-input-background !important;
}
}
.dropdown {

View File

@ -12,6 +12,7 @@ $tertiary-invert: findColorInvert($tertiary);
$primary-light:#c1dae0;
$primary-lighter: #e6f6f8;
$primary-lighter-hover: #d1e6e6;
$primary-dark: #55A0AF;
$primary-darker: darken($primary-dark, 5%);
@ -24,6 +25,7 @@ $tainacan-input-color: #1d1d1d;
$tainacan-input-background: #e5e5e5;
$tainacan-placeholder-color: #898d8f;
$draggable-border-color: #d8d8d8;
$gray-hover: #dcdcdc;
$gray: #b1b1b1;
$gray-invert: findColorInvert($gray);
@ -63,6 +65,7 @@ $header-height: 53px;
$subheader-height: 82px;
$side-menu-width: 180px;
$filter-menu-width: 200px;
$filter-menu-width-theme: 270px;
$page-height: calc(100% - 53px);
// Overall Pages padding:

View File

@ -1,32 +1,49 @@
.tainacan-cards-container {
min-height: 200px;
padding: 0;
display: flex;
flex-wrap: wrap;
flex-grow: 1;
flex-shrink: 1;
justify-content: center;
.selected-card {
background-color: $primary-lighter;
.metadata-title {
background-color: $primary-lighter-hover;
}
}
.tainacan-card {
padding: 16px 44px 24px 44px;
padding: 0px;
flex-basis: 0;
margin: 0.75rem;
max-width: 410px;
min-width: 410px;
cursor: pointer;
&:hover {
background-color: $tainacan-input-background !important;
}
.card-checkbox {
position: absolute;
margin-left: -28px;
margin-top: -2px;
margin-left: 1.0rem;
margin-top: 9px;
}
.actions-area {
position: relative;
float: right;
top: -37px;
right: -30px;
width: 60px;
top: -7px;
padding-right: 12px;
width: 80px;
display: flex;
justify-content: space-between;
visibility: hidden;
opacity: 0;
transition: visibility 0.2s, opacity 0.2s;
}
&:hover .actions-area, &:hover .card-line, &.selected-card .card-line {
&:hover .actions-area {
visibility: visible;
opacity: 1.0;
}
@ -40,27 +57,38 @@
visibility: hidden;
}
img {
width: 130px;
width: 170px;
height: auto;
border-radius: 2px;
margin-right: 1.5rem;
}
&:hover img {
border-radius: 0;
}
.metadata-title {
flex-shrink: 0;
font-size: 0.875rem;
margin-bottom: 0.875rem;
padding: 0.75rem 2.75rem;
text-overflow: ellipsis;
white-space: nowrap;
overflow: hidden;
margin-bottom: -27px;
}
&:hover .metadata-title {
background-color: $gray-hover !important;
}
.media {
margin-bottom: -6px;
width: 100%;
.list-metadata {
padding-top: 12px;
padding: 0.75rem 1.5rem;
flex: 1;
font-size: 0.6875rem;
color: gray;
overflow: hidden;
width: 100%;
.metadata-label {
font-size: 0.75rem;

View File

@ -14,13 +14,17 @@
.tainacan-grid-item {
max-width: 258px;
flex-basis: 0;
margin: 16px 24px 24px 24px;
margin: 1rem 1.5rem 1.5rem 1.5rem;
text-align: center;
&:hover {
background-color: $tainacan-input-background;
}
.grid-item-checkbox {
position: absolute;
margin-top: 9px;
margin-left: 12px;
margin-left: 1rem;
}
.actions-area {
position: relative;
@ -36,6 +40,10 @@
background-color: $tainacan-input-background;
border-bottom-left-radius: 2px;
border-bottom-right-radius: 2px;
a {
margin-left: 12px;
}
}
&:hover .actions-area {
visibility: visible;
@ -66,7 +74,8 @@
white-space: nowrap;
overflow: hidden;
text-align: left;
padding: 6px 12px;
padding: 6px 1rem;
cursor: pointer;
}
}

View File

@ -1,6 +1,6 @@
<?php
return [
return apply_filters('tainacan-admin-i18n',[
// Advanced search comparators
'is_equal_to' => __( 'Equal', 'tainacan' ),
@ -47,6 +47,8 @@ return [
'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' ),
'new_mapped_item' => __( 'New mapped collection', 'tainacan' ),
'new_blank_collection' => __( 'New Blank Collection', 'tainacan' ),
'split' => __( 'Split', 'tainacan' ),
'unified' => __( 'Unified', 'tainacan' ),
'add_more_one_search_field' => __( 'Add more one search field', 'tainacan' ),
@ -130,7 +132,7 @@ return [
'label_available_filters' => __( 'Available Filters', 'tainacan' ),
'label_available_filter_types' => __( 'Available Filter Types', 'tainacan' ),
'label_per_page' => __( 'per Page', 'tainacan' ),
'label_table_fields' => __( 'Metadata on table', 'tainacan' ),
'label_displayed_metadata' => __( 'Displayed metadata', 'tainacan' ),
'label_required' => __( 'Required', 'tainacan' ),
'label_allow_multiple' => __( 'Allow multiple values', 'tainacan' ),
'label_default_value' => __( 'Default value', 'tainacan' ),
@ -211,6 +213,7 @@ return [
'label_grid' => __( 'Grid', 'tainacan' ),
'label_table' => __( 'Table', 'tainacan' ),
'label_cards' => __( 'Cards', 'tainacan' ),
'label_visualization' => __( 'Visualization', 'tainacan' ),
// Instructions. More complex sentences to guide user and placeholders
'instruction_delete_selected_collections' => __( 'Delete selected collections', 'tainacan' ),
@ -319,5 +322,5 @@ return [
'tainacan-filter-category-taginput' => __( 'Taxonomy Tag Input', 'tainacan' ),
'tainacan-filter-category-checkbox' => __( 'Taxonomy Check Box', 'tainacan' ),
'tainacan-filter-category-selectbox' => __( 'Taxonomy Select Box', 'tainacan' )
]
]);
?>

View File

@ -98,8 +98,9 @@ export default {
}
.input, .textarea {
font-size: 14px;
border: none;
border: 1px solid $tainacan-input-background;
border-radius: 1px !important;
background-color: white;
box-shadow: none !important;
&:focus, &:active {
@ -117,6 +118,8 @@ export default {
display: block;
}
.b-checkbox.checkbox {
align-items: baseline;
margin-bottom: 5px;
input[type="checkbox"] {
box-shadow: none !important;
@ -266,7 +269,6 @@ export default {
}
.select select{
border: none;
border-radius: 1;
padding: 4px 16px;
color: #1d1d1d;
@ -282,9 +284,19 @@ export default {
.filters-menu {
height: auto;
min-width: $filter-menu-width;
min-width: $filter-menu-width-theme;
background-color: unset;
border-right: 1px solid $tainacan-input-background;
border-right: 0;
padding: 25px 25px 25px 4.1666667%;
}
.search-control {
border-bottom: 0;
}
.table-container {
padding-left: 4.166666667%;
padding-right: 4.166666667%;
}
#items-list-area {

View File

@ -5,6 +5,7 @@ namespace Tainacan\API\EndPoints;
use \Tainacan\API\REST_Controller;
use Tainacan\Repositories;
use Tainacan\Entities;
use Tainacan\Entities\Collection;
/**
* Represents the Collections REST Controller
@ -249,6 +250,8 @@ class REST_Collections_Controller extends REST_Controller {
'collection' => $body
], 400);
}
$this->collection = new Collection();
try {
$prepared_post = $this->prepare_item_for_database( $body );
@ -280,7 +283,8 @@ class REST_Collections_Controller extends REST_Controller {
* @throws \Exception
*/
public function create_item_permissions_check( $request ) {
return $this->collection->can_edit();
$dummy = new Entities\Collection();
return current_user_can($dummy->get_capabilities()->edit_posts);
}
/**
@ -294,7 +298,7 @@ class REST_Collections_Controller extends REST_Controller {
foreach ($request as $key => $value){
$set_ = 'set_' . $key;
$this->collection->$set_($value);
if(method_exists($this->collection, $set_)) $this->collection->$set_($value);
}
return $this->collection;

View File

@ -5,6 +5,7 @@ namespace Tainacan\API\EndPoints;
use \Tainacan\API\REST_Controller;
use Tainacan\Entities;
use Tainacan\Repositories;
use Tainacan\Entities\Entity;
class REST_Export_Controller extends REST_Controller {
private $item_metadata_repository;
@ -126,7 +127,7 @@ class REST_Export_Controller extends REST_Controller {
}
/**
* @param \Tainacan\Entities\Entity $item
* @param \Tainacan\Entities\Item $item
* @param \WP_REST_Request $request
*
* @return array|\WP_Error|\WP_REST_Response
@ -142,6 +143,118 @@ class REST_Export_Controller extends REST_Controller {
return $prepared_item;
}
/**
*
* @param \WP_REST_Request $request
* @param \WP_Query|Entities\Item $query
* @param array $args
* @return \WP_Error|number
*/
public function export($request, $query, $args) {
$type = \Tainacan\Exposers\Exposers::request_has_type($request);
$path = wp_upload_dir();
$path = $path['path'];
$filename = $path.date('YmdHis').'-tainacan-export.'.$type->get_extension();
$pid = -1;
$log = \Tainacan\Entities\Log::create(
__('Export Process', 'tainacan'),
__('Exporting Data', 'tainacan').'\nArgs: '.print_r($args, true),
['file' => $filename],
[],
'processing'
);
$body = json_decode( $request->get_body(), true );
$background = ! (isset($body['export-background']) && $body['export-background'] == false);
if( $background ) {
$pid = pcntl_fork();
} else {
$pid = true;
}
if ($pid === -1) {
$error = new \WP_Error('could not fork');
$log = \Tainacan\Entities\Log::create(
__('Export Process Error', 'tainacan'),
__('Exporting Error', 'tainacan').'\\nArgs: '.print_r($args, true).'\\nError: could not fork',
$error,
[],
'error'
);
remove_filter( 'rest_request_after_callbacks', [\Tainacan\Exposers\Exposers::get_instance(), 'rest_request_after_callbacks'], 10, 3 ); //exposer mapping
remove_filter( 'tainacan-rest-response', [\Tainacan\Exposers\Exposers::get_instance(), 'rest_response'], 10, 2 ); // exposer types
return $log;
} elseif ($pid) { // we are the parent or run at foreground
try {
ignore_user_abort(true);
set_time_limit(0);
ini_set("memory_limit", "256M");
if($background) { // wait for child to respond and exit and reconnect database if is forked
$status = null;
pcntl_wait($status);
global $wpdb;
$wpdb->db_connect();
}
$response = [];
if(isset($request['collection_id'])) { // One Colletion
$collection_id = $request['collection_id'];
$items = $query;
if ($items->have_posts()) {
while ( $items->have_posts() ) { //TODO write line by line
$items->the_post();
$item = new Entities\Item($items->post);
$prepared_item = $this->prepare_item_for_response($item, $request);
array_push($response, $prepared_item);
file_put_contents('/tmp/2', print_r($prepared_item, true), FILE_APPEND);
}
wp_reset_postdata();
}
} elseif (isset($request['item_id'])) { // One Item
$item = new Entities\Item($request['item_id']);
if($item->get_id() > 0) {
$prepared_item = $this->prepare_item_for_response($item, $request);
$response = [$prepared_item];
}
} else { // Export All
}
$rest_response = new \WP_REST_Response(apply_filters('tainacan-rest-response', $response, $request));
//file_put_contents($filename, $rest_response->get_data());
file_put_contents('/tmp/1', print_r($rest_response->get_data(), true));
if($background) {
$log->set_status('publish');
$logs = \Tainacan\Repositories\Logs::get_instance();
$logs->update($log);
exit(1);
} else {
return $rest_response->get_data();
}
} catch (\Exception $e) {
if($background) {
exit(1);
} else {
throw $e;
}
}
} else { // we are the child
remove_filter( 'rest_request_after_callbacks', [\Tainacan\Exposers\Exposers::get_instance(), 'rest_request_after_callbacks'], 10, 3 ); //exposer mapping
remove_filter( 'tainacan-rest-response', [\Tainacan\Exposers\Exposers::get_instance(), 'rest_response'], 10, 2 ); // exposer types
return $log;
}
}
/**
* @param \WP_REST_Request $request
@ -149,36 +262,38 @@ class REST_Export_Controller extends REST_Controller {
* @return \WP_Error|\WP_REST_Response
*/
public function get_items( $request ) {
$args = $this->prepare_filters($request);
$args = $this->prepare_filters($request); // TODO default args
$rest_response = new \WP_REST_Response([], 200); // TODO error, empty response
if(isset($request['collection_id'])) {
if(isset($request['collection_id'])) { // One Colletion
$collection_id = $request['collection_id'];
$items = $this->items_repository->fetch($args, $collection_id, 'WP_Query');
$response = [];
if ($items->have_posts()) {
while ( $items->have_posts() ) {
$items->the_post();
$item = new Entities\Item($items->post);
$prepared_item = $this->prepare_item_for_response($item, $request);
array_push($response, $prepared_item);
}
wp_reset_postdata();
}
$response = $this->export($request, $items, $args);
$total_items = $items->found_posts;
$max_pages = ceil($total_items / (int) $items->query_vars['posts_per_page']);
$rest_response = new \WP_REST_Response(apply_filters('tainacan-rest-response', $response, $request), 200);
$ret = $response instanceof Entity ? $response->__toArray() : $response;
$rest_response = new \WP_REST_Response($ret, 200);
$rest_response->header('X-WP-Total', (int) $total_items);
$rest_response->header('X-WP-TotalPages', (int) $max_pages);
} elseif (isset($request['item_id'])) { // One Item
$item = new Entities\Item($request['item_id']);
if($item->get_id() > 0) {
$response = $this->export($request, $item, $args);
$total_items = 1;
$max_pages = 1;
$rest_response = new \WP_REST_Response($response->__toArray(), 200);
$rest_response->header('X-WP-Total', 1);
$rest_response->header('X-WP-TotalPages', 1);
}
} else { // Export All
}
return $rest_response;
}

View File

@ -0,0 +1,70 @@
<?php
namespace Tainacan\API\EndPoints;
use \Tainacan\API\REST_Controller;
class REST_Field_Mappers_Controller extends REST_Controller {
/**
* REST_Field_Mappers_Controller constructor.
*/
public function __construct() {
$this->rest_base = 'field-mappers';
parent::__construct();
}
public function register_routes() {
register_rest_route($this->namespace, '/' . $this->rest_base,
array(
array(
'methods' => \WP_REST_Server::READABLE,
'callback' => array($this, 'get_items'),
'permission_callback' => array($this, 'get_items_permissions_check'),
)
)
);
}
/**
* @param \Tainacan\Exposers\Mappers\Mapper $mapper
* @param \WP_REST_Request $request
*map
* @return mixed|\WP_Error|\WP_REST_Response
*/
public function prepare_item_for_response( $mapper, $request ) {
$field_arr = $mapper->_toArray();
return $field_arr;
}
/**
* @param \WP_REST_Request $request
*
* @return \WP_Error|\WP_REST_Response
*/
public function get_items( $request ) {
$Tainacan_Exposers = \Tainacan\Exposers\Exposers::get_instance();
$field_mappers = $Tainacan_Exposers->get_mappers( 'OBJECT' );
$prepared = [];
foreach ($field_mappers as $field_mapper){
array_push($prepared, $this->prepare_item_for_response($field_mapper, $request));
}
return new \WP_REST_Response($prepared, 200);
}
/**
* @param \WP_REST_Request $request
*
* @return bool|\WP_Error
*/
public function get_items_permissions_check( $request ) {
return true;
}
}
?>

View File

@ -14,6 +14,7 @@ $rest_logs_controller = new \Tainacan\API\EndPoints\REST_Logs_Controlle
$rest_field_types_controller = new \Tainacan\API\EndPoints\REST_Field_Types_Controller();
$rest_filter_types_controller = new \Tainacan\API\EndPoints\REST_Filter_Types_Controller();
new \Tainacan\API\EndPoints\REST_Export_Controller();
new \Tainacan\API\EndPoints\REST_Field_Mappers_Controller();
// Add here other endpoints imports
?>

View File

@ -0,0 +1,337 @@
<?php
namespace Tainacan;
/**
* Abstract Tainacan_Background_Process class.
*
* Uses https://github.com/A5hleyRich/wp-background-processing to handle DB
* updates in the background.
*
* @package WooCommerce/Classes
*/
defined( 'ABSPATH' ) || exit;
if ( ! class_exists( 'WP_Async_Request', false ) ) {
include_once TAINACAN_CLASSES_DIR . '/lib/wp-async-request.php';
}
if ( ! class_exists( 'WP_Background_Process', false ) ) {
include_once TAINACAN_CLASSES_DIR . '/lib/wp-background-process.php';
}
/**
* Tainacan_Background_Process class.
*/
abstract class Background_Process extends \WP_Background_Process {
/**
* Table name where the queue is stored
* @var string
*/
protected $table = '';
/**
* Prefix
*
* (default value: 'wp')
*
* @var string
* @access protected
*/
protected $prefix = 'tnc-bg';
/**
* Action
*
* (default value: 'background_process')
*
* @var string
* @access protected
*/
protected $action = 'process';
/**
* Initiate new background process
*/
public function __construct() {
parent::__construct();
global $wpdb;
$this->table = $wpdb->prefix . 'tnc_bg_process';
}
/**
* Save queue
*
* @return $this
*/
public function save($priority = 10) {
if ( ! empty( $this->data ) ) {
global $wpdb;
$wpdb->insert(
$this->table,
[
'data' => maybe_serialize($this->data),
'user_id' => get_current_user_id(),
'priority' => $priority,
'action' => $this->action,
'queued_on' => date('Y-m-d H:i:s')
]
);
}
return $this;
}
/**
* Update queue
*
* @param string $key Key.
* @param array $data Data.
*
* @return $this
*/
public function update( $key, $data ) {
if ( ! empty( $data ) ) {
global $wpdb;
$wpdb->update(
$this->table,
[
'data' => maybe_serialize($data),
'processed_last' => date('Y-m-d H:i:s')
],
['ID' => $key]
);
}
return $this;
}
/**
* Mark a process as done
*
* @param string $key Key.
* @param array $data Data.
*
* @return $this
*/
public function close( $key ) {
global $wpdb;
$wpdb->update(
$this->table,
['done' => 1],
['ID' => $key],
['%d']
);
return $this;
}
/**
* Delete queue
*
* @param string $key Key.
*
* @return $this
*/
public function delete( $key ) {
global $wpdb;
$wpdb->delete(
$this->table,
['ID' => $key]
);
return $this;
}
/**
* Is queue empty
*
* @return bool
*/
protected function is_queue_empty() {
global $wpdb;
$table = $this->table;
$count = $wpdb->get_var( $wpdb->prepare( "
SELECT COUNT(*)
FROM {$table}
WHERE action = %s AND
done = false
", $this->action ) );
return ( $count > 0 ) ? false : true;
}
/**
* Get batch
*
* @return stdClass Return the first batch from the queue
*/
protected function get_batch() {
global $wpdb;
$table = $this->table;
$query = $wpdb->get_row( $wpdb->prepare( "
SELECT *
FROM {$table}
WHERE action = %s
AND done = FALSE
ORDER BY priority DESC, queued_on ASC
LIMIT 1
", $this->action ) );
$batch = new \stdClass();
$batch->key = $query->ID;
$batch->data = maybe_unserialize( $query->data );
return $batch;
}
/**
* Handle
*
* Pass each queue item to the task handler, while remaining
* within server memory and time limit constraints.
*
* Tainacan comments: This is where we changed the mos from otiginal class.
* Each batch is a single array of data. There is no queue inside a batch.
*/
protected function handle() {
$this->lock_process();
//error_log('new request');
do {
$batch = $this->get_batch();
// TODO: find a way to catch and log PHP errors as
try {
$task = $this->task( $batch->data, $batch->key );
} catch (\Exception $e) {
// TODO: Add Stacktrace
$this->write_error_log($batch->key, ['Fatal Error: ' . $e->getMessage()]);
$this->write_error_log($batch->key, ['Process aborted']);
$task = false;
}
// Update or close current batch.
if ( false !== $task ) {
$this->update( $batch->key, $task );
} else {
$this->close( $batch->key );
}
} while ( ! $this->time_exceeded() && ! $this->memory_exceeded() && ! $this->is_queue_empty() );
$this->unlock_process();
// Start next batch or complete process.
if ( ! $this->is_queue_empty() ) {
$this->dispatch();
} else {
$this->complete();
}
wp_die();
}
/**
* Delete all batches.
*
* @return WC_Background_Process
*/
public function delete_all_batches() {
global $wpdb;
$table = $this->table;
$wpdb->query( $wpdb->prepare( "DELETE FROM {$table} WHERE done = FALSE AND action LIKE %s", $this->action ) ); // @codingStandardsIgnoreLine.
return $this;
}
/**
* Kill process.
*
* Stop processing queue items, clear cronjob and delete all batches.
*/
public function kill_process() {
if ( ! $this->is_queue_empty() ) {
$this->delete_all_batches();
wp_clear_scheduled_hook( $this->cron_hook_identifier );
}
}
/**
* LOG
*/
protected function write_log_to_file($key, array $log, $type = '') {
$upload_dir = wp_upload_dir();
$upload_dir = trailingslashit( $upload_dir['basedir'] );
$logs_folder = $upload_dir . 'tainacan';
if (sizeof($log) < 1) {
return false;
}
if (!is_dir($logs_folder)) {
if (!mkdir($logs_folder)) {
return false;
}
}
$suffix = $type ? '-' . $type : '';
$filename = 'bg-' . $this->action . '-' . $key . $suffix . '.log';
$filepath = $logs_folder . '/' . $filename;
file_put_contents($filepath, $this->recursive_stingify_log_array($log), FILE_APPEND);
//$fh = fopen($filepath, 'a');
//
//foreach ($log as $message) {
// fwrite($fh, $message."\n");
//}
//
//fclose($fh);
}
protected function write_log($key, $log) {
$this->write_log_to_file($key, $log);
}
protected function write_error_log($key, $log) {
$this->write_log_to_file($key, $log, 'error');
}
private function recursive_stingify_log_array(array $log, $break = true) {
$return = '';
foreach ($log as $k => $l) {
if (!is_numeric($k)) {
$return .= $k . ': ';
}
if (is_array($l)) {
//$return .= $this->recursive_stingify_log_array($l, false);
$return .= print_r($l, true);
} elseif (is_string($l)) {
$return .= $l;
}
$return .="\n";
//$return .= $break ? "\n" : ', ';
}
return $return;
}
}

View File

@ -699,8 +699,14 @@ class Collection extends Entity {
*
* @return void
*/
function set_moderators_ids( array $value ) {
function set_moderators_ids( $value ) {
if(!is_array($value)) {
if(empty($value)) {
$value = [];
} else {
throw new \Exception('moderators_ids must be a array of users ids');
}
}
// make sure you never have duplicated moderators
$value = array_unique($value);

View File

@ -377,7 +377,7 @@ class Entity {
}
/**
* Return if user can read this entity
* Return if user can edit this entity
* @param int|\WP_User|null $user the user for capability check, null for the current user
* @return bool
*/
@ -387,7 +387,7 @@ class Entity {
}
/**
* Return if user can read this entity
* Return if user can delete this entity
* @param int|\WP_User|null $user the user for capability check, null for the current user
* @return bool
*/
@ -397,7 +397,7 @@ class Entity {
}
/**
* Return if user can read this entity
* Return if user can publish this entity
* @param int|\WP_User|null $user the user for capability check, null for the current user
* @return bool
*/

View File

@ -273,23 +273,27 @@ class Log extends Entity {
* @param string $desc
* @param mixed $new_value
* @param array $diffs
* @param string $status 'publish', 'private' or 'pending'
* @param string $status 'publish', 'private', 'pending', 'processing' or 'error'
* @param int $parent
*
* @return \Tainacan\Entities\Log
* @throws \Exception
*/
public static function create( $msn = false, $desc = '', $new_value = null, $diffs = [], $status = 'publish' ) {
public static function create( $msn = false, $desc = '', $new_value = null, $diffs = [], $status = 'publish', $parent = 0 ) {
$log = new Log();
$log->set_title( $msn );
$log->set_description( $desc );
$log->set_status( $status );
$log->set_log_diffs( $diffs );
if($parent > 0) $log->set_parent($parent);
if(array_search( 'Tainacan\Traits\Entity_Collection_Relation', class_uses($new_value))) {
$log->set_collection_id( $new_value->get_collection_id() );
} elseif($new_value instanceof Collection){
$log->set_collection_id( $new_value->get_id());
if(is_object($new_value) || is_string($new_value)) {
if(array_search( 'Tainacan\Traits\Entity_Collection_Relation', class_uses($new_value))) {
$log->set_collection_id( $new_value->get_collection_id() );
} elseif($new_value instanceof Collection){
$log->set_collection_id( $new_value->get_id());
}
}
if ( ! is_null( $new_value ) ) {

View File

@ -88,7 +88,7 @@
} else {
const instance = this;
axios.post(`/taxonomy/${this.taxonomy_id}/terms`, {
axios.post(`/taxonomy/${this.taxonomy_id}/terms?hideempty=0&order=asc`, {
name: this.name,
parent: this.parent
})

View File

@ -82,7 +82,7 @@
}
},
getTermsFromTaxonomy(){
axios.get('/taxonomy/' + this.taxonomy + '/terms?hideempty=0' ).then( res => {
axios.get('/taxonomy/' + this.taxonomy + '/terms?hideempty=0&order=asc' ).then( res => {
for (let item of res.data) {
this.terms.push( item );
}

View File

@ -55,7 +55,7 @@
},
methods: {
getValuesCategory( taxonomy ){
return axios.get('/taxonomy/' + taxonomy + '/terms?hideempty=0' ).then( res => {
return axios.get('/taxonomy/' + taxonomy + '/terms?hideempty=0&order=asc' ).then( res => {
for (let item of res.data) {
this.taxonomy = item.taxonomy;
this.options.push(item);
@ -69,7 +69,7 @@
let promise = null;
this.isLoading = true;
axios.get('/collection/'+ this.collection +'/fields/' + this.field + '?context=edit')
axios.get('/collection/'+ this.collection +'/fields/' + this.field)
.then( res => {
let field = res.data;
promise = this.getValuesCategory( field.field_type_options.taxonomy_id );

View File

@ -71,7 +71,7 @@
let promise = null;
this.isLoading = true;
axios.get('/collection/'+ this.collection +'/fields/' + this.field + '?context=edit')
axios.get('/collection/'+ this.collection +'/fields/' + this.field)
.then( res => {
let field = res.data;
promise = this.getValuesCategory( field.field_type_options.taxonomy_id );

View File

@ -22,10 +22,10 @@
this.field = ( this.field_id ) ? this.field_id : this.filter.field.field_id ;
this.type = ( this.filter_type ) ? this.filter_type : this.filter.field.field_type;
let in_route = '/collection/' + this.isRepositoryLevel + '/fields/' + this.field +'?context=edit';
let in_route = '/collection/' + this.collection + '/fields/' + this.field;
if(this.isRepositoryLevel){
in_route = '/fields?context=edit';
in_route = '/fields/' + this.field;
}
axios.get(in_route)
@ -83,8 +83,9 @@
let promise = null;
this.options = [];
const q = query;
axios.get('/collection/'+ this.collection +'/fields/' + this.field + '?context=edit')
const endpoint = this.isRepositoryLevel ? '/fields/' + this.field : '/collection/'+ this.collection +'/fields/' + this.field;
axios.get(endpoint)
.then( res => {
let field = res.data;
promise = this.getValuesCategory( field.field_type_options.taxonomy_id, q );
@ -102,7 +103,7 @@
});
},
getValuesCategory( taxonomy, query ){
return axios.get('/taxonomy/' + taxonomy + '/terms?hideempty=0' ).then( res => {
return axios.get('/taxonomy/' + taxonomy + '/terms?hideempty=0&order=asc' ).then( res => {
for (let term of res.data) {
if( term.name.toLowerCase().indexOf( query.toLowerCase() ) >= 0 ){
this.taxonomy = term.taxonomy;
@ -129,7 +130,7 @@
}
},
getTerm( taxonomy, id ){
return axios.get('/taxonomy/' + taxonomy + '/terms/' + id ).then( res => {
return axios.get('/taxonomy/' + taxonomy + '/terms/' + id + '?order=asc&hideempty=0' ).then( res => {
this.$console.log(res);
})
.catch(error => {

View File

@ -174,7 +174,6 @@
padding-top: 0px !important;
select {
font-size: 14px;
border: none;
border-radius: 1px !important;
font-weight: normal;
height: 30px !important;
@ -207,7 +206,6 @@
.input, .textarea, .taginput-container {
font-size: 14px;
border: none !important;
border-radius: 1px !important;
background-color: white;
color: $tainacan-input-color;

View File

@ -0,0 +1,163 @@
<?php
/**
* WP Async Request
*
* @package WP-Background-Processing
*/
if ( ! class_exists( 'WP_Async_Request' ) ) {
/**
* Abstract WP_Async_Request class.
*
* @abstract
*/
abstract class WP_Async_Request {
/**
* Prefix
*
* (default value: 'wp')
*
* @var string
* @access protected
*/
protected $prefix = 'wp';
/**
* Action
*
* (default value: 'async_request')
*
* @var string
* @access protected
*/
protected $action = 'async_request';
/**
* Identifier
*
* @var mixed
* @access protected
*/
protected $identifier;
/**
* Data
*
* (default value: array())
*
* @var array
* @access protected
*/
protected $data = array();
/**
* Initiate new async request
*/
public function __construct() {
$this->identifier = $this->prefix . '_' . $this->action;
add_action( 'wp_ajax_' . $this->identifier, array( $this, 'maybe_handle' ) );
add_action( 'wp_ajax_nopriv_' . $this->identifier, array( $this, 'maybe_handle' ) );
}
/**
* Set data used during the request
*
* @param array $data Data.
*
* @return $this
*/
public function data( $data ) {
$this->data = $data;
return $this;
}
/**
* Dispatch the async request
*
* @return array|WP_Error
*/
public function dispatch() {
$url = add_query_arg( $this->get_query_args(), $this->get_query_url() );
$args = $this->get_post_args();
return wp_remote_post( esc_url_raw( $url ), $args );
}
/**
* Get query args
*
* @return array
*/
protected function get_query_args() {
if ( property_exists( $this, 'query_args' ) ) {
return $this->query_args;
}
return array(
'action' => $this->identifier,
'nonce' => wp_create_nonce( $this->identifier ),
);
}
/**
* Get query URL
*
* @return string
*/
protected function get_query_url() {
if ( property_exists( $this, 'query_url' ) ) {
return $this->query_url;
}
return admin_url( 'admin-ajax.php' );
}
/**
* Get post args
*
* @return array
*/
protected function get_post_args() {
if ( property_exists( $this, 'post_args' ) ) {
return $this->post_args;
}
return array(
'timeout' => 0.01,
'blocking' => false,
'body' => $this->data,
'cookies' => $_COOKIE,
'sslverify' => apply_filters( 'https_local_ssl_verify', false ),
);
}
/**
* Maybe handle
*
* Check for correct nonce and pass to handler.
*/
public function maybe_handle() {
// Don't lock up other requests while processing
session_write_close();
check_ajax_referer( $this->identifier, 'nonce' );
$this->handle();
wp_die();
}
/**
* Handle
*
* Override this method to perform any actions required
* during the async request.
*/
abstract protected function handle();
}
}

View File

@ -0,0 +1,506 @@
<?php
/**
* WP Background Process
*
* @package WP-Background-Processing
*/
if ( ! class_exists( 'WP_Background_Process' ) ) {
/**
* Abstract WP_Background_Process class.
*
* @abstract
* @extends WP_Async_Request
*/
abstract class WP_Background_Process extends WP_Async_Request {
/**
* Action
*
* (default value: 'background_process')
*
* @var string
* @access protected
*/
protected $action = 'background_process';
/**
* Start time of current process.
*
* (default value: 0)
*
* @var int
* @access protected
*/
protected $start_time = 0;
/**
* Cron_hook_identifier
*
* @var mixed
* @access protected
*/
protected $cron_hook_identifier;
/**
* Cron_interval_identifier
*
* @var mixed
* @access protected
*/
protected $cron_interval_identifier;
/**
* Initiate new background process
*/
public function __construct() {
parent::__construct();
$this->cron_hook_identifier = $this->identifier . '_cron';
$this->cron_interval_identifier = $this->identifier . '_cron_interval';
add_action( $this->cron_hook_identifier, array( $this, 'handle_cron_healthcheck' ) );
add_filter( 'cron_schedules', array( $this, 'schedule_cron_healthcheck' ) );
}
/**
* Dispatch
*
* @access public
* @return void
*/
public function dispatch() {
// Schedule the cron healthcheck.
$this->schedule_event();
// Perform remote post.
return parent::dispatch();
}
/**
* Push to queue
*
* @param mixed $data Data.
*
* @return $this
*/
public function push_to_queue( $data ) {
$this->data[] = $data;
return $this;
}
/**
* Save queue
*
* @return $this
*/
public function save() {
$key = $this->generate_key();
if ( ! empty( $this->data ) ) {
update_site_option( $key, $this->data );
}
return $this;
}
/**
* Update queue
*
* @param string $key Key.
* @param array $data Data.
*
* @return $this
*/
public function update( $key, $data ) {
if ( ! empty( $data ) ) {
update_site_option( $key, $data );
}
return $this;
}
/**
* Delete queue
*
* @param string $key Key.
*
* @return $this
*/
public function delete( $key ) {
delete_site_option( $key );
return $this;
}
/**
* Generate key
*
* Generates a unique key based on microtime. Queue items are
* given a unique key so that they can be merged upon save.
*
* @param int $length Length.
*
* @return string
*/
protected function generate_key( $length = 64 ) {
$unique = md5( microtime() . rand() );
$prepend = $this->identifier . '_batch_';
return substr( $prepend . $unique, 0, $length );
}
/**
* Maybe process queue
*
* Checks whether data exists within the queue and that
* the process is not already running.
*/
public function maybe_handle() {
// Don't lock up other requests while processing
session_write_close();
if ( $this->is_process_running() ) {
// Background process already running.
wp_die();
}
if ( $this->is_queue_empty() ) {
// No data to process.
wp_die();
}
check_ajax_referer( $this->identifier, 'nonce' );
$this->handle();
wp_die();
}
/**
* Is queue empty
*
* @return bool
*/
protected function is_queue_empty() {
global $wpdb;
$table = $wpdb->options;
$column = 'option_name';
if ( is_multisite() ) {
$table = $wpdb->sitemeta;
$column = 'meta_key';
}
$key = $wpdb->esc_like( $this->identifier . '_batch_' ) . '%';
$count = $wpdb->get_var( $wpdb->prepare( "
SELECT COUNT(*)
FROM {$table}
WHERE {$column} LIKE %s
", $key ) );
return ( $count > 0 ) ? false : true;
}
/**
* Is process running
*
* Check whether the current process is already running
* in a background process.
*/
protected function is_process_running() {
if ( get_site_transient( $this->identifier . '_process_lock' ) ) {
// Process already running.
return true;
}
return false;
}
/**
* Lock process
*
* Lock the process so that multiple instances can't run simultaneously.
* Override if applicable, but the duration should be greater than that
* defined in the time_exceeded() method.
*/
protected function lock_process() {
$this->start_time = time(); // Set start time of current process.
$lock_duration = ( property_exists( $this, 'queue_lock_time' ) ) ? $this->queue_lock_time : 60; // 1 minute
$lock_duration = apply_filters( $this->identifier . '_queue_lock_time', $lock_duration );
set_site_transient( $this->identifier . '_process_lock', microtime(), $lock_duration );
}
/**
* Unlock process
*
* Unlock the process so that other instances can spawn.
*
* @return $this
*/
protected function unlock_process() {
delete_site_transient( $this->identifier . '_process_lock' );
return $this;
}
/**
* Get batch
*
* @return stdClass Return the first batch from the queue
*/
protected function get_batch() {
global $wpdb;
$table = $wpdb->options;
$column = 'option_name';
$key_column = 'option_id';
$value_column = 'option_value';
if ( is_multisite() ) {
$table = $wpdb->sitemeta;
$column = 'meta_key';
$key_column = 'meta_id';
$value_column = 'meta_value';
}
$key = $wpdb->esc_like( $this->identifier . '_batch_' ) . '%';
$query = $wpdb->get_row( $wpdb->prepare( "
SELECT *
FROM {$table}
WHERE {$column} LIKE %s
ORDER BY {$key_column} ASC
LIMIT 1
", $key ) );
$batch = new stdClass();
$batch->key = $query->$column;
$batch->data = maybe_unserialize( $query->$value_column );
return $batch;
}
/**
* Handle
*
* Pass each queue item to the task handler, while remaining
* within server memory and time limit constraints.
*/
protected function handle() {
$this->lock_process();
do {
$batch = $this->get_batch();
foreach ( $batch->data as $key => $value ) {
$task = $this->task( $value );
if ( false !== $task ) {
$batch->data[ $key ] = $task;
} else {
unset( $batch->data[ $key ] );
}
if ( $this->time_exceeded() || $this->memory_exceeded() ) {
// Batch limits reached.
break;
}
}
// Update or delete current batch.
if ( ! empty( $batch->data ) ) {
$this->update( $batch->key, $batch->data );
} else {
$this->delete( $batch->key );
}
} while ( ! $this->time_exceeded() && ! $this->memory_exceeded() && ! $this->is_queue_empty() );
$this->unlock_process();
// Start next batch or complete process.
if ( ! $this->is_queue_empty() ) {
$this->dispatch();
} else {
$this->complete();
}
wp_die();
}
/**
* Memory exceeded
*
* Ensures the batch process never exceeds 90%
* of the maximum WordPress memory.
*
* @return bool
*/
protected function memory_exceeded() {
$memory_limit = $this->get_memory_limit() * 0.9; // 90% of max memory
$current_memory = memory_get_usage( true );
$return = false;
if ( $current_memory >= $memory_limit ) {
$return = true;
}
return apply_filters( $this->identifier . '_memory_exceeded', $return );
}
/**
* Get memory limit
*
* @return int
*/
protected function get_memory_limit() {
if ( function_exists( 'ini_get' ) ) {
$memory_limit = ini_get( 'memory_limit' );
} else {
// Sensible default.
$memory_limit = '128M';
}
if ( ! $memory_limit || -1 === intval( $memory_limit ) ) {
// Unlimited, set to 32GB.
$memory_limit = '32000M';
}
return intval( $memory_limit ) * 1024 * 1024;
}
/**
* Time exceeded.
*
* Ensures the batch never exceeds a sensible time limit.
* A timeout limit of 30s is common on shared hosting.
*
* @return bool
*/
protected function time_exceeded() {
$finish = $this->start_time + apply_filters( $this->identifier . '_default_time_limit', 20 ); // 20 seconds
$return = false;
if ( time() >= $finish ) {
$return = true;
}
return apply_filters( $this->identifier . '_time_exceeded', $return );
}
/**
* Complete.
*
* Override if applicable, but ensure that the below actions are
* performed, or, call parent::complete().
*/
protected function complete() {
// Unschedule the cron healthcheck.
$this->clear_scheduled_event();
}
/**
* Schedule cron healthcheck
*
* @access public
* @param mixed $schedules Schedules.
* @return mixed
*/
public function schedule_cron_healthcheck( $schedules ) {
$interval = apply_filters( $this->identifier . '_cron_interval', 5 );
if ( property_exists( $this, 'cron_interval' ) ) {
$interval = apply_filters( $this->identifier . '_cron_interval', $this->cron_interval );
}
// Adds every 5 minutes to the existing schedules.
$schedules[ $this->identifier . '_cron_interval' ] = array(
'interval' => MINUTE_IN_SECONDS * $interval,
'display' => sprintf( __( 'Every %d Minutes' ), $interval ),
);
return $schedules;
}
/**
* Handle cron healthcheck
*
* Restart the background process if not already running
* and data exists in the queue.
*/
public function handle_cron_healthcheck() {
if ( $this->is_process_running() ) {
// Background process already running.
exit;
}
if ( $this->is_queue_empty() ) {
// No data to process.
$this->clear_scheduled_event();
exit;
}
$this->handle();
exit;
}
/**
* Schedule event
*/
protected function schedule_event() {
if ( ! wp_next_scheduled( $this->cron_hook_identifier ) ) {
wp_schedule_event( time(), $this->cron_interval_identifier, $this->cron_hook_identifier );
}
}
/**
* Clear scheduled event
*/
protected function clear_scheduled_event() {
$timestamp = wp_next_scheduled( $this->cron_hook_identifier );
if ( $timestamp ) {
wp_unschedule_event( $timestamp, $this->cron_hook_identifier );
}
}
/**
* Cancel Process
*
* Stop processing queue items, clear cronjob and delete batch.
*
*/
public function cancel_process() {
if ( ! $this->is_queue_empty() ) {
$batch = $this->get_batch();
$this->delete( $batch->key );
wp_clear_scheduled_hook( $this->cron_hook_identifier );
}
}
/**
* Task
*
* Override this method to perform any actions required on each
* queue item. Return the modified item for further processing
* in the next pass through. Or, return false to remove the
* item from the queue.
*
* @param mixed $item Queue item to iterate over.
*
* @return mixed
*/
abstract protected function task( $item, $key );
}
}

View File

@ -211,7 +211,7 @@ class Logs extends Repository {
}
public function update( $object, $new_values = null ) {
return $this->insert($object);
}
public function fetch_last() {

View File

@ -691,7 +691,7 @@ abstract class Repository {
public function diff( $old = 0, $new ) {
$old_entity = null;
if ( $old === 0 ) { // self diff or other entity?
if ( $old === 0 || is_array($old) && count($old) == 0 ) { // self diff or other entity?
$id = $new->get_id();
if ( ! empty( $id ) ) { // there is a repository entity?

View File

@ -8,7 +8,6 @@ const TAINACAN_TRAITS_DIR = __DIR__ . '/traits/';
const TAINACAN_VENDOR_DIR = __DIR__ . '/../vendor/';
const TAINACAN_TAPI_DIR = __DIR__ . '/../api/';
const TAINACAN_ENDPOINTS_DIR = __DIR__ . '/../api/endpoints/';
const TAINACAN_HELPERS_DIR = __DIR__ . '/../helpers/';
const TAINACAN_IMPORTER_DIR = __DIR__ . '/../importer/';
const TAINACAN_EXPOSERS_DIR = __DIR__ . '/../exposers/';
@ -25,11 +24,17 @@ const DIRS = [
TAINACAN_EXPOSERS_DIR
];
require_once('libs/wp-async-request.php');
require_once('libs/wp-background-process.php');
require_once('class-tainacan-background-process.php');
require_once(TAINACAN_IMPORTER_DIR . 'class-tainacan-bg-importer.php');
require_once(TAINACAN_VENDOR_DIR . 'autoload.php');
require_once(TAINACAN_HELPERS_DIR . 'class-tainacan-helpers-html.php');
require_once(TAINACAN_IMPORTER_DIR . 'class-tainacan-importer.php');
require_once(TAINACAN_IMPORTER_DIR . 'class-tainacan-importer-handler.php');
require_once(TAINACAN_EXPOSERS_DIR . 'class-tainacan-exposers.php');
spl_autoload_register('tainacan_autoload');
function tainacan_autoload($class_name){
@ -130,4 +135,5 @@ require_once(__DIR__ . '/../theme-helper/class-tainacan-theme-helper.php');
require_once(__DIR__ . '/../theme-helper/template-tags.php');
$Tainacan_Theme_Helper = \Tainacan\Theme_Helper::get_instance();
?>

View File

@ -22,11 +22,9 @@ class DevInterface {
}
private function __construct() {
add_action('add_meta_boxes', array(&$this, 'register_metaboxes'));
add_action('save_post', array(&$this, 'save_post'), 10, 2);
add_action('admin_enqueue_scripts', array(&$this, 'add_admin_js'));
add_action('admin_init', [$this, 'admin_init']);
$Tainacan_Collections = \Tainacan\Repositories\Collections::get_instance();
$Tainacan_Filters = \Tainacan\Repositories\Filters::get_instance();
$Tainacan_Logs = \Tainacan\Repositories\Logs::get_instance();
@ -43,6 +41,14 @@ class DevInterface {
}
public function admin_init() {
if ( function_exists('get_current_screen')) { // check if is in wordpress builtin admin screen
add_action('add_meta_boxes', array(&$this, 'register_metaboxes'));
add_action('save_post', array(&$this, 'save_post'), 10, 2);
add_action('admin_enqueue_scripts', array(&$this, 'add_admin_js'));
}
}
function add_admin_js() {
global $TAINACAN_BASE_URL;
$components = ( has_filter( 'tainacan_register_web_components' ) ) ? apply_filters('tainacan_register_web_components') : [];
@ -442,7 +448,7 @@ class DevInterface {
}
$entity->set_mapped_property($prop, $value);
$entity->set($prop, $value);
if ($entity->validate_prop($prop)) {
@ -460,8 +466,8 @@ class DevInterface {
update_post_meta($post_id, 'filter_type_options', $_POST['filter_type_'.strtolower( $value ) ] );
update_post_meta($post_id, 'filter_type', wp_slash( get_class( new $class() ) ) );
} elseif ($mapped['map'] == 'meta' || $mapped['map'] == 'meta_multi') {
$repo->insert_metadata($entity, $prop);
$diffs = [];
$repo->insert_metadata($entity, $prop, $diffs);
}
}
@ -494,7 +500,8 @@ class DevInterface {
// for new Items
if (!$entity->get_collection_id()) {
$entity->set_collection($cpts[$post_type]);
$Tainacan_Items->insert_metadata($entity, 'collection_id');
$diffs = [];
$Tainacan_Items->insert_metadata($entity, 'collection_id', $diffs);
}

View File

@ -13,6 +13,7 @@ class Exposers {
protected $types = [];
protected $mappers = [];
private static $instance = null;
private static $request = null;
const MAPPER_CLASS_PREFIX = 'Tainacan\Exposers\Mappers\\';
public static function get_instance() {
@ -35,8 +36,9 @@ class Exposers {
do_action('tainacan-register-exposer-mappers');
add_filter( 'rest_request_after_callbacks', [$this, 'rest_request_after_callbacks'], 10, 3 ); //exposer mapping
add_filter( 'tainacan-rest-response', [$this, 'rest_response'], 10, 2 ); // exposer types
add_filter( 'rest_request_after_callbacks', [$this, 'rest_request_after_callbacks'], 10, 3 ); //exposer types
add_filter( 'tainacan-rest-response', [$this, 'rest_response'], 10, 2 ); // exposer mapper
add_filter( 'tainacan-admin-i18n', [$this, 'mappers_i18n']);
}
/**
@ -45,12 +47,15 @@ class Exposers {
* @param $class_name string | object The class name or the instance
*/
public function register_exposer_type( $class_name ){
$obj = $class_name;
if( is_object( $class_name ) ){
$class_name = get_class( $class_name );
} else {
$obj = new $class_name;
}
if(!in_array( $class_name, $this->types)){
$this->types[] = $class_name;
$this->types[$obj->slug] = $class_name;
}
}
@ -60,12 +65,15 @@ class Exposers {
* @param $class_name string | object The class name or the object instance
*/
public function register_exposer_mapper( $class_name ){
$obj = $class_name;
if( is_object( $class_name ) ){
$class_name = get_class( $class_name );
} else {
$obj = new $class_name;
}
if(!in_array( $class_name, $this->mappers)){
$this->mappers[] = $class_name;
$this->mappers[$obj->slug] = $class_name;
}
}
@ -77,6 +85,15 @@ class Exposers {
* @return string
*/
public function check_class_name($class_name, $root = false, $prefix = 'Tainacan\Exposers\Types\\') {
if(is_string($class_name)) {
if(array_key_exists($class_name, $this->types)) {
$class_name = $this->types[$class_name];
$prefix = '';
} elseif( array_key_exists($class_name, $this->mappers)) {
$class_name = $this->mappers[$class_name];
$prefix = '';
}
}
$class = $prefix.sanitize_text_field($class_name);
$class = str_replace(['-', ' '], ['_', '_'], $class);
@ -90,7 +107,7 @@ class Exposers {
* @return array
*/
public function rest_response($item_arr, $request) {
if($request->get_method() == 'GET' && substr($request->get_route(), 0, strlen('/tainacan/v2')) == '/tainacan/v2') {
if($request->get_method() == 'GET' && $this->is_tainacan_request($request)) {
if($exposer = $this->request_has_mapper($request)) {
if(substr($request->get_route(), 0, strlen('/tainacan/v2/items')) == '/tainacan/v2/items') { //TODO do it at rest not here
$repos_items = \Tainacan\Repositories\Items::get_instance();
@ -152,6 +169,15 @@ class Exposers {
return $ret;
}
/**
* check if is a tainacan request
* @param \WP_REST_Request $request
* @return boolean
*/
public function is_tainacan_request($request) {
return substr($request->get_route(), 0, strlen('/tainacan/v2')) == '/tainacan/v2';
}
/**
* adapt request response to exposer type
* @param \WP_REST_Response $response
@ -160,11 +186,17 @@ class Exposers {
* @return \WP_REST_Response
*/
public function rest_request_after_callbacks( $response, $handler, $request ) {
if($request->get_method() == 'GET' && substr($request->get_route(), 0, strlen('/tainacan/v2')) == '/tainacan/v2') {
if($exposer = $this->request_has_type($request)) {
return $exposer->rest_request_after_callbacks($response, $handler, $request);
}
}
if($this->is_tainacan_request($request) && $response instanceof \WP_REST_Response ) {
if($request->get_method() == 'GET') {
if($exposer = $this->request_has_type($request)) {
return $exposer->rest_request_after_callbacks($response, $handler, $request);
}
} elseif($request->get_method() == 'POST') {
if($mapper = $this->request_has_mapper($request)) {
return $this->create_mapped_fields( $response, $handler, $request, $mapper );
}
}
}
// default JSON response
return $response;
}
@ -178,7 +210,7 @@ class Exposers {
return in_array($this->check_class_name($type), $this->types);
}
/**
* Return Type with request has type, false otherwise
* Return Type if request has type, false otherwise
* @param \WP_REST_Request $request
* @return Types\Type|boolean false
*/
@ -231,4 +263,80 @@ class Exposers {
}
return false; // No mapper need, using Tainacan defautls
}
/**
* Add mappers data to translations
* @param array $i18n_strings
* @return array
*/
public function mappers_i18n($i18n_strings) {
foreach ($this->mappers as $mapper) {
$obj = new $mapper;
$i18n_strings[$obj->slug] = $obj->slug; // For url breadcrumb translations
$i18n_strings[$obj->name] = $obj->name;
}
return $i18n_strings;
}
/**
* Return list of registered mappers
* @param string $output output format, ARRAY_N or OBJECT
*/
public function get_mappers($output = ARRAY_N) {
$ret = [];
switch ($output) {
case OBJECT:
foreach ($this->mappers as $mapper) {
$ret[] = new $mapper;
}
break;
case ARRAY_N:
default:
return $this->mappers;
break;
}
return $ret;
}
/**
*
* @param \WP_REST_Response $response
* @param \WP_REST_Server $handler
* @param \WP_REST_Request $request
* @param Mapper $mapper
*/
public function create_mapped_fields( $response, $handler, $request, $mapper ) {
if($response instanceof \WP_REST_Response && $response->get_status() == 201) {
$collection_array = $response->get_data();
$id = $collection_array['id'];
$mapper_fields = $mapper->metadata;
if(is_array($mapper_fields) ) {
$Tainacan_Fields = \Tainacan\Repositories\Fields::get_instance();
foreach ($mapper_fields as $slug => $mapper_field) {
if(array_key_exists('core_field', $mapper_field) && $mapper_field['core_field'] != false) continue;
$field = new \Tainacan\Entities\Field();
if(
array_key_exists('field_type', $mapper_field) &&
$mapper_field['field_type'] != false &&
class_exists($mapper_field['field_type'])
) {
$field->set_field_type($mapper_field['field_type']);
} else {
$field->set_field_type('Tainacan\Field_Types\Text');
}
$field->set_name($mapper_field['label']);
$field->set_description($mapper_field['URI']);
$field->set_exposer_mapping([
$mapper->slug => $slug
]);
$field->set_status('publish');
$field->set_collection_id($id);
$field->set_slug($slug);
if($field->validate()) $Tainacan_Fields->insert($field);
}
}
}
return $response;
}
}

View File

@ -29,15 +29,17 @@ class Dublin_Core extends Mapper {
],
'date' => [
'URI' => 'http://purl.org/dc/elements/1.1/date',
'label' => 'Date'
'label' => 'Date',
'field_type' => 'date'
],
'description' => [
'URI' => 'http://purl.org/dc/elements/1.1/description',
'label' => 'Description'
'label' => 'Description',
'core_field' => 'description'
],
'format' => [
'URI' => 'http://purl.org/dc/elements/1.1/format',
'label' => 'Format'
'label' => 'Format',
],
'identifier' => [
'URI' => 'http://purl.org/dc/elements/1.1/identifier',
@ -69,7 +71,8 @@ class Dublin_Core extends Mapper {
],
'title' => [
'URI' => 'http://purl.org/dc/elements/1.1/title',
'label' => 'Title'
'label' => 'Title',
'core_field' => 'title'
],
'type' => [
'URI' => 'http://purl.org/dc/elements/1.1/type',

View File

@ -7,8 +7,42 @@ abstract class Mapper {
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
/**
* array of supported metadata, false for not validade the list format:
* ['slug'] => [
* 'URI' => 'http://...', // URI of the field description
* 'label' => 'Label', // Label to show on UI
* 'field_type' => 'date', // Tainacan recomended field type, default text
* 'core_field' => 'description' // if have a core tainacan field, what?
* ['date' => [
* 'URI' => 'http://purl.org/dc/elements/1.1/date',
* 'label' => 'Date',
* 'field_type' => 'date'
* ],
* 'description' => [
* 'URI' => 'http://purl.org/dc/elements/1.1/description',
* 'label' => 'Description',
* 'core_field' => 'description'
* ]]
* @var array
*/
public $metadata = false;
public $prefix = ''; // Tag prefix like "dc:"
public $sufix = ''; // Tag sufix
public $header = false; // API response header or file header to be used with
public function _toArray() {
return [
'slug' => $this->slug,
'name' => $this->name,
'allow_extra_fields' => $this->allow_extra_fields,
'context_url' => $this->context_url,
'metadata' => $this->metadata,
'prefix' => $this->prefix,
'sufix' => $this->sufix,
'header' => $this->header
];
}
}

View File

@ -12,7 +12,8 @@ class Csv extends Type {
* List of supported mappers
* @var array
*/
public $mappers = ['Value'];
public $mappers = ['Value'];
public $slug = 'csv'; // type slug for url safe
/**
*

View File

@ -8,7 +8,8 @@ namespace Tainacan\Exposers\Types;
*/
class Html extends Type {
public $mappers = ['Value'];
public $mappers = ['Value'];
public $slug = 'html'; // type slug for url safe
/**
*

View File

@ -9,6 +9,7 @@ namespace Tainacan\Exposers\Types;
class OAI_PMH extends Xml {
public $mappers = ['Dublin Core'];
public $slug = 'oai-pmh'; // type slug for url safe
const XML_OAI_DC_NAMESPACE = "http://www.openarchives.org/OAI/2.0/oai_dc/";
/**

View File

@ -9,6 +9,7 @@ namespace Tainacan\Exposers\Types;
class Txt extends Type {
public $mappers = ['Value'];
public $slug = 'txt'; // type slug for url safe
/**
*

View File

@ -9,6 +9,8 @@ namespace Tainacan\Exposers\Types;
abstract class Type {
protected $mappers = true; // List of supported mapper, leave true for all
protected $extension = 'tnc'; // extension sufix for multi operation system compatibility
public $slug = ''; // type slug for url safe
/**
* Change response after api callbacks
@ -19,7 +21,14 @@ abstract class Type {
*/
public abstract function rest_request_after_callbacks( $response, $handler, $request );
/**
* Return list of supported mappers for this type
*/
public function get_mappers() {
return apply_filters('tainacan-exporser-type-mappers', $this->mappers, $this);
}
public function get_extension() {
return $this->extension;
}
}

View File

@ -7,6 +7,14 @@ namespace Tainacan\Exposers\Types;
*
*/
class Xml extends Type {
/**
* {@inheritdoc}
* @see \Tainacan\Exposers\Types\Type::extension
* @var string
*/
protected $extension = 'xml';
public $slug = 'xml'; // type slug for url safe
/**
*
* {@inheritDoc}

View File

@ -0,0 +1,36 @@
<?php
namespace Tainacan;
class Background_Importer extends Background_Process {
/**
* @var string
*/
protected $action = 'import';
function task($data, $key) {
$className = $data['class_name'];
if (class_exists($className)) {
$object = new $className($data);
$runned = $object->run();
$this->write_log($key, $object->get_log());
$this->write_error_log($key, $object->get_error_log());
if (false === $runned) {
return false;
}
return $object->_to_Array();
}
return false;
}
}
?>

View File

@ -5,8 +5,12 @@ use Tainacan;
class CSV extends Importer {
public function __construct() {
parent::__construct();
protected $manual_mapping = true;
protected $manual_collection = true;
public function __construct($attributes = array()) {
parent::__construct($attributes);
$this->set_default_options([
'delimiter' => ','
@ -17,7 +21,7 @@ class CSV extends Importer {
/**
* @inheritdoc
*/
public function get_fields(){
public function get_source_fields(){
$file = new \SplFileObject( $this->tmp_file, 'r' );
$file->seek(0 );
return $file->fgetcsv( $this->get_option('delimiter') );
@ -27,9 +31,9 @@ class CSV extends Importer {
/**
* @inheritdoc
*/
public function process_item( $index ){
public function process_item( $index, $collection_definition ){
$processedItem = [];
$headers = $this->get_fields();
$headers = $this->get_source_fields();
// search the index in the file and get values
$file = new \SplFileObject( $this->tmp_file, 'r' );
@ -50,6 +54,8 @@ class CSV extends Importer {
foreach ($headers as $index => $header) {
$processedItem[ $header ] = $values[ $index ];
}
$this->set_progress_current($index+1);
return $processedItem;
}
@ -57,7 +63,7 @@ class CSV extends Importer {
/**
* @inheritdoc
*/
public function get_total_items_from_source(){
public function get_progress_total_from_source(){
$file = new \SplFileObject( $this->tmp_file, 'r' );
$file->seek(PHP_INT_MAX);
// -1 removing header

View File

@ -0,0 +1,26 @@
<?php
namespace Tainacan;
class Importer_Handler {
function __construct() {
$this->bg_importer = new Background_Importer();
}
function add_to_queue($importer_object) {
$data = $importer_object->_to_Array();
$this->bg_importer->data($data)->save()->dispatch();
}
}
global $Tainacan_Importer_Handler;
$Tainacan_Importer_Handler = new Importer_Handler();
?>

View File

@ -5,98 +5,198 @@ use Tainacan\Entities;
abstract class Importer {
private $id;
private $processed_items = [];
/**
* indicates wether this importer will create all the fields collection and set the mapping
* without user interaction
*
* if set to true, user will have the ability to choose to create a new collection upon importing.
*
* The importer will have to implement the create_fields_and_mapping() method.
*
* @var bool
*/
protected $import_structure_and_mapping = false;
/**
* The collection the items are going to be imported to.
* The ID for this importer session
*
* When creating a new importer session via API, an id is returned and used to access this
* importer instance in the SESSION array
*
* @var \Tainacan\Entities\Collection
* @var identifier
*/
public $collection;
/**
* The mapping from the source metadata structure to the Field Ids of the destination collection
*
* The format is an array where the keys are the field IDs of the destination collection and the
* values are the identifier from the source. This coulb be an ID or a string or whatever the importer finds appropriate to http_persistent_handles_clean
*
* @var array
*/
public $mapping;
private $repository_mapping;
private $id;
/**
* The path to the temporary file created when user uploads a file
* @var string
*/
public $tmp_file;
protected $tmp_file;
/**
* The total number of items to be imported.
* @var int
*/
protected $total_items;
/**
* THe number of items to be processes in each step
* @var int
*/
private $items_per_step = 100;
/**
* The index of the item to start the import in the next step.
* Wether Tainacan must present the user with an interface to manually map
* the metadata from the source to the target collection.
*
* (items are imported in a series of steps, via ajax, to avoid timeout)
* @var int
* If set to true in the child class, it must implement the method
* get_source_fields() to return the field found in the source.
*
* Note that this will only work when importing items to one single collection.
* @var bool
*/
private $start = 0;
private $inside_step_pointer = 0;
protected $manual_mapping = false;
/**
* Wether Tainacan will let the user choose a destination collection.
*
* If set to true, the API endpoints will handle Collection creation and will assign it to
* the importer object using add_collection() method.
*
* Otherwise, the child importer class must create the collections and add them to the collections property also
* using add_collection()
*
* @var bool
*/
protected $manual_collection = true;
/**
* The log with everything that happened during the import process. It generates a report afterwards
* The total number of iterations to be imported.
*
* if not possible to calculate, inform 0 (zero) and no progress bar will be displayed.
*
* @var int
*/
protected $progress_total;
protected $progress_current;
/**
* This array holds the structure that the default step 'process_collections' will handle.
*
* Its an array of the target collections, with their IDs, an identifier from the source, the total number of items to be imported, the mapping array
* from the source structure to the ID of the metadata fields in tainacan
*
* The format of the map is an array where the keys are the metadata IDs of the destination collection and the
* values are the identifier from the source. This could be an ID or a string or whatever the importer finds appropriate to handle
*
* The source_id can be anyhting you like, that helps you relate this collection to your source.
*
* Example of the structure of this propery for one collection:
* 0 => [
* 'id' => 12,
* 'map' => [
* 30 => 'column1'
* 31 => 'column2'
* ],
* 'total_items' => 1234,
* 'source_id' => 55
* ],
*
* use add_collection() and remove_collection() to interact with thiis array.
*
*
* @var array
*/
protected $collections = [];
/**
* Stores the options for the importer. Each importer might use this property to save
* their own specific option
* @var array
*/
public $logs = [];
private $options = [];
private $default_options = [];
/**
* Stores the default options for the importer options
* @var array
*/
protected $default_options = [];
private $accpets = [
'file' => true,
'url' => false,
];
private $is_repository = false;
private $steps = [];
/**
* Declares what are the steps the importer will run, in the right order.
*
* By default, there is only one step, and the callback is the process_collections method
* that process items for the collections in the collections array.
*
* Child classes may declare as many steps as they want and can keep this default step to use
* this method for import the items. But it is optional.
*
* @var array
*/
protected $steps = [
[
'name' => 'Import Items',
'callback' => 'process_collections'
]
];
/**
* Transients is used to store temporary data to be used accross multiple requests
*
* Add and remove transient data using add_transient() and delete_transient() methods
*
* Transitens can be strings, numbers or arrays. Avoid storing objects.
*
* @var array
*/
private $transients = [];
private $current_step = 0;
private $in_step_count = 0;
private $current_collection = 0;
private $current_collection_item = 0;
private $url = '';
private $log = [];
private $error_log = [];
/**
* List of attributes that are saved in DB and that are used to
* reconstruct the object
* @var array
*/
private $array_attributes = [
'url',
'current_collection_item',
'current_collection',
'in_step_count',
'current_step',
'transients',
'options',
'collections',
'tmp_file'
];
public function __construct() {
public function __construct($attributess = array()) {
if (!session_id()) {
@session_start();
}
$this->id = uniqid();
$_SESSION['tainacan_importer'][$this->get_id()] = $this;
if (!empty($attributess)) {
foreach ($attributess as $attr => $value) {
$method = 'set_' . $attr;
if (method_exists($this, $method)) {
$this->$method($value);
}
}
}
}
public function _to_Array() {
$return = [];
foreach ($this->array_attributes as $attr) {
$method = 'get_' . $attr;
$return[$attr] = $this->$method();
}
$return['class_name'] = get_class($this);
return $return;
}
/////////////////////
// Getters and setters
/**
* @return string
*/
@ -133,82 +233,141 @@ abstract class Importer {
return false;
}
/**
* @return array Mapping
*/
public function get_mapping(){
return $this->mapping;
public function get_current_step() {
return $this->current_step;
}
public function set_current_step($value) {
$this->current_step = $value;
}
public function get_in_step_count() {
return $this->in_step_count;
}
public function set_in_step_count($value) {
$this->in_step_count = $value;
}
public function get_current_collection() {
return $this->current_collection;
}
public function set_current_collection($value) {
$this->current_collection = $value;
}
public function get_current_collection_item() {
return $this->current_collection_item;
}
public function set_current_collection_item($value) {
$this->current_collection_item = $value;
}
public function get_tmp_file(){
return $this->tmp_file;
}
/**
* @return array Array with ids inserted in Tainacan
*/
public function get_processed_items(){
return $this->processed_items;
public function set_tmp_file($filepath){
$this->tmp_file = $filepath;
}
/**
* @return array the last index from source
public function get_progress_current() {
return $this->progress_current;
}
public function set_progress_current($value) {
$this->progress_current = $value;
}
public function get_collections() {
return $this->collections;
}
public function set_collections($value) {
$this->collections = $value;
}
/**
* Gets the options for this importer, including default values for options
* that were not set yet.
* @return array Importer options
*/
public function get_logs(){
return $this->logs;
public function get_options() {
return array_merge($this->default_options, $this->options);
}
/**
* @param Tainacan\Entities\Collection $collection
*/
public function set_collection( Entities\Collection $collection ){
$this->collection = $collection;
/**
* Set the options array
* @param array $options
*/
public function set_options($options) {
$this->options = $options;
}
/**
* Set the default options values.
*
* Must be called from the __construct method of the child importer class to set default values.
*
* @param array $options
*/
protected function set_default_options($options) {
$this->default_options = $options;
}
public function set_steps($steps) {
$this->steps = $steps;
}
/**
* save an associative array with tainacan field id as index and field from source as value
public function get_steps() {
return $this->steps;
}
/**
* return the total progress number to calculate progress
*
* @param array $mapping Mapping importer-fields
* @return int Total of items
*/
public function set_mapping( $mapping){
if(!empty($mapping))
{
$this->mapping = $mapping;
}
}
public function set_repository_mapping( $mapping, $item_id ){
if(!empty($mapping) && !empty($item_id))
{
$this->repository_mapping[$item_id] = $mapping;
}else return false;
}
public function get_repository_mapping($item_id)
{
if(!empty($item_id))
{
return $this->repository_mapping[$item_id];
}else return false;
}
/**
* set how many items should be processes in each step
*
* @param $size The total of items
*/
public function set_items_per_step( $size ){
$this->items_per_step = $size;
}
/**
* @param int $start the first index to init the process
*/
public function set_start( $start ){
$this->start = $start;
}
public function get_progress_total() {
if ( !isset( $this->progress_total ) ) {
if ( method_exists($this, 'get_progress_total_from_source') ) {
$this->progress_total = $this->get_progress_total_from_source();
} else {
$this->progress_total = 0;
}
}
return $this->progress_total;
}
private function get_transients() {
return $this->transients;
}
private function set_transients(array $data) {
$this->transients = $data;
}
public function get_log() {
return $this->log;
}
public function get_error_log() {
return $this->error_log;
}
////////////////////////////////////
// Utilities
/**
* @param $file File to be managed by importer
* @return bool
*/
public function set_file( $file ){
public function add_file( $file ){
$new_file = $this->upload_file( $file );
if ( is_numeric( $new_file ) ) {
$this->tmp_file = get_attached_file( $new_file );
@ -216,31 +375,36 @@ abstract class Importer {
return false;
}
}
public function set_inside_step_pointer($step_pointer)
{
if(is_numeric($step_pointer) && $step_pointer >= 0)
{
$this->inside_step_pointer = $step_pointer;
}else
{
$this->inside_step_pointer = 0;
}
}
public function get_inside_step_pointer()
{
return $this->inside_step_pointer;
}
/**
* log the actions from importer
*
* @param $type
* @param $message
* @param $messagelog
*/
public function add_log($type, $message ){
$this->logs[] = [ 'type' => $type, 'message' => $message ];
public function add_log($message ){
$this->log[] = $message;
}
public function add_error_log($message ){
$this->error_log[] = $message;
}
public function add_collection(array $collection) {
if (isset($collection['id'])) {
$this->remove_collection($collection['id']);
$this->collections[] = $collection;
}
}
public function remove_collection($col_id) {
foreach ($this->get_collections() as $index => $col) {
if ($col['id'] == $col_id) {
unset($this->collections[$index]);
break;
}
}
}
/**
* internal function to upload the file
@ -268,55 +432,9 @@ abstract class Importer {
$file = fopen( $this->get_id().'.txt', 'w' );
fwrite( $file, $tmp['body'] );
fclose( $file );
return $this->set_file( $this->get_id().'.txt' );
return $this->add_file( $this->get_id().'.txt' );
}
}
/**
* get the fields of file/url to allow mapping
* should return an array
*
* @return array $fields_source the fields from the source
*/
abstract public function get_fields();
/**
* get values for a single item
*
* @param $index
* @return array with field_source's as the index and values for the
* item
*
* Ex: [ 'Field1' => 'value1', 'Field2' => [ 'value2','value3' ]
*/
abstract public function process_item( $index );
/**
* return the all items found
*
* @return int Total of items
*/
public function get_total_items() {
if ( !isset( $this->total_items ) ) {
$this->total_items = $this->get_total_items_from_source();
}
return $this->total_items;
}
/**
* Method implemented by the child importer class to return the number of items to be imported
* @return int
*/
abstract public function get_total_items_from_source();
/**
* Gets the options for this importer, including default values for options
* that were not set yet.
* @return array Importer options
*/
public function get_options() {
return array_merge($this->default_options, $this->options);
}
/**
* Gets one option from the options array.
@ -331,25 +449,6 @@ abstract class Importer {
return isset($options[$key]) ? $options[$key] : '';
}
/**
* Set the default options values.
*
* Must be called from the __construct method of the child importer class to set default values.
*
* @param array $options
*/
protected function set_default_options($options) {
$this->default_options = $options;
}
/**
* Set the options array
* @param array $options
*/
public function set_options($options) {
$this->options = $options;
}
/**
* Adds a new method accepeted by the importer
*
@ -366,38 +465,6 @@ abstract class Importer {
return false;
}
protected function get_start()
{
return $this->start;
}
protected function get_items_per_step()
{
return $this->items_per_step;
}
/**
* Sets importer as repository importer
*/
public function set_repository()
{
$this->is_repository = true;
}
public function set_steps($steps)
{
$this->steps =$steps;
}
public function is_finished()
{
if($this->current_step >= count($this->steps))
{
return true;
}
return false;
}
/**
* Removes method accepeted by the importer
*
@ -414,59 +481,180 @@ abstract class Importer {
return false;
}
/**
* process a limited size of items
*
* @param int $start the index of the item to start processing from
*/
public function process( $start ){
$end = $start + $this->items_per_step;
public function add_transient($key, $data) {
$this->transients[$key] = $data;
}
public function delete_transient($key) {
if (isset($this->transients[$key]))
unset($this->transients[$key]);
}
public function get_transient($key) {
if (isset($this->transients[$key]))
return $this->transients[$key];
return null;
}
while ( $start < $end && count( $this->get_processed_items() ) < $this->get_total_items() ) {
$processed_item = $this->process_item( $start );
if( $processed_item) {
$this->insert( $start, $processed_item );
} else {
$this->add_log('error', 'failed on item '.$start );
break;
}
$start++;
public function is_finished()
{
if($this->current_step >= count($this->steps))
{
return true;
}
$this->set_start($start);
return false;
}
///////////////////////////////
// Abstract methods
/**
* get the fields of file/url to allow mapping
* should return an array
*
* Used when $manual_mapping is set to true, to build the mapping interface
*
* @return array $fields_source the fields from the source
*/
public function get_source_fields() {}
/**
* get values for a single item
*
* @param $index
* @return array with field_source's as the index and values for the
* item
*
* Ex: [ 'Field1' => 'value1', 'Field2' => [ 'value2','value3' ]
*/
abstract public function process_item( $index, $collection_id );
/**
* Method implemented by the child importer class to return the total number of interations the importer must run
*
* Used to build the progress bar
*
* @return int
*/
public function get_progress_total_from_source() {}
////////////////////////////////////////
// Core methods
/**
* process an item from the collections queue
*
*/
public function process_collections() {
$current_collection = $this->get_current_collection();
$collections = $this->get_collections();
$collection_definition = isset($collections[$current_collection]) ? $collections[$current_collection] : false;
$current_collection_item = $this->get_current_collection_item();
$processed_item = $this->process_item( $current_collection_item, $collection_definition );
if( $processed_item) {
$this->insert( $processed_item, $current_collection );
} else {
$this->add_error_log('failed on item '. $start );
}
return $this->next_item();
}
protected function next_item() {
$current_collection = $this->get_current_collection();
$current_collection_item = $this->get_current_collection_item();
$collections = $this->get_collections();
$collection = $collections[$current_collection];
$current_collection_item ++;
$this->set_current_collection_item($current_collection_item);
if ($current_collection_item >= $collection['total_items']) {
return $this->next_collection();
}
return $current_collection_item;
}
protected function next_collection() {
$current_collection = $this->get_current_collection();
$collections = $this->get_collections();
$this->set_current_collection_item(0);
$current_collection ++;
if (isset($collections[$current_collection])) {
$this->set_current_collection($current_collection);
return $current_collection;
}
return false;
}
protected function next_step() {
$current_step = $this->get_current_step();
$steps = $this->get_steps();
$current_step ++;
$this->set_current_step($current_step);
if (isset($steps[$current_step])) {
return $current_step;
}
return false;
}
/**
* insert processed item from source to Tainacan
*
* @param int $index the source id unique for the item
* @param array $processed_item Associative array with field source's as index with
* its value or values
* @return Tainacan\Entities\Item Item inserted
*/
public function insert( $index, $processed_item ){
$Tainacan_Fields = \Tainacan\Repositories\Fields::get_instance();
public function insert( $processed_item, $collection_index ) {
$collections = $this->get_collections();
$collection_definition = isset($collections[$collection_index]) ? $collections[$collection_index] : false;
if ( !$collection_definition || !is_array($collection_definition) || !isset($collection_definition['id']) || !isset($collection_definition['map']) ) {
$this->add_error_log('Collection misconfigured');
return false;
}
$collection = \Tainacan\Repositories\Collections::get_instance()->fetch($collection_definition['id']);
$Tainacan_Fields = \Tainacan\Repositories\Fields::get_instance();
$Tainacan_Item_Metadata = \Tainacan\Repositories\Item_Metadata::get_instance();
$Tainacan_Items = \Tainacan\Repositories\Items::get_instance();
$isUpdate = ( is_array( $this->processed_items ) && isset( $this->processed_items[ $index ] ) )
? $this->processed_items[ $index ] : 0;
$item = new Entities\Item( $isUpdate );
$item = new Entities\Item();
$itemMetadataArray = [];
if( !isset( $this->mapping ) ){
$this->add_log('error','Mapping is not set');
return false;
}
if( is_array( $processed_item ) ){
foreach ( $processed_item as $field_source => $values ){
$tainacan_field_id = array_search( $field_source, $this->mapping );
$tainacan_field_id = array_search( $field_source, $collection_definition['map'] );
$field = $Tainacan_Fields->fetch( $tainacan_field_id );
if( $field instanceof Entities\Field ){
$singleItemMetadata = new Entities\Item_Metadata_Entity( $item, $field);
$singleItemMetadata = new Entities\Item_Metadata_Entity( $item, $field); // *empty item will be replaced by inserted in the next foreach
$singleItemMetadata->set_value( $values );
$itemMetadataArray[] = $singleItemMetadata;
}
@ -474,97 +662,85 @@ abstract class Importer {
}
}
if( !empty( $itemMetadataArray ) && $this->collection instanceof Entities\Collection ){
$item->set_collection( $this->collection );
if( !empty( $itemMetadataArray ) && $collection instanceof Entities\Collection ){
$item->set_collection( $collection );
if( $item->validate() ){
$insertedItem = $Tainacan_Items->insert( $item );
} else {
$this->add_log( 'error', 'Item ' . $index . ': ' ); // TODO add the $item->get_errors() array
$this->add_error_log( 'Item ' . $index . ': ' ); // TODO add the $item->get_errors() array
return false;
}
foreach ( $itemMetadataArray as $itemMetadata ) {
$itemMetadata->set_item( $insertedItem );
$itemMetadata->set_item( $insertedItem ); // *I told you
if( $itemMetadata->validate() ){
$result = $Tainacan_Item_Metadata->insert( $itemMetadata );
} else {
$this->add_log( 'error', 'Item ' . $index . ' on field '. $itemMetadata->get_field()->get_name()
$this->add_error_log( 'Item ' . $insertedItem->get_id() . ' on field '. $itemMetadata->get_field()->get_name()
.' has error ' . $itemMetadata->get_errors() );
continue;
}
if( $result ){
$values = ( is_array( $itemMetadata->get_value() ) ) ? implode( PHP_EOL, $itemMetadata->get_value() ) : $itemMetadata->get_value();
$this->add_log( 'success', 'Item ' . $index .
$this->add_log( 'Item ' . $insertedItem->get_id() .
' has inserted the values: ' . $values . ' on field: ' . $itemMetadata->get_field()->get_name() );
} else {
$this->add_log( 'error', 'Item ' . $index . ' has an error' );
$this->add_error_log( 'Item ' . $insertedItem->get_id() . ' has an error' );
}
}
$insertedItem->set_status('publish' );
// inserted the id on processed item with its index as array index
$this->processed_items[ $index ] = $insertedItem->get_id();
if($insertedItem->validate()) {
$insertedItem = $Tainacan_Items->update( $insertedItem );
} else {
$this->add_log( 'error', 'Item ' . $index . ': ' . $insertedItem->get_errors()[0]['title'] ); // TODO add the $item->get_errors() array
//error_log(print_r($insertedItem->get_errors(), true));
//$this->add_error_log( 'Item ' . $index . ': ' . $insertedItem->get_errors()[0]['title'] ); // TODO add the $item->get_errors() array
return false;
}
return $insertedItem;
} else {
$this->add_log( 'error', 'Collection not set');
$this->add_error_log( 'Collection not set');
return false;
}
}
/**
* run the process
* runs one iteration
*/
public function run(){
if($this->is_repository && $this->current_step < count($this->steps))
{
//$process_name = key($this->steps);
$function_name = current($this->steps);
$inside_step_pointer = $this->{$function_name}();//If unlike numeric this means that still there is stuff to process
if($inside_step_pointer === false || (!is_numeric($inside_step_pointer) || $inside_step_pointer < 0))
{
//Move on to the next step
next($this->steps);
$this->current_step++;
$this->set_inside_step_pointer(0);
}else if(is_numeric($inside_step_pointer) && $inside_step_pointer > 0)
{
$this->set_inside_step_pointer($inside_step_pointer);
}
}
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);
if ($this->is_finished()) {
return false;
}
$steps = $this->get_steps();
$current_step = $this->get_current_step();
$method_name = $steps[$current_step]['callback'];
$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());
}
if (method_exists($this, $method_name)) {
$result = $this->$method_name();
} else {
$this->add_error_log( 'Callback not found for step ' . $steps[$current_step]['name']);
$result = false;
}
if($result === false || (!is_numeric($result) || $result < 0)) {
//Move on to the next step
$this->set_in_step_count(0);
$return = $this->next_step();
} else if(is_numeric($result) && $result > 0) {
$this->set_in_step_count($result);
$return = $result;
}
return $return;
}
}

View File

@ -8,13 +8,15 @@
namespace Tainacan\Importer;
class Old_Tainacan extends Importer
{
class Old_Tainacan extends Importer{
protected $manual_mapping = true;
protected $manual_collection = true;
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";
@ -30,15 +32,39 @@ class Old_Tainacan extends Importer
'socialdb_property_fixed_attachments'
],
$steps = [
'Creating all categories' => 'create_categories',
'Create empty collections' => 'create_collections',
'Creating relationships metadata' => 'create_relationships_meta',
'Create repository metadata' => 'treat_repo_meta',
'Create collections metadata' => 'treat_collection_metas',
'Create collections items' => 'create_collection_items',
"Finishing" => 'clear'
], $tainacan_api_address, $wordpress_api_address;
[
'name' => 'Create categories, collections and metadata',
'callback' => 'create_structure'
],
[
'name' => 'Import Items',
'callback' => 'process_collections'
],
[
'name' => 'Finishing',
'callback' => 'clear'
]
], $tainacan_api_address, $wordpress_api_address, $actual_collection;
/**
* create structure tainacan
*/
public function create_structure(){
$this->create_categories();
$this->create_collections();
$this->create_relationships_meta();
$this->treat_repo_meta();
$this->treat_collection_metas();
return false;
}
/**
* helper method to set the actual collection in loop
*/
public function set_actual_collection($collection){
$this->actual_collection = $collection;
}
public function create_categories()
{
@ -53,8 +79,10 @@ class Old_Tainacan extends Importer
$categories_array = $this->remove_same_name($categories_array);
list($inside_step_pointer, $end) = $this->get_begin_end($categories_array);
if($inside_step_pointer === false) return false;
// list($inside_step_pointer, $end) = $this->get_begin_end($categories_array);
// if($inside_step_pointer === false) return false;
$inside_step_pointer = 0;
$end = ( $categories_array ) ? count( $categories_array) : 0;
$created_categories = [];
while($inside_step_pointer < $end)
@ -96,8 +124,10 @@ class Old_Tainacan extends Importer
$created_collections = [];
if($collections_array)
{
list($inside_step_pointer, $end) = $this->get_begin_end($collections_array);
if($inside_step_pointer === false) return false;
// list($inside_step_pointer, $end) = $this->get_begin_end($collections_array);
// if($inside_step_pointer === false) return false;
$inside_step_pointer = 0;
$end = ( $collections_array ) ? count( $collections_array) : 0;
while($inside_step_pointer < $end)
{
@ -192,17 +222,19 @@ class Old_Tainacan extends Importer
$created_categories = $this->read_from_file("categories");
$relationships = $this->read_from_file("relationships");
list($inside_step_pointer, $end) = $this->get_begin_end($created_collections);
if($inside_step_pointer === false) return false;
// list($inside_step_pointer, $end) = $this->get_begin_end($created_collections);
// if($inside_step_pointer === false) return false;
$inside_step_pointer = 0;
$end = ( $created_collections ) ? count( $created_collections) : 0;
$Tainacan_Fields = \Tainacan\Repositories\Fields::get_instance();
$Fields_Repository = \Tainacan\Repositories\Fields::get_instance();
$Repository_Collections = \Tainacan\Repositories\Collections::get_instance();
for($i = 0; $i < $inside_step_pointer; $i++)
{
next($created_collections);
}
//for($i = 0; $i < $inside_step_pointer; $i++)
// {
// next($created_collections);
// }
while($inside_step_pointer < $end)
{
@ -210,13 +242,19 @@ class Old_Tainacan extends Importer
$new_collection_id = $collection_info['new_id'];
$old_collection_id = key($created_collections);
$collection = $Repository_Collections->fetch($new_collection_id);
$this->set_collection($collection);
$this->set_actual_collection($collection);
$file_fields = $this->get_collection_fields($old_collection_id);
$mapping = $this->create_collection_meta($file_fields, $Fields_Repository, $Tainacan_Fields, $created_repository_fields, $created_categories, $relationships);
$this->set_repository_mapping($mapping, $old_collection_id);
$this->add_collection([
'id' => $new_collection_id,
'map' => $mapping,
'total_items' => $this->get_total_items_from_source( $old_collection_id )
]);
// $this->set_repository_mapping($mapping, $old_collection_id);
next($created_collections);
$inside_step_pointer++;
}
@ -266,7 +304,7 @@ class Old_Tainacan extends Importer
$mapping[$new_id] = $created_repository_fields[$old_field_id]['name'];
}else
{
$fields = $Tainacan_Fields->fetch_by_collection( $this->collection, [], 'OBJECT' );
$fields = $Tainacan_Fields->fetch_by_collection( $this->actual_collection, [], 'OBJECT' );
foreach ($fields as $field)
{
@ -353,7 +391,7 @@ class Old_Tainacan extends Importer
$newField->set_collection_id('default');
}else
{
$newField->set_collection($this->collection);
$newField->set_collection($this->actual_collection);
}
}else //Set compound as field parent
{
@ -399,7 +437,7 @@ class Old_Tainacan extends Importer
$new_collection_id = $collection_info['new_id'];
$old_collection_id = key($created_collections);
$collection = $Repository_Collections->fetch($new_collection_id);
$this->set_collection($collection);
$this->set_actual_collection($collection);
$mapping = $this->get_repository_mapping($old_collection_id);
$this->set_mapping($mapping);
@ -481,7 +519,7 @@ class Old_Tainacan extends Importer
private function get_begin_end($items)
{
$inside_step_pointer = $this->get_inside_step_pointer();
$inside_step_pointer = $this->get_in_step_count();
$total_items = count($items);
if($inside_step_pointer >= $total_items)
@ -489,7 +527,7 @@ class Old_Tainacan extends Importer
return [false, false];
}
$end = $this->get_inside_step_pointer() + $this->get_items_per_step();
$end = $this->get_in_step_count() + $this->get_items_per_step();
if($end > $total_items)
{
$end = $total_items;
@ -552,7 +590,7 @@ class Old_Tainacan extends Importer
{
if(is_wp_error($result))
{
$this->add_log('error', $result->get_error_message());
$this->add_error_log($result->get_error_message());
return false;
}else if(isset($result['body']))
{
@ -653,18 +691,19 @@ class Old_Tainacan extends Importer
* get values for a single item
*
* @param $index
* @param $collection_id
* @return array with field_source's as the index and values for the
* item
*
* Ex: [ 'Field1' => 'value1', 'Field2' => [ 'value2','value3' ]
*/
public function process_item($index)
public function process_item( $index, $collection_id )
{
$processedItem = [];
$headers = $this->get_fields();
// search the index in the file and get values
$file = new \SplFileObject( $this->tmp_file, 'r' );
/*$file = new \SplFileObject( $this->tmp_file, 'r' );
$file_content = unserialize($file->fread($file->getSize()));
$values = $file_content->items[$index];
foreach ($headers as $header)
@ -687,7 +726,7 @@ class Old_Tainacan extends Importer
$processedItem[$header] = $values->item->{$header};
}
}
}
}*/
return $processedItem;
}
@ -733,7 +772,7 @@ class Old_Tainacan extends Importer
$newField->set_field_type('Tainacan\Field_Types\\'.$type);
$newField->set_collection($this->collection);
$newField->set_collection($this->actual_collection);
$newField->validate(); // there is no user input here, so we can be sure it will validate.
$newField = $fields_repository->insert($newField);
@ -759,19 +798,29 @@ class Old_Tainacan extends Importer
{
}
/**
* Method implemented by the child importer class to return the number of items to be imported
* @return int
*/
public function get_total_items_from_source()
{
if(!isset($this->tmp_file)){
return 0;
}
$file = new \SplFileObject( $this->tmp_file, 'r' );
$file_content = unserialize($file->fread($file->getSize()));
return $this->total_items = $file_content->found_items;
public function get_total_items_from_source( $collection_id ) {
$info = wp_remote_get( $this->get_url() . $this->tainacan_api_address . "/collections/".$collection_id."/items" );
$info = json_decode($info['body']);
return $this->total_items = $info->found_items;
}
/**
* Method implemented by the child importer class to return the number of items to be imported
* @return int
*/
public function get_progress_total_from_source(){
}
/**
* Method implemented by the child importer class to return the number of items to be imported
* @return int
*/
public function get_source_fields(){
}
}

View File

@ -0,0 +1,275 @@
<?php
/**
* Test Importer
*
* Example Importer class
*
* used to learn how to write an importer and to
* create test collections and items
*
* Example how to invoke it
*
* add_action('init', function() {
* if ( isset($_GET['run_test_importer']) && $_GET['run_test_importer'] == 'go' ) {
* global $Tainacan_Importer_Handler;
* $test = new \Tainacan\Importer\Test_Importer();
* $Tainacan_Importer_Handler->add_to_queue($test);
* }
* });
*
* Put this code somewhere and access any URL of your site with ?run_test_importer=go
*
* TODO: check validate() methods and write log & abort importer in case of error.
*/
namespace Tainacan\Importer;
use \Tainacan\Entities;
class Test_Importer extends Importer {
protected $manual_mapping = false;
protected $manual_collection = false;
protected $steps = [
[
'name' => 'Create Taxonomies',
'callback' => 'create_taxonomies'
],
[
'name' => 'Create Collections',
'callback' => 'create_collections'
],
[
'name' => 'Import Items',
'callback' => 'process_collections'
],
[
'name' => 'Post-configure taxonomies',
'callback' => 'close_taxonomies'
],
[
'name' => 'Finalize',
'callback' => 'finish_processing'
]
];
public function __construct($attributes = array()) {
parent::__construct($attributes);
$this->tax_repo = \Tainacan\Repositories\Taxonomies::get_instance();
$this->col_repo = \Tainacan\Repositories\Collections::get_instance();
$this->items_repo = \Tainacan\Repositories\Items::get_instance();
$this->fields_repo = \Tainacan\Repositories\Fields::get_instance();
}
public function create_taxonomies() {
$tax1 = new Entities\Taxonomy();
$tax1->set_name('Color');
$tax1->set_allow_insert('yes');
$tax1->set_status('publish');
$tax1->validate();
$tax1 = $this->tax_repo->insert($tax1);
$this->add_transient('tax_1_id', $tax1->get_id());
$tax2 = new Entities\Taxonomy();
$tax2->set_name('Quality');
$tax2->set_allow_insert('yes');
$tax2->set_status('publish');
$tax2->validate();
$tax2 = $this->tax_repo->insert($tax2);
$this->add_transient('tax_2_id', $tax2->get_id());
return false;
}
public function create_collections() {
$col1 = new Entities\Collection();
$col1->set_name('Collection 1');
$col1->set_status('publish');
$col1->validate();
$col1 = $this->col_repo->insert($col1);
$col2 = new Entities\Collection();
$col2->set_name('Collection 2');
$col2->set_status('publish');
$col2->validate();
$col2 = $this->col_repo->insert($col2);
$col1_map = [];
$col2_map = [];
// fields
// core fields
$col1_core_title = $col1->get_core_title_field();
$col1_core_description = $col1->get_core_description_field();
$col1_map[$col1_core_title->get_id()] = 'field1';
$col1_map[$col1_core_description->get_id()] = 'field2';
$field = new Entities\Field();
$field->set_name('Color');
$field->set_collection($col1);
$field->set_field_type('Tainacan\Field_Types\Category');
$field->set_field_type_options([
'taxonomy_id' => $this->get_transient('tax_1_id'),
'allow_new_terms' => true
]);
$field->set_status('publish');
$field->validate();
$field = $this->fields_repo->insert($field);
$col1_map[$field->get_id()] = 'field3';
$this->add_transient('tax_1_field', $field->get_id());
$field = new Entities\Field();
$field->set_name('Quality');
$field->set_collection($col1);
$field->set_field_type('Tainacan\Field_Types\Category');
$field->set_field_type_options([
'taxonomy_id' => $this->get_transient('tax_2_id'),
'allow_new_terms' => true
]);
$field->set_status('publish');
$field->validate();
$field = $this->fields_repo->insert($field);
$col1_map[$field->get_id()] = 'field4';
$this->add_transient('tax_2_field', $field->get_id());
$this->add_collection([
'id' => $col1->get_id(),
'map' => $col1_map,
'total_items' => $this->get_col1_number_of_items(),
'source_id' => 'col1'
]);
// Collection 2
// core fields
$col2_core_title = $col2->get_core_title_field();
$col2_core_description = $col2->get_core_description_field();
$col2_map[$col2_core_title->get_id()] = 'field1';
$col2_map[$col2_core_description->get_id()] = 'field2';
$field = new Entities\Field();
$field->set_name('Test Field');
$field->set_collection($col2);
$field->set_field_type('Tainacan\Field_Types\Text');
$field->set_status('publish');
$field->validate();
$field = $this->fields_repo->insert($field);
$col2_map[$field->get_id()] = 'field3';
$this->add_collection([
'id' => $col2->get_id(),
'map' => $col2_map,
'total_items' => $this->get_col2_number_of_items(),
'source_id' => 'col2'
]);
return false;
}
public function close_taxonomies() {
$tax1 = $this->tax_repo->fetch( $this->get_transient('tax_1_id') );
$tax1->set_allow_insert('no');
$tax1->validate();
$tax1 = $this->tax_repo->insert($tax1);
$tax2 = $this->tax_repo->fetch( $this->get_transient('tax_2_id') );
$tax2->set_allow_insert('no');
$tax2->validate();
$tax2 = $this->tax_repo->insert($tax2);
$field1 = $this->fields_repo->fetch( $this->get_transient('tax_1_field') );
$options = $field1->get_field_type_options();
$options['allow_new_terms'] = false;
$field1->set_field_type_options($options);
$field1->validate();
$this->fields_repo->insert($field1);
$field2 = $this->fields_repo->fetch( $this->get_transient('tax_2_field') );
$options = $field2->get_field_type_options();
$options['allow_new_terms'] = false;
$field2->set_field_type_options($options);
$field2->validate();
$this->fields_repo->insert($field2);
}
public function finish_processing() {
// Lets just pretend we are doing something really important
$important_stuff = 5;
$current = $this->get_in_step_count();
if ($current <= $important_stuff) {
// This is very important
sleep(5);
$current ++;
return $current;
} else {
return false;
}
}
public function process_item($index, $collection_definition) {
$method = 'get_' . $collection_definition['source_id'] . '_item';
$item = $this->$method($index);
return $item;
}
/**
* Dummy methods
*
* This could be reading from a file, or making requests to an API
*
* Here we are just returning random values
*/
public function get_col1_number_of_items() {
return 10;
}
public function get_col2_number_of_items() {
return 20;
}
public function get_col1_item($index) {
$terms1 = [
'orange', 'red', 'purple', 'blue', 'black', 'yellow'
];
$terms2 = [
'good', 'awesome', 'disgusting', 'bad', 'horrible', 'regular'
];
return [
'field1' => 'Title ' . $index,
'field2' => 'Description ' . $index,
'field3' => $terms1[array_rand($terms1)],
'field4' => $terms2[array_rand($terms2)],
];
}
public function get_col2_item($index) {
return [
'field1' => 'Collection 2 item ' . $index,
'field2' => 'Collection 2 item description ' . $index,
'field3' => 'Collection 2 whatever ' . $index,
];
}
}

View File

@ -171,7 +171,7 @@ export const updateTerm = ({ commit }, { categoryId, termId, name, description,
export const fetchTerms = ({ commit }, categoryId ) => {
return new Promise((resolve, reject) => {
axios.tainacan.get(`/taxonomy/${categoryId}/terms/?hideempty=0`)
axios.tainacan.get(`/taxonomy/${categoryId}/terms/?hideempty=0&order=asc`)
.then(res => {
let terms = res.data;
commit('setTerms', terms);

View File

@ -184,15 +184,16 @@ export const updateCollection = ({ commit }, {
});
};
export const sendCollection = ( { commit }, { name, description, status }) => {
export const sendCollection = ( { commit }, { name, description, status, mapper }) => {
return new Promise(( resolve, reject ) => {
axios.tainacan.post('/collections/', {
name: name,
description: description,
status: status
status: status,
'exposer-map': mapper
})
.then( res => {
commit('setCollection', { name: name, description: description, status: status });
commit('setCollection', { name: name, description: description, status: status, mapper: mapper });
resolve( res.data );
})
.catch(error => {

View File

@ -126,7 +126,25 @@ export const fetchFieldTypes = ({commit}) => {
});
}
export const updateFieldTypes = ({commit}, fieldTypes) => {
commit('setFieldTypes', fieldTypes);
};
export const fetchFieldMappers = ({commit}) => {
return new Promise((resolve, reject) => {
axios.tainacan.get('/field-mappers')
.then((res) => {
let fieldMappers = res.data;
commit('setFieldMappers', fieldMappers);
resolve(fieldMappers);
})
.catch((error) => {
console.log(error);
reject(error);
});
});
}
export const updateFieldMappers = ({commit}, fieldMappers) => {
commit('setFieldMappers', fieldMappers);
};

View File

@ -5,4 +5,8 @@ export const getFields = state => {
export const getFieldTypes = state => {
return state.fieldTypes;
}
export const getFieldMappers = state => {
return state.fieldMappers;
}

View File

@ -5,6 +5,7 @@ import * as mutations from './mutations';
const state = {
fields: [],
fieldTypes: [],
fieldMappers: [],
};
export default {

View File

@ -18,3 +18,7 @@ export const setFields = (state, fields) => {
export const setFieldTypes = (state, fieldTypes) => {
state.fieldTypes = fieldTypes;
}
export const setFieldMappers = (state, fieldMappers) => {
state.fieldMappers = fieldMappers;
}

27
src/setup-db.php Normal file
View File

@ -0,0 +1,27 @@
<?php
function tainacan_create_bd_process_db() {
global $wpdb;
$table_name = $wpdb->prefix . 'tnc_bg_process';
$charset_collate = $wpdb->get_charset_collate();
$max_index_length = 191;
$query = "CREATE TABLE IF NOT EXISTS $table_name (
ID bigint(20) unsigned NOT NULL auto_increment,
user_id bigint(20) unsigned NOT NULL default '0',
priority bigint(20) unsigned NOT NULL default 10,
queued_on datetime NOT NULL default '0000-00-00 00:00:00',
processed_last datetime NOT NULL default '0000-00-00 00:00:00',
data longtext NOT NULL,
action text NOT NULL,
done boolean not null default 0,
PRIMARY KEY (ID),
KEY user_id (user_id),
KEY action (action($max_index_length))
) $charset_collate;\n";
$wpdb->query($query);
}
?>

View File

@ -16,13 +16,15 @@ const TAINACAN_API_DIR = __DIR__ . '/api/';
const TAINACAN_CLASSES_DIR = __DIR__ . '/classes/';
require_once(TAINACAN_CLASSES_DIR . 'tainacan-creator.php');
require_once(TAINACAN_API_DIR . 'tainacan-rest-creator.php');
require_once('setup-db.php');
// DEV Interface, used for debugging
require_once('dev-interface/class-tainacan-dev-interface.php');
function tnc_enable_dev_wp_interface() {
return defined('TNC_ENABLE_DEV_WP_INTERFACE') && true === TNC_ENABLE_DEV_WP_INTERFACE ? true : false;
}
if ( tnc_enable_dev_wp_interface() ) {
require_once('dev-interface/class-tainacan-dev-interface.php');
require_once('dev-interface/class-tainacan-helpers-html.php');
$Tainacan_Dev_interface = \Tainacan\DevInterface\DevInterface::get_instance();
}
@ -34,3 +36,4 @@ add_action( 'plugins_loaded', 'tainacan_load_plugin_textdomain' );
$Tainacan_Capabilities = \Tainacan\Capabilities::get_instance();
register_activation_hook( __FILE__, array( $Tainacan_Capabilities, 'init' ) );
register_activation_hook( __FILE__, 'tainacan_create_bd_process_db' );

View File

@ -57,15 +57,15 @@ class TAINACAN_REST_Export_Controller extends TAINACAN_UnitApiTestCase {
$item_metadata = $Tainacan_Item_Metadata->insert($item_metadata);
$item2 = $this->tainacan_entity_factory->create_entity(
'item',
array(
'title' => 'item_teste_Export2',
'description' => 'adasdasdsa2',
'collection' => $collection
),
true,
true
);
'item',
array(
'title' => 'item_teste_Export2',
'description' => 'adasdasdsa2',
'collection' => $collection
),
true,
true
);
$item_metadata2 = new \Tainacan\Entities\Item_Metadata_Entity($item2, $field);
@ -76,15 +76,15 @@ class TAINACAN_REST_Export_Controller extends TAINACAN_UnitApiTestCase {
$item_metadata2 = $Tainacan_Item_Metadata->insert($item_metadata2);
$item3 = $this->tainacan_entity_factory->create_entity(
'item',
array(
'title' => 'item_teste_Export3',
'description' => 'adasdasdsa3',
'collection' => $collection
),
true,
true
);
'item',
array(
'title' => 'item_teste_Export3',
'description' => 'adasdasdsa3',
'collection' => $collection
),
true,
true
);
$item_metadata3 = new \Tainacan\Entities\Item_Metadata_Entity($item3, $field);
@ -103,16 +103,35 @@ class TAINACAN_REST_Export_Controller extends TAINACAN_UnitApiTestCase {
$item_exposer_json = json_encode([
'exposer-type' => 'Xml',
'exposer-map' => 'Value',
'export-background' => false
]);
$query = [
'orderby' => 'id',
'order' => 'asc',
];
$request = new \WP_REST_Request('GET', $this->namespace . '/export/collection/' . $collection->get_id() );
$request->set_query_params($query);
$request->set_body($item_exposer_json);
$response = $this->server->dispatch($request);
$this->assertEquals(200, $response->get_status());
$data = $response->get_data();
$this->assertInstanceOf('SimpleXMLElement', @simplexml_load_string($data));
$this->assertInstanceOf('SimpleXMLElement', $xml = @simplexml_load_string($data));
$this->assertEquals(3, $xml->count());
$i = 0;
foreach ($xml->children() as $xml_item ) {
$fields = $items[$i]->get_fields();
foreach ($fields as $field_meta) {
$field = $field_meta->get_field();
$this->assertEquals($field_meta->get_value(), $xml_item->{$field->get_name()});
//echo "{$field->get_name()}:{$field_meta->get_value()}"; // uncomment if need debug
}
$i++;
}
}
}
?>
?>

View File

@ -166,6 +166,58 @@ class TAINACAN_REST_Exposers extends TAINACAN_UnitApiTestCase {
}
/**
* @group exposers-slug
*/
public function test_exposer_map_by_slug() {
global $Tainacan_Fields, $Tainacan_Item_Metadata;
extract($this->create_meta_requirements());
$item__metadata_json = json_encode([
'values' => 'TestValues_exposers_slug',
]);
$request = new \WP_REST_Request('POST', $this->namespace . '/item/' . $this->item->get_id() . '/metadata/' . $this->field->get_id() );
$request->set_body($item__metadata_json);
$response = $this->server->dispatch($request);
$this->assertEquals(200, $response->get_status());
$data = $response->get_data();
$this->assertEquals($this->item->get_id(), $data['item']['id']);
$this->assertEquals('TestValues_exposers_slug', $data['value']);
$item_exposer_json = json_encode([
'exposer-map' => 'dublin-core',
]);
$request = new \WP_REST_Request('GET', $this->namespace . '/item/' . $this->item->get_id() . '/metadata/'. $this->field->get_id() );
$request->set_body($item_exposer_json);
$response = $this->server->dispatch($request);
$this->assertEquals(200, $response->get_status());
$data = $response->get_data();
$this->assertEquals('TestValues_exposers_slug', $data['dc:language']);
$item_exposer_json = json_encode([
'exposer-type' => 'xml',
'exposer-map' => 'dublin-core',
]);
$request = new \WP_REST_Request('GET', $this->namespace . '/item/' . $this->item->get_id() . '/metadata' );
$request->set_body($item_exposer_json);
$response = $this->server->dispatch($request);
$this->assertEquals(200, $response->get_status());
$data = $response->get_data();
$xml = new \SimpleXMLElement($data);
$rdf = $xml->children(\Tainacan\Exposers\Mappers\Dublin_Core::XML_RDF_NAMESPACE);
$dc = $rdf->children(\Tainacan\Exposers\Mappers\Dublin_Core::XML_DC_NAMESPACE);
$this->assertEquals('adasdasdsa', $dc->description);
$this->assertEquals('item_teste_Expose', $dc->title);
$this->assertEquals('TestValues_exposers_slug', $dc->language);
}
/**
* @group oai-pmh
*/
@ -345,6 +397,42 @@ class TAINACAN_REST_Exposers extends TAINACAN_UnitApiTestCase {
$this->assertEquals('TestValues_exposers', $data['teste_Expose']);
}
/**
* @group mapped_new_collection
*/
public function test_mapped_new_collection() {
$collection_JSON = json_encode([
'exposer-map' => 'Dublin Core',
'name' => 'TesteJsonAddDublin_Core',
'description' => 'Teste JSON Dublin Core mapped',
'status' => 'publish'
]);
$mapper = new \Tainacan\Exposers\Mappers\Dublin_Core();
$request = new \WP_REST_Request('POST', $this->namespace . '/collections');
$request->set_body($collection_JSON);
$response = $this->server->dispatch($request);
$this->assertEquals(201, $response->get_status(), sprintf('response: %s', print_r($response, true)));
$collection_array = $response->get_data();
$id = $collection_array['id'];
$Tainacan_collections = \Tainacan\Repositories\Collections::get_instance();
$collection = $Tainacan_collections->fetch($id);
$Tainacan_Fields = \Tainacan\Repositories\Fields::get_instance();
$fields = $Tainacan_Fields->fetch_by_collection( $collection, [ 'order' => 'id' ], 'OBJECT' );
$this->assertEquals(count($mapper->metadata), count($fields));
foreach ($fields as $field) {
$this->assertTrue(array_key_exists($field->get_slug(), $mapper->metadata));
if(! array_key_exists('core_field', $mapper->metadata[$field->get_slug()]) || $mapper->metadata[$field->get_slug()]['core_field'] == false) {
$this->assertEquals($mapper->metadata[$field->get_slug()]['URI'], $field->get_description());
}
$this->assertEquals($mapper->metadata[$field->get_slug()]['label'], $field->get_name());
}
}
}
?>

View File

@ -0,0 +1,37 @@
<?php
namespace Tainacan\Tests;
/**
* @group api
*/
class TAINACAN_REST_Field_Mappers_Controller extends TAINACAN_UnitApiTestCase {
public function test_get_field_mappers(){
$field_mapper_request = new \WP_REST_Request('GET', $this->namespace . '/field-mappers');
$field_mapper_response = $this->server->dispatch($field_mapper_request);
$data = $field_mapper_response->get_data();
$Tainacan_Fields = \Tainacan\Exposers\Exposers::get_instance();
$field_mappers = $Tainacan_Fields->get_mappers("OBJECT");
$this->assertEquals(count($field_mappers), count($data));
for ($i = 0; $i < count($data); $i++) {
$this->assertEquals($field_mappers[$i]->slug, $data[$i]['slug']);
$this->assertEquals($field_mappers[$i]->name, $data[$i]['name']);
$this->assertEquals($field_mappers[$i]->allow_extra_fields, $data[$i]['allow_extra_fields']);
$this->assertEquals($field_mappers[$i]->context_url, $data[$i]['context_url']);
$this->assertEquals($field_mappers[$i]->metadata, $data[$i]['metadata']);
$this->assertEquals($field_mappers[$i]->prefix, $data[$i]['prefix']);
$this->assertEquals($field_mappers[$i]->sufix, $data[$i]['sufix']);
$this->assertEquals($field_mappers[$i]->header, $data[$i]['header']);
}
}
}
?>

View File

@ -11,7 +11,7 @@ use Tainacan\Importer;
class ImporterTests extends TAINACAN_UnitTestCase {
public function test_intance_old_tainacan()
/*public function test_intance_old_tainacan()
{
$collection = $this->tainacan_entity_factory->create_entity(
'collection',
@ -27,10 +27,10 @@ class ImporterTests extends TAINACAN_UnitTestCase {
$old_tainacan_importer = new Importer\Old_Tainacan();
$id = $old_tainacan_importer->get_id();
$_SESSION['tainacan_importer'][$id]->set_collection( $collection );
$this->assertEquals( $collection->get_id(), $_SESSION['tainacan_importer'][$id]->collection->get_id() );
}
this->assertEquals( $collection->get_id(), $_SESSION['tainacan_importer'][$id]->collection->get_id() );
}*/
/*public function test_automapping_old_tainacan()
public function test_automapping_old_tainacan()
{
//$Tainacan_Items = \Tainacan\Repositories\Items::get_instance();
//$Tainacan_Fields = \Tainacan\Repositories\Fields::get_instance();
@ -38,29 +38,30 @@ class ImporterTests extends TAINACAN_UnitTestCase {
$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;
//}
//$_SESSION['tainacan_importer'][$id]->set_file( './tests/attachment/json_old_tainacan.txt' );
$url = 'http://localhost/wordpress_tainacan/';
$_SESSION['tainacan_importer'][$id]->set_url($url);
$_SESSION['tainacan_importer'][$id]->set_repository();
$url_repository = 'http://localhost/wordpress_tainacan/';
$url_repository = '';
if( $url_repository !== '' ){
$_SESSION['tainacan_importer'][$id]->set_url($url_repository);
while (!$_SESSION['tainacan_importer'][$id]->is_finished())
{
$_SESSION['tainacan_importer'][$id]->run();
while (!$_SESSION['tainacan_importer'][$id]->is_finished())
{
$_SESSION['tainacan_importer'][$id]->run();
}
$Tainacan_Collections = \Tainacan\Repositories\Collections::get_instance();
$collections = $Tainacan_Collections->fetch([], 'OBJECT');
$this->assertEquals(3, $collections, 'total collection in this url does not match');
$this->assertTrue(true);
}
$Tainacan_Collections = \Tainacan\Repositories\Collections::get_instance();
$collections = $Tainacan_Collections->fetch([], 'OBJECT');
$this->assertEquals(3, $collections, 'total collection in this url does not match');
$this->assertTrue(true);
}*/
}
/*public function test_file_old_tainacan () {
$Tainacan_Items = \Tainacan\Repositories\Items::get_instance();
@ -134,30 +135,7 @@ class ImporterTests extends TAINACAN_UnitTestCase {
$this->assertEquals( $_SESSION['tainacan_importer'][$id]->get_total_items(), count( $items ) );
}*/
/**
* @group importer
*/
public function test_instance_csv () {
$collection = $this->tainacan_entity_factory->create_entity(
'collection',
array(
'name' => 'Other',
'description' => 'adasdasdsa',
'default_order' => 'DESC',
'status' => 'publish'
),
true
);
$csv_importer = new Importer\CSV();
$id = $csv_importer->get_id();
$_SESSION['tainacan_importer'][$id]->set_collection( $collection );
// here the session is init already
$this->assertEquals( $collection->get_id(), $_SESSION['tainacan_importer'][$id]->collection->get_id() );
}
/**
* @group importer
*/
@ -168,8 +146,6 @@ class ImporterTests extends TAINACAN_UnitTestCase {
$csv_importer = new Importer\CSV();
$id = $csv_importer->get_id();
$_SESSION['tainacan_importer'][$id]->set_items_per_step(2);
// open the file "demosaved.csv" for writing
$file = fopen($file_name, 'w');
@ -193,21 +169,16 @@ class ImporterTests extends TAINACAN_UnitTestCase {
// Close the file
fclose($file);
$_SESSION['tainacan_importer'][$id]->set_file( $file_name );
if(!isset( $_SESSION['tainacan_importer'][$id]->tmp_file )){
#TODO: Remove dependence of web server (see fetch_from_remote)
$this->markTestSkipped('This test need a apache installation available.');
}
$_SESSION['tainacan_importer'][$id]->set_tmp_file( $file_name );
// file isset on importer
$this->assertTrue( isset( $_SESSION['tainacan_importer'][$id]->tmp_file ) );
$this->assertTrue( !empty( $_SESSION['tainacan_importer'][$id]->get_tmp_file() ) );
// count size of csv
$this->assertEquals( 5, $_SESSION['tainacan_importer'][$id]->get_total_items() );
$this->assertEquals( 5, $_SESSION['tainacan_importer'][$id]->get_progress_total_from_source() );
// get fields to mapping
$headers = $_SESSION['tainacan_importer'][$id]->get_fields();
$headers = $_SESSION['tainacan_importer'][$id]->get_source_fields();
$this->assertEquals( $headers[4], 'Column 5' );
// inserting the collection
@ -221,10 +192,12 @@ class ImporterTests extends TAINACAN_UnitTestCase {
),
true
);
// set the importer
$_SESSION['tainacan_importer'][$id]->set_collection( $collection );
$collection_definition = [
'id' => $collection->get_id(),
'total_items' => $_SESSION['tainacan_importer'][$id]->get_progress_total_from_source(),
];
// get collection fields to map
$fields = $Tainacan_Fields->fetch_by_collection( $collection, [], 'OBJECT' ) ;
@ -233,29 +206,30 @@ class ImporterTests extends TAINACAN_UnitTestCase {
foreach ( $fields as $index => $field ){
$map[$field->get_id()] = $headers[$index];
}
$collection_definition['map'] = $map;
// set the mapping
$_SESSION['tainacan_importer'][$id]->set_mapping( $map );
// check is equal
$this->assertEquals( $_SESSION['tainacan_importer'][$id]->get_mapping(), $map );
// add the collection
$_SESSION['tainacan_importer'][$id]->add_collection( $collection_definition );
//execute the process
$this->assertEquals(2, $_SESSION['tainacan_importer'][$id]->run(), 'first step should import 2 items');
$this->assertEquals(4, $_SESSION['tainacan_importer'][$id]->run(), 'second step should import 2 items');
$this->assertEquals(5, $_SESSION['tainacan_importer'][$id]->run(), 'third step should import 3 items');
$this->assertEquals(5, $_SESSION['tainacan_importer'][$id]->run(), 'if call run again after finish, do nothing');
$this->assertEquals(1, $_SESSION['tainacan_importer'][$id]->run(), 'first step should import 1 item');
$this->assertEquals(2, $_SESSION['tainacan_importer'][$id]->run(), 'second step should import 2 items');
$this->assertEquals(3, $_SESSION['tainacan_importer'][$id]->run(), 'third step should import 3 items');
$this->assertEquals(4, $_SESSION['tainacan_importer'][$id]->run(), 'third step should import 4 items');
$this->assertEquals(false, $_SESSION['tainacan_importer'][$id]->run(), '5 items and return false because its finished');
$this->assertEquals(false, $_SESSION['tainacan_importer'][$id]->run(), 'if call run again after finish, do nothing');
$items = $Tainacan_Items->fetch( [], $collection, 'OBJECT' );
$this->assertEquals( $_SESSION['tainacan_importer'][$id]->get_total_items(), count( $items ) );
$this->assertEquals( $_SESSION['tainacan_importer'][$id]->get_progress_total_from_source(), count( $items ) );
}
/**
* @group importer
*/
public function test_fetch_file(){
/*
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' );
@ -267,4 +241,5 @@ class ImporterTests extends TAINACAN_UnitTestCase {
$this->assertTrue( isset( $_SESSION['tainacan_importer'][$id]->tmp_file ) );
}
*/
}