Merge develop

This commit is contained in:
weryques 2018-02-22 13:31:02 -03:00
parent 360876c89a
commit 5bc352f433
129 changed files with 25658 additions and 2934 deletions

3
.gitignore vendored
View File

@ -13,6 +13,9 @@ node_modules
npm-debug.log
src/assets/*.js
last-js-build.md5
last-sass-build.md5
last-composer-build.md5
last-package-build.md5
src/admin/scss/.sass-cache
src/assets/css/tainacan-admin.css
src/assets/css/tainacan-admin.css.map

View File

@ -3,28 +3,54 @@
source build-config.cfg
## Only run npm build if there was a change in a .js or .vue file
current_md5=$(<last-js-build.md5)
current_md5_js=$(<last-js-build.md5)
current_md5_sass=$(<last-sass-build.md5)
current_md5_composer=$(<last-composer-build.md5)
current_md5_package=$(<last-package-build.md5)
current_OS=`uname`
# For macOS (Darwin)
if [ $current_OS == "Darwin" ]; then
find src -type f \( -name "*.js" -or -name "*.vue" \) -exec md5 {} \; | sort -k 2 | md5 > last-js-build.md5
find src -type f \( -name "*.scss" \) -exec md5 {} \; | sort -k 2 | md5 > last-sass-build.md5
find src -type f \( -name "composer.json" \) -exec md5 {} \; | sort -k 2 | md5 > last-composer-build.md5
find src -type f \( -name "package.json" \) -exec md5 {} \; | sort -k 2 | md5 > last-package-build.md5
else
find src -type f \( -name "*.js" -or -name "*.vue" \) -exec md5sum {} \; | sort -k 2 | md5sum > last-js-build.md5
find src -type f \( -name "*.scss" \) -exec md5sum {} \; | sort -k 2 | md5sum > last-sass-build.md5
find src -type f \( -name "composer.json" \) -exec md5sum {} \; | sort -k 2 | md5sum > last-composer-build.md5
find src -type f \( -name "package.json" \) -exec md5sum {} \; | sort -k 2 | md5sum > last-package-build.md5
fi
new_md5=$(<last-js-build.md5)
if [ "$current_md5" != "$new_md5" ]
new_md5_package=$(<last-package-build.md5)
if [ "$current_md5_package" != "$new_md5_package" ]
then
## Install js dependencies
npm install
fi
new_md5_composer=$(<last-composer-build.md5)
if [ "$current_md5_composer" != "$new_md5_composer" ]
then
## Install composer dependencies
composer install
fi
new_md5_sass=$(<last-sass-build.md5)
if [ "$current_md5_sass" != "$new_md5_sass" ]
then
## Compile SASS
sh compile-sass.sh
fi
new_md5_js=$(<last-js-build.md5)
if [ "$current_md5_js" != "$new_md5_js" ]
then
npm run build
fi
### END npm build ###
## Compile SASS
sh compile-sass.sh
## Install composer dependencies
composer install
echo "Updating files in $destination"
rm -rf $destination

View File

@ -3,36 +3,36 @@
The typical workflow when you create a Digital Repository with Tainacan is:
* Create a collection
* Configure which metadata (fields) item in this collections will have
* Configure which field (fields) item in this collections will have
* Configure the collection
* Configure which filters will be used when browsing the collection
* Upload item to the collection or import them from a source
## Collections
A collection is a group of items, that have the same set of metadata. Every item uploaded to your digital repository will be part of a collection. For instance, you could have a "paintings" collections, with metadata such as Title, Author, Country, Tecnique, etc and another collection for "films", with Title, Director, Country and Genre.
A collection is a group of items, that have the same set of field. Every item uploaded to your digital repository will be part of a collection. For instance, you could have a "paintings" collections, with field such as Title, Author, Country, Tecnique, etc and another collection for "films", with Title, Director, Country and Genre.
For each collection you can set a different set of metadata and they can share common taxonomies, which means you could browse for items in a specific Country, and get both paintings and films in your results.
For each collection you can set a different set of field and they can share common taxonomies, which means you could browse for items in a specific Country, and get both paintings and films in your results.
Collections can also have child collections, which will inherit parent's collection metadata and can add their own set of additional information.
Collections can also have child collections, which will inherit parent's collection field and can add their own set of additional information.
## Items
Items are the actual content of yout repository. The painting, the film, the book and so on. They belong to a collection and have all the metadata configured in the collection.
Items are the actual content of yout repository. The painting, the film, the book and so on. They belong to a collection and have all the field configured in the collection.
In WordPress, each item is a post and its post type represents its collection.
## Metadata
## Field
Every collection have a set of metadata. They are the description of the items of this collection.
Every collection have a set of field. They are the description of the items of this collection.
Each metadata has a set of settings. Is it required? Is it supposed to be unique (an ID number for example)? Does it accept multiple values? What is it Field Type? (see the complete list of metadata attributes).
Each field has a set of settings. Is it required? Is it supposed to be unique (an ID number for example)? Does it accept multiple values? What is it Field Type? (see the complete list of field attributes).
(Note: you can import/export presets of metadata)
(Note: you can import/export presets of field)
## Field Types
Field types are the objects that represent the types of metadata that can be used. Examples of Field Types are "Text", "Long text", "Date", "Multiple selection (checkbox)", "Relationship with another item", etc (see full list).
Field types are the objects that represent the types of field that can be used. Examples of Field Types are "Text", "Long text", "Date", "Multiple selection (checkbox)", "Relationship with another item", etc (see full list).
Each field type object have its own settings and web component that will be used to render the interface.
@ -40,13 +40,13 @@ Field Types can be created via plugins and extend the default set of types shipp
## Filters
For every collection, you may choose which metadata will be used to filter the results in a faceted search interface. These are the filters.
For every collection, you may choose which field will be used to filter the results in a faceted search interface. These are the filters.
Filters give the ability to the user to filter items in a collection using a Filter Type.
## Filter Types
Filter types are the different types of interface to filter items in a collections based on one specific Metadata. Examples of Filter Types are "text", "datepicker", "date range picker", "number range slider", etc.
Filter types are the different types of interface to filter items in a collections based on one specific Field. Examples of Filter Types are "text", "datepicker", "date range picker", "number range slider", etc.
Each Filter Type object have its own settings and web component that will be used to render the interface.

121
docs/permissions.md Normal file
View File

@ -0,0 +1,121 @@
# Tainacan Users Permissions
This page explains how permissions are handled in Tainacan. What are the users roles available and what each one of them can do.
Tainacan handles user permissions in the very same way WordPress does, so if you are used to WordPress Roles and Permissions, you wont have any trouble.
Default WordPress roles are assigned with new capabilities to work with Collections, Items and other Tainacan specific operations. Additionaly Tainacan creates new roles, relative to the core WordPress roles, that have the same Tainacan specific capabilities, but dont have acces to the rest of the administrative panel of WordPress. For example, WordPress Editor can manage everything inside Tainacan, and they can also create and publish pages, in the other hand, Tainacan Editors can't.
If you want to change permissions for specific roles or users, or even create new roles, you can allways use one of the many WordPress plugins available for that.
In short, these are the roles and their main characteristics. A detailed description can be found in the next session.
* Subscriber: Can't really do anything inside tainacan.
* Colaborator / Tainacan Colaborator: Can create items, but not to publish them.
* Author / Tainacan Author: Can create and publish items, but can not edit published items nor edit other user's items.
* Editor / Tainacan Editor: Can create, publish and edit other users's items.
* Administrator: Rules the world.
## Roles and permissions
Here you will find a detailed explanation of what each role can do with each part of Tainacan.
### Collections
These are the capabilities related to collection management.
**Note about Collection moderators**: Collection moderators have the same capabilities an editor has, but only in relation to the collections he or she is moderating. Even if the user is a subscriber, he will act as if he/she was an editor for that specific collection.
| | Admin | Editor | Author | Collaborator |
|------------------------------|-------|--------|--------|--------------|
| Edit Collections | y | y | y | y |
| Delete Collections | y | y | y | y |
| Publish Collections | y | y | y | |
| Edit Published Collections | y | y | y | |
| Delete Published Collections | y | y | y | |
| Edit Others Collections | y | y | | |
| Delete Others Collections | y | y | | |
| Read Private Collections | y | y | | |
| Edit Private Collections | y | y | | |
| Delete Private Collections | y | y | | |
#### Edit Collections
> Capability name: edit_tainacan-collections
Who's got it: Everyone but subscribers
Allows to create and edit one's own collections. Does not allow to publish them.
#### Edit Others Collections
> Capability name: edit_others_tainacan-collections
Who's got it: Administrators, Editors and Tainacan Editors
Allows to edit other user's Collections (and items)
#### Edit Published Collections
> Capability name: edit_published_tainacan-collections
Who's got it: Administrators, Editors and Tainacan Editors
Allows to edit collections details after it was published
#### Edit Private Collections
> Capability name: edit_private_tainacan-collections
Who's got it: Administrators, Editors and Tainacan Editors
Allows to edit collections marked as private
#### Publish Collections
> Capability name: publish_tainacan-collections
Who's got it: Administrators, Editors, Tainacan Editors, Authors and Tainacan Authors
Allows to edit collections marked as private
#### Delete Collections
> Capability name: delete_tainacan-collections
Who's got it: Everyone but subscribers
Allows to delete one's own collections.
#### Delete Others Collections
> Capability name: delete_others_tainacan-collections
Who's got it: Administrators, Editors and Tainacan Editors
Allows to delete other user's Collections (and items)
#### Delete Published Collections
> Capability name: delete_published_tainacan-collections
Who's got it: Administrators, Editors and Tainacan Editors
Allows to delete collections after it was published
#### Delete Private Collections
> Capability name: delete_private_tainacan-collections
Who's got it: Administrators, Editors and Tainacan Editors
Allows to delete collections marked as private
#### Read Private Collections
> Capability name: read_private_tainacan-collections
Who's got it: Administrators, Editors and Tainacan Editors
Allows to view collections marked as private and its items.
### Items
### Categories
### Fields
### Filters

4431
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -9,25 +9,31 @@
},
"dependencies": {
"axios": "^0.17.1",
"bootstrap": "^3.3.7",
"element-ui": "^2.0.11",
"vue": "^2.0.1",
"buefy": "^0.6.3",
"bulma": "^0.6.2",
"mdi": "^2.1.19",
"node-sass": "^4.7.2",
"sass-loader": "^6.0.6",
"vue": "^2.5.13",
"vuex": "^3.0.1"
},
"devDependencies": {
"babel-core": "^6.0.0",
"babel-loader": "^6.0.0",
"autoprefixer": "^7.2.5",
"babel-core": "^6.26.0",
"babel-loader": "^7.1.2",
"babel-preset-env": "^1.6.1",
"babel-preset-es2015": "^6.0.0",
"babel-preset-stage-2": "^6.24.1",
"cross-env": "^3.0.0",
"css-loader": "^0.25.0",
"postcss-loader": "^2.0.10",
"style-loader": "^0.19.1",
"element-theme-chalk": "^2.0.11",
"element-theme-chalk": "^2.1.0",
"file-loader": "^0.9.0",
"postcss-loader": "^2.1.0",
"style-loader": "^0.19.1",
"vue-custom-element": "^1.0.13",
"vue-loader": "^9.7.0",
"webpack": "2.1.0-beta.25",
"webpack-dev-server": "2.1.0-beta.0"
"vue-loader": "^13.7.1",
"vue-template-compiler": "^2.5.13",
"webpack": "^3.10.0",
"webpack-dev-server": "^2.11.1"
}
}

71
src/admin/admin.vue Normal file
View File

@ -0,0 +1,71 @@
<template>
<div id="tainacan-admin-app" class="columns is-fullheight">
<nav id="primary-menu" role="navigation" aria-label="main navigation" class="column is-2 is-sidebar-menu">
<aside class="menu">
<h1 class="menu-level-title">{{ $i18n.get('repository') }}</h1>
<ul class="menu-list">
<li><router-link tag="a" to="/collections">{{ $i18n.get('collections')}}</router-link></li>
<li><router-link tag="a" to="/items">{{ $i18n.get('items')}}</router-link></li>
<li><router-link tag="a" to="/fields">{{ $i18n.get('fields')}}</router-link></li>
<li><router-link tag="a" to="/filters">{{ $i18n.get('filters')}}</router-link></li>
<li><router-link tag="a" to="/categories">{{ $i18n.get('categories')}}</router-link></li>
<li><router-link tag="a" to="/events">{{ $i18n.get('events')}}</router-link></li>
<li><a class="navbar-item" :href="wordpressAdmin">Wordpress</a></li>
</ul>
</aside>
</nav>
<div class="container column is-main-content">
<router-view></router-view>
</div>
</div>
</template>
<script>
export default {
name: "AdminPage",
data(){
return {
logoHeader: '../wp-content/plugins/tainacan/admin/images/tainacan_logo_header.png',
wordpressAdmin: window.location.origin + window.location.pathname.replace('admin.php', ''),
}
}
}
</script>
<style lang="scss">
@import "./scss/_variables.scss";
.is-fullheight {
height: 100%;
margin-bottom: 0px;
margin-top: 0px;
}
#primary-menu {
background-color: $primary;
li{
a {color: white !important;}
a:hover {color: $primary !important;}
}
}
.is-main-content {
padding-bottom: 0px;
padding-top: 0px;
}
#secondary-menu {
background-color: $secondary;
li{
a {color: white !important;}
a:hover {color: $secondary !important;}
}
}
</style>

View File

@ -9,26 +9,42 @@ class Admin {
private $menu_slug = 'tainacan_admin';
public function __construct() {
add_action( 'admin_menu', array(&$this, 'add_admin_menu') );
add_filter( 'admin_body_class', array(&$this, 'admin_body_class') );
add_action('admin_enqueue_scripts', array(&$this, 'add_user_admin_js'));
}
function add_admin_menu() {
$page_suffix = add_menu_page( __('Tainacan', 'tainacan'), __('Tainacan', 'tainacan'), 'edit_posts', $this->menu_slug, array(&$this, 'admin_page') );
add_action( 'load-' . $page_suffix, array(&$this, 'load_admin_page'));
}
function load_admin_page() {
add_action( 'admin_enqueue_scripts', array(&$this, 'add_admin_css') );
add_action( 'admin_enqueue_scripts', array(&$this, 'add_admin_css'), 90 );
}
function login_styles_reset( $style ) {
if ( strpos( $style, 'wp-admin-css') !== FALSE ) {
$style = NULL;
}
return $style;
}
function add_admin_css() {
global $TAINACAN_BASE_URL;
global $TAINACAN_BASE_URL;
wp_enqueue_style('tainacan-admin-page', $TAINACAN_BASE_URL . '/assets/css/tainacan-admin.css' );
$undesired_wp_styles = [ 'admin-menu', 'admin-bar', 'code-editor', 'color-picker',
'customize-controls', 'customize-nav-menus', 'customize-widgets', 'dashboard',
'dashicons', 'deprecated-media', 'edit', 'wp-pointer', 'farbtastic', 'forms', 'common',
'install', 'wp-auth-check', 'site-icon', 'buttons', 'l10n', 'list-tables','login',
'media', 'nav-menus', 'revisions', 'themes', 'widgets', 'wp-admin'];
wp_dequeue_style($undesired_wp_styles);
wp_deregister_style($undesired_wp_styles);
}
function admin_body_class($classes) {
@ -44,26 +60,24 @@ class Admin {
// TODO move it to a separate file and start the Vue project
echo "<div id='tainacan-admin-app'></div>";
//wp_enqueue_script( 'tainacan-dev-admin', $TAINACAN_BASE_URL . '/assets/dev_admin-components.js', [] , null, true);
wp_enqueue_script( 'tainacan-user-admin', $TAINACAN_BASE_URL . '/assets/user_admin-components.js', [] , null, true);
$tainacan_admin_i18n = require('tainacan-admin-i18n.php');
$components = ( has_filter( 'tainacan_register_web_components' ) ) ? apply_filters('tainacan_register_web_components') : [];
$settings = [
'root' => esc_url_raw( rest_url() ).'tainacan/v2',
'nonce' => wp_create_nonce( 'wp_rest' ),
'components' => $components,
'i18n' => $tainacan_admin_i18n
];
wp_localize_script( 'tainacan-user-admin', 'wp_settings', $settings );
}
function add_user_admin_js() {
global $TAINACAN_BASE_URL;
$components = ( has_filter( 'tainacan_register_web_components' ) ) ? apply_filters('tainacan_register_web_components') : [];
wp_enqueue_script('wp-settings',$TAINACAN_BASE_URL . '/js/wp-settings.js');
$settings = [
'root' => esc_url_raw( rest_url() ).'tainacan/v2',
'nonce' => wp_create_nonce( 'wp_rest' ),
'components' => $components
];
wp_localize_script( 'wp-settings', 'wp_settings', $settings );
wp_enqueue_script( 'custom-elements', $TAINACAN_BASE_URL . '/assets/customelements.min.js');
}
}

View File

@ -0,0 +1,212 @@
<template>
<div>
<section class="section">
<b-field grouped group-multiline>
<button v-if="selectedCollections.length > 0" class="button field is-danger" @click="deleteSelectedCollections()"><span>Deletar coleções selecionadas </span><b-icon icon="delete"></b-icon></button>
<b-select
v-model="collectionsPerPage"
@input="onChangeCollectionsPerPage"
:disabled="collections.length <= 0">
<option value="2">2 coleções por página</option>
<option value="10">10 coleções por página</option>
<option value="15">15 coleções por página</option>
<option value="20">20 coleções por página</option>
</b-select>
</b-field>
<b-table
ref="collectionTable"
:data="collections"
@selection-change="handleSelectionChange"
:checked-rows.sync="selectedCollections"
checkable
:loading="isLoading"
hoverable
striped
selectable
paginated
backend-pagination
:total="totalCollections"
:per-page="collectionsPerPage"
@page-change="onPageChange">
<template slot-scope="props">
<b-table-column field="featured_image" width="55" sortable="{{ false }}">
<template v-if="props.row.featured_image" slot-scope="scope">
<img class="table-thumb" :src="`${props.row.featured_image}`"/>
</template>
</b-table-column>
<b-table-column label="Nome" field="props.row.name">
{{ props.row.name }}
</b-table-column>
<b-table-column property="description" label="Descrição" show-overflow-tooltip field="props.row.description">
{{ props.row.description }}
</b-table-column>
<b-table-column label="Ações" width="110">
<a @click.prevent.stop="goToCollectionPage(props.row.id)"><b-icon icon="eye"></a>
<a @click.prevent.stop="goToCollectionEditPage(props.row.id)"><b-icon icon="pencil"></a>
<a @click.prevent.stop="deleteOneCollection(props.row.id)"><b-icon icon="delete"></a>
</b-table-column>
</template>
<!-- Empty state image -->
<template slot="empty">
<section class="section">
<div class="content has-text-grey has-text-centered">
<p>
<b-icon
icon="inbox"
size="is-large">
</b-icon>
</p>
<p>Nenhuma coleção ainda neste repositório.</p>
<router-link tag="button" class="button is-primary"
:to="{ path: `/collections/new` }">
Criar Coleção
</router-link>
</div>
</section>
</template>
</b-table>
</section>
</div>
</template>
<script>
import { mapActions, mapGetters } from 'vuex'
export default {
name: 'CollectionsList',
data(){
return {
selectedCollections: [],
tableFields: [],
isLoading: false,
totalCollections: 0,
page: 1,
collectionsPerPage: 2
}
},
methods: {
...mapActions('collection', [
'fetchCollections',
'deleteCollection'
]),
...mapGetters('collection', [
'getCollections'
]),
deleteOneCollection(collectionId) {
this.$dialog.confirm({
message: 'Deseja realmente deletar esta Coleção?',
onConfirm: () => {
this.deleteCollection(collectionId).then(() => {
this.loadCollections();
this.$toast.open({
duration: 3000,
message: `Coleção deletada`,
position: 'is-bottom',
type: 'is-secondary',
queue: true
})
for (let i = 0; i < this.selectedCollections.length; i++) {
if (this.selectedCollections[i].id == this.collectionId)
this.selectedCollections.splice(i, 1);
}
}).catch(() =>
this.$toast.open({
duration: 3000,
message: `Erro ao deletar coleção`,
position: 'is-bottom',
type: 'is-danger',
queue: true
})
);
}
});
},
deleteSelectedCollections() {
this.$dialog.confirm({
message: 'Deseja realmente todos as coleções selecionadas?',
onConfirm: () => {
for (let collection of this.selectedCollections) {
this.deleteCollection(collection.id)
.then((res) => {
this.loadCollections();
this.$toast.open({
duration: 3000,
message: `Coleção deletada`,
position: 'is-bottom',
type: 'is-secondary',
queue: false
})
}).catch((err) => {
this.$toast.open({
duration: 3000,
message: `Erro ao deletar coleção`,
position: 'is-bottom',
type: 'is-danger',
queue: false
});
});
}
this.selectedCollections = [];
}
});
},
handleSelectionChange(value) {
},
goToCollectionPage(collectionId) {
this.$router.push(`/collections/${collectionId}`);
},
goToCollectionEditPage(collectionId) {
this.$router.push(`/collections/${collectionId}/edit`);
},
onChangeCollectionsPerPage(value) {
this.collectionsPerPage = value;
this.loadCollections();
},
onPageChange(page) {
this.page = page;
this.loadCollections();
},
loadCollections() {
this.isLoading = true;
this.fetchCollections({ 'page': this.page, 'collectionsPerPage': this.collectionsPerPage })
.then((res) => {
this.isLoading = false;
this.totalCollections = res.total;
})
.catch((error) => {
this.isLoading = false;
});
}
},
computed: {
collections(){
return this.getCollections();
}
},
mounted(){
this.loadCollections();
}
}
</script>
<style scoped>
.table-thumb {
max-height: 38px !important;
vertical-align: middle !important;
}
tr { cursor: pointer !important; }
</style>

View File

@ -0,0 +1,242 @@
<template>
<div>
<section class="section">
<b-field grouped group-multiline>
<button v-if="selectedItems.length > 0" class="button field is-danger" @click="deleteSelectedItems()"><span>Deletar itens selecionados </span><b-icon icon="delete"></b-icon></button>
<b-dropdown>
<button class="button" slot="trigger">
<span>Campos da tabela</span>
<b-icon icon="menu-down"></b-icon>
</button>
<b-dropdown-item v-for="(column, index) in tableFields"
:key="index"
class="control" custom>
<b-checkbox v-model="column.visible"
:native-value="column.field">
{{ column.label }}
</b-checkbox>
</b-dropdown-item>
</b-dropdown>
<b-select
v-model="itemsPerPage"
@input="onChangeItemsPerPage"
:disabled="items.length <= 0">
<option value="2">2 itens por página</option>
<option value="10">10 itens por página</option>
<option value="15">15 itens por página</option>
<option value="20">20 itens por página</option>
</b-select>
</b-field>
<b-table ref="itemsTable"
:data="items"
@selection-change="handleSelectionChange"
:checked-rows.sync="selectedItems"
checkable
:loading="isLoading"
hoverable
striped
selectable
paginated
backend-pagination
:total="totalItems"
:per-page="itemsPerPage"
@page-change="onPageChange"
backend-sorting>
<template slot-scope="props">
<b-table-column v-for="(column, index) in tableFields"
:key="index"
:label="column.label"
:visible="column.visible"
:width="column.field == 'row_actions' ? 110 : column.field == 'featured_image' ? 55 : undefined ">
<template v-if="column.field != 'featured_image' && column.field != 'row_actions'">{{
props.row.metadata[column.slug].multiple == 'yes' ? props.row.metadata[column.slug].value.join(', ') : props.row.metadata[column.slug].value
}}</template>
<template v-if="column.field == 'featured_image'">
<img class="table-thumb" :src="`${ props.row[column.slug] }`"/>
</template>
<template v-if="column.field == 'row_actions'">
<a @click.prevent.stop="goToItemPage(props.row.id)"><b-icon icon="eye"></a>
<a @click.prevent.stop="goToItemEditPage(props.row.id)"><b-icon icon="pencil"></a>
<a @click.prevent.stop="deleteOneItem(props.row.id)"><b-icon icon="delete"></a>
</template>
</b-table-column>
</template>
<!-- Empty state image -->
<template slot="empty">
<section class="section">
<div class="content has-text-grey has-text-centered">
<p>
<b-icon
icon="inbox"
size="is-large">
</b-icon>
</p>
<p>Nenhum item ainda nesta coleção.</p>
<router-link tag="button" class="button is-primary"
:to="{ path: `/collections/${collectionId}/items/new` }">
Criar Item
</router-link>
</div>
</section>
</template>
</b-table>
</section>
</div>
</template>
<script>
import { mapActions, mapGetters } from 'vuex'
export default {
name: 'ItemsList',
data(){
return {
selectedItems: [],
tableFields: [],
isLoading: false,
totalItems: 0,
page: 1,
itemsPerPage: 2
}
},
props: {
collectionId: Number
},
methods: {
...mapActions('collection', [
'fetchItems',
'deleteItem',
'fetchFields'
]),
...mapGetters('collection', [
'getItems',
'getFields'
]),
deleteOneItem(itemId) {
this.$dialog.confirm({
message: 'Deseja realmente deletar este Item?',
onConfirm: () => {
this.deleteItem(itemId).then((res) => {
this.loadItems();
this.$toast.open({
duration: 3000,
message: `Item deletado`,
position: 'is-bottom',
type: 'is-secondary',
queue: true
});
for (let i = 0; i < this.selectedItems.length; i++) {
if (this.selectedItems[i].id == this.itemId)
this.selectedItems.splice(i, 1);
}
}).catch(( error ) => {
this.$toast.open({
duration: 3000,
message: `Erro ao deletar item`,
position: 'is-bottom',
type: 'is-danger',
queue: true
})
});
}
});
},
deleteSelectedItems() {
this.$dialog.confirm({
message: 'Deseja realmente todos os itens selecionados?',
onConfirm: () => {
for (let item of this.selectedItems) {
this.deleteItem(item.id)
.then((res) => {
this.loadItems();
this.$toast.open({
duration: 3000,
message: `Item deletado`,
position: 'is-bottom',
type: 'is-secondary',
queue: false
});
}).catch((err) => {
this.$toast.open({
duration: 3000,
message: `Erro ao deletar item`,
position: 'is-bottom',
type: 'is-danger',
queue: false
});
});
}
this.selectedItems = [];
}
});
},
handleSelectionChange() {
},
onChangeItemsPerPage(value) {
this.itemsPerPage = value;
this.loadItems();
},
goToItemPage(itemId) {
this.$router.push(`/collections/${this.collectionId}/items/${itemId}`);
},
goToItemEditPage(itemId) {
this.$router.push(`/collections/${this.collectionId}/items/${itemId}/edit`);
},
onPageChange(page) {
this.page = page;
this.loadItems();
},
loadItems() {
this.isLoading = true;
this.fetchItems({ 'collectionId': this.collectionId, 'page': this.page, 'itemsPerPage': this.itemsPerPage })
.then((res) => {
this.isLoading = false;
this.totalItems = res.total;
})
.catch((error) => {
this.isLoading = false;
});
}
},
computed: {
items(){
return this.getItems();
}
},
mounted(){
this.loadItems();
this.fetchFields(this.collectionId).then((res) => {
let rawFields = res;
for (let field of rawFields) {
this.tableFields.push(
{ label: field.name, field: field.description, slug: field.slug, visible: true }
);
}
this.tableFields.push({ label: 'Ações', field: 'row_actions', slug: 'actions', visible: true });
}).catch();
}
}
</script>
<style scoped>
.table-thumb {
max-height: 55px !important;
vertical-align: middle !important;
}
tr { cursor: pointer }
</style>

View File

@ -1,121 +0,0 @@
<template>
<div id="tainacan-admin-app">
<el-container>
<el-header>
Header
</el-header>
<el-container>
<el-aside width="200px">
Aside
</el-aside>
<el-main>
<el-row>
<el-col :span="8" v-for="(collection, index) in collections" :key="collection.id" :offset="index > 0 ? 2 : 0">
<el-card :body-style="{ padding: '0px' }">
<img src="" class="image" :alt="collection.name">
<div style="padding: 14px;">
<span>{{ collection.name }}</span>
<div class="bottom clearfix">
<time class="time">{{collection.description}}</time>
<el-button type="text" class="button">Detalhes</el-button>
</div>
</div>
</el-card>
</el-col>
</el-row>
</el-main>
</el-container>
</el-container>
</div>
</template>
<script>
import { mapActions, mapGetters } from 'vuex'
export default {
name: "admin",
data(){
return {
}
},
methods: {
...mapActions('collection', [
'fetchCollections'
]),
...mapGetters('collection', [
'getCollections'
])
},
computed: {
collections(){
return this.getCollections();
}
},
created(){
this.fetchCollections();
}
}
</script>
<style scoped>
.el-header, .el-footer {
background-color: #B3C0D1;
color: #333;
line-height: 60px;
}
.el-aside {
background-color: #D3DCE6;
color: #333;
line-height: 200px;
}
.el-main {
background-color: #E9EEF3;
color: #333;
line-height: 160px;
}
body > .el-container {
margin-bottom: 40px;
}
.el-container:nth-child(5) .el-aside,
.el-container:nth-child(6) .el-aside {
line-height: 260px;
}
.el-container:nth-child(7) .el-aside {
line-height: 320px;
}
.time {
font-size: 13px;
color: #999;
}
.bottom {
margin-top: 13px;
line-height: 12px;
}
.button {
padding: 0;
float: right;
}
.image {
width: 100%;
display: block;
}
.clearfix:before,
.clearfix:after {
display: table;
content: "";
}
.clearfix:after {
clear: both
}
</style>

View File

@ -1,18 +1,61 @@
import Vue from 'vue'
import Buefy from 'buefy'
import ElementUI from 'element-ui'
import 'element-ui/lib/theme-chalk/index.css'
Vue.use(Buefy);
import AdminPage from '../html/admin.vue'
//import { eventBus } from '../../js/event-bus-web-components'
import AdminPage from '../admin.vue'
import store from '../../js/store/store'
import router from './router'
Vue.use(ElementUI);
router.beforeEach((to, from, next) => {
document.title = to.meta.title
next()
});
// eventBus.listener();
//------------------------------------------------
// FROM DEV
// include vue-custom-element plugin to Vue
import Text from '../../classes/field-types/text/Text.vue';
import Textarea from '../../classes/field-types/textarea/Textarea.vue';
import Selectbox from '../../classes/field-types/selectbox/Selectbox.vue';
import Checkbox from '../../classes/field-types/checkbox/Checkbox.vue';
import Radio from '../../classes/field-types/radio/Radio.vue';
import Numeric from '../../classes/field-types/numeric/Numeric.vue';
import Date from '../../classes/field-types/date/Date.vue';
import Relationship from '../../classes/field-types/relationship/Relationship.vue';
import TaincanFormItem from '../../classes/field-types/tainacan-form-item.vue';
Vue.component('tainacan-text', Text);
Vue.component('tainacan-textarea', Textarea);
Vue.component('tainacan-selectbox', Selectbox);
Vue.component('tainacan-checkbox', Checkbox);
Vue.component('tainacan-radio', Radio);
Vue.component('tainacan-numeric', Numeric);
Vue.component('tainacan-date', Date);
Vue.component('tainacan-relationship', Relationship);
Vue.component('tainacan-form-item', TaincanFormItem);
//------------------------------------------------
// I18N PLUGIN
const I18NPlugin = {};
I18NPlugin.install = function (Vue, options = {}) {
Vue.prototype.$i18n = {
get: function (key) {
let string = wp_settings.i18n[key];
return (string != undefined && string != null && string != '' ) ? string : "ERROR: Invalid i18n key!";
}
}
}
Vue.use(I18NPlugin);
new Vue({
el: '#tainacan-admin-app',
store,
router,
render: h => h(AdminPage)
});

50
src/admin/js/router.js Normal file
View File

@ -0,0 +1,50 @@
import Vue from 'vue';
import VueRouter from 'vue-router'
import AdminPage from '../admin.vue'
import CollectionsPage from '../pages/collections-page.vue'
import CollectionPage from '../pages/collection-page.vue'
import CollectionEditionPage from '../pages/collection-edition-page.vue'
import ItemsPage from '../pages/items-page.vue'
import ItemPage from '../pages/item-page.vue'
import ItemEditionPage from '../pages/item-edition-page.vue'
import FieldsPage from '../pages/fields-page.vue'
import FiltersPage from '../pages/filters-page.vue'
import CategoriesPage from '../pages/categories-page.vue'
import EventsPage from '../pages/events-page.vue'
import CollectionsList from '../components/collections-list.vue'
import ItemsList from '../components/items-list.vue'
Vue.use(VueRouter);
const routes = [
{ path: '/', component: CollectionsPage, meta: {title: 'Admin Page'} },
{ path: '/collections', component: CollectionsPage, meta: {title: 'Collections Page'} },
{ path: '/collections/new', component: CollectionEditionPage, meta: {title: 'Create Collection'} },
{ path: '/collections/:id', component: CollectionPage, meta: {title: 'Collections Page'} },
{ path: '/collections/:id/edit', component: CollectionEditionPage, meta: {title: 'Edit Collection'} },
{ path: '/collections/:id/items/new', component: ItemEditionPage, meta: {title: 'Create Item'} },
{ path: '/collections/:collection_id/items/:id/edit', component: ItemEditionPage, meta: {title: 'Edit Item'} },
{ path: '/collection/:collection_id/items', component: ItemsPage, meta: {title: 'Items Page'} },
{ path: '/collections/:collection_id/items/:id', component: ItemPage, meta: {title: 'Item Page'} },
{ path: '/items', component: ItemsPage, meta: {title: 'Items Page'} },
{ path: '/filters', component: FiltersPage, meta: {title: 'Filters Page'} },
{ path: '/fields', component: FieldsPage, meta: {title: 'Fields Page'} },
{ path: '/categories', component: CategoriesPage, meta: {title: 'Categories Page'} },
{ path: '/events', component: EventsPage, meta: {title: 'Events Page'} },
{ path: '*', redirect: '/'}
]
export default new VueRouter ({
routes
})

View File

@ -0,0 +1,21 @@
<template>
<div>
<h1>Categories Page</h1>
</div>
</template>
<script>
export default {
name: 'CategoriesPage',
data(){
return {
}
}
}
</script>
<style scoped>
</style>

View File

@ -0,0 +1,186 @@
<template>
<div>
<h1 class="is-size-3">Collection creation <b-tag v-if="collection != null && collection != undefined" :type="'is-' + getStatusColor(collection.status)" v-text="collection.status"></b-tag></h1>
<form label-width="120px">
<b-field label="Título">
<b-input
v-model="form.name">
</b-input>
</b-field>
<b-field label="Descrição">
<b-input
type="textarea"
v-model="form.description"
>
</b-input>
</b-field>
<b-field label="Status">
<b-select
v-model="form.status"
placeholder="Selecione um status">
<option
v-for="statusOption in statusOptions"
:key="statusOption.value"
:value="statusOption.value"
:disabled="statusOption.disabled">{{ statusOption.label }}
</option>
</b-select>
</b-field>
<b-field
label="Imagem">
<b-upload v-model="form.files"
multiple
drag-drop>
<section class="section">
<div class="content has-text-centered">
<p>
<b-icon
icon="upload"
size="is-large">
</b-icon>
</p>
<p>Arraste uma imagem aqui <em>ou clique para enviar</em></p>
</div>
</section>
</b-upload>
</b-field>
<button
class="button"
type="button"
@click="cancelBack">Cancelar</button>
<a
@click="onSubmit"
class="button is-success is-hovered">Salvar</a>
</form>
<b-loading :active.sync="isLoading" :canCancel="false">
</div>
</template>
<script>
import { mapActions, mapGetters } from 'vuex'
export default {
name: 'CollectionEditionPage',
data(){
return {
collectionId: Number,
collection: null,
isLoading: false,
form: {
name: '',
status: '',
description: '',
files:[]
},
// Can be obtained from api later
statusOptions: [{
value: 'publish',
label: 'Publicado'
}, {
value: 'draft',
label: 'Rascunho'
}, {
value: 'private',
label: 'Privado'
}, {
value: 'trash',
label: 'Lixo'
}]
}
},
methods: {
...mapActions('collection', [
'sendCollection',
'updateCollection',
'fetchCollection'
]),
...mapGetters('collection',[
'getCollection'
]),
onSubmit() {
// Puts loading on Draft Collection creation
this.isLoading = true;
let data = {collection_id: this.collectionId, name: this.form.name, description: this.form.description, status: this.form.status};
this.updateCollection(data).then(updatedCollection => {
this.collection = updatedCollection;
// Fill this.form data with current data.
this.form.name = this.collection.name;
this.form.description = this.collection.description;
this.form.status = this.collection.status;
this.isLoading = false;
this.$router.push('/collections/' + this.collectionId);
});
},
getStatusColor(status) {
switch(status) {
case 'publish':
return 'success'
case 'draft':
return 'info'
case 'private':
return 'warning'
case 'trash':
return 'danger'
default:
return 'info'
}
},
createNewCollection() {
// Puts loading on Draft Collection creation
this.isLoading = true;
// Creates draft Collection
let data = { name: '', description: '', status: 'auto-draft'};
this.sendCollection(data).then(res => {
this.collectionId = res.id;
this.collection = res;
// Fill this.form data with current data.
this.form.name = this.collection.name;
this.form.description = this.collection.description;
this.form.status = this.collection.status;
this.isLoading = false;
})
.catch(error => console.log(error));
},
cancelBack(){
this.$router.push('/collections/' + this.collectionId);
}
},
created(){
if (this.$route.fullPath.split("/").pop() == "new") {
this.createNewCollection();
} else if (this.$route.fullPath.split("/").pop() == "edit") {
this.isLoading = true;
// Obtains current Collection ID from URL
this.pathArray = this.$route.fullPath.split("/").reverse();
this.collectionId = this.pathArray[1];
this.fetchCollection(this.collectionId).then(res => {
this.collection = res;
// Fill this.form data with current data.
this.form.name = this.collection.name;
this.form.description = this.collection.description;
this.form.status = this.collection.status;
this.isLoading = false;
});
}
}
}
</script>

View File

@ -0,0 +1,98 @@
<template>
<div class="columns is-fullheight">
<nav id="secondary-menu" role="navigation" aria-label="secondary navigation" class="column is-2 is-sidebar-menu">
<aside class="menu">
<ul class="menu-list">
<li><router-link tag="a" to="">{{ $i18n.get('items')}}</router-link></li>
<li><router-link tag="a" to="">{{ $i18n.get('edit')}}</router-link></li>
<li><router-link tag="a" to="">{{ $i18n.get('fields')}}</router-link></li>
<li><router-link tag="a" to="">{{ $i18n.get('filters')}}</router-link></li>
</ul>
</aside>
</nav>
<section class="container column is-main-content" v-if="collection != null">
<div class="card">
<div class="card-image" v-if="collection.featured_image">
<figure class="image is-4by3">
<img :src="collection.featured_image" class="image" :alt="collection.name">
</figure>
</div>
<div class="card-content">
<div class="media">
<div class="media-content">
<p class="title is-4">{{ collection.name }}</p>
</div>
</div>
<div class="content">
{{collection.description}}
</div>
</div>
<footer class="card-footer">
<router-link
tag="a"
class="card-footer-item"
:to="{ path: `/collections/${collection.id}/edit` }">
Editar Coleção
</router-link>
<router-link
tag="a"
class="card-footer-item"
:to="{ path: `/collections/${collection.id}/items/new`, params: { collection_id: collection.id }}">
Criar Item
</router-link>
<router-link
tag="a"
class="card-footer-item"
:to="{ path: `/collection/${collection.id}/items/`, params: { collection_id: collection.id }}">
Ver todos os itens
</router-link>
</footer>
</div>
<items-list :collectionId="collectionId"></items-list>
</section>
</div>
</template>
<script>
import { mapActions, mapGetters } from 'vuex'
import ItemsList from '../components/items-list.vue';
export default {
name: 'CollectionPage',
data(){
return {
collectionId: Number
}
},
methods: {
...mapActions('collection', [
'fetchCollection'
]),
...mapGetters('collection', [
'getCollection'
]),
createItem() {
}
},
components: {
'items-list': ItemsList
},
computed: {
collection(){
return this.getCollection();
}
},
created(){
this.collectionId = new Number(this.$route.params.id);
this.fetchCollection(this.collectionId);
}
}
</script>
<style scoped>
</style>

View File

@ -0,0 +1,31 @@
<template>
<div>
<h1>Collections Page</h1>
<router-link tag="button" class="button is-primary"
:to="{ path: `/collections/new` }">
Criar Coleção
</router-link>
<collections-list></collections-list>
</div>
</template>
<script>
import CollectionsList from '../components/collections-list.vue';
export default {
name: 'CollectionsPage',
data(){
return {
}
},
components: {
CollectionsList
}
}
</script>
<style scoped>
</style>

View File

@ -0,0 +1,21 @@
<template>
<div>
<h1>Events (or Activites) Page</h1>
</div>
</template>
<script>
export default {
name: 'EventsPage',
data(){
return {
}
}
}
</script>
<style scoped>
</style>

View File

@ -0,0 +1,21 @@
<template>
<div>
<h1>Fields Page</h1>
</div>
</template>
<script>
export default {
name: 'FieldsPage',
data(){
return {
}
}
}
</script>
<style scoped>
</style>

View File

@ -0,0 +1,21 @@
<template>
<div>
<h1>Filters Page</h1>
</div>
</template>
<script>
export default {
name: 'FiltersPage',
data(){
return {
}
}
}
</script>
<style scoped>
</style>

View File

@ -0,0 +1,204 @@
<template>
<div>
<h1 class="is-size-3">Item creation <b-tag v-if="item != null && item != undefined" :type="'is-' + getStatusColor(item.status)" v-text="item.status"></b-tag></h1>
<form label-width="120px">
<b-field label="Status">
<b-select
v-model="form.status"
placeholder="Selecione um status">
<option
v-for="statusOption in statusOptions"
:key="statusOption.value"
:value="statusOption.value"
:disabled="statusOption.disabled">{{ statusOption.label }}
</option>
</b-select>
</b-field>
<b-field
label="Imagem">
<b-upload v-model="form.files"
multiple
drag-drop>
<section class="section">
<div class="content has-text-centered">
<p>
<b-icon
icon="upload"
size="is-large">
</b-icon>
</p>
<p>Arraste uma imagem aqui <em>ou clique para enviar</em></p>
</div>
</section>
</b-upload>
</b-field>
<tainacan-form-item
v-for="(field, index) in fieldList"
v-bind:key="index"
:field="field"></tainacan-form-item>
<button
class="button"
type="button"
@click="cancelBack">Cancelar</button>
<a
@click="onSubmit"
class="button is-success is-hovered">Salvar</a>
</form>
<b-loading :active.sync="isLoading" :canCancel="false">
</div>
</template>
<script>
import { mapActions, mapGetters } from 'vuex'
export default {
name: 'ItemEditionPage',
data(){
return {
itemId: Number,
item: null,
collectionId: Number,
isLoading: false,
form: {
collectionId: Number,
status: '',
files:[]
},
// Can be obtained from api later
statusOptions: [{
value: 'publish',
label: 'Publicado'
}, {
value: 'draft',
label: 'Rascunho'
}, {
value: 'private',
label: 'Privado'
}, {
value: 'trash',
label: 'Lixo'
}]
}
},
methods: {
...mapActions('item', [
'sendItem',
'updateItem',
'fetchFields',
'sendField',
'fetchItem',
'cleanFields'
]),
...mapGetters('item',[
'getFields',
'getItem'
]),
onSubmit() {
// Puts loading on Item edition
this.isLoading = true;
let data = {item_id: this.itemId, status: this.form.status};
this.updateItem(data).then(updatedItem => {
this.item = updatedItem;
// Fill this.form data with current data.
this.form.status = this.item.status;
this.isLoading = false;
this.$router.push('/collections/' + this.form.collectionId + '/items/' + this.itemId);
}).catch(error => {
console.log(error);
this.isLoading = false;
});
},
getStatusColor(status) {
switch(status) {
case 'publish':
return 'success'
case 'draft':
return 'info'
case 'private':
return 'warning'
case 'trash':
return 'danger'
default:
return 'info'
}
},
createNewItem() {
// Puts loading on Draft Item creation
this.isLoading = true;
// Creates draft Item
let data = {collection_id: this.form.collectionId, status: 'auto-draft'};
this.sendItem(data).then(res => {
this.itemId = res.id;
this.item = res;
// Fill this.form data with current data.
this.form.status = this.item.status;
this.loadMetadata();
})
.catch(error => console.log(error));
},
loadMetadata() {
// Obtains Item Field
this.fetchFields(this.itemId).then(res => {
this.isLoading = false;
});
},
cancelBack(){
this.$router.push('/collections/' + this.collectionId);
}
},
computed: {
fieldList(){
return this.getFields();
}
},
created(){
// Obtains collection ID
this.cleanFields();
this.collectionId = ( this.$route.params.collection_id ) ? this.$route.params.collection_id : this.$route.params.id;
this.form.collectionId = this.collectionId;
if (this.$route.fullPath.split("/").pop() == "new") {
this.createNewItem();
} else if (this.$route.fullPath.split("/").pop() == "edit") {
this.isLoading = true;
// Obtains current Item ID from URL
this.pathArray = this.$route.fullPath.split("/").reverse();
this.itemId = this.pathArray[1];
this.fetchItem(this.itemId).then(res => {
this.item = res;
// Fill this.form data with current data.
this.form.status = this.item.status;
this.loadMetadata();
});
}
}
}
</script>
<style scoped>
</style>

View File

@ -0,0 +1,114 @@
<template>
<div>
<b-loading :active.sync="isLoading" :canCancel="false">
</b-loading>
<div class="card">
<div class="card-image" v-if="item.featured_image">
<figure class="image is-4by3">
<img :src="item.featured_image" class="image" :alt="item.title">
</figure>
</div>
<div class="card-content">
<div class="media">
<div class="media-content">
<p class="title is-4">{{ item.title }}</p>
<!--p class="subtitle is-6">@johnsmith</p-->
</div>
</div>
<div class="content">
{{item.description}}
</div>
</div>
<footer class="card-footer">
<router-link
class="card-footer-item" :to="{ path: `/collections/${collectionId}`}">
Ver Coleção
</router-link>
<router-link
class="card-footer-item" :to="{ path: `/collections/${collectionId}/items/${itemId}/edit`}">
Editar Item
</router-link>
</footer>
</div>
</div>
</template>
<script>
import { mapActions, mapGetters } from 'vuex'
export default {
name: 'ItemPage',
data(){
return {
collectionId: Number,
itemId: Number,
isLoading: false
}
},
methods: {
...mapActions('item', [
'fetchItem'
]),
...mapGetters('item', [
'getItem'
]),
},
computed: {
item(){
return this.getItem();
}
},
created(){
// Obtains item and collection ID
this.collectionId = this.$route.params.collection_id;
this.itemId = this.$route.fullPath.split("/").pop();
// Puts loading on Item Loading
this.isLoading = true;
let loadingInstance = this;
// Obtains Item
this.fetchItem(this.itemId).then(res => {
loadingInstance.isLoading = false;
});
}
}
</script>
<style scoped>
.time {
font-size: 13px;
color: #999;
}
.bottom {
margin-top: 13px;
line-height: 12px;
}
el-button {
padding: 0;
float: right;
}
.image {
width: 100%;
display: block;
}
.clearfix:before,
.clearfix:after {
display: table;
content: "";
}
.clearfix:after {
clear: both
}
</style>

View File

@ -0,0 +1,21 @@
<template>
<div>
<h1>Items Page</h1>
</div>
</template>
<script>
export default {
name: 'ItemsPage',
data(){
return {
}
}
}
</script>
<style scoped>
</style>

View File

@ -0,0 +1,22 @@
// Import Bulma's core and variables
@import "../../../node_modules/bulma/sass/utilities/_all.sass";
// Tainacan custom colors
$primary: #25a189;
$primary-invert: findColorInvert($primary);
$secondary: #01295c;
$secondary-invert: findColorInvert($primary);
// Setup $colors to use as bulma classes
$colors: (
"white": ($white, $black),
"black": ($black, $white),
"light": ($light, $light-invert),
"dark": ($dark, $dark-invert),
"primary": ($primary, $primary-invert),
"secondary": ($secondary, $secondary-invert),
"info": ($info, $info-invert),
"success": ($success, $success-invert),
"warning": ($warning, $warning-invert),
"danger": ($danger, $danger-invert)
);

View File

@ -1,15 +1,53 @@
// Tainacan custom colors and bulma's core
@import "./_variables.scss";
body.tainacan-admin-page #adminmenumain {
// Links
$link: $primary;
$link-invert: $primary-invert;
$link-focus-border: $primary;
// Bulma's modal (needs to be greather than taincan-admin-app
$modal-z: 9999999;
// Import Bulma and Buefy styles
@import "../../assets/css/fonts/materialdesignicons.css";
@import "../../../node_modules/bulma/bulma.sass";
@import "../../../node_modules/buefy/src/scss/buefy.scss";
// Clears wordpress content
body.tainacan-admin-page #adminmenumain, body.tainacan-admin-page #wpfooter, body.tainacan-admin-page #wp-auth-check-wrap {
display: none;
}
#tainacan-admin-app {
background: #fff;
background: #ffffff;
position: fixed;
top: 0;
left: 0;
bottom: 0;
right: 0;
width: 100%;
z-index: 99999999999;
z-index: 999999;
overflow-y: auto;
height: 100%;
margin: 0px !important;
}
/* Rules for sizing the icon. */
.material-icons.md-18 { font-size: 18px; }
.material-icons.md-24 { font-size: 24px; }
.material-icons.md-36 { font-size: 36px; }
.material-icons.md-48 { font-size: 48px; }
/* Rules for using icons as black on a light background. */
.material-icons.md-dark { color: rgba(0, 0, 0, 0.54); }
.material-icons.md-dark.md-inactive { color: rgba(0, 0, 0, 0.26); }
/* Rules for using icons as white on a dark background. */
.material-icons.md-light { color: rgba(255, 255, 255, 1); }
.material-icons.md-light.md-inactive { color: rgba(255, 255, 255, 0.3); }
// Buefy notices (toast)
.notices {
z-index: 99999999 !important;
}

View File

@ -0,0 +1,24 @@
<?php
return [
// Tainacan common terms
'repository' => __('Repository', 'tainacan'),
'collections' => __('Collections', 'tainacan'),
'items' => __('Items', 'tainacan'),
'fields' => __('Fields', 'tainacan'),
'filters' => __('Filters', 'tainacan'),
'categories' => __('Categories', 'tainacan'),
'events' => __('Events', 'tainacan'),
'collection' => __('Collection', 'tainacan'),
'item' => __('Item', 'tainacan'),
'field' => __('Field', 'tainacan'),
'filter' => __('Filter', 'tainacan'),
'category' => __('Categorie', 'tainacan'),
'event' => __('Event', 'tainacan'),
// Actions
'edit' => __('Edit', 'tainacan'),
'new' => __('New', 'tainacan'),
]
?>

View File

@ -0,0 +1,191 @@
<?php
class TAINACAN_REST_Controller extends WP_REST_Controller {
/**
* @param $object
* @param $new_values
*
* @return Tainacan\Entities\Entity
*/
protected function prepare_item_for_updating($object, $new_values){
foreach ($new_values as $key => $value) {
try {
$set_ = 'set_' . $key;
$object->$set_( $value );
} catch (\Error $error){
// Do nothing
}
}
return $object;
}
/**
* @param $entity
*
* @return array
*/
protected function get_only_needed_attributes($entity, $map){
$entity_prepared = [
'id' => $entity->get_id(),
'description' => $entity->get_description(),
];
if(array_key_exists('modification_date', $map)){
$entity_prepared['modification_date'] = $entity->get_modification_date();
}
if(array_key_exists('creation_date', $map)){
$entity_prepared['creation_date'] = $entity->get_creation_date();
}
if(array_key_exists('author_id', $map)){
$entity_prepared['author_id'] = $entity->get_author_id();
}
if(array_key_exists('name', $map)){
$entity_prepared['name'] = $entity->get_name();
} elseif(array_key_exists('title', $map)){
$entity_prepared['title'] = $entity->get_title();
}
if(array_key_exists('featured_image', $map)){
$entity_prepared['featured_image'] = $entity->get_featured_img();
}
if(array_key_exists('columns', $map)){
$entity_prepared['columns'] = $entity->get_columns();
}
if(array_key_exists('status', $map)){
$entity_prepared['status'] = $entity->get_status();
}
return $entity_prepared;
}
/**
* @param $request
*
* @return array
*/
protected function prepare_filters($request){
$map = [
'name' => 'title',
'title' => 'title',
'id' => 'p',
'pageid' => 'page_id',
'authorid' => 'author_id',
'authorname' => 'author_name',
'search' => 's',
'status' => 'post_status',
'offset' => 'offset',
'metaquery' => 'meta_query',
'datequery' => 'date_query',
'order' => 'order',
'orderby' => 'orderby',
'metakey' => 'meta_key',
'metavalue' => 'meta_value',
'metavaluenum' => 'meta_value_num',
'metacompare' => 'meta_compare',
'hideempty' => 'hide_empty',
'perpage' => 'posts_per_page',
'paged' => 'paged',
];
$meta_query = [
'key' => 'key',
'value' => 'value',
'compare' => 'compare',
'relation' => 'relation',
'type' => 'type',
];
$date_query = [
'year' => 'year',
'month' => 'month',
'day' => 'day',
'week' => 'week',
'hour' => 'hour',
'minute' => 'minute',
'second' => 'second',
'compare' => 'compare',
'dayofweek' => 'dayofweek',
'inclusive' => 'inclusive',
'before' => 'before',
'after' => 'after',
];
$args = [];
foreach ($map as $mapped => $mapped_v){
if(isset($request[$mapped])){
if($mapped === 'metaquery'){
$args = $this->prepare_meta($mapped, $request, $meta_query, $mapped_v, $args);
} elseif($mapped === 'datequery'){
$args = $this->prepare_meta($mapped, $request, $date_query, $mapped_v, $args);
}
else {
$args[ $mapped_v ] = $request[ $mapped ];
}
}
}
$args['perm'] = 'readable';
return $args;
}
/**
* @param $mapped
* @param $request
* @param $query
* @param $mapped_v
* @param $args
*
* @return mixed
*/
private function prepare_meta($mapped, $request, $query, $mapped_v, $args){
$request_meta_query = $request[$mapped];
// If is a multidimensional array (array of array)
if($this->contains_array($request_meta_query)) {
foreach ( $request_meta_query as $index1 => $a ) {
foreach ( $query as $mapped_meta => $meta_v ) {
if ( isset( $a[ $meta_v ] ) ) {
$args[ $mapped_v ][ $index1 ][ $meta_v ] = $request[ $mapped ][ $index1 ][ $meta_v ];
}
}
}
} else {
foreach ( $query as $mapped_meta => $meta_v ) {
if(isset($request[$mapped][$meta_v])) {
$args[ $mapped_v ][ $meta_v ] = $request[ $mapped ][ $meta_v ];
}
}
}
return $args;
}
/**
* @param $array
*
* @return bool
*/
protected function contains_array($array){
foreach ($array as $value){
if(is_array($value)){
return true;
}
}
return false;
}
}
?>

View File

@ -8,7 +8,7 @@ use Tainacan\Entities;
*
* @uses Entities\Collection and Repositories\Collections
* */
class TAINACAN_REST_Collections_Controller extends WP_REST_Controller {
class TAINACAN_REST_Collections_Controller extends TAINACAN_REST_Controller {
private $collections_repository;
private $collection;
@ -41,20 +41,20 @@ class TAINACAN_REST_Collections_Controller extends WP_REST_Controller {
'methods' => WP_REST_Server::READABLE,
'callback' => array($this, 'get_items'),
'permission_callback' => array($this, 'get_items_permissions_check'),
'args' => $this->get_item_schema()
//'args' => $this->get_item_schema()
),
array(
'methods' => WP_REST_Server::CREATABLE,
'callback' => array($this, 'create_item'),
'permission_callback' => array($this, 'create_item_permissions_check'),
'args' => $this->get_endpoint_args_for_item_schema(WP_REST_Server::CREATABLE),
//'args' => $this->get_endpoint_args_for_item_schema(WP_REST_Server::CREATABLE),
),
));
register_rest_route($this->namespace, '/' . $this->rest_base . '/(?P<collection_id>[\d]+)', array(
array(
'methods' => WP_REST_Server::READABLE,
'callback' => array($this, 'get_item'),
'args' => $this->get_collection_params(),
//'args' => $this->get_collection_params(),
'permission_callback' => array($this, 'get_item_permissions_check'),
),
array(
@ -78,11 +78,35 @@ class TAINACAN_REST_Collections_Controller extends WP_REST_Controller {
* @return WP_Error|WP_REST_Response
*/
public function get_items($request){
$collections = $this->collections_repository->fetch();
$args = $this->prepare_filters($request);
$response = $this->prepare_item_for_response($collections, $request);
$collections = $this->collections_repository->fetch($args);
return new WP_REST_Response($response, 200);
$map = $this->collections_repository->get_map();
$response = [];
if($collections->have_posts()){
while ($collections->have_posts()){
$collections->the_post();
$collection = new Entities\Collection($collections->post);
array_push($response, $this->get_only_needed_attributes($collection, $map));
}
wp_reset_postdata();
}
$total_collections = $collections->found_posts;
$max_pages = ceil($total_collections / (int) $collections->query_vars['posts_per_page']);
$rest_response = new WP_REST_Response($response, 200);
$rest_response->header('X-WP-Total', (int) $total_collections);
$rest_response->header('X-WP-TotalPages', (int) $max_pages);
return $rest_response;
}
/**
@ -111,22 +135,7 @@ class TAINACAN_REST_Collections_Controller extends WP_REST_Controller {
* @return mixed|string|void|WP_Error|WP_REST_Response
*/
public function prepare_item_for_response($item, $request){
if($item instanceof WP_Query){
$collections_as_json = [];
if ($item->have_posts()) {
while ( $item->have_posts() ) {
$item->the_post();
$collection = new Entities\Collection($item->post);
array_push($collections_as_json, $collection->__toArray());
}
wp_reset_postdata();
}
return $collections_as_json;
}
elseif(!empty($item)){
if(!empty($item)){
return $item->__toArray();
}
@ -141,7 +150,12 @@ class TAINACAN_REST_Collections_Controller extends WP_REST_Controller {
* @throws Exception
*/
public function get_items_permissions_check($request){
return $this->collections_repository->can_read($this->collection);
if ( 'edit' === $request['context'] && ! current_user_can('edit_tainacan-collections') ) {
return false;
}
return true;
}
/**
@ -153,7 +167,12 @@ class TAINACAN_REST_Collections_Controller extends WP_REST_Controller {
*/
public function get_item_permissions_check($request){
$collection = $this->collections_repository->fetch($request['collection_id']);
return $this->collections_repository->can_read($collection);
if($collection instanceof Entities\Collection) {
return $collection->can_read();
}
return false;
}
/**
@ -165,17 +184,17 @@ class TAINACAN_REST_Collections_Controller extends WP_REST_Controller {
* @return array|WP_Error|WP_REST_Response
*/
public function create_item( $request ) {
$request = json_decode($request->get_body(), true);
$body = json_decode($request->get_body(), true);
if(empty($request)){
if(empty($body)){
return new WP_REST_Response([
'error_message' => __('Body can not be empty.', 'tainacan'),
'collection' => $request
'collection' => $body
], 400);
}
try {
$prepared_post = $this->prepare_item_for_database( $request );
$prepared_post = $this->prepare_item_for_database( $body );
} catch (\Error $exception){
return new WP_REST_Response($exception->getMessage(), 400);
}
@ -183,13 +202,15 @@ class TAINACAN_REST_Collections_Controller extends WP_REST_Controller {
if($prepared_post->validate()) {
$collection = $this->collections_repository->insert( $prepared_post );
return new WP_REST_Response($collection->__toArray(), 201);
$response = $this->prepare_item_for_response($collection, $request);
return new WP_REST_Response($response, 201);
}
return new WP_REST_Response([
'error_message' => __('One or more values are invalid.', 'tainacan'),
'errors' => $prepared_post->get_errors(),
'collection' => $prepared_post->__toArray()
'collection' => $this->prepare_item_for_response($prepared_post, $request)
], 400);
}
@ -252,7 +273,12 @@ class TAINACAN_REST_Collections_Controller extends WP_REST_Controller {
*/
public function delete_item_permissions_check( $request ) {
$collection = $this->collections_repository->fetch($request['collection_id']);
return $this->collections_repository->can_delete($collection);
if($collection instanceof Entities\Collection) {
return $collection->can_delete();
}
return false;
}
/**
@ -268,19 +294,40 @@ class TAINACAN_REST_Collections_Controller extends WP_REST_Controller {
$body = json_decode($request->get_body(), true);
if(!empty($body)){
$attributes = ['ID' => $collection_id];
$attributes = [];
foreach ($body as $att => $value){
$attributes[$att] = $value;
}
$updated_collection = $this->collections_repository->update($attributes);
$collection = $this->collections_repository->fetch($collection_id);
return new WP_REST_Response($updated_collection->__toArray(), 200);
if($collection) {
$prepared_collection = $this->prepare_item_for_updating( $collection, $attributes );
if ( $prepared_collection->validate() ) {
$updated_collection = $this->collections_repository->update( $collection );
$response = $this->prepare_item_for_response($updated_collection, $request);
return new WP_REST_Response( $response, 200 );
}
return new WP_REST_Response([
'error_message' => __('One or more values are invalid.', 'tainacan'),
'errors' => $prepared_collection->get_errors(),
'collection' => $this->prepare_item_for_response($prepared_collection, $request)
], 400);
}
return new WP_REST_Response([
'error_message' => __('Collection with that ID not found', 'tainacan' ),
'collection_id' => $collection_id
], 400);
}
return new WP_REST_Response([
'error_message' => 'The body could not be empty',
'error_message' => __('The body could not be empty', 'tainacan'),
'body' => $body
], 400);
}
@ -296,16 +343,12 @@ class TAINACAN_REST_Collections_Controller extends WP_REST_Controller {
*/
public function update_item_permissions_check( $request ) {
$collection = $this->collections_repository->fetch($request['collection_id']);
return $this->collections_repository->can_edit($collection);
}
/**
* @return array|mixed|void
*/
public function get_collection_params() {
$query_params = $this->collections_repository->get_map();
if($collection instanceof Entities\Collection) {
return $collection->can_edit();
}
return apply_filters("rest_{$this->collection->get_post_type()}_collection_params", $query_params, $this->collection->get_post_type());
return false;
}
/**
@ -321,7 +364,7 @@ class TAINACAN_REST_Collections_Controller extends WP_REST_Controller {
]
];
return apply_filters("rest_{$this->collection->get_post_type()}_collection_params", $args, $this->collection->get_post_type());
return $args;
}
/**
@ -330,7 +373,7 @@ class TAINACAN_REST_Collections_Controller extends WP_REST_Controller {
public function get_item_schema() {
$args = $this->collections_repository->get_map();
return apply_filters("rest_{$this->collection->get_post_type()}_collection_params", $args, $this->collection->get_post_type());
return $args;
}
}

View File

@ -0,0 +1,344 @@
<?php
use Tainacan\Entities;
use Tainacan\Repositories;
class TAINACAN_REST_Fields_Controller extends TAINACAN_REST_Controller {
private $field;
private $item_metadata_repository;
private $item_repository;
private $collection_repository;
private $field_repository;
public function __construct() {
$this->namespace = 'tainacan/v2';
$this->rest_base = 'fields';
add_action('rest_api_init', array($this, 'register_routes'));
add_action('init', array(&$this, 'init_objects'), 11);
}
/**
* Initialize objects after post_type register
*/
public function init_objects() {
$this->field = new Entities\Field();
$this->field_repository = new Repositories\Fields();
$this->item_metadata_repository = new Repositories\Item_Metadata();
$this->item_repository = new Repositories\Items();
$this->collection_repository = new Repositories\Collections();
}
/**
* If POST on field/collection/<collection_id>, then
* a field will be created in matched collection and all your item will receive this field
*
* If POST on field/item/<item_id>, then a value will be added in a field and field passed
* id body of requisition
*
* Both of GETs return the field of matched objects
*/
public function register_routes() {
register_rest_route($this->namespace, '/collection/(?P<collection_id>[\d]+)/' . $this->rest_base . '/(?P<field_id>[\d]+)',
array(
array(
'methods' => WP_REST_Server::DELETABLE,
'callback' => array($this, 'delete_item'),
'permission_callback' => array($this, 'delete_item_permissions_check')
),
array(
'methods' => WP_REST_Server::EDITABLE,
'callback' => array($this, 'update_item'),
'permission_callback' => array($this, 'update_item_permissions_check')
)
)
);
register_rest_route($this->namespace, '/collection/(?P<collection_id>[\d]+)/' . $this->rest_base,
array(
array(
'methods' => WP_REST_Server::READABLE,
'callback' => array($this, 'get_items'),
'permission_callback' => array($this, 'get_items_permissions_check'),
//'args' => $this->get_collection_params(),
),
array(
'methods' => WP_REST_Server::CREATABLE,
'callback' => array($this, 'create_item'),
'permission_callback' => array($this, 'create_item_permissions_check')
),
)
);
}
/**
* @param WP_REST_Request $request
*
* @return object|void|WP_Error
*/
public function prepare_item_for_database( $request ) {
$meta = json_decode($request[0]->get_body(), true);
foreach ($meta as $key => $value){
$set_ = 'set_' . $key;
$this->field->$set_($value);
}
$collection = new Entities\Collection($request[1]);
$this->field->set_collection($collection);
}
/**
* @param WP_REST_Request $request
*
* @return WP_Error|WP_REST_Response
*/
public function create_item( $request ) {
if(!empty($request->get_body())){
$collection_id = $request['collection_id'];
try {
$this->prepare_item_for_database( [ $request, $collection_id ] );
} catch (\Error $exception){
return new WP_REST_Response($exception->getMessage(), 400);
}
if($this->field->validate()) {
$this->field_repository->insert( $this->field );
$items = $this->item_repository->fetch([], $collection_id, 'WP_Query');
$field_added = '';
if($items->have_posts()){
while ($items->have_posts()){
$items->the_post();
$item = new Entities\Item($items->post);
$item_meta = new Entities\Item_Metadata_Entity($item, $this->field);
$field_added = $this->item_metadata_repository->insert($item_meta);
}
$response = $this->prepare_item_for_response($field_added->get_field(), $request);
return new WP_REST_Response($response, 201);
}
else {
$response = $this->prepare_item_for_response($this->field, $request);
return new WP_REST_Response($response, 201);
}
} else {
return new WP_REST_Response([
'error_message' => __('One or more values are invalid.', 'tainacan'),
'errors' => $this->field->get_errors(),
'field' => $this->prepare_item_for_response($this->field, $request),
], 400);
}
}
return new WP_REST_Response([
'error_message' => __('Body can not be empty.', 'tainacan'),
'item' => $request->get_body()
], 400);
}
/**
* @param $request
*
* @return bool|WP_Error
* @throws Exception
*/
public function create_item_permissions_check( $request ) {
return $this->collection_repository->can_edit(new Entities\Collection());
}
/**
* @param mixed $item
* @param WP_REST_Request $request
*
* @return array|WP_Error|WP_REST_Response
*/
public function prepare_item_for_response( $item, $request ) {
if(!empty($item)){
return $item->__toArray();
}
return $item;
}
/**
* @param WP_REST_Request $request
*
* @return WP_Error|WP_REST_Response
*/
public function get_items( $request ) {
$collection_id = $request['collection_id'];
$args = $this->prepare_filters($request);
$collection = new Entities\Collection($collection_id);
$collection_metadata = $this->field_repository->fetch_by_collection($collection, $args, 'OBJECT');
$prepared_item = [];
foreach ($collection_metadata as $item){
$prepared_item[] = $this->prepare_item_for_response($item, $request);
}
return new WP_REST_Response($prepared_item, 200);
}
/**
* @param WP_REST_Request $request
*
* @return bool|WP_Error
* @throws Exception
*/
public function get_items_permissions_check( $request ) {
if (isset($request['collection_id'])) {
if ( 'edit' === $request['context'] && ! current_user_can('edit_tainacan-fields') ) {
return false;
}
return true;
}
return false;
}
/**
* @return array
*/
public function get_collection_params() {
return parent::get_collection_params(); // TODO: Change the autogenerated stub
}
/**
* @param WP_REST_Request $request
*
* @return WP_Error|WP_REST_Response
*/
public function delete_item( $request ) {
if(!empty($request->get_body())){
$body = json_decode($request->get_body());
$collection_id = $request['collection_id'];
$field_id = $body['field_id'];
return new WP_REST_Response(['error' => 'Not Implemented.'], 400);
}
}
/**
* @param WP_REST_Request $request
*
* @return bool|WP_Error
* @throws Exception
*/
public function delete_item_permissions_check( $request ) {
if (isset($request['collection_id'])) {
$collection = $this->collection_repository->fetch($request['collection_id']);
if ($collection instanceof Entities\Collection) {
return $collection->can_delete();
}
}
return false;
}
/**
* @param WP_REST_Request $request
*
* @return WP_Error|WP_REST_Response
*/
public function update_item( $request ) {
$collection_id = $request['collection_id'];
$body = json_decode($request->get_body(), true);
if(!empty($body)){
$attributes = [];
$field_id = $request['field_id'];
foreach ($body['values'] as $att => $value){
$attributes[$att] = $value;
}
$field = $this->field_repository->fetch($field_id);
if($field){
$prepared_metadata = $this->prepare_item_for_updating($field, $attributes);
if($prepared_metadata->validate()){
$updated_metadata = $this->field_repository->update($prepared_metadata);
$items = $this->item_repository->fetch([], $collection_id, 'WP_Query');
$up_metadata = '';
if($items->have_posts()){
while ($items->have_posts()){
$items->the_post();
$item = new Entities\Item($items->post);
$item_meta = new Entities\Item_Metadata_Entity($item, $updated_metadata);
$up_metadata = $this->item_metadata_repository->update($item_meta);
}
$response = $this->prepare_item_for_response($up_metadata->get_field(), $request);
return new WP_REST_Response($response, 200);
}
$response = $this->prepare_item_for_response($updated_metadata, $request);
return new WP_REST_Response($response, 200);
}
return new WP_REST_Response([
'error_message' => __('One or more values are invalid.', 'tainacan'),
'errors' => $prepared_metadata->get_errors(),
'metadata' => $this->prepare_item_for_response($prepared_metadata, $request)
], 400);
}
return new WP_REST_Response([
'error_message' => __('Field with that ID not found', 'tainacan'),
'field_id' => $field_id
], 400);
}
return new WP_REST_Response([
'error_message' => __('The body could not be empty', 'tainacan'),
'body' => $body
], 400);
}
/**
* @param WP_REST_Request $request
*
* @return bool|WP_Error
* @throws Exception
*/
public function update_item_permissions_check( $request ) {
if(isset($request['collection_id'])) {
$collection = $this->collection_repository->fetch($request['collection_id']);
if ($collection instanceof Entities\Collection) {
return $collection->can_edit();
}
}
return false;
}
}
?>

View File

@ -3,12 +3,12 @@
use Tainacan\Entities;
use Tainacan\Repositories;
class TAINACAN_REST_Filters_Controller extends WP_REST_Controller {
class TAINACAN_REST_Filters_Controller extends TAINACAN_REST_Controller {
private $collection;
private $collection_repository;
private $metadata;
private $metadata_repository;
private $field;
private $field_repository;
private $filter;
private $filter_repository;
@ -31,20 +31,22 @@ class TAINACAN_REST_Filters_Controller extends WP_REST_Controller {
$this->collection = new Entities\Collection();
$this->collection_repository = new Repositories\Collections();
$this->metadata = new Entities\Metadata();
$this->metadata_repository = new Repositories\Metadatas();
$this->field = new Entities\Field();
$this->field_repository = new Repositories\Fields();
$this->filter = new Entities\Filter();
$this->filter_repository = new Repositories\Filters();
}
public function register_routes() {
register_rest_route($this->namespace, '/' . $this->rest_base, array(
register_rest_route($this->namespace, '/collection/(?P<collection_id>[\d]+)/field/(?P<field_id>[\d]+)/' . $this->rest_base, array(
array(
'methods' => WP_REST_Server::CREATABLE,
'callback' => array($this, 'create_item'),
'permission_callback' => array($this, 'create_item_permissions_check')
),
));
register_rest_route($this->namespace, '/' . $this->rest_base, array(
array(
'methods' => WP_REST_Server::READABLE,
'callback' => array($this, 'get_items'),
@ -79,8 +81,8 @@ class TAINACAN_REST_Filters_Controller extends WP_REST_Controller {
public function prepare_item_for_database( $request ) {
$body = json_decode($request->get_body(), true);
$collection_id = $body['collection_id'];
$metadata_id = $body['metadata_id'];
$collection_id = $request['collection_id'];
$field_id = $request['field_id'];
$filter = $body['filter'];
$received_type = $body['filter_type'];
@ -107,7 +109,7 @@ class TAINACAN_REST_Filters_Controller extends WP_REST_Controller {
}
$this->filter->set_collection_id($collection_id);
$this->filter->set_metadata($metadata_id);
$this->filter->set_field($field_id);
$this->filter->set_filter_type($filter_type);
}
@ -124,17 +126,17 @@ class TAINACAN_REST_Filters_Controller extends WP_REST_Controller {
if ($this->filter->validate()){
$filter_inserted = $this->filter_repository->insert($this->filter);
return new WP_REST_Response($filter_inserted->__toArray(), 200);
return new WP_REST_Response($this->prepare_item_for_response($filter_inserted, $request), 200);
}
return new WP_REST_Response([
'error_message' => 'One or more attributes are invalid',
'error_message' => __('One or more attributes are invalid', 'tainacan'),
'error' => $this->filter->get_errors()
], 400);
}
return new WP_REST_Response([
'error_message' => 'The body could not be empty.',
'error_message' => __('The body could not be empty', 'tainacan'),
'body' => $request->get_body()
], 400);
}
@ -145,7 +147,16 @@ class TAINACAN_REST_Filters_Controller extends WP_REST_Controller {
* @return bool|WP_Error
*/
public function create_item_permissions_check( $request ) {
return $this->filter_repository->can_edit($this->filter);
$body = json_decode($request->get_body(), true);
$metadata = $this->field_repository->fetch($request['field_id']);
$collection = $this->collection_repository->fetch($request['collection_id']);
if(($metadata instanceof Entities\Field) && ($collection instanceof Entities\Collection)) {
return $this->filter_repository->can_edit($this->filter) && $metadata->can_edit() && $collection->can_edit();
}
return false;
}
/**
@ -163,11 +174,11 @@ class TAINACAN_REST_Filters_Controller extends WP_REST_Controller {
$filter = $this->filter_repository->delete($args);
return new WP_REST_Response($filter->__toArray(), 200);
return new WP_REST_Response($this->prepare_item_for_response($filter, $request), 200);
}
return new WP_REST_Response([
'error_message' => 'The body could not be empty',
'error_message' => __('The body could not be empty', 'tainacan'),
'body' => $request->get_body()
], 400);
}
@ -179,7 +190,12 @@ class TAINACAN_REST_Filters_Controller extends WP_REST_Controller {
*/
public function delete_item_permissions_check( $request ) {
$filter = $this->filter_repository->fetch($request['filter_id']);
return $this->filter_repository->can_delete($filter);
if ($filter instanceof Entities\Filter) {
return $filter->can_delete();
}
return false;
}
/**
@ -193,19 +209,39 @@ class TAINACAN_REST_Filters_Controller extends WP_REST_Controller {
$body = json_decode($request->get_body(), true);
if(!empty($body)){
$attributes = ['ID' => $filter_id];
$attributes = [];
foreach ($body as $att => $value){
$attributes[$att] = $value;
}
$updated_filter = $this->filter_repository->update($attributes);
$filter = $this->filter_repository->fetch($filter_id);
if($filter) {
$prepared_filter = $this->prepare_item_for_updating($filter, $attributes);
if($prepared_filter->validate()) {
$updated_filter = $this->filter_repository->update( $prepared_filter );
return new WP_REST_Response($this->prepare_item_for_response($updated_filter, $request), 200);
}
return new WP_REST_Response([
'error_message' => __('One or more values are invalid.', 'tainacan'),
'errors' => $prepared_filter->get_errors(),
'filters' => $this->prepare_item_for_response($prepared_filter, $request)
], 400);
}
return new WP_REST_Response([
'error_message' => __('Filter with that ID not found', 'tainacan' ),
'filter_id' => $filter_id
], 400);
return new WP_REST_Response($updated_filter->__toArray(), 200);
}
return new WP_REST_Response([
'error_message' => 'The body could not be empty',
'error_message' => __('The body could not be empty', 'tainacan'),
'body' => $body
], 400);
@ -218,28 +254,26 @@ class TAINACAN_REST_Filters_Controller extends WP_REST_Controller {
*/
public function update_item_permissions_check( $request ) {
$filter = $this->filter_repository->fetch($request['filter_id']);
return $this->filter_repository->can_edit($filter);
if ($filter instanceof Entities\Filter) {
return $filter->can_edit();
}
return false;
}
/**
* @param mixed $object
* @param $item
* @param WP_REST_Request $request
*
* @return array|mixed|WP_Error|WP_REST_Response
*/
public function prepare_item_for_response( $object, $request ) {
if(is_array($object)){
$filters = [];
foreach ($object as $item){
$filters[] = $item->__toArray();
}
return $filters;
public function prepare_item_for_response( $item, $request ) {
if(!empty($item)) {
return $item->__toArray();
}
return $object;
return $item;
}
/**
@ -248,9 +282,14 @@ class TAINACAN_REST_Filters_Controller extends WP_REST_Controller {
* @return WP_Error|WP_REST_Response
*/
public function get_items( $request ) {
$filters = $this->filter_repository->fetch([], 'OBJECT');
$args = $this->prepare_filters($request);
$response = $this->prepare_item_for_response($filters, $request);
$filters = $this->filter_repository->fetch($args, 'OBJECT');
$response = [];
foreach ($filters as $filter){
array_push($response, $this->prepare_item_for_response($filter, $request));
}
return new WP_REST_Response($response, 200);
}
@ -274,7 +313,7 @@ class TAINACAN_REST_Filters_Controller extends WP_REST_Controller {
$filter = $this->filter_repository->fetch($filter_id);
return new WP_REST_Response($filter->__toArray(), 200);
return new WP_REST_Response($this->prepare_item_for_response($filter, $request), 200);
}
/**
@ -284,7 +323,12 @@ class TAINACAN_REST_Filters_Controller extends WP_REST_Controller {
*/
public function get_item_permissions_check( $request ) {
$filter = $this->filter_repository->fetch($request['filter_id']);
return $this->filter_repository->can_read($filter);
if ($filter instanceof Entities\Filter) {
return $filter->can_read();
}
return false;
}
}
?>

View File

@ -0,0 +1,236 @@
<?php
use Tainacan\Entities;
use Tainacan\Repositories;
class TAINACAN_REST_Item_Metadata_Controller extends TAINACAN_REST_Controller {
private $field;
private $item_metadata_repository;
private $item_repository;
private $collection_repository;
private $field_repository;
public function __construct() {
$this->namespace = 'tainacan/v2';
$this->rest_base = 'metadata';
add_action('rest_api_init', array($this, 'register_routes'));
add_action('init', array(&$this, 'init_objects'), 11);
}
/**
* Initialize objects after post_type register
*/
public function init_objects() {
$this->field = new Entities\Field();
$this->field_repository = new Repositories\Fields();
$this->item_metadata_repository = new Repositories\Item_Metadata();
$this->item_repository = new Repositories\Items();
$this->collection_repository = new Repositories\Collections();
}
/**
* If POST on field/collection/<collection_id>, then
* a field will be created in matched collection and all your item will receive this field
*
* If POST on field/item/<item_id>, then a value will be added in a field and field passed
* id body of requisition
*
* Both of GETs return the field of matched objects
*/
public function register_routes() {
register_rest_route($this->namespace, '/item/(?P<item_id>[\d]+)/' . $this->rest_base . '/(?P<metadata_id>[\d]+)',
array(
array(
'methods' => WP_REST_Server::EDITABLE,
'callback' => array($this, 'update_item'),
'permission_callback' => array($this, 'update_item_permissions_check')
)
)
);
register_rest_route($this->namespace, '/item/(?P<item_id>[\d]+)/'. $this->rest_base,
array(
array(
'methods' => WP_REST_Server::READABLE,
'callback' => array($this, 'get_items'),
'permission_callback' => array($this, 'get_items_permissions_check'),
//'args' => $this->get_collection_params(),
)
)
);
}
/**
* @param WP_REST_Request $request
*
* @return object|void|WP_Error
*/
public function prepare_item_for_database( $request ) {
$meta = json_decode($request[0]->get_body(), true);
foreach ($meta as $key => $value){
$set_ = 'set_' . $key;
$this->field->$set_($value);
}
$collection = new Entities\Collection($request[1]);
$this->field->set_collection($collection);
}
/**
* @param mixed $item
* @param WP_REST_Request $request
*
* @return array|WP_Error|WP_REST_Response
*/
public function prepare_item_for_response( $item, $request ) {
return $item->__toArray();
}
/**
* @param WP_REST_Request $request
*
* @return WP_Error|WP_REST_Response
*/
public function get_items( $request ) {
$item_id = $request['item_id'];
$item = $this->item_repository->fetch($item_id);
$items_metadata = $item->get_fields();
$prepared_item = [];
foreach ($items_metadata as $item_metadata){
$index = array_push($prepared_item, $this->prepare_item_for_response($item_metadata, $request));
$prepared_item[$index-1]['field']['field_type_object'] = $this->prepare_item_for_response( $item_metadata->get_field()->get_field_type_object(), $request);
}
return new WP_REST_Response($prepared_item, 200);
}
/**
* @param WP_REST_Request $request
*
* @return bool|WP_Error
* @throws Exception
*/
public function get_items_permissions_check( $request ) {
if(isset($request['item_id'])){
$item = $this->item_repository->fetch($request['item_id']);
if($item instanceof Entities\Item) {
return $item->can_read();
}
}
return false;
}
/**
* @return array
*/
public function get_collection_params() {
return parent::get_collection_params(); // TODO: Change the autogenerated stub
}
/**
* @param WP_REST_Request $request
*
* @return WP_Error|WP_REST_Response
*/
public function delete_item( $request ) {
if(!empty($request->get_body())){
$body = json_decode($request->get_body());
$collection_id = $request['collection_id'];
$field_id = $body['metadata_id'];
return new WP_REST_Response(['error' => 'Not Implemented.'], 400);
}
}
/**
* @param WP_REST_Request $request
*
* @return bool|WP_Error
* @throws Exception
*/
public function delete_item_permissions_check( $request ) {
if(isset($request['item_id'])){
$item = $this->item_repository->fetch($request['item_id']);
if($item instanceof Entities\Item) {
return $item->can_delete();
}
}
return false;
}
/**
* @param WP_REST_Request $request
*
* @return WP_Error|WP_REST_Response
*/
public function update_item( $request ) {
$body = json_decode( $request->get_body(), true );
if($body) {
$item_id = $request['item_id'];
$field_id = $request['metadata_id'];
$value = $body['values'];
$item = $this->item_repository->fetch( $item_id );
$field = $this->field_repository->fetch( $field_id );
$item_metadata = new Entities\Item_Metadata_Entity( $item, $field );
$item_metadata->set_value( $value );
if ( $item_metadata->validate() ) {
$field_updated = $this->item_metadata_repository->update( $item_metadata );
$prepared_item = $this->prepare_item_for_response($field_updated, $request);
$prepared_item['field']['field_type_object'] = $this->prepare_item_for_response($field_updated->get_field()->get_field_type_object(), $request);
return new WP_REST_Response( $prepared_item, 200 );
} else {
return new WP_REST_Response( [
'error_message' => __( 'One or more values are invalid.', 'tainacan' ),
'errors' => $item_metadata->get_errors(),
'item_metadata' => $this->prepare_item_for_response($item_metadata, $request),
], 400 );
}
}
return new WP_REST_Response([
'error_message' => 'The body could not be empty',
'body' => $body
], 400);
}
/**
* @param WP_REST_Request $request
*
* @return bool|WP_Error
* @throws Exception
*/
public function update_item_permissions_check( $request ) {
if (isset($request['item_id'])) {
$item = $this->item_repository->fetch($request['item_id']);
if ($item instanceof Entities\Item) {
return $item->can_edit();
}
}
return false;
}
}
?>

View File

@ -8,7 +8,7 @@ use Tainacan\Entities;
* @uses Tainacan\Repositories\
* @uses Tainacan\Entities\
*/
class TAINACAN_REST_Items_Controller extends WP_REST_Controller {
class TAINACAN_REST_Items_Controller extends TAINACAN_REST_Controller {
private $items_repository;
private $item;
private $item_metadata;
@ -41,21 +41,21 @@ class TAINACAN_REST_Items_Controller extends WP_REST_Controller {
*/
public function register_routes() {
register_rest_route(
$this->namespace, '/' . $this->rest_base . '/collection/(?P<collection_id>[\d]+)',
$this->namespace, '/collection/(?P<collection_id>[\d]+)/' . $this->rest_base,
array(
array(
'methods' => WP_REST_Server::READABLE,
'callback' => array($this, 'get_items'),
'permission_callback' => array($this, 'get_items_permissions_check'),
'args' => $this->get_collection_params(),
//'args' => $this->get_collection_params(),
),
array(
'methods' => WP_REST_Server::CREATABLE,
'callback' => array($this, 'create_item'),
'permission_callback' => array($this, 'create_item_permissions_check'),
'args' => $this->get_endpoint_args_for_item_schema(WP_REST_Server::CREATABLE),
//'args' => $this->get_endpoint_args_for_item_schema(WP_REST_Server::CREATABLE),
),
'schema' => array($this, 'get_public_item_schema'),
//'schema' => array($this, 'get_public_item_schema'),
));
register_rest_route(
$this->namespace, '/' . $this->rest_base . '/(?P<item_id>[\d]+)',
@ -78,6 +78,21 @@ class TAINACAN_REST_Items_Controller extends WP_REST_Controller {
));
}
private function add_metadata_to_item($item_object, $item_array){
$item_metadata = $item_object->get_fields();
foreach($item_metadata as $index => $me){
$field = $me->get_field();
$slug = $field->get_slug();
$item_array['metadata'][$slug]['name'] = $field->get_name();
$item_array['metadata'][$slug]['value'] = $me->get_value();
$item_array['metadata'][$slug]['multiple'] = $field->get_multiple();
}
return $item_array;
}
/**
* @param mixed $item
* @param WP_REST_Request $request
@ -85,22 +100,10 @@ class TAINACAN_REST_Items_Controller extends WP_REST_Controller {
* @return mixed|string|void|WP_Error|WP_REST_Response
*/
public function prepare_item_for_response( $item, $request ) {
if (!empty($item) && $item instanceof WP_Query){
$items_as_array = [];
if(!empty($item)){
$item_array = $item->__toArray();
if ($item->have_posts()) {
while ( $item->have_posts() ) {
$item->the_post();
$ite = new Entities\Item($item->post);
array_push($items_as_array, $ite->__toArray());
}
wp_reset_postdata();
}
return $items_as_array;
} elseif(!empty($item)){
return $item->__toArray();
return $this->add_metadata_to_item($item, $item_array);
}
return $item;
@ -127,12 +130,38 @@ class TAINACAN_REST_Items_Controller extends WP_REST_Controller {
* @return WP_Error|WP_REST_Response
*/
public function get_items( $request ) {
$args = $this->prepare_filters($request);
$collection_id = $request['collection_id'];
$items = $this->items_repository->fetch([], $collection_id, 'WP_Query');
$items = $this->items_repository->fetch($args, $collection_id, 'WP_Query');
$response = $this->prepare_item_for_response($items, $request);
$map = $this->items_repository->get_map();
return new WP_REST_Response($response, 200);
$response = [];
if ($items->have_posts()) {
while ( $items->have_posts() ) {
$items->the_post();
$item = new Entities\Item($items->post);
$limited_item = $this->get_only_needed_attributes($item, $map);
$limited_item = $this->add_metadata_to_item($item, $limited_item);
array_push($response, $limited_item);
}
wp_reset_postdata();
}
$total_items = $items->found_posts;
$max_pages = ceil($total_items / (int) $items->query_vars['posts_per_page']);
$rest_response = new WP_REST_Response($response, 200);
$rest_response->header('X-WP-Total', (int) $total_items);
$rest_response->header('X-WP-TotalPages', (int) $max_pages);
return $rest_response;
}
/**
@ -142,18 +171,29 @@ class TAINACAN_REST_Items_Controller extends WP_REST_Controller {
*/
public function get_item_permissions_check( $request ) {
$item = $this->items_repository->fetch($request['item_id']);
return $this->items_repository->can_read($item);
if ($item instanceof Entities\Item) {
return $item->can_read();
}
return false;
}
public function get_items_permissions_check( $request ) {
$collection = $this->collections_repository->fetch($request['collection_id']);
return $this->collections_repository->can_read($collection);
if ($collection instanceof Entities\Collection) {
return $collection->can_read();
}
return false;
}
/**
* @param WP_REST_Request $request
*
* @return object|Entities\Item|WP_Error
* @throws Exception
*/
public function prepare_item_for_database( $request ) {
@ -168,29 +208,14 @@ class TAINACAN_REST_Items_Controller extends WP_REST_Controller {
$this->item->set_collection($collection);
$metadata = get_post_meta($collection->get_id());
if(!empty($metadata)) {
foreach ($metadata as $key => $value){
$new_metadata = new Entities\Metadata();
try {
$set_ = 'set_' . $key;
$new_metadata->$set_( $value );
} catch (\Error $exception){
// Do nothing
}
}
}
return $new_metadata;
return $this->item;
}
/**
* @param WP_REST_Request $request
*
* @return WP_Error|WP_REST_Response
* @throws Exception
*/
public function create_item( $request ) {
$collection_id = $request['collection_id'];
@ -204,7 +229,7 @@ class TAINACAN_REST_Items_Controller extends WP_REST_Controller {
}
try {
$metadata = $this->prepare_item_for_database( [ $item, $collection_id ] );
$this->prepare_item_for_database( [ $item, $collection_id ] );
} catch (\Error $exception){
return new WP_REST_Response($exception->getMessage(), 400);
}
@ -212,17 +237,14 @@ class TAINACAN_REST_Items_Controller extends WP_REST_Controller {
if($this->item->validate()) {
$item = $this->items_repository->insert($this->item );
$item_metadata = new Entities\Item_Metadata_Entity($item, $metadata );
$metadata_added = $this->item_metadata->insert( $item_metadata );
return new WP_REST_Response($metadata_added->get_item()->__toArray(), 201 );
return new WP_REST_Response($this->prepare_item_for_response($item, $request), 201 );
}
return new WP_REST_Response([
'error_message' => __('One or more values are invalid.', 'tainacan'),
'errors' => $this->item->get_errors(),
'item' => $this->item->__toArray()
'item' => $this->prepare_item_for_response($this->item, $request)
], 400);
}
@ -234,11 +256,12 @@ class TAINACAN_REST_Items_Controller extends WP_REST_Controller {
*/
public function create_item_permissions_check( $request ) {
$collection = $this->collections_repository->fetch($request['collection_id']);
if ($collection instanceof Entities\Collection) {
return $collection->get_items_capabilities()->edit_posts;
if ($collection instanceof Entities\Collection) {
return current_user_can($collection->get_items_capabilities()->edit_posts);
}
return false;
}
/**
@ -267,7 +290,12 @@ class TAINACAN_REST_Items_Controller extends WP_REST_Controller {
*/
public function delete_item_permissions_check( $request ) {
$item = $this->items_repository->fetch($request['item_id']);
return $this->items_repository->can_delete($item);
if ($item instanceof Entities\Item) {
return $item->can_delete();
}
return false;
}
/**
@ -281,19 +309,38 @@ class TAINACAN_REST_Items_Controller extends WP_REST_Controller {
$body = json_decode($request->get_body(), true);
if(!empty($body)){
$attributes = ['ID' => $item_id];
$attributes = [];
foreach ($body as $att => $value){
$attributes[$att] = $value;
}
$updated_item = $this->items_repository->update($attributes);
$item = $this->items_repository->fetch($item_id);
return new WP_REST_Response($updated_item->__toArray(), 200);
if($item){
$prepared_item = $this->prepare_item_for_updating($item, $attributes);
if($prepared_item->validate()){
$updated_item = $this->items_repository->update($prepared_item);
return new WP_REST_Response($this->prepare_item_for_response($updated_item, $request), 200);
}
return new WP_REST_Response([
'error_message' => __('One or more values are invalid.', 'tainacan'),
'errors' => $prepared_item->get_errors(),
'item' => $this->prepare_item_for_response($prepared_item, $request)
], 400);
}
return new WP_REST_Response([
'error_message' => __('Item with that ID not found', 'tainacan' ),
'item_id' => $item_id
], 400);
}
return new WP_REST_Response([
'error_message' => 'The body could not be empty',
'error_message' => __('The body could not be empty', 'tainacan'),
'body' => $body
], 400);
}
@ -306,7 +353,12 @@ class TAINACAN_REST_Items_Controller extends WP_REST_Controller {
*/
public function update_item_permissions_check( $request ) {
$item = $this->items_repository->fetch($request['item_id']);
return $this->items_repository->can_edit($item);
if ($item instanceof Entities\Item) {
return $item->can_edit();
}
return false;
}
}

View File

@ -0,0 +1,137 @@
<?php
use Tainacan\Entities;
use Tainacan\Repositories;
class TAINACAN_REST_Logs_Controller extends TAINACAN_REST_Controller {
private $logs_repository;
private $log;
/**
* TAINACAN_REST_Logs_Controller constructor.
*/
public function __construct() {
$this->namespace = 'tainacan/v2';
$this->rest_base = 'logs';
add_action('rest_api_init', array($this, 'register_routes'));
add_action('init', array($this, 'init_objects'));
}
public function init_objects(){
$this->logs_repository = new Repositories\Logs();
$this->log = new Entities\Log();
}
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'),
)
)
);
register_rest_route($this->namespace, '/' . $this->rest_base . '/(?P<log_id>[\d]+)',
array(
array(
'methods' => WP_REST_Server::READABLE,
'callback' => array($this, 'get_item'),
'permission_callback' => array($this, 'get_item_permissions_check'),
)
)
);
}
/**
* @param mixed $item
* @param WP_REST_Request $request
*
* @return array|WP_Error|WP_REST_Response
*/
public function prepare_item_for_response( $item, $request ) {
if(!empty($item)){
return $item->__toArray();
}
return $item;
}
/**
* @param WP_REST_Request $request
*
* @return WP_Error|WP_REST_Response
*/
public function get_items( $request ) {
$args = $this->prepare_filters($request);
$logs = $this->logs_repository->fetch($args);
$map = $this->logs_repository->get_map();
$response = [];
if($logs->have_posts()){
while ($logs->have_posts()){
$logs->the_post();
$collection = new Entities\Log($logs->post);
array_push($response, $this->get_only_needed_attributes($collection, $map));
}
wp_reset_postdata();
}
$total_logs = $logs->found_posts;
$max_pages = ceil($total_logs / (int) $logs->query_vars['posts_per_page']);
$rest_response = new WP_REST_Response($response, 200);
$rest_response->header('X-WP-Total', (int) $total_logs);
$rest_response->header('X-WP-TotalPages', (int) $max_pages);
return $rest_response;
}
/**
* @param WP_REST_Request $request
*
* @return bool|WP_Error
*/
public function get_items_permissions_check( $request ) {
return $this->logs_repository->can_read($this->log);
}
/**
* @param WP_REST_Request $request
*
* @return WP_Error|WP_REST_Response
*/
public function get_item( $request ) {
$log_id = $request['log_id'];
$log = $this->logs_repository->fetch($log_id);
$prepared_log = $this->prepare_item_for_response( $log, $request );
return new WP_REST_Response($prepared_log, 200);
}
/**
* @param WP_REST_Request $request
*
* @return bool|WP_Error
*/
public function get_item_permissions_check( $request ) {
$log = $this->logs_repository->fetch($request['log_id']);
if($log instanceof Entities\Log){
return $log->can_read();
}
return false;
}
}
?>

View File

@ -1,387 +0,0 @@
<?php
use Tainacan\Entities;
use Tainacan\Repositories;
class TAINACAN_REST_Metadata_Controller extends WP_REST_Controller {
private $metadata;
private $metadata_repository;
private $item_metadata_repository;
private $item_repository;
private $collection_repository;
public function __construct() {
$this->namespace = 'tainacan/v2';
$this->rest_base = 'metadata';
add_action('rest_api_init', array($this, 'register_routes'));
add_action('init', array(&$this, 'init_objects'), 11);
}
/**
* Initialize objects after post_type register
*/
public function init_objects() {
$this->metadata = new Entities\Metadata();
$this->metadata_repository = new Repositories\Metadatas();
$this->item_metadata_repository = new Repositories\Item_Metadata();
$this->item_repository = new Repositories\Items();
$this->collection_repository = new Repositories\Collections();
}
/**
* If POST on metadata/collection/<collection_id>, then
* a metadata will be created in matched collection and all your item will receive this metadata
*
* If POST on metadata/item/<item_id>, then a value will be added in a field and metadata passed
* id body of requisition
*
* Both of GETs return the metadata of matched objects
*/
public function register_routes() {
/*
Removing this undocumented endpoint.. it seems wrong.
register_rest_route($this->namespace, '/' . $this->rest_base . '/collection/(?P<collection_id>[\d]+)',
array(
array(
'methods' => WP_REST_Server::READABLE,
'callback' => array($this, 'get_items'),
'permission_callback' => array($this, 'get_items_permissions_check'),
'args' => $this->get_collection_params(),
),
array(
'methods' => WP_REST_Server::CREATABLE,
'callback' => array($this, 'create_item'),
'permission_callback' => array($this, 'create_item_permissions_check')
),
array(
'methods' => WP_REST_Server::DELETABLE,
'callback' => array($this, 'delete_item'),
'permission_callback' => array($this, 'delete_item_permissions_check')
),
array(
'methods' => WP_REST_Server::EDITABLE,
'callback' => array($this, 'update_item'),
'permission_callback' => array($this, 'update_item_permissions_check')
)
)
);
*/
register_rest_route($this->namespace, '/' . $this->rest_base . '/item/(?P<item_id>[\d]+)',
array(
array(
'methods' => WP_REST_Server::READABLE,
'callback' => array($this, 'get_items'),
'permission_callback' => array($this, 'get_items_permissions_check'),
'args' => $this->get_collection_params(),
),
array(
'methods' => WP_REST_Server::CREATABLE,
'callback' => array($this, 'create_item'),
'permission_callback' => array($this, 'create_item_permissions_check')
),
array(
'methods' => WP_REST_Server::EDITABLE,
'callback' => array($this, 'update_item'),
'permission_callback' => array($this, 'update_item_permissions_check')
)
)
);
}
/**
* @param WP_REST_Request $request
*
* @return object|void|WP_Error
*/
public function prepare_item_for_database( $request ) {
$meta = json_decode($request[0]->get_body(), true);
foreach ($meta as $key => $value){
$set_ = 'set_' . $key;
$this->metadata->$set_($value);
}
$collection = new Entities\Collection($request[1]);
$this->metadata->set_collection($collection);
}
/**
* @param WP_REST_Request $request
*
* @return WP_Error|WP_REST_Response
*/
public function create_item( $request ) {
if(!empty($request['collection_id'])){
$collection_id = $request['collection_id'];
try {
$this->prepare_item_for_database( [ $request, $collection_id ] );
} catch (\Error $exception){
return new WP_REST_Response($exception->getMessage(), 400);
}
if($this->metadata->validate()) {
$this->metadata_repository->insert( $this->metadata );
$items = $this->item_repository->fetch([], $collection_id, 'WP_Query');
$metadata_added = '';
if($items->have_posts()){
while ($items->have_posts()){
$items->the_post();
$item = new Entities\Item($items->post);
$item_meta = new Entities\Item_Metadata_Entity($item, $this->metadata);
$metadata_added = $this->item_metadata_repository->insert($item_meta);
}
return new WP_REST_Response($metadata_added->get_metadata()->__toArray(), 201);
}
else {
return new WP_REST_Response($this->metadata->__toArray(), 201);
}
} else {
return new WP_REST_Response([
'error_message' => __('One or more values are invalid.', 'tainacan'),
'errors' => $this->metadata->get_errors(),
'metadata' => $this->metadata->__toArray(),
], 400);
}
} elseif (!empty($request['item_id']) && !empty($request->get_body())){
$body = json_decode($request->get_body(), true);
$item_id = $request['item_id'];
$metadata_id = $body['metadata_id'];
$value = $body['values'];
$item = $this->item_repository->fetch($item_id);
$metadata = $this->metadata_repository->fetch($metadata_id);
$item_metadata = new Entities\Item_Metadata_Entity($item, $metadata);
$item_metadata->set_value($value);
if($item_metadata->validate()) {
$metadata_updated = $this->item_metadata_repository->insert( $item_metadata );
return new WP_REST_Response( $metadata_updated->__toArray(), 201 );
} else {
return new WP_REST_Response([
'error_message' => __('One or more values are invalid.', 'tainacan'),
'errors' => $item_metadata->get_errors(),
'item_metadata' => $item_metadata->__toArray(),
], 400);
}
} else {
return new WP_REST_Response([
'error_message' => __('Body can not be empty.', 'tainacan'),
'item' => $request->get_body()
], 400);
}
}
/**
* @param $request
*
* @return bool|WP_Error
* @throws Exception
*/
public function create_item_permissions_check( $request ) {
if(!empty($request['item_id'])){
return $this->item_repository->can_edit(new Entities\Item());
}
return $this->collection_repository->can_edit(new Entities\Collection());
}
/**
* @param mixed $item
* @param WP_REST_Request $request
*
* @return array|WP_Error|WP_REST_Response
*/
public function prepare_item_for_response( $item, $request ) {
$metadata_as = [];
if($request['item_id']) {
foreach ( $item as $metadata ) {
$metadata_as[] = $metadata->__toArray();
}
} else if($request['collection_id']){
foreach ( $item as $metadata ) {
$metadata_as[] = $metadata->__toArray();
}
}
return $metadata_as;
}
/**
* @param WP_REST_Request $request
*
* @return WP_Error|WP_REST_Response
*/
public function get_items( $request ) {
if(!empty($request['collection_id'])){
$collection_id = $request['collection_id'];
$collection = new Entities\Collection($collection_id);
$collection_metadata = $this->metadata_repository->fetch_by_collection($collection, [], 'OBJECT');
$prepared_item = $this->prepare_item_for_response($collection_metadata, $request);
return new WP_REST_Response($prepared_item, 200);
}
$item_id = $request['item_id'];
$item = new Entities\Item($item_id);
$item_metadata = $this->item_metadata_repository->fetch($item, 'OBJECT');
$prepared_item = $this->prepare_item_for_response($item_metadata, $request);
return new WP_REST_Response($prepared_item, 200);
}
/**
* @param WP_REST_Request $request
*
* @return bool|WP_Error
* @throws Exception
*/
public function get_items_permissions_check( $request ) {
if(!empty($request['item_id'])){
return $this->item_repository->can_read(new Entities\Item());
}
return $this->collection_repository->can_read(new Entities\Collection());
}
/**
* @return array
*/
public function get_collection_params() {
return parent::get_collection_params(); // TODO: Change the autogenerated stub
}
/**
* @param WP_REST_Request $request
*
* @return WP_Error|WP_REST_Response
*/
public function delete_item( $request ) {
if(!empty($request->get_body())){
$body = json_decode($request->get_body());
$collection_id = $request['collection_id'];
$metadata_id = $body['metadata_id'];
return new WP_REST_Response(['error' => 'Not Implemented.'], 400);
}
}
/**
* @param WP_REST_Request $request
*
* @return bool|WP_Error
* @throws Exception
*/
public function delete_item_permissions_check( $request ) {
if(!empty($request['item_id'])){
return $this->item_repository->can_delete(new Entities\Item());
}
return $this->collection_repository->can_delete(new Entities\Collection());
}
/**
* @param WP_REST_Request $request
*
* @return WP_Error|WP_REST_Response
*/
public function update_item( $request ) {
if($request['item_id']) {
$body = json_decode( $request->get_body(), true );
$item_id = $request['item_id'];
$metadata_id = $body['metadata_id'];
$value = $body['values'];
$item = $this->item_repository->fetch( $item_id );
$metadata = $this->metadata_repository->fetch( $metadata_id );
$item_metadata = new Entities\Item_Metadata_Entity( $item, $metadata );
$item_metadata->set_value( $value );
if ( $item_metadata->validate() ) {
$metadata_updated = $this->item_metadata_repository->update( $item_metadata );
return new WP_REST_Response( $metadata_updated->__toArray(), 200 );
} else {
return new WP_REST_Response( [
'error_message' => __( 'One or more values are invalid.', 'tainacan' ),
'errors' => $item_metadata->get_errors(),
'item_metadata' => $item_metadata->__toArray(),
], 400 );
}
}
// We need to rethink this endpoint. Its confusing...
// and there is no need to iterato through all items...
$collection_id = $request['collection_id'];
$body = json_decode($request->get_body(), true);
if(!empty($body)){
$attributes = ['ID' => $body['metadata_id']];
foreach ($body['values'] as $att => $value){
$attributes[$att] = $value;
}
$updated_metadata = $this->metadata_repository->update($attributes);
$items = $this->item_repository->fetch([], $collection_id, 'WP_Query');
$up_metadata = '';
if($items->have_posts()){
while ($items->have_posts()){
$items->the_post();
$item = new Entities\Item($items->post);
$item_meta = new Entities\Item_Metadata_Entity($item, $updated_metadata);
$up_metadata = $this->item_metadata_repository->update($item_meta);
}
return new WP_REST_Response($up_metadata->get_metadata()->__toArray(), 201);
}
return new WP_REST_Response($updated_metadata->__toArray(), 201);
}
return new WP_REST_Response([
'error_message' => 'The body could not be empty',
'body' => $body
], 400);
}
/**
* @param WP_REST_Request $request
*
* @return bool|WP_Error
* @throws Exception
*/
public function update_item_permissions_check( $request ) {
$item = $this->item_repository->fetch($request['item_id'] ? $request['item_id'] : $request['collection_id']);
return $this->item_repository->can_edit($item);
}
}
?>

View File

@ -3,7 +3,7 @@
use Tainacan\Entities;
use Tainacan\Repositories;
class TAINACAN_REST_Taxonomies_Controller extends \WP_REST_Controller {
class TAINACAN_REST_Taxonomies_Controller extends TAINACAN_REST_Controller {
private $taxonomy;
private $taxonomy_repository;
@ -62,6 +62,16 @@ class TAINACAN_REST_Taxonomies_Controller extends \WP_REST_Controller {
)
)
);
register_rest_route(
$this->namespace, '/' . $this->rest_base . '/(?P<taxonomy_id>[\d]+)/collection/(?P<collection_id>[\d]+)',
array(
array(
'methods' => WP_REST_Server::EDITABLE,
'callback' => array($this, 'update_item'),
'permission_callback' => array($this, 'update_item_permissions_check')
)
)
);
}
/**
@ -71,17 +81,11 @@ class TAINACAN_REST_Taxonomies_Controller extends \WP_REST_Controller {
* @return array|WP_Error|WP_REST_Response
*/
public function prepare_item_for_response( $item, $request ) {
$taxonomies = [];
if($request['taxonomy_id']){
if(!empty($item)) {
return $item->__toArray();
}
foreach ( $item as $it ) {
$taxonomies[] = $it->__toArray();
}
return $taxonomies;
return $item;
}
/**
@ -118,7 +122,12 @@ class TAINACAN_REST_Taxonomies_Controller extends \WP_REST_Controller {
*/
public function get_item_permissions_check( $request ) {
$taxonomy = $this->taxonomy_repository->fetch($request['taxonomy_id']);
return $this->taxonomy_repository->can_read($taxonomy);
if ($taxonomy instanceof Entities\Taxonomy) {
return $taxonomy->can_read();
}
return false;
}
/**
@ -158,12 +167,12 @@ class TAINACAN_REST_Taxonomies_Controller extends \WP_REST_Controller {
return new WP_REST_Response($deleted, 400);
}
return new WP_REST_Response($deleted->__toArray(), 200);
return new WP_REST_Response($this->prepare_item_for_response($deleted, $request), 200);
}
return new WP_REST_Response([
'error_message' => __('Taxonomy with this id ('. $taxonomy_id .') not found.', 'tainacan'),
'taxonomy' => $taxonomy->__toArray()
'taxonomy' => $this->prepare_item_for_response($taxonomy, $request)
], 400);
}
@ -174,7 +183,13 @@ class TAINACAN_REST_Taxonomies_Controller extends \WP_REST_Controller {
*/
public function delete_item_permissions_check( $request ) {
$taxonomy = $this->taxonomy_repository->fetch($request['taxonomy_id']);
return $this->taxonomy_repository->can_delete($taxonomy);
if ($taxonomy instanceof Entities\Taxonomy) {
return $taxonomy->can_delete();
}
return false;
}
/**
@ -183,11 +198,16 @@ class TAINACAN_REST_Taxonomies_Controller extends \WP_REST_Controller {
* @return WP_Error|WP_REST_Response
*/
public function get_items( $request ) {
$taxonomies = $this->taxonomy_repository->fetch([], 'OBJECT');
$args = $this->prepare_filters($request);
$taxonomies_prepared = $this->prepare_item_for_response($taxonomies, $request);
$taxonomies = $this->taxonomy_repository->fetch($args, 'OBJECT');
return new WP_REST_Response($taxonomies_prepared, 200);
$response = [];
foreach ($taxonomies as $taxonomy) {
array_push($response, $this->prepare_item_for_response( $taxonomy, $request ));
}
return new WP_REST_Response($response, 200);
}
/**
@ -213,12 +233,12 @@ class TAINACAN_REST_Taxonomies_Controller extends \WP_REST_Controller {
if($this->taxonomy->validate()){
$taxonomy = $this->taxonomy_repository->insert($this->taxonomy);
return new WP_REST_Response($taxonomy->__toArray(), 201);
return new WP_REST_Response($this->prepare_item_for_response($taxonomy, $request), 201);
} else {
return new WP_REST_Response([
'error_message' => __('One or more values are invalid.', 'tainacan'),
'errors' => $this->taxonomy->get_errors(),
'item_metadata' => $this->taxonomy->__toArray(),
'item_metadata' => $this->prepare_item_for_response($this->taxonomy, $request),
], 400);
}
} else {
@ -248,20 +268,45 @@ class TAINACAN_REST_Taxonomies_Controller extends \WP_REST_Controller {
$body = json_decode($request->get_body(), true);
if(!empty($body)){
$attributes = ['ID' => $taxonomy_id];
if(!empty($body) || isset($request['collection_id'])){
$attributes = [];
foreach ($body as $att => $value){
$attributes[$att] = $value;
if(isset($request['collection_id'])) {
$collection_id = $request['collection_id'];
$attributes = [ 'collection' => $collection_id ];
} else {
foreach ( $body as $att => $value ) {
$attributes[ $att ] = $value;
}
}
$updated_taxonomy = $this->taxonomy_repository->update($attributes);
$taxonomy = $this->taxonomy_repository->fetch($taxonomy_id);
return new WP_REST_Response($updated_taxonomy->__toArray(), 200);
if($taxonomy){
$prepared_taxonomy = $this->prepare_item_for_updating($taxonomy, $attributes);
if($prepared_taxonomy->validate()){
$updated_taxonomy = $this->taxonomy_repository->update($prepared_taxonomy);
return new WP_REST_Response($this->prepare_item_for_response($updated_taxonomy, $request), 200);
}
return new WP_REST_Response([
'error_message' => __('One or more values are invalid.', 'tainacan'),
'errors' => $prepared_taxonomy->get_errors(),
'taxonomy' => $this->prepare_item_for_response($prepared_taxonomy, $request)
], 400);
}
return new WP_REST_Response([
'error_message' => __('Taxonomy with that ID not found', 'tainacan' ),
'taxonomy_id' => $taxonomy_id
], 400);
}
return new WP_REST_Response([
'error_message' => 'The body could not be empty',
'error_message' => __('The body could not be empty', 'tainacan'),
'body' => $body
], 400);
}
@ -273,7 +318,12 @@ class TAINACAN_REST_Taxonomies_Controller extends \WP_REST_Controller {
*/
public function update_item_permissions_check( $request ) {
$taxonomy = $this->taxonomy_repository->fetch($request['taxonomy_id']);
return $this->taxonomy_repository->can_edit($taxonomy);
if ($taxonomy instanceof Entities\Taxonomy) {
return $taxonomy->can_edit();
}
return false;
}
}

View File

@ -3,7 +3,7 @@
use Tainacan\Entities;
use Tainacan\Repositories;
class TAINACAN_REST_Terms_Controller extends WP_REST_Controller {
class TAINACAN_REST_Terms_Controller extends TAINACAN_REST_Controller {
private $term;
private $terms_repository;
private $taxonomy;
@ -31,7 +31,7 @@ class TAINACAN_REST_Terms_Controller extends WP_REST_Controller {
}
public function register_routes() {
register_rest_route($this->namespace, '/' . $this->rest_base . '/taxonomy/(?P<taxonomy_id>[\d]+)',
register_rest_route($this->namespace, '/taxonomy/(?P<taxonomy_id>[\d]+)/' . $this->rest_base,
array(
array(
'methods' => WP_REST_Server::CREATABLE,
@ -45,7 +45,7 @@ class TAINACAN_REST_Terms_Controller extends WP_REST_Controller {
)
)
);
register_rest_route($this->namespace, '/' . $this->rest_base . '/(?P<term_id>[\d]+)/taxonomy/(?P<taxonomy_id>[\d]+)',
register_rest_route($this->namespace,'/taxonomy/(?P<taxonomy_id>[\d]+)/'. $this->rest_base . '/(?P<term_id>[\d]+)' ,
array(
array(
'methods' => WP_REST_Server::DELETABLE,
@ -109,7 +109,7 @@ class TAINACAN_REST_Terms_Controller extends WP_REST_Controller {
$term_inserted = $this->terms_repository->fetch($term_id, $taxonomy);
return new WP_REST_Response($term_inserted->__toArray(), 200);
return new WP_REST_Response($this->prepare_item_for_response($term_inserted, $request), 200);
} else {
return new WP_REST_Response([
'error_message' => 'One or more attributes are invalid.',
@ -131,9 +131,11 @@ class TAINACAN_REST_Terms_Controller extends WP_REST_Controller {
*/
public function create_item_permissions_check( $request ) {
$taxonomy = $this->taxonomy_repository->fetch($request['taxonomy_id']);
if ($taxonomy instanceof Entities\Taxonomy) {
return $taxonomy->can_edit();
}
return false;
}
@ -168,9 +170,11 @@ class TAINACAN_REST_Terms_Controller extends WP_REST_Controller {
*/
public function delete_item_permissions_check( $request ) {
$taxonomy = $this->taxonomy_repository->fetch($request['taxonomy_id']);
if ($taxonomy instanceof Entities\Taxonomy) {
return $taxonomy->can_edit();
}
return false;
}
@ -186,12 +190,8 @@ class TAINACAN_REST_Terms_Controller extends WP_REST_Controller {
$body = json_decode($request->get_body(), true);
if(!empty($body)){
$taxonomy_name = $this->taxonomy_repository->fetch($taxonomy_id)->get_db_identifier();
$identifiers = [
'term_id' => $term_id,
'tax_name' => $taxonomy_name
];
$taxonomy = $this->taxonomy_repository->fetch($taxonomy_id);
$tax_name = $taxonomy->get_db_identifier();
$attributes = [];
@ -199,13 +199,33 @@ class TAINACAN_REST_Terms_Controller extends WP_REST_Controller {
$attributes[$att] = $value;
}
$updated_term = $this->terms_repository->update([$attributes, $identifiers]);
$term = $this->terms_repository->fetch($term_id, $taxonomy);
return new WP_REST_Response($updated_term->__toArray(), 200);
if($term){
$prepared_term = $this->prepare_item_for_updating($term, $attributes);
if($prepared_term->validate()){
$updated_term = $this->terms_repository->update($prepared_term, $tax_name);
return new WP_REST_Response($this->prepare_item_for_response($updated_term, $request), 200);
}
return new WP_REST_Response([
'error_message' => __('One or more values are invalid.', 'tainacan'),
'errors' => $prepared_term->get_errors(),
'term' => $this->prepare_item_for_response($prepared_term, $request)
], 400);
}
return new WP_REST_Response([
'error_message' => __('Term or Taxonomy with that IDs not found', 'tainacan' ),
'term_id' => $term_id,
'taxonomy_id' => $taxonomy_id
], 400);
}
return new WP_REST_Response([
'error_message' => 'The body could not be empty',
'error_message' => __('The body could not be empty', 'tainacan'),
'body' => $body
], 400);
}
@ -217,9 +237,11 @@ class TAINACAN_REST_Terms_Controller extends WP_REST_Controller {
*/
public function update_item_permissions_check( $request ) {
$taxonomy = $this->taxonomy_repository->fetch($request['taxonomy_id']);
if ($taxonomy instanceof Entities\Taxonomy) {
return $taxonomy->can_edit();
}
return false;
}
@ -230,15 +252,8 @@ class TAINACAN_REST_Terms_Controller extends WP_REST_Controller {
* @return array|mixed|WP_Error|WP_REST_Response
*/
public function prepare_item_for_response( $item, $request ) {
if(is_array($item)){
$prepared = [];
foreach ($item as $term){
$prepared[] = $term->__toArray();
}
return $prepared;
if(!empty($item)){
return $item->__toArray();
}
return $item;
@ -254,13 +269,16 @@ class TAINACAN_REST_Terms_Controller extends WP_REST_Controller {
$taxonomy = $this->taxonomy_repository->fetch($taxonomy_id);
$args = json_decode($request->get_body(), true);
$args = $this->prepare_filters($request);
$terms = $this->terms_repository->fetch($args, $taxonomy);
$prepared_terms = $this->prepare_item_for_response($terms, $request);
$response = [];
foreach ($terms as $term) {
array_push($response, $this->prepare_item_for_response( $term, $request ));
}
return new WP_REST_Response($prepared_terms, 200);
return new WP_REST_Response($response, 200);
}
/**
@ -270,7 +288,12 @@ class TAINACAN_REST_Terms_Controller extends WP_REST_Controller {
*/
public function get_items_permissions_check( $request ) {
$taxonomy = $this->taxonomy_repository->fetch($request['taxonomy_id']);
return $this->taxonomy_repository->can_read($taxonomy);
if ($taxonomy instanceof Entities\Taxonomy) {
return $taxonomy->can_read();
}
return false;
}
/**
@ -286,7 +309,7 @@ class TAINACAN_REST_Terms_Controller extends WP_REST_Controller {
$term = $this->terms_repository->fetch($term_id, $taxonomy);
return new WP_REST_Response($term->__toArray(), 200);
return new WP_REST_Response($this->prepare_item_for_response($term, $request), 200);
}
/**
@ -296,7 +319,12 @@ class TAINACAN_REST_Terms_Controller extends WP_REST_Controller {
*/
public function get_item_permissions_check( $request ) {
$taxonomy = $this->taxonomy_repository->fetch($request['taxonomy_id']);
return $this->taxonomy_repository->can_read($taxonomy);
if ($taxonomy instanceof Entities\Taxonomy) {
return $taxonomy->can_read();
}
return false;
}
}

View File

@ -1,11 +1,14 @@
<?php
$rest_collections_controller = new TAINACAN_REST_Collections_Controller();
$rest_items_controller = new TAINACAN_REST_Items_Controller();
$rest_metadata_controller = new TAINACAN_REST_Metadata_Controller();
$rest_taxonomies_controller = new TAINACAN_REST_Taxonomies_Controller();
$rest_terms_controller = new TAINACAN_REST_Terms_Controller();
$rest_filters_controller = new TAINACAN_REST_Filters_Controller();
$rest_controller = new TAINACAN_REST_Controller();
$rest_collections_controller = new TAINACAN_REST_Collections_Controller();
$rest_items_controller = new TAINACAN_REST_Items_Controller();
$rest_fields_controller = new TAINACAN_REST_Fields_Controller();
$rest_taxonomies_controller = new TAINACAN_REST_Taxonomies_Controller();
$rest_terms_controller = new TAINACAN_REST_Terms_Controller();
$rest_filters_controller = new TAINACAN_REST_Filters_Controller();
$rest_item_metadata_controller = new TAINACAN_REST_Item_Metadata_Controller();
$rest_logs_controller = new TAINACAN_REST_Logs_Controller();
// Add here other endpoints imports
?>

Binary file not shown.

Binary file not shown.

Binary file not shown.

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 2.2 MiB

Binary file not shown.

Binary file not shown.

Binary file not shown.

File diff suppressed because it is too large Load Diff

View File

@ -1,38 +0,0 @@
(function(){
'use strict';var h=new function(){};var aa=new Set("annotation-xml color-profile font-face font-face-src font-face-uri font-face-format font-face-name missing-glyph".split(" "));function n(b){var a=aa.has(b);b=/^[a-z][.0-9_a-z]*-[\-.0-9_a-z]*$/.test(b);return!a&&b}function p(b){var a=b.isConnected;if(void 0!==a)return a;for(;b&&!(b.__CE_isImportDocument||b instanceof Document);)b=b.parentNode||(window.ShadowRoot&&b instanceof ShadowRoot?b.host:void 0);return!(!b||!(b.__CE_isImportDocument||b instanceof Document))}
function q(b,a){for(;a&&a!==b&&!a.nextSibling;)a=a.parentNode;return a&&a!==b?a.nextSibling:null}
function t(b,a,c){c=c?c:new Set;for(var d=b;d;){if(d.nodeType===Node.ELEMENT_NODE){var e=d;a(e);var f=e.localName;if("link"===f&&"import"===e.getAttribute("rel")){d=e.import;if(d instanceof Node&&!c.has(d))for(c.add(d),d=d.firstChild;d;d=d.nextSibling)t(d,a,c);d=q(b,e);continue}else if("template"===f){d=q(b,e);continue}if(e=e.__CE_shadowRoot)for(e=e.firstChild;e;e=e.nextSibling)t(e,a,c)}d=d.firstChild?d.firstChild:q(b,d)}}function u(b,a,c){b[a]=c};function v(){this.a=new Map;this.o=new Map;this.f=[];this.b=!1}function ba(b,a,c){b.a.set(a,c);b.o.set(c.constructor,c)}function w(b,a){b.b=!0;b.f.push(a)}function x(b,a){b.b&&t(a,function(a){return y(b,a)})}function y(b,a){if(b.b&&!a.__CE_patched){a.__CE_patched=!0;for(var c=0;c<b.f.length;c++)b.f[c](a)}}function z(b,a){var c=[];t(a,function(b){return c.push(b)});for(a=0;a<c.length;a++){var d=c[a];1===d.__CE_state?b.connectedCallback(d):A(b,d)}}
function B(b,a){var c=[];t(a,function(b){return c.push(b)});for(a=0;a<c.length;a++){var d=c[a];1===d.__CE_state&&b.disconnectedCallback(d)}}
function C(b,a,c){c=c?c:{};var d=c.w||new Set,e=c.s||function(a){return A(b,a)},f=[];t(a,function(a){if("link"===a.localName&&"import"===a.getAttribute("rel")){var c=a.import;c instanceof Node&&(c.__CE_isImportDocument=!0,c.__CE_hasRegistry=!0);c&&"complete"===c.readyState?c.__CE_documentLoadHandled=!0:a.addEventListener("load",function(){var c=a.import;if(!c.__CE_documentLoadHandled){c.__CE_documentLoadHandled=!0;var f=new Set(d);f.delete(c);C(b,c,{w:f,s:e})}})}else f.push(a)},d);if(b.b)for(a=0;a<
f.length;a++)y(b,f[a]);for(a=0;a<f.length;a++)e(f[a])}
function A(b,a){if(void 0===a.__CE_state){var c=a.ownerDocument;if(c.defaultView||c.__CE_isImportDocument&&c.__CE_hasRegistry)if(c=b.a.get(a.localName)){c.constructionStack.push(a);var d=c.constructor;try{try{if(new d!==a)throw Error("The custom element constructor did not produce the element being upgraded.");}finally{c.constructionStack.pop()}}catch(m){throw a.__CE_state=2,m;}a.__CE_state=1;a.__CE_definition=c;if(c.attributeChangedCallback)for(c=c.observedAttributes,d=0;d<c.length;d++){var e=c[d],
f=a.getAttribute(e);null!==f&&b.attributeChangedCallback(a,e,null,f,null)}p(a)&&b.connectedCallback(a)}}}v.prototype.connectedCallback=function(b){var a=b.__CE_definition;a.connectedCallback&&a.connectedCallback.call(b)};v.prototype.disconnectedCallback=function(b){var a=b.__CE_definition;a.disconnectedCallback&&a.disconnectedCallback.call(b)};
v.prototype.attributeChangedCallback=function(b,a,c,d,e){var f=b.__CE_definition;f.attributeChangedCallback&&-1<f.observedAttributes.indexOf(a)&&f.attributeChangedCallback.call(b,a,c,d,e)};function D(b,a){this.c=b;this.a=a;this.b=void 0;C(this.c,this.a);"loading"===this.a.readyState&&(this.b=new MutationObserver(this.f.bind(this)),this.b.observe(this.a,{childList:!0,subtree:!0}))}function E(b){b.b&&b.b.disconnect()}D.prototype.f=function(b){var a=this.a.readyState;"interactive"!==a&&"complete"!==a||E(this);for(a=0;a<b.length;a++)for(var c=b[a].addedNodes,d=0;d<c.length;d++)C(this.c,c[d])};function ca(){var b=this;this.b=this.a=void 0;this.f=new Promise(function(a){b.b=a;b.a&&a(b.a)})}function F(b){if(b.a)throw Error("Already resolved.");b.a=void 0;b.b&&b.b(void 0)};function G(b){this.i=!1;this.c=b;this.m=new Map;this.j=function(b){return b()};this.g=!1;this.l=[];this.u=new D(b,document)}
G.prototype.define=function(b,a){var c=this;if(!(a instanceof Function))throw new TypeError("Custom element constructors must be functions.");if(!n(b))throw new SyntaxError("The element name '"+b+"' is not valid.");if(this.c.a.get(b))throw Error("A custom element with name '"+b+"' has already been defined.");if(this.i)throw Error("A custom element is already being defined.");this.i=!0;var d,e,f,m,l;try{var g=function(b){var a=k[b];if(void 0!==a&&!(a instanceof Function))throw Error("The '"+b+"' callback must be a function.");
return a},k=a.prototype;if(!(k instanceof Object))throw new TypeError("The custom element constructor's prototype is not an object.");d=g("connectedCallback");e=g("disconnectedCallback");f=g("adoptedCallback");m=g("attributeChangedCallback");l=a.observedAttributes||[]}catch(r){return}finally{this.i=!1}a={localName:b,constructor:a,connectedCallback:d,disconnectedCallback:e,adoptedCallback:f,attributeChangedCallback:m,observedAttributes:l,constructionStack:[]};ba(this.c,b,a);this.l.push(a);this.g||
(this.g=!0,this.j(function(){return da(c)}))};function da(b){if(!1!==b.g){b.g=!1;for(var a=b.l,c=[],d=new Map,e=0;e<a.length;e++)d.set(a[e].localName,[]);C(b.c,document,{s:function(a){if(void 0===a.__CE_state){var e=a.localName,f=d.get(e);f?f.push(a):b.c.a.get(e)&&c.push(a)}}});for(e=0;e<c.length;e++)A(b.c,c[e]);for(;0<a.length;){for(var f=a.shift(),e=f.localName,f=d.get(f.localName),m=0;m<f.length;m++)A(b.c,f[m]);(e=b.m.get(e))&&F(e)}}}G.prototype.get=function(b){if(b=this.c.a.get(b))return b.constructor};
G.prototype.whenDefined=function(b){if(!n(b))return Promise.reject(new SyntaxError("'"+b+"' is not a valid custom element name."));var a=this.m.get(b);if(a)return a.f;a=new ca;this.m.set(b,a);this.c.a.get(b)&&!this.l.some(function(a){return a.localName===b})&&F(a);return a.f};G.prototype.v=function(b){E(this.u);var a=this.j;this.j=function(c){return b(function(){return a(c)})}};window.CustomElementRegistry=G;G.prototype.define=G.prototype.define;G.prototype.get=G.prototype.get;
G.prototype.whenDefined=G.prototype.whenDefined;G.prototype.polyfillWrapFlushCallback=G.prototype.v;var H=window.Document.prototype.createElement,ea=window.Document.prototype.createElementNS,fa=window.Document.prototype.importNode,ga=window.Document.prototype.prepend,ha=window.Document.prototype.append,ia=window.DocumentFragment.prototype.prepend,ja=window.DocumentFragment.prototype.append,I=window.Node.prototype.cloneNode,J=window.Node.prototype.appendChild,K=window.Node.prototype.insertBefore,L=window.Node.prototype.removeChild,M=window.Node.prototype.replaceChild,N=Object.getOwnPropertyDescriptor(window.Node.prototype,
"textContent"),O=window.Element.prototype.attachShadow,P=Object.getOwnPropertyDescriptor(window.Element.prototype,"innerHTML"),Q=window.Element.prototype.getAttribute,R=window.Element.prototype.setAttribute,S=window.Element.prototype.removeAttribute,T=window.Element.prototype.getAttributeNS,U=window.Element.prototype.setAttributeNS,ka=window.Element.prototype.removeAttributeNS,la=window.Element.prototype.insertAdjacentElement,ma=window.Element.prototype.prepend,na=window.Element.prototype.append,
V=window.Element.prototype.before,oa=window.Element.prototype.after,pa=window.Element.prototype.replaceWith,qa=window.Element.prototype.remove,ra=window.HTMLElement,W=Object.getOwnPropertyDescriptor(window.HTMLElement.prototype,"innerHTML"),sa=window.HTMLElement.prototype.insertAdjacentElement;function ta(){var b=X;window.HTMLElement=function(){function a(){var a=this.constructor,d=b.o.get(a);if(!d)throw Error("The custom element being constructed was not registered with `customElements`.");var e=d.constructionStack;if(!e.length)return e=H.call(document,d.localName),Object.setPrototypeOf(e,a.prototype),e.__CE_state=1,e.__CE_definition=d,y(b,e),e;var d=e.length-1,f=e[d];if(f===h)throw Error("The HTMLElement constructor was either called reentrantly for this constructor or called multiple times.");
e[d]=h;Object.setPrototypeOf(f,a.prototype);y(b,f);return f}a.prototype=ra.prototype;return a}()};function Y(b,a,c){function d(a){return function(d){for(var c=[],e=0;e<arguments.length;++e)c[e-0]=arguments[e];for(var e=[],f=[],k=0;k<c.length;k++){var r=c[k];r instanceof Element&&p(r)&&f.push(r);if(r instanceof DocumentFragment)for(r=r.firstChild;r;r=r.nextSibling)e.push(r);else e.push(r)}a.apply(this,c);for(c=0;c<f.length;c++)B(b,f[c]);if(p(this))for(c=0;c<e.length;c++)f=e[c],f instanceof Element&&z(b,f)}}c.h&&(a.prepend=d(c.h));c.append&&(a.append=d(c.append))};function ua(){var b=X;u(Document.prototype,"createElement",function(a){if(this.__CE_hasRegistry){var c=b.a.get(a);if(c)return new c.constructor}a=H.call(this,a);y(b,a);return a});u(Document.prototype,"importNode",function(a,c){a=fa.call(this,a,c);this.__CE_hasRegistry?C(b,a):x(b,a);return a});u(Document.prototype,"createElementNS",function(a,c){if(this.__CE_hasRegistry&&(null===a||"http://www.w3.org/1999/xhtml"===a)){var d=b.a.get(c);if(d)return new d.constructor}a=ea.call(this,a,c);y(b,a);return a});
Y(b,Document.prototype,{h:ga,append:ha})};function va(){var b=X;function a(a,d){Object.defineProperty(a,"textContent",{enumerable:d.enumerable,configurable:!0,get:d.get,set:function(a){if(this.nodeType===Node.TEXT_NODE)d.set.call(this,a);else{var c=void 0;if(this.firstChild){var e=this.childNodes,l=e.length;if(0<l&&p(this))for(var c=Array(l),g=0;g<l;g++)c[g]=e[g]}d.set.call(this,a);if(c)for(a=0;a<c.length;a++)B(b,c[a])}}})}u(Node.prototype,"insertBefore",function(a,d){if(a instanceof DocumentFragment){var c=Array.prototype.slice.apply(a.childNodes);
a=K.call(this,a,d);if(p(this))for(d=0;d<c.length;d++)z(b,c[d]);return a}c=p(a);d=K.call(this,a,d);c&&B(b,a);p(this)&&z(b,a);return d});u(Node.prototype,"appendChild",function(a){if(a instanceof DocumentFragment){var c=Array.prototype.slice.apply(a.childNodes);a=J.call(this,a);if(p(this))for(var e=0;e<c.length;e++)z(b,c[e]);return a}c=p(a);e=J.call(this,a);c&&B(b,a);p(this)&&z(b,a);return e});u(Node.prototype,"cloneNode",function(a){a=I.call(this,a);this.ownerDocument.__CE_hasRegistry?C(b,a):x(b,a);
return a});u(Node.prototype,"removeChild",function(a){var c=p(a),e=L.call(this,a);c&&B(b,a);return e});u(Node.prototype,"replaceChild",function(a,d){if(a instanceof DocumentFragment){var e=Array.prototype.slice.apply(a.childNodes);a=M.call(this,a,d);if(p(this))for(B(b,d),d=0;d<e.length;d++)z(b,e[d]);return a}var e=p(a),c=M.call(this,a,d),m=p(this);m&&B(b,d);e&&B(b,a);m&&z(b,a);return c});N&&N.get?a(Node.prototype,N):w(b,function(b){a(b,{enumerable:!0,configurable:!0,get:function(){for(var a=[],b=
0;b<this.childNodes.length;b++)a.push(this.childNodes[b].textContent);return a.join("")},set:function(a){for(;this.firstChild;)L.call(this,this.firstChild);J.call(this,document.createTextNode(a))}})})};function wa(b){var a=Element.prototype;function c(a){return function(c){for(var d=[],e=0;e<arguments.length;++e)d[e-0]=arguments[e];for(var e=[],l=[],g=0;g<d.length;g++){var k=d[g];k instanceof Element&&p(k)&&l.push(k);if(k instanceof DocumentFragment)for(k=k.firstChild;k;k=k.nextSibling)e.push(k);else e.push(k)}a.apply(this,d);for(d=0;d<l.length;d++)B(b,l[d]);if(p(this))for(d=0;d<e.length;d++)l=e[d],l instanceof Element&&z(b,l)}}V&&(a.before=c(V));V&&(a.after=c(oa));pa&&u(a,"replaceWith",function(a){for(var d=
[],c=0;c<arguments.length;++c)d[c-0]=arguments[c];for(var c=[],m=[],l=0;l<d.length;l++){var g=d[l];g instanceof Element&&p(g)&&m.push(g);if(g instanceof DocumentFragment)for(g=g.firstChild;g;g=g.nextSibling)c.push(g);else c.push(g)}l=p(this);pa.apply(this,d);for(d=0;d<m.length;d++)B(b,m[d]);if(l)for(B(b,this),d=0;d<c.length;d++)m=c[d],m instanceof Element&&z(b,m)});qa&&u(a,"remove",function(){var a=p(this);qa.call(this);a&&B(b,this)})};function xa(){var b=X;function a(a,c){Object.defineProperty(a,"innerHTML",{enumerable:c.enumerable,configurable:!0,get:c.get,set:function(a){var d=this,e=void 0;p(this)&&(e=[],t(this,function(a){a!==d&&e.push(a)}));c.set.call(this,a);if(e)for(var f=0;f<e.length;f++){var k=e[f];1===k.__CE_state&&b.disconnectedCallback(k)}this.ownerDocument.__CE_hasRegistry?C(b,this):x(b,this);return a}})}function c(a,c){u(a,"insertAdjacentElement",function(a,d){var e=p(d);a=c.call(this,a,d);e&&B(b,d);p(a)&&z(b,d);
return a})}O&&u(Element.prototype,"attachShadow",function(a){return this.__CE_shadowRoot=a=O.call(this,a)});P&&P.get?a(Element.prototype,P):W&&W.get?a(HTMLElement.prototype,W):w(b,function(b){a(b,{enumerable:!0,configurable:!0,get:function(){return I.call(this,!0).innerHTML},set:function(a){var b="template"===this.localName,d=b?this.content:this,c=H.call(document,this.localName);for(c.innerHTML=a;0<d.childNodes.length;)L.call(d,d.childNodes[0]);for(a=b?c.content:c;0<a.childNodes.length;)J.call(d,
a.childNodes[0])}})});u(Element.prototype,"setAttribute",function(a,c){if(1!==this.__CE_state)return R.call(this,a,c);var d=Q.call(this,a);R.call(this,a,c);c=Q.call(this,a);b.attributeChangedCallback(this,a,d,c,null)});u(Element.prototype,"setAttributeNS",function(a,c,f){if(1!==this.__CE_state)return U.call(this,a,c,f);var d=T.call(this,a,c);U.call(this,a,c,f);f=T.call(this,a,c);b.attributeChangedCallback(this,c,d,f,a)});u(Element.prototype,"removeAttribute",function(a){if(1!==this.__CE_state)return S.call(this,
a);var c=Q.call(this,a);S.call(this,a);null!==c&&b.attributeChangedCallback(this,a,c,null,null)});u(Element.prototype,"removeAttributeNS",function(a,c){if(1!==this.__CE_state)return ka.call(this,a,c);var d=T.call(this,a,c);ka.call(this,a,c);var e=T.call(this,a,c);d!==e&&b.attributeChangedCallback(this,c,d,e,a)});sa?c(HTMLElement.prototype,sa):la?c(Element.prototype,la):console.warn("Custom Elements: `Element#insertAdjacentElement` was not patched.");Y(b,Element.prototype,{h:ma,append:na});wa(b)};/*
Copyright (c) 2016 The Polymer Project Authors. All rights reserved.
This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
Code distributed by Google as part of the polymer project is also
subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
*/
var Z=window.customElements;if(!Z||Z.forcePolyfill||"function"!=typeof Z.define||"function"!=typeof Z.get){var X=new v;ta();ua();Y(X,DocumentFragment.prototype,{h:ia,append:ja});va();xa();document.__CE_hasRegistry=!0;var customElements=new G(X);Object.defineProperty(window,"customElements",{configurable:!0,enumerable:!0,value:customElements})};
}).call(self);
//# sourceMappingURL=custom-elements.min.js.map

View File

@ -49,7 +49,7 @@ class Capabilities {
"read"
]
],
"tainacan-metadata"=> [
"tainacan-field"=> [
"administrator"=> [
"delete_posts",
"delete_private_posts",
@ -332,6 +332,10 @@ class Capabilities {
$role->add_cap($collection_items_caps->$cap);
}
}
// Refresh roles capabilities for current user to have instant effect
global $current_user;
$current_user->get_role_caps();
}
/**

View File

@ -122,6 +122,49 @@ class Collection extends Entity {
return get_post_type_capabilities((object) $args);
}
/**
* @return mixed|null
*/
function get_featured_img(){
return $this->get_mapped_property('featured_image');
}
/**
* @param $value
*/
function set_featured_img($value){
$this->set_mapped_property('featured_image', $value);
}
/**
* @return mixed|null
*/
function get_modification_date(){
return $this->get_mapped_property('modification_date');
}
/**
* @return mixed|null
*/
function get_creation_date(){
return $this->get_mapped_property('creation_date');
}
/**
* @return mixed|null
*/
function get_author_id(){
return $this->get_mapped_property('author_id');
}
/**
* @return mixed|null
*/
function get_url(){
return $this->get_mapped_property('url');
}
/**
* Get collection name
*
@ -202,6 +245,15 @@ class Collection extends Entity {
function get_default_view_mode() {
return $this->get_mapped_property('default_view_mode');
}
/**
* Get collection fields ordination
*
* @return string
*/
function get_fields_order() {
return $this->get_mapped_property('fields_order');
}
/**
* Get collection moderators ids
@ -224,17 +276,17 @@ class Collection extends Entity {
}
/**
* Get collection metadata.
* Get collection field.
*
* Returns an array of \Entity\Metadata objects, representing all the metadata of the collection.
* Returns an array of \Entity\Field objects, representing all the field of the collection.
*
* @see \Tainacan\Repositories\Metadatas->fetch()
* @see \Tainacan\Repositories\Fields->fetch()
*
* @return [\Tainacan\Entities\Metadata] array
* @return [\Tainacan\Entities\Field] array
*/
function get_metadata() {
$Tainacan_Metadatas = new \Tainacan\Repositories\Metadatas();
return $Tainacan_Metadatas->fetch_by_collection( $this, [], 'OBJECT' );
function get_fields() {
$Tainacan_Fields = new \Tainacan\Repositories\Fields();
return $Tainacan_Fields->fetch_by_collection( $this, [], 'OBJECT' );
}
/**
@ -332,6 +384,16 @@ class Collection extends Entity {
function set_default_view_mode($value) {
$this->set_mapped_property('default_view_mode', $value);
}
/**
* Set collection fields ordination
*
* @param [string] $value
* @return void
*/
function set_fields_order($value) {
$this->set_mapped_property('fields_order', $value);
}
/**
* Set collection moderators ids
@ -387,5 +449,17 @@ class Collection extends Entity {
* (the same as moderators_ids but gets and sets WP_User objects)
*
*/
/**
* Validate Collection
* @return bool
*/
function validate() {
if ( !in_array($this->get_status(), apply_filters('tainacan-status-require-validation', ['publish','future','private'])) )
return true;
return parent::validate();
}
}

View File

@ -129,7 +129,7 @@ class Entity {
* @return mixed property value
*/
public function get_mapped_property($prop) {
if (isset($this->$prop) && !empty($this->$prop)){
if (isset($this->$prop) ){
return $this->$prop;
}
//prop is not set at object, try to get from database
@ -170,8 +170,9 @@ class Entity {
$this->reset_errors();
foreach ($map as $prop => $mapped) {
if (!$this->validate_prop($prop))
$is_valid = false;
if (!$this->validate_prop($prop)) {
$is_valid = false;
}
}
$this->set_validated($is_valid);
@ -209,9 +210,8 @@ class Entity {
}
} else {
if (!$validation->validate($prop_value)) {
$this->add_error('invalid', $message);
$is_valid = false;
$this->add_error('invalid', $message);
$is_valid = false;
}
}
}

View File

@ -5,36 +5,45 @@ namespace Tainacan\Entities;
defined( 'ABSPATH' ) or die( 'No script kiddies please!' );
/**
* Represents the Entity Metadata
* Represents the Entity Field
*/
class Metadata extends Entity {
class Field extends Entity {
// Collection getter and setter declared here
use \Tainacan\Traits\Entity_Collection_Relation;
protected static $post_type = 'tainacan-metadata';
protected static $post_type = 'tainacan-field';
/**
* {@inheritDoc}
* @see \Tainacan\Entities\Entity::repository
* @var string
*/
protected $repository = 'Tainacan_Metadatas';
protected $repository = 'Tainacan_Fields';
public function __toString(){
return 'Hello, my name is '. $this->get_name();
}
/**
* Return the metadata name
* Return the field name
*
* @return string
*/
function get_name() {
return $this->get_mapped_property('name');
}
/**
* Get field slug
*
* @return string
*/
function get_slug() {
return $this->get_mapped_property('slug');
}
/**
* Return the metadata order type
* Return the field order type
*
* @return string
*/
@ -52,7 +61,7 @@ class Metadata extends Entity {
}
/**
* Return the metadata description
* Return the field description
*
* @return string
*/
@ -61,7 +70,7 @@ class Metadata extends Entity {
}
/**
* Return if is a required metadata
* Return if is a required field
*
* @return boolean
*/
@ -70,7 +79,7 @@ class Metadata extends Entity {
}
/**
* Return if is a multiple metadata
* Return if is a multiple field
*
* @return boolean
*/
@ -88,7 +97,7 @@ class Metadata extends Entity {
}
/**
* Return if metadata is key
* Return if field is key
*
* @return boolean
*/
@ -115,7 +124,7 @@ class Metadata extends Entity {
}
/**
* Return the metadata default value
* Return the field default value
*
* @return string || integer
*/
@ -152,9 +161,10 @@ class Metadata extends Entity {
function get_field_options(){
return $this->get_mapped_property('field_type_options');
}
/**
* Set the metadata name
* Set the field name
*
* @param [string] $value
* @return void
@ -164,7 +174,23 @@ class Metadata extends Entity {
}
/**
* Set manually the order of the metadata
* Set the field slug
*
* If you dont set the field slug, it will be set automatically based on the name and
* following WordPress default behavior of creating slugs for posts.
*
* If you set the slug for an existing one, WordPress will append a number at the end of in order
* to make it unique (e.g slug-1, slug-2)
*
* @param [string] $value
* @return void
*/
function set_slug($value) {
$this->set_mapped_property('slug', $value);
}
/**
* Set manually the order of the field
*
* @param [string] $value
* @return void
@ -174,7 +200,7 @@ class Metadata extends Entity {
}
/**
* Set the metadata parent ID
* Set the field parent ID
*
* @param [integer] $value The ID from parent
* @return void
@ -184,7 +210,7 @@ class Metadata extends Entity {
}
/**
* Set metadata description
* Set field description
*
* @param [string] $value The text description
* @return void
@ -194,7 +220,7 @@ class Metadata extends Entity {
}
/**
* Allow the metadata be required
* Allow the field be required
*
* @param [boolean] $value
* @return void
@ -234,7 +260,7 @@ class Metadata extends Entity {
}
/**
* Set mask for the metadata
* Set mask for the field
*
* @param [string] $value
* @return void

View File

@ -32,6 +32,13 @@ class Filter extends Entity {
return $this->get_mapped_property('name');
}
/**
* @return mixed|null
*/
function get_description(){
return $this->get_mapped_property('description');
}
/**
* Return the filter order type
*
@ -50,14 +57,15 @@ class Filter extends Entity {
return $this->get_mapped_property('color');
}
/**
* Return the metadata
*
* @return Metadata
*/
function get_metadata() {
$id = $this->get_mapped_property('metadata');
return new Metadata( $id );
/**
* Return the field
*
* @return Field
* @throws \Exception
*/
function get_field() {
$id = $this->get_mapped_property('field');
return new Field( $id );
}
/**
@ -131,15 +139,15 @@ class Filter extends Entity {
}
/**
* Define the filter metadata
* Define the filter field
*
* @param \Tainacan\Entities\Metadata
* @param \Tainacan\Entities\Field
* @return void
*/
function set_metadata( $value ){
$id = ( $value instanceof Metadata ) ? $value->get_id() : $value;
function set_field( $value ){
$id = ( $value instanceof Field ) ? $value->get_id() : $value;
$this->set_mapped_property('metadata', $id);
$this->set_mapped_property('field', $id);
}
/**

View File

@ -5,7 +5,7 @@ namespace Tainacan\Entities;
defined( 'ABSPATH' ) or die( 'No script kiddies please!' );
/**
* Represents the Item Metadata Entity
* Represents the Item Field Entity
*/
class Item_Metadata_Entity extends Entity {
protected static $post_type = false;
@ -16,20 +16,20 @@ class Item_Metadata_Entity extends Entity {
*/
protected $repository = 'Tainacan_Item_Metadata';
function __construct(Item $item, Metadata $metadata) {
function __construct(Item $item, Field $field) {
$this->set_item($item);
$this->set_metadata($metadata);
$this->set_field($field);
}
public function __toString(){
return 'Hello, I\'m the Item Metadata Entity';
return 'Hello, I\'m the Item Field Entity';
}
public function __toArray(){
$as_array['value'] = $this->get_value();
$as_array['item'] = $this->get_item()->__toArray();
$as_array['metadata'] = $this->get_metadata()->__toArray();
$as_array['field'] = $this->get_field()->__toArray();
return $as_array;
}
@ -45,7 +45,7 @@ class Item_Metadata_Entity extends Entity {
}
/**
* Define the metadata value
* Define the field value
*
* @param [integer | string] $value
* @return void
@ -55,13 +55,13 @@ class Item_Metadata_Entity extends Entity {
}
/**
* Define the metadata
* Define the field
*
* @param Metadata $metadata
* @param Field $field
* @return void
*/
function set_metadata(Metadata $metadata) {
$this->metadata = $metadata;
function set_field(Field $field) {
$this->field = $field;
}
/**
@ -74,16 +74,16 @@ class Item_Metadata_Entity extends Entity {
}
/**
* Return the metadata
* Return the field
*
* @return Metadata
* @return Field
*/
function get_metadata() {
return $this->metadata;
function get_field() {
return $this->field;
}
/**
* Return the metadata value
* Return the field value
*
* @return string | integer
*/
@ -96,30 +96,30 @@ class Item_Metadata_Entity extends Entity {
}
/**
* Return true if metadata is multiple, else return false
* Return true if field is multiple, else return false
*
* @return boolean
*/
function is_multiple() {
return $this->get_metadata()->is_multiple();
return $this->get_field()->is_multiple();
}
/**
* Return true if metadata is key
* Return true if field is key
*
* @return boolean
*/
function is_collection_key() {
return $this->get_metadata()->is_collection_key();
return $this->get_field()->is_collection_key();
}
/**
* Return true if metadata is required
* Return true if field is required
*
* @return boolean
*/
function is_required() {
return $this->get_metadata()->is_required();
return $this->get_field()->is_required();
}
/**
@ -129,13 +129,23 @@ class Item_Metadata_Entity extends Entity {
*/
function validate() {
$value = $this->get_value();
$metadata = $this->get_metadata();
$field = $this->get_field();
$item = $this->get_item();
if (empty($value) && $this->is_required()) {
$this->add_error('required', $metadata->get_name() . ' is required');
$this->add_error('required', $field->get_name() . ' is required');
return false;
}
$classFieldType = $field->get_field_type_object();
if( is_object( $classFieldType ) ){
if( method_exists ( $classFieldType , 'validate' ) ){
if( ! $classFieldType->validate( $this ) ) {
$this->add_error('field_type_error', $classFieldType->get_errors() );
return false;
}
}
}
if ($this->is_multiple()) {
@ -154,19 +164,19 @@ class Item_Metadata_Entity extends Entity {
}
if ($this->is_required() && !$one_filled) {
$this->add_error('required', $metadata->get_name() . ' is required');
$this->add_error('required', $field->get_name() . ' is required');
return false;
}
if (!$valid) {
$this->add_error('invalid', $metadata->get_name() . ' is invalid');
$this->add_error('invalid', $field->get_name() . ' is invalid');
return false;
}
$this->set_as_valid();
return true;
} else {
$this->add_error('invalid', $metadata->get_name() . ' is invalid');
$this->add_error('invalid', $field->get_name() . ' is invalid');
return false;
}
} else {
@ -177,14 +187,14 @@ class Item_Metadata_Entity extends Entity {
$test = $Tainacan_Items->fetch([
'meta_query' => [
[
'key' => $this->metadata->get_id(),
'key' => $this->field->get_id(),
'value' => $value
],
]
], $item->get_collection());
if ($test->have_posts()) {
$this->add_error('key_exists', $metadata->get_name() . ' is a collection key and there is another item with the same value');
$this->add_error('key_exists', $field->get_name() . ' is a collection key and there is another item with the same value');
return false;
}
}

View File

@ -28,6 +28,48 @@ class Item extends Entity {
return 'Hello, my name is '. $this->get_title();
}
/**
* @return mixed|null
*/
function get_featured_img(){
return $this->get_mapped_property('featured_image');
}
/**
* @param $value
*/
function set_featured_img($value){
$this->set_mapped_property('featured_image', $value);
}
/**
* @return mixed|null
*/
function get_modification_date(){
return $this->get_mapped_property('modification_date');
}
/**
* @return mixed|null
*/
function get_creation_date(){
return $this->get_mapped_property('creation_date');
}
/**
* @return mixed|null
*/
function get_author_id(){
return $this->get_mapped_property('author_id');
}
/**
* @return mixed|null
*/
function get_url(){
return $this->get_mapped_property('url');
}
/**
* Return the item ID
*
@ -123,58 +165,20 @@ class Item extends Entity {
}
/**
* Return a Metadata or a List of Metadata
* Return a List of ItemMetadata objects
*
* @return array || Metadata
* It will return all fields associeated with the collection this item is part of.
*
* If the item already has a value for any of the fields, it will be available.
*
* @return array Array of ItemMetadata objects
*/
function get_metadata() {
global $Tainacan_Metadatas;
function get_fields() {
global $Tainacan_Item_Metadata;
return $Tainacan_Item_Metadata->fetch($this, 'OBJECT');
if (isset($this->metadata))
return $this->metadata;
$collection = $this->get_collection();
$all_metadata = [];
if ($collection) {
$meta_list = $Tainacan_Metadatas->fetch_by_collection( $collection, [], 'OBJECT' );
foreach ($meta_list as $meta) {
$all_metadata[$meta->get_id()] = new Item_Metadata_Entity($this, $meta);
}
}
return $all_metadata;
}
/**
* Define the Metadata
*
* @param Metadata $new_metadata
* @param [string || integer || array] $value
* @return void
*/
function add_metadata(Metadata $new_metadata, $value) {
//TODO Multiple metadata must receive an array as value
$item_metadata = new Item_Metadata_Entity($this, $new_metadata);
$item_metadata->set_value($value);
$current_meta = $this->get_metadata();
$current_meta[$new_metadata->get_id()] = $item_metadata;
$this->set_metadata($current_meta);
}
/**
* Aux function for @method add_metadata
*
* @param array $metadata
* @return void
*/
function set_metadata(Array $metadata) {
$this->metadata = $metadata;
}
/**
* set meta cap object
*/
@ -191,4 +195,48 @@ class Item extends Entity {
}
}
}
/**
*
* {@inheritDoc}
* @see \Tainacan\Entities\Entity::validate()
*/
function validate(){
if ( !in_array($this->get_status(), apply_filters('tainacan-status-require-validation', ['publish','future','private'])) )
return true;
if( parent::validate() ){
$arrayItemMetadata = $this->get_fields();
if( $arrayItemMetadata ){
foreach ( $arrayItemMetadata as $itemMetadata ) {
// avoid core fields to re-validate
$pos = strpos($itemMetadata->get_field()->get_field_type(), 'Core');
if( $pos !== false ){
continue;
}
if( !$itemMetadata->validate() ){
$errors = $itemMetadata->get_errors();
$this->add_error( $itemMetadata->get_field()->get_name(), $errors );
return false;
}
}
}
return true;
}
return false;
}
/**
* {@inheritDoc}
* @see \Tainacan\Entities\Entity::validate()
*/
public function validate_core_fields(){
if ( !in_array($this->get_status(), apply_filters('tainacan-status-require-validation', ['publish','future','private'])) )
return true;
return parent::validate();
}
}

View File

@ -84,15 +84,6 @@ class Taxonomy extends Entity {
return $this->get_mapped_property('name');
}
/**
* Return the parent id
*
* @return integer
*/
function get_parent() {
return $this->get_mapped_property('parent');
}
/**
* Return the description
*
@ -142,14 +133,6 @@ class Taxonomy extends Entity {
$this->set_mapped_property('name', $value);
}
/**
* Define the parent ID
*
* @param [integer] $value
*/
function set_parent($value) {
$this->set_mapped_property('parent', $value);
}
/**
* Define the slug

View File

@ -22,9 +22,10 @@ class Term extends Entity {
* @param int $which
* @param string $taxonomy
*/
function __construct($which = 0, $taxonomy = '' ) {
function __construct($which = 0, $taxonomy = false ) {
$this->set_taxonomy( $taxonomy );
if ($taxonomy)
$this->set_taxonomy( $taxonomy );
if ( is_numeric( $which ) && $which > 0) {
$post = get_term_by('id', $which, $taxonomy);

View File

@ -1,83 +1,64 @@
<template>
<div class="component">
<label
v-for="option in getOptions"
:for="option.replace(' ','-') + '-checkbox'">
<input
type="checkbox"
:id="option.replace(' ','-') + '-checkbox'"
:value="option"
:checked="isChecked(option)"
@change="sendValue($event)"> {{ option }} <br>
</label>
<div class="block">
<div
v-for="option,index in getOptions"
class="field">
<b-checkbox
v-model="checked"
:native-value="option"
>{{ option }}</b-checkbox>
</div>
</div>
</template>
<script>
import store from '../../../js/store/store';
export default {
store,
data(){
return {
checked:[]
}
},
created(){
if(this.value instanceof Array)
this.checked = this.value;
},
props: {
name: {
type: String
field: {
type: Object
},
options: {
type: String
},
item_id: {
type: Number
},
metadata_id: {
type: Number
},
value: {
type: [ String,Number ]
},
value: [String, Number, Array]
},
created(){
this.setInitValueOnStore();
watch: {
checked: function(val){
this.checked = val;
this.onChecked();
}
},
computed: {
getOptions(){
const values = ( this.options ) ? this.options.split("\n") : '';
return values;
if ( this.options && this.options !== '' ){
return this.options.split("\n");
}
else if (this.field) {
const fields = this.field.field.field_type_options.options;
return ( fields ) ? fields.split("\n") : [];
}
return [];
}
},
methods: {
setInitValueOnStore (){
const array_values = JSON.parse( this.value );
if ( array_values.length > 0 ){
this.checked = array_values;
this.$store.dispatch('item/setSingleMetadata', { item_id: this.item_id, metadata_id: this.metadata_id, values: array_values });
}
onChecked() {
this.$emit('blur');
this.onInput(this.checked)
},
sendValue ( event ){
let index = this.checked.indexOf( event.target.value );
if( index >= 0 ){
this.checked.splice(index,1);
}else{
this.checked.push( event.target.value );
}
this.$store.dispatch('item/sendMetadata', { item_id: this.item_id, metadata_id: this.metadata_id, values: this.checked });
},
isChecked ( value ){
let index = this.checked.indexOf( value );
return index >= 0;
onInput($event) {
this.inputValue = $event;
this.$emit('input', this.inputValue);
}
}
}
</script>
<style scoped="">
#postcustomstuff table input, #postcustomstuff table select, #postcustomstuff table textarea {
width: auto;
margin: 8px;
}
</style>
</script>

View File

@ -13,6 +13,7 @@ class Checkbox extends Field_Type {
// call field type constructor
parent::__construct();
parent::set_primitive_type('date');
$this->component = 'tainacan-checkbox';
}
/**
@ -23,10 +24,10 @@ class Checkbox extends Field_Type {
public function render( $itemMetadata ){
$options = ( isset( $this->options['options'] ) ) ? $this->options['options'] : '';
return '<tainacan-checkbox options="'.$options.'"
metadata_id ="'.$itemMetadata->get_metadata()->get_id().'"
field_id ="'.$itemMetadata->get_field()->get_id().'"
item_id="'.$itemMetadata->get_item()->get_id().'"
value=\''.json_encode( $itemMetadata->get_value() ).'\'
name="'.$itemMetadata->get_metadata()->get_name().'"></tainacan-checkbox>';
name="'.$itemMetadata->get_field()->get_name().'"></tainacan-checkbox>';
}
/**
@ -37,7 +38,7 @@ class Checkbox extends Field_Type {
<tr>
<td>
<label><?php echo __('Options','tainacan'); ?></label><br/>
<small><?php echo __('Insert the options, separate by lines for the metadata value','tainacan'); ?></small>
<small><?php echo __('Insert the options, separate by lines for the field value','tainacan'); ?></small>
</td>
<td>
<textarea name="field_type_checkbox[options]"><?php echo ( isset( $this->options['options'] ) ) ? $this->options['options'] : ''; ?></textarea>

View File

@ -0,0 +1,62 @@
<?php
namespace Tainacan\Field_Types;
defined( 'ABSPATH' ) or die( 'No script kiddies please!' );
/**
* Class TainacanFieldType
*/
class Core_Description extends Field_Type {
function __construct(){
// call field type constructor
parent::__construct();
parent::set_primitive_type('string');
$this->core = true;
$this->related_mapped_prop = 'description';
$this->component = 'tainacan-textarea';
}
/**
* @param $itemMetadata \Tainacan\Entities\Item_Metadata_Entity The instace of the entity itemMetadata
* @return string
*/
public function render( $itemMetadata ){
return '<tainacan-textarea
field_id ="'.$itemMetadata->get_field()->get_id().'"
item_id="'.$itemMetadata->get_item()->get_id().'"
value=\''.json_encode( $itemMetadata->get_value() ).'\'
name="'.$itemMetadata->get_field()->get_name().'"></tainacan-textarea>';
}
/**
* generate the fields for this field type
*/
public function form(){
}
/**
* Description core Field type is stored as the item content (description)
*
* Lets validate it as the item description
*
* @param TainacanEntitiesItem_Metadata_Entity $item_metadata
* @return bool Valid or not
*/
public function validate(\Tainacan\Entities\Item_Metadata_Entity $item_metadata) {
$item = $item_metadata->get_item();
if ( !in_array($item->get_status(), apply_filters('tainacan-status-require-validation', ['publish','future','private'])) )
return true;
$item->set_description($item_metadata->get_value());
return $item->validate_prop('description');
}
}

View File

@ -0,0 +1,61 @@
<?php
namespace Tainacan\Field_Types;
defined( 'ABSPATH' ) or die( 'No script kiddies please!' );
/**
* Class TainacanFieldType
*/
class Core_Title extends Field_Type {
function __construct(){
// call field type constructor
parent::__construct();
parent::set_primitive_type('string');
$this->core = true;
$this->related_mapped_prop = 'title';
$this->component = 'tainacan-text';
}
/**
* @param $itemMetadata \Tainacan\Entities\Item_Metadata_Entity The instace of the entity itemMetadata
* @return string
*/
public function render( $itemMetadata ){
return '<tainacan-text field_id ="'.$itemMetadata->get_field()->get_id().'"
item_id="'.$itemMetadata->get_item()->get_id().'"
value=\''.json_encode( $itemMetadata->get_value() ).'\'
name="'.$itemMetadata->get_field()->get_name().'"></tainacan-text>';
}
/**
* generate the fields for this field type
*/
public function form(){
}
/**
* Title core Field type is stored as the item title
*
* Lets validate it as the item title
*
* @param TainacanEntitiesItem_Metadata_Entity $item_metadata
* @return bool Valid or not
*/
public function validate(\Tainacan\Entities\Item_Metadata_Entity $item_metadata) {
$item = $item_metadata->get_item();
if ( !in_array($item->get_status(), apply_filters('tainacan-status-require-validation', ['publish','future','private'])) )
return true;
$item->set_title($item_metadata->get_value());
return $item->validate_prop('title');
}
}

View File

@ -1,56 +1,25 @@
<template>
<div class="component">
<p>{{ name }}</p>
<input type="date" :value="getValue()" @blur="changeValue( $event )">
</div>
<b-datepicker
v-model="dateValue"
@blur="onBlur"
@input="onInput($event)"></b-datepicker>
</template>
<script>
export default {
props: {
name: { type: String },
item_id: { type: Number },
metadata_id: { type: Number },
value: { type: [ String,Number ] },
errorsMsg: { type: [ String,Number ] },
data() {
return {
dateValue: new Date()
}
},
methods: {
changeValue( event ){
this.$emit('changeValue', { item_id: this.item_id, metadata_id: this.metadata_id, values: event.target.value } );
onBlur() {
this.$emit('blur');
},
getValue(){
try{
return JSON.parse( this.value );
}catch(e){
console.log('invalid json value');
}
return this.value;
},
getErrors(){
try{
return JSON.parse( this.errorsMsg );
}catch(e){
console.log('invalid json error');
}
return this.errorsMsg;
onInput($event) {
this.dateValue = $event;
this.$emit('input', this.dateValue);
}
}
}
</script>
<style scoped>
input[type="text"] {
display: block;
margin: 0;
width: 100%;
border-radius: 6px;
font-family: sans-serif;
font-size: 18px;
appearance: none;
box-shadow: none;
color:green;
}
input[type="text"]:focus {
outline: none;
}
</style>

View File

@ -14,6 +14,7 @@ class Date extends Field_Type {
// call field type constructor
parent::__construct();
parent::set_primitive_type('date');
$this->component = 'tainacan-date';
}
/**
@ -22,10 +23,10 @@ class Date extends Field_Type {
*/
public function render( $itemMetadata ){
return '<tainacan-date metadata_id ="'.$itemMetadata->get_metadata()->get_id().'"
return '<tainacan-date field_id ="'.$itemMetadata->get_field()->get_id().'"
item_id="'.$itemMetadata->get_item()->get_id().'"
value=\''.json_encode( $itemMetadata->get_value() ).'\'
name="'.$itemMetadata->get_metadata()->get_name().'"></tainacan-date>';
name="'.$itemMetadata->get_field()->get_name().'"></tainacan-date>';
}
/**

View File

@ -11,7 +11,29 @@ abstract class Field_Type {
private $primitive_type;
public $options;
public $errors;
/**
* Indicates wether this is a core Field Type or not
*
* Core field types are used by Title and description fields. These fields:
* * Can only be used once, they belong to the repository and can not be deleted
* * Its values are saved in th wp_post table, and not as post_meta
*
*/
public $core = false;
/**
* Used by core field types to indicate where it should be saved
*/
public $related_mapped_prop = false;
/**
* The name of the web component used by this field type
* @var string
*/
public $component;
abstract function render( $itemMetadata );
public function __construct(){
@ -19,11 +41,11 @@ abstract class Field_Type {
}
public function register_field_type(){
global $Tainacan_Metadatas;
$Tainacan_Metadatas->register_field_type( $this );
global $Tainacan_Fields;
$Tainacan_Fields->register_field_type( $this );
}
public function validate($value) {
public function validate(\Tainacan\Entities\Item_Metadata_Entity $item_metadata) {
return true;
}
@ -39,6 +61,14 @@ abstract class Field_Type {
$this->primitive_type = $primitive_type;
}
public function get_errors() {
return $this->errors;
}
public function get_component() {
return $this->component;
}
/**
* @param $options
*/
@ -52,5 +82,17 @@ abstract class Field_Type {
public function form(){
}
public function __toArray(){
$attributes = [];
$attributes['className'] = get_class($this);
$attributes['core'] = $this->core;
$attributes['component'] = $this->get_component();
$attributes['primitive_type'] = $this->get_primitive_type();
return $attributes;
}
}

View File

@ -1,59 +1,27 @@
<template>
<div class="component">
<p>{{ name }}</p>
<input type="text" @blur="changeValue($event)" :value="getValue()">
<p v-for="error in getErrors()">
{{ error }}
</p>
</div>
<b-input
type="number"
:value="inputValue"
@blur="onBlur"
@change="onBlur"
@input="onInput($event)"></b-input>
</template>
<script>
export default {
props: {
name: { type: String },
item_id: { type: Number },
metadata_id: { type: Number },
value: { type: [ String,Number ] },
errorsMsg: { type: [ String,Number ] },
data() {
return {
inputValue: ''
}
},
methods: {
changeValue( event ){
this.$emit('changeValue', { item_id: this.item_id, metadata_id: this.metadata_id, values: event.target.value } );
onBlur() {
this.$emit('blur');
},
getValue(){
try{
return JSON.parse( this.value );
}catch(e){
console.log('invalid json value');
}
return this.value;
},
getErrors(){
try{
return JSON.parse( this.errorsMsg );
}catch(e){
console.log('invalid json error msg');
}
return this.errorsMsg;
onInput($event) {
this.inputValue = $event;
this.$emit('input', this.inputValue);
}
}
}
</script>
<style scoped>
input[type="text"] {
display: block;
margin: 0;
width: 100%;
border-radius: 6px;
font-family: sans-serif;
font-size: 18px;
appearance: none;
box-shadow: none;
color:green;
}
input[type="text"]:focus {
outline: none;
}
</style>

View File

@ -13,6 +13,7 @@ class Numeric extends Field_Type {
// call field type constructor
parent::__construct();
parent::set_primitive_type('float');
$this->component = 'tainacan-numeric';
}
/**
@ -22,10 +23,10 @@ class Numeric extends Field_Type {
public function render( $itemMetadata ){
return '<tainacan-numeric
metadata_id ="'.$itemMetadata->get_metadata()->get_id().'"
field_id ="'.$itemMetadata->get_field()->get_id().'"
item_id="'.$itemMetadata->get_item()->get_id().'"
value=\''.json_encode( $itemMetadata->get_value() ).'\'
name="'.$itemMetadata->get_metadata()->get_name().'"></tainacan-numeric>';
name="'.$itemMetadata->get_field()->get_name().'"></tainacan-numeric>';
}
/**

View File

@ -1,14 +1,13 @@
<template>
<div class="component">
<label
v-for="option in getOptions"
:for="option.replace(' ','-') + '-checkbox'">
<input
type="radio"
:id="option.replace(' ','-') + '-checkbox'"
:value="option"
v-model="checked"> {{ option }}<br>
</label>
<div>
<b-radio
v-for="option,index in getOptions"
:key="index"
v-model="checked"
@input="onChecked(option)"
:label="option"
:native-value="option"
border>{{ option }}</b-radio>
</div>
</template>
@ -20,8 +19,8 @@
}
},
props: {
name: {
type: String
field: {
type: Object
},
options: {
type: String
@ -29,16 +28,25 @@
},
computed: {
getOptions(){
const values = ( this.options ) ? this.options.split("\n") : '';
return values;
if ( this.options && this.options !== '' ){
return this.options.split("\n");
}
else if (this.field) {
const fields = this.field.field.field_type_options.options;
return ( fields ) ? fields.split("\n") : [];
}
return [];
}
},
methods: {
onChecked(option) {
this.$emit('blur');
this.onInput(this.checked)
},
onInput($event) {
this.inputValue = $event;
this.$emit('input', this.inputValue);
}
}
}
</script>
<style scoped="">
#postcustomstuff table input, #postcustomstuff table select, #postcustomstuff table textarea {
width: auto;
margin: 8px;
}
</style>
</script>

View File

@ -13,6 +13,7 @@ class Radio extends Field_Type {
// call field type constructor
parent::__construct();
parent::set_primitive_type('');
$this->component = 'tainacan-radio';
}
/**
@ -22,7 +23,12 @@ class Radio extends Field_Type {
public function render( $itemMetadata ){
$options = ( isset( $this->options['options'] ) ) ? $this->options['options'] : '';
return '<tainacan-radio options="'.$options.'" name="'.$itemMetadata->get_metadata()->get_name().'"></tainacan-radio>';
return '<tainacan-radio
options="' . $options . '"
field_id ="'.$itemMetadata->get_field()->get_id().'"
item_id="'.$itemMetadata->get_item()->get_id().'"
value=\''.json_encode( $itemMetadata->get_value() ).'\'
options="'.$options.'" name="'.$itemMetadata->get_field()->get_name().'"></tainacan-radio>';
}
/**
@ -33,7 +39,7 @@ class Radio extends Field_Type {
<tr>
<td>
<label><?php echo __('Options','tainacan'); ?></label><br/>
<small><?php echo __('Insert the options, separate by lines for the metadata value','tainacan'); ?></small>
<small><?php echo __('Insert the options, separate by lines for the field value','tainacan'); ?></small>
</td>
<td>
<textarea name="field_type_radio[options]"><?php echo ( isset( $this->options['options'] ) ) ? $this->options['options'] : ''; ?></textarea>

View File

@ -1,77 +1,79 @@
<template>
<div class="component">
<p>{{ name }}</p>
<select
class="form-control"
:disabled="!getOptions"
v-model="manageValue">
<option
v-for="option in getOptions"
:selected="option == ''">
{{ option }}
</option>
</select>
<div>
<b-autocomplete
v-model="selected"
:data="options"
@input="search"
:loading="loading"
field="label"
@select="option => setResults(option) ">
</b-autocomplete>
</div>
</template>
<script>
import store from '../../../js/store/store';
import debounce from 'lodash/debounce'
import axios from '../../../js/axios/axios'
export default {
store,
data(){
return {
selected:''
results:'',
selected:'',
options: [],
loading: false,
collectionId: 0,
inputValue: null
}
},
props: {
name: {
type: String
field: {
type: Object
},
options: {
type: String
},
item_id: {
collection_id: {
type: Number
},
metadata_id: {
type: Number
},
value: {
type: [ String,Number ]
},
},
created(){
this.setInitValueOnStore();
},
computed: {
getOptions(){
const values = ( this.options ) ? this.options.split("\n") : false;
return values;
},
manageValue : {
get(){
let metadata = this.$store.getters['item/getMetadata'].find(metadata => metadata.metadata_id === this.metadata_id );
if( metadata ){
return metadata.values;
}else if( this.value ){
return JSON.parse( this.value );
}
},
set( value ){
this.$store.dispatch('item/sendMetadata', { item_id: this.item_id, metadata_id: this.metadata_id, values: value });
}
}
},
methods: {
setInitValueOnStore(){
if ( this.value ){
this.$store.dispatch('item/setSingleMetadata', { item_id: this.item_id, metadata_id: this.metadata_id, values: JSON.parse( this.value ) });
setResults(option){
if(!option)
return;
this.results = option.value;
this.onChecked()
},
onChecked() {
this.$emit('blur');
this.onInput(this.results)
},
onInput($event) {
this.inputValue = $event;
this.$emit('input', this.inputValue);
},
search(query){
if (query !== '') {
this.loading = true;
this.options = [];
let collectionId = ( this.field && this.field.field.field_type_options.collection_id ) ? this.field.field.field_type_options.collection_id : this.collection_id;
axios.get('/collection/'+collectionId+'/items')
.then( res => {
let result = [];
this.loading = false;
result = res.data.filter(item => {
return item.title.toLowerCase()
.indexOf(query.toLowerCase()) > -1;
});
for (let item of result) {
this.options.push({ label: item.title, value: item.id })
}
})
.catch(error => {
console.log(error);
});
} else {
this.options = [];
}
}
}
}
</script>
<style scoped>
</style>
</script>

View File

@ -15,6 +15,7 @@ class Relationship extends Field_Type {
// call field type constructor
parent::__construct();
parent::set_primitive_type('');
$this->component = 'tainacan-relationship';
}
/**
@ -23,7 +24,12 @@ class Relationship extends Field_Type {
*/
public function render( $itemMetadata ){
return '<tainacan-relationship name="'.$itemMetadata->get_metadata()->get_name().'"></tainacan-relationship>';
return '<tainacan-relationship
collection_id="' . $this->options['collection_id'] . '"
field_id ="'.$itemMetadata->get_field()->get_id().'"
item_id="'.$itemMetadata->get_item()->get_id().'"
value=\''.json_encode( $itemMetadata->get_value() ).'\'
name="'.$itemMetadata->get_field()->get_name().'"></tainacan-relationship>';
}
/**
@ -43,8 +49,8 @@ class Relationship extends Field_Type {
<?php if( $this->options['collection_id'] ): ?>
<tr>
<td>
<label><?php echo __('Metadata for search','tainacan'); ?></label><br/>
<small><?php echo __('Selected metadata to help in the search','tainacan'); ?></small>
<label><?php echo __('Field for search','tainacan'); ?></label><br/>
<small><?php echo __('Selected field to help in the search','tainacan'); ?></small>
</td>
<td>
<?php Helpers\HtmlHelpers::metadata_checkbox_list(
@ -67,7 +73,7 @@ class Relationship extends Field_Type {
<?php if( isset( $this->options['collection_id'] ) ): ?>
<?php
//filter only related metadata
//filter only related field
$args = array( 'meta_query' => array ( array(
'key' => 'field_type',
'value' => 'Tainacan\Field_Types\Relationship',
@ -77,7 +83,7 @@ class Relationship extends Field_Type {
<tr>
<td>
<label><?php echo __('Inverse','tainacan'); ?></label><br/>
<small><?php echo __('Select the relationship inverse for this metadata','tainacan'); ?></small>
<small><?php echo __('Select the relationship inverse for this field','tainacan'); ?></small>
</td>
<td>
<?php Helpers\HtmlHelpers::metadata_dropdown(

View File

@ -1,77 +1,53 @@
<template>
<div class="component">
<p>{{ name }}</p>
<select
class="form-control"
:disabled="!getOptions"
v-model="manageValue">
<div>
<b-select v-model="selected" @input="onChecked()">
<option
v-for="option in getOptions"
:selected="option == ''">
{{ option }}
</option>
</select>
v-for="option,index in getOptions"
:key="index"
:label="option"
:value="option"
border>{{ option }}</option>
</b-select>
</div>
</template>
<script>
import store from '../../../js/store/store';
export default {
store,
data(){
return {
selected:''
}
},
props: {
name: {
type: String
field: {
type: Object
},
options: {
type: String
},
item_id: {
type: Number
},
metadata_id: {
type: Number
},
value: {
type: [ String,Number ]
},
},
created(){
this.setInitValueOnStore();
value: [String, Number, Array],
},
computed: {
getOptions(){
const values = ( this.options ) ? this.options.split("\n") : false;
return values;
},
manageValue : {
get(){
let metadata = this.$store.getters['item/getMetadata'].find(metadata => metadata.metadata_id === this.metadata_id );
if( metadata ){
return metadata.values;
}else if( this.value ){
return JSON.parse( this.value );
}
},
set( value ){
this.$store.dispatch('item/sendMetadata', { item_id: this.item_id, metadata_id: this.metadata_id, values: value });
if ( this.options && this.options !== '' ){
return this.options.split("\n");
}
else if ( this.field && this.field.field.field_type_options.options ) {
const fields = this.field.field.field_type_options.options;
return ( fields ) ? fields.split("\n") : [];
}
return [];
}
},
methods: {
setInitValueOnStore(){
if ( this.value ){
this.$store.dispatch('item/updateMetadata', { item_id: this.item_id, metadata_id: this.metadata_id, values: JSON.parse( this.value ) });
}
onChecked() {
this.$emit('blur');
this.onInput(this.selected)
},
onInput($event) {
this.$emit('input', $event);
}
}
}
</script>
<style scoped>
</style>
</script>

View File

@ -13,6 +13,7 @@ class Selectbox extends Field_Type {
// call field type constructor
parent::__construct();
parent::set_primitive_type('');
$this->component = 'tainacan-selectbox';
}
/**
@ -22,11 +23,12 @@ class Selectbox extends Field_Type {
public function render( $itemMetadata ){
$options = ( isset( $this->options['options'] ) ) ? $this->options['options'] : '';
return '<tainacan-selectbox options="'.$options.'"
metadata_id ="'.$itemMetadata->get_metadata()->get_id().'"
return '<tainacan-selectbox
options="' . $options . '"
field_id ="'.$itemMetadata->get_field()->get_id().'"
item_id="'.$itemMetadata->get_item()->get_id().'"
value=\''.json_encode( $itemMetadata->get_value() ).'\'
name="'.$itemMetadata->get_metadata()->get_name().'"></tainacan-selectbox>';
name="'.$itemMetadata->get_field()->get_name().'"></tainacan-selectbox>';
}
/**
@ -37,7 +39,7 @@ class Selectbox extends Field_Type {
<tr>
<td>
<label><?php echo __('Options','tainacan'); ?></label><br/>
<small><?php echo __('Insert the options, separate by lines for the metadata value','tainacan'); ?></small>
<small><?php echo __('Insert the options, separate by lines for the field value','tainacan'); ?></small>
</td>
<td>
<textarea name="field_type_selectbox[options]"><?php echo ( isset( $this->options['options'] ) ) ? $this->options['options'] : ''; ?></textarea>

View File

@ -0,0 +1,81 @@
<template>
<b-field :label="field.field.name"
:message="getErrorMessage"
:type="fieldTypeMessage">
<div>
<component :is="field.field.field_type_object.component" v-model="inputs[0]" :field="field" @blur="changeValue()"></component>
<div v-if="field.field.multiple == 'yes'">
<div v-if="index > 0" v-for="(input, index) in inputsList " v-bind:key="index" class="multiple-inputs">
<component :is="field.field.field_type_object.component" v-model="inputs[index]" :field="field" @blur="changeValue()"></component><a class="button" v-if="index > 0" @click="removeInput(index)">-</a>
</div>
<a class="button" @click="addInput">+</a>
</div>
</div>
</b-field>
</template>
<script>
import { eventBus } from '../../js/event-bus-web-components'
export default {
name: 'TainacanFormItem',
props: {
field: {}
},
data(){
return {
inputs: [],
fieldTypeMessage:''
}
},
computed: {
inputsList() {
return this.inputs;
},
getErrorMessage() {
let msg = '';
let errors = eventBus.getErrors(this.field.field.id);
if ( errors) {
this.fieldTypeMessage = 'is-danger';
for (let index in errors) {
msg += errors[index] + '\n';
}
} else {
this.fieldTypeMessage = '';
}
return msg;
}
},
created(){
this.getValue();
},
methods: {
changeValue(){
eventBus.$emit('input', { item_id: this.field.item.id, field_id: this.field.field.id, values: this.inputs } );
},
getValue(){
if (this.field.value instanceof Array) {
this.inputs = this.field.value;
if (this.inputs.length == 0)
this.inputs.push('');
} else {
this.field.value == null || this.field.value == undefined ? this.inputs.push('') : this.inputs.push(this.field.value);
}
},
addInput(){
this.inputs.push('');
this.changeValue();
},
removeInput(index) {
this.inputs.splice(index, 1);
this.changeValue();
}
}
}
</script>
<style scoped>
.multiple-inputs {
display: flex;
}
</style>

View File

@ -1,87 +1,30 @@
<template>
<div class="component" :class="{ invalid : hasError }">
<input :placeholder="name" type="text" @change="setValue( $event )" :value="getValue()" >
<p
v-for="error in getErrors()">
{{ error }}
</p>
</div>
<b-input :value="inputValue"
@blur="onBlur"
@input="onInput($event)"></b-input>
</template>
<script>
import store from '../../../js/store/store';
export default {
store,
data(){
data() {
return {
hasError: false
inputValue: ''
}
},
props: {
name: { type: String },
item_id: { type: Number },
metadata_id: { type: Number },
value: { type: [ String,Number ] }
},
created(){
this.setInitValueOnStore();
value: [String, Number, Object],
},
methods: {
getValue(){
let metadata = this.$store.getters['item/getMetadata'].find(metadata => metadata.metadata_id === this.metadata_id );
if( metadata ){
return metadata.values;
}
return '';
onBlur() {
this.$emit('blur');
},
getErrors(){
const messages = [];
const metadata = this.$store.getters['item/getError'].find(error => error.metadata_id === this.metadata_id );
if( metadata && metadata.error ){
const object_messages = metadata.error[0];
for(let error_type in object_messages){
messages.push( object_messages[ error_type ] );
}
this.hasError = true;
}else{
this.hasError = false;
}
return messages;
},
setInitValueOnStore(){
if ( this.value ){
this.$store.dispatch('item/updateMetadata', { item_id: this.item_id, metadata_id: this.metadata_id, values: JSON.parse( this.value ) });
}
},
setValue( event ){
this.$store.dispatch('item/sendMetadata', { item_id: this.item_id, metadata_id: this.metadata_id, values: event.target.value });
onInput($event) {
this.inputValue = $event;
this.$emit('input', this.inputValue);
}
},
created(){
this.inputValue = this.value;
}
}
</script>
<style scoped>
p{
padding:10px 10px 10px 5px;
font-style: oblique ;
}
input[type="text"] {
background: transparent;
font-size:18px;
padding:10px 10px 10px 5px;
display:block;
width:300px;
border-top-color:transparent;
border-bottom:1px solid #757575;
}
input[type="text"]:focus {
outline: none;
}
.component.invalid input{
background: #ff9999;
}
</style>
</script>

View File

@ -13,6 +13,7 @@ class Text extends Field_Type {
// call field type constructor
parent::__construct();
parent::set_primitive_type('string');
$this->component = 'tainacan-text';
}
/**
@ -21,10 +22,10 @@ class Text extends Field_Type {
*/
public function render( $itemMetadata ){
return '<tainacan-text metadata_id ="'.$itemMetadata->get_metadata()->get_id().'"
return '<tainacan-text field_id ="'.$itemMetadata->get_field()->get_id().'"
item_id="'.$itemMetadata->get_item()->get_id().'"
value=\''.json_encode( $itemMetadata->get_value() ).'\'
name="'.$itemMetadata->get_metadata()->get_name().'"></tainacan-text>';
name="'.$itemMetadata->get_field()->get_name().'"></tainacan-text>';
}
/**

View File

@ -1,31 +1,25 @@
<template>
<div class="component">
<p>{{ name }}</p>
<textarea cols="30" rows="10"></textarea>
</div>
<b-input type="textarea"
:value="inputValue"
@blur="onBlur"
@input="onInput($event)"></b-input>
</template>
<script>
export default {
props: {
name: { type: String }
data() {
return {
inputValue: ''
}
},
methods: {
onBlur() {
this.$emit('blur');
},
onInput($event) {
this.inputValue = $event;
this.$emit('input', this.inputValue);
}
}
}
</script>
<style scoped>
textarea {
display: block;
margin: 0;
width: 100%;
border-radius: 6px;
font-family: sans-serif;
font-size: 18px;
appearance: none;
box-shadow: none;
color:green;
}
textarea:focus {
outline: none;
}
</style>

View File

@ -13,6 +13,7 @@ class Textarea extends Field_Type {
// call field type constructor
parent::__construct();
parent::set_primitive_type('string');
$this->component = 'tainacan-textarea';
}
/**
@ -21,7 +22,11 @@ class Textarea extends Field_Type {
*/
public function render( $itemMetadata ){
return '<tainacan-textarea name="'.$itemMetadata->get_metadata()->get_name().'"></tainacan-textarea>';
return '<tainacan-textarea
field_id ="'.$itemMetadata->get_field()->get_id().'"
item_id="'.$itemMetadata->get_item()->get_id().'"
value=\''.json_encode( $itemMetadata->get_value() ).'\'
name="'.$itemMetadata->get_field()->get_name().'"></tainacan-textarea>';
}
/**

View File

@ -9,7 +9,7 @@ abstract class Filter_Type {
private $supported_types = [];
public $options;
abstract function render( $metadata );
abstract function render( $field );
public function get_supported_types(){
return $this->supported_types;

View File

@ -14,7 +14,7 @@ class List_Filter extends Filter_Type {
}
/**
* @param $metadata
* @param $field
* @return string
*/

View File

@ -14,7 +14,7 @@ class Range extends Filter_Type {
}
/**
* @param $metadata
* @param $field
* @return string
*/
public function render( $filter ){

View File

@ -21,30 +21,67 @@ class Collections extends Repository {
*/
public function get_map() {
return apply_filters('tainacan-get-map-'.$this->get_name(), [
'name' => [
'map' => 'post_title',
'name' => [
'map' => 'post_title',
'title' => __('Name', 'tainacan'),
'type' => 'string',
'description'=> __('Name of the collection', 'tainacan'),
'validation' => v::stringType(),
'type' => 'string',
'description' => __('Name of the collection', 'tainacan'),
'validation' => v::stringType()->notEmpty(),
],
'status' => [
'map' => 'post_status',
'title' => __('Status', 'tainacan'),
'type' => 'string',
'default' => '',
'description' => __('The posts status', 'tainacan')
],
'author_id' => [
'map' => 'post_author',
'title' => __('Author', 'tainacan'),
'type' => 'string',
'description' => __('The collection author\'s user ID (numeric string)', 'tainacan')
],
'creation_date' => [
'map' => 'post_date',
'title' => __('Creation Date', 'tainacan'),
'type' => 'string',
'description' => __('The collection creation date', 'tainacan')
],
'modification_date' => [
'map' => 'post_modified',
'title' => __('Modification Date', 'tainacan'),
'type' => 'string',
'description' => __('The collection modification date', 'tainacan')
],
'url' => [
'map' => 'guid',
'title' => __('Collection URL', 'tainacan'),
'type' => 'string',
'description' => __('The collection URL', 'tainacan')
],
'featured_image' => [
'map' => 'thumbnail',
'title' => __('Featured Image', 'tainacan'),
'type' => 'string',
'description' => __('The collection thumbnail URL')
],
'order' => [
'map' => 'menu_order',
'title' => __('Order', 'tainacan'),
'title' => __('Order', 'tainacan'),
'type' => 'string',
'description'=> __('Collection order. Field used if collections are manually ordered', 'tainacan'),
//'validation' => v::stringType(),
],
'parent' => [
'map' => 'post_parent',
'title' => __('Parent Collection', 'tainacan'),
'title' => __('Parent Collection', 'tainacan'),
'type' => 'integer',
'description'=> __('Parent collection ID', 'tainacan'),
//'validation' => v::stringType(),
],
'description' => [
'map' => 'post_content',
'title' => __('Description', 'tainacan'),
'title' => __('Description', 'tainacan'),
'type' => 'string',
'description'=> __('Collection description', 'tainacan'),
'default' => '',
@ -52,7 +89,7 @@ class Collections extends Repository {
],
'slug' => [
'map' => 'post_name',
'title' => __('Slug', 'tainacan'),
'title' => __('Slug', 'tainacan'),
'type' => 'string',
'description'=> __('A unique and santized string representation of the collection, used to build the collection URL', 'tainacan'),
//'validation' => v::stringType(),
@ -60,7 +97,7 @@ class Collections extends Repository {
'default_orderby' => [
'map' => 'meta',
'title' => __('Default Order field', 'tainacan'),
'title' => __('Default Order field', 'tainacan'),
'type' => 'string',
'description'=> __('Default property items in this collections will be ordered by', 'tainacan'),
'default' => 'name',
@ -68,7 +105,7 @@ class Collections extends Repository {
],
'default_order' => [
'map' => 'meta',
'title' => __('Default order', 'tainacan'),
'title' => __('Default order', 'tainacan'),
'description'=> __('Default order for items in this collection. ASC or DESC', 'tainacan'),
'type' => 'string',
'default' => 'ASC',
@ -76,18 +113,25 @@ class Collections extends Repository {
],
'columns' => [
'map' => 'meta',
'title' => __('Columns', 'tainacan'),
'title' => __('Columns', 'tainacan'),
'type' => 'string',
'description'=> __('List of collections property that will be displayed in the table view', 'tainacan'),
//'validation' => v::stringType(),
],
'default_view_mode' => [
'map' => 'meta',
'title' => __('Default view mode', 'tainacan'),
'title' => __('Default view mode', 'tainacan'),
'type' => 'string',
'description'=> __('Collection default visualization mode', 'tainacan'),
//'validation' => v::stringType(),
],
'fields_order' => [
'map' => 'meta',
'title' => __('Ordination fields', 'tainacan'),
'type' => 'string',
'description'=> __('Collection fields ordination', 'tainacan'),
//'validation' => v::stringType(),
],
/*
Isnt it just post status private?
@ -104,7 +148,7 @@ class Collections extends Repository {
* Properties yet to be implemented
*
* Moderators (a property attached to the collection or to the user?)
* geo metadata?
* geo field?
*
*
*
@ -171,28 +215,19 @@ class Collections extends Repository {
* @see \Tainacan\Repositories\Repository::insert()
*/
public function insert($collection){
global $Tainacan_Fields;
$this->pre_update_moderators($collection);
$new_collection = parent::insert($collection);
$Tainacan_Fields->register_core_fields( $new_collection );
$collection->register_collection_item_post_type();
$this->update_moderators($new_collection);
return $new_collection;
}
public function update($object){
$map = $this->get_map();
$entity = [];
foreach ($object as $key => $value) {
if($key != 'ID') {
$entity[$map[$key]['map']] = $value ;
} elseif ($key == 'ID'){
$entity[$key] = (int) $value;
}
}
return new Entities\Collection(wp_update_post($entity));
public function update($object, $new_values = null){
return $this->insert($object);
}
/**
@ -232,7 +267,6 @@ class Collections extends Repository {
} elseif(is_array($args)) {
$args = array_merge([
'posts_per_page' => -1,
'post_status' => 'publish',
], $args);
$args = $this->parse_fetch_args($args);

View File

@ -0,0 +1,568 @@
<?php
namespace Tainacan\Repositories;
use Tainacan\Entities;
use Tainacan\Field_Types;
defined( 'ABSPATH' ) or die( 'No script kiddies please!' );
use \Respect\Validation\Validator as v;
/**
* Class Fields
*/
class Fields extends Repository {
public $entities_type = '\Tainacan\Entities\Field';
protected $default_metadata = 'default';
public $field_types = [];
public $core_fields = [
'Tainacan\Field_Types\Core_Title',
'Tainacan\Field_Types\Core_Description'
];
/**
* Register specific hooks for field repository
*/
function __construct() {
parent::__construct();
add_filter('pre_trash_post', array( &$this, 'disable_delete_core_fields' ), 10, 2 );
add_filter('pre_delete_post', array( &$this, 'force_delete_core_fields' ), 10, 3 );
}
public function get_map() {
return apply_filters('tainacan-get-map-'.$this->get_name(), [
'name' => [
'map' => 'post_title',
'title' => __('Name', 'tainacan'),
'type' => 'string',
'description'=> __('Name of the field', 'tainacan'),
'on_error' => __('The name should be a text value and not empty', 'tainacan'),
'validation' => v::stringType()->notEmpty(),
],
'slug' => [
'map' => 'post_name',
'title' => __('Slug', 'tainacan'),
'type' => 'string',
'description'=> __('A unique and santized string representation of the field', 'tainacan'),
//'validation' => v::stringType(),
],
'order' => [
'map' => 'menu_order',
'title' => __('Order', 'tainacan'),
'type' => 'string',
'description'=> __('Field order. Field used if collections are manually ordered', 'tainacan'),
'on_error' => __('The menu order should be a numeric value', 'tainacan'),
//'validation' => v::numeric(),
],
'parent' => [
'map' => 'parent',
'title' => __('Parent', 'tainacan'),
'type' => 'integer',
'description'=> __('Parent field', 'tainacan'),
//'on_error' => __('The Parent should be numeric value', 'tainacan'),
//'validation' => v::numeric(),
],
'description' => [
'map' => 'post_content',
'title' => __('Description', 'tainacan'),
'type' => 'string',
'description'=> __('The field description', 'tainacan'),
'default' => '',
//'on_error' => __('The description should be a text value', 'tainacan'),
//'validation' => v::stringType()->notEmpty(),
],
'field_type' => [
'map' => 'meta',
'title' => __('Type', 'tainacan'),
'type' => 'string',
'description'=> __('The field type', 'tainacan'),
'on_error' => __('Field type is empty', 'tainacan'),
'validation' => v::stringType()->notEmpty(),
],
'required' => [
'map' => 'meta',
'title' => __('Required', 'tainacan'),
'type' => 'string',
'description'=> __('The field is required', 'tainacan'),
'on_error' => __('Field required field is invalid', 'tainacan'),
'validation' => v::stringType()->in(['yes', 'no']), // yes or no
'default' => 'no'
],
'collection_key' => [
'map' => 'meta',
'title' => __('Collection key', 'tainacan'),
'type' => 'string',
'description'=> __('Field value should not be repeated', 'tainacan'),
'on_error' => __('Collection key is invalid', 'tainacan'),
'validation' => v::stringType()->in(['yes', 'no']), // yes or no
'default' => 'no'
],
'multiple' => [
'map' => 'meta',
'title' => __('Multiple', 'tainacan'),
'type' => 'string',
'description'=> __('Allow multiple fields for the field', 'tainacan'),
'on_error' => __('Multiple fields is invalid', 'tainacan'),
'validation' => v::stringType()->in(['yes', 'no']), // yes or no. It cant be multiple if its collection_key
'default' => 'no'
],
'cardinality' => [
'map' => 'meta',
'title' => __('Cardinality', 'tainacan'),
'type' => 'string',
'description'=> __('Number of multiples possible fields', 'tainacan'),
'on_error' => __('The number of fields not allowed', 'tainacan'),
'validation' => v::numeric()->positive(),
'default' => 1
],
'privacy' => [
'map' => 'meta',
'title' => __('Privacy', 'tainacan'),
'type' => 'string',
'description'=> __('The field should be omitted in item view', 'tainacan'),
'on_error' => __('Privacy is invalid', 'tainacan'),
'validation' => v::stringType()->in(['yes', 'no']), // yes or no. It cant be multiple if its collection_key
'default' => 'no'
],
'mask' => [
'map' => 'meta',
'title' => __('Mask', 'tainacan'),
'type' => 'string',
'description'=> __('The mask to be used in the field', 'tainacan'),
//'on_error' => __('Mask is invalid', 'tainacan'),
//'validation' => ''
],
'default_value' => [
'map' => 'meta',
'title' => __('Default value', 'tainacan'),
'type' => 'string',
'description'=> __('The value default fot the field', 'tainacan'),
],
'field_type_options' => [ // not showed in form
'map' => 'meta',
'title' => __('Field Type options', 'tainacan'),
'type' => 'string',
'description'=> __('Options specific for field type', 'tainacan'),
// 'validation' => ''
],
'collection_id' => [ // not showed in form
'map' => 'meta',
'title' => __('Collection', 'tainacan'),
'type' => 'integer',
'description'=> __('The collection ID', 'tainacan'),
//'validation' => ''
],
'can_delete' => [
'map' => 'meta',
'title' => __('Can delete', 'tainacan'),
'type' => 'string',
'description'=> __('The field can be deleted', 'tainacan'),
'on_error' => __('Can delete is invalid', 'tainacan'),
'validation' => v::stringType()->in(['yes', 'no']), // yes or no. It cant be multiple if its collection_key
'default' => 'yes'
],
]);
}
public function register_post_type() {
$labels = array(
'name' => __('Field', 'tainacan'),
'singular_name' => __('Field', 'tainacan'),
'add_new' => __('Add new', 'tainacan'),
'add_new_item' => __('Add new Field', 'tainacan'),
'edit_item' => __('Edit Field', 'tainacan'),
'new_item' => __('New Field', 'tainacan'),
'view_item' => __('View Field', 'tainacan'),
'search_items' => __('Search Field', 'tainacan'),
'not_found' => __('No Field found ', 'tainacan'),
'not_found_in_trash' => __('No Field found in trash', 'tainacan'),
'parent_item_colon' => __('Parent Field:', 'tainacan'),
'menu_name' => __('Fields', 'tainacan')
);
$args = array(
'labels' => $labels,
'hierarchical' => true,
//'supports' => array('title'),
//'taxonomies' => array(self::TAXONOMY),
'public' => true,
'show_ui' => tnc_enable_dev_wp_interface(),
'show_in_menu' => tnc_enable_dev_wp_interface(),
//'menu_position' => 5,
//'show_in_nav_menus' => false,
'publicly_queryable' => true,
'exclude_from_search' => true,
'has_archive' => true,
'query_var' => true,
'can_export' => true,
'rewrite' => true,
'map_meta_cap' => true,
'capability_type' => Entities\Field::get_post_type(),
'supports' => [
'title',
'editor',
'page-attributes'
]
);
register_post_type(Entities\Field::get_post_type(), $args);
}
/**
* constant used in default field in attribute collection_id
*
* @return string the value of constant
*/
public function get_default_metadata_attribute(){
return $this->default_metadata;
}
/**
* register field types class on array of types
*
* @param $class_name string | object The class name or the instance
*/
public function register_field_type( $class_name ){
if( is_object( $class_name ) ){
$class_name = get_class( $class_name );
}
if(!in_array( $class_name, $this->field_types)){
$this->field_types[] = $class_name;
}
}
/**
* register field types class on array of types
*
* @param $class_name string | object The class name or the instance
*/
public function unregister_field_type( $class_name ){
if( is_object( $class_name ) ){
$class_name = get_class( $class_name );
}
$key = array_search( $class_name, $this->field_types );
if($key !== false){
unset( $this->field_types[$key] );
}
}
/**
* fetch field based on ID or WP_Query args
*
* field are stored as posts. Check WP_Query docs
* to learn all args accepted in the $args parameter (@see https://developer.wordpress.org/reference/classes/wp_query/)
* You can also use a mapped property, such as name and description, as an argument and it will be mapped to the
* appropriate WP_Query argument
*
* @param array $args WP_Query args || int $args the field id
* @param string $output The desired output format (@see \Tainacan\Repositories\Repository::fetch_output() for possible values)
*
* @return \WP_Query|Array an instance of wp query OR array of entities;
* @throws \Exception
*/
public function fetch( $args, $output = null ) {
if( is_numeric($args) ){
$existing_post = get_post($args);
if ($existing_post instanceof \WP_Post) {
return new Entities\Field($existing_post);
} else {
return [];
}
} elseif (is_array($args)) {
$args = array_merge([
'posts_per_page' => -1,
'post_status' => 'publish'
], $args);
$args['post_type'] = Entities\Field::get_post_type();
$wp_query = new \WP_Query($args);
return $this->fetch_output($wp_query, $output);
}
}
/**
* fetch field by collection, searches all field available
*
* @param Entities\Collection $collection
* @param array $args WP_Query args plus disabled_fields
* @param string $output The desired output format (@see \Tainacan\Repositories\Repository::fetch_output() for possible values)
*
* @return Array Entities\Field
* @throws \Exception
*/
public function fetch_by_collection(Entities\Collection $collection, $args = [], $output = null){
$collection_id = $collection->get_id();
//get parent collections
$parents = get_post_ancestors( $collection_id );
//insert the actual collection
$parents[] = $collection_id;
//search for default field
$parents[] = $this->get_default_metadata_attribute();
$meta_query = array(
'key' => 'collection_id',
'value' => $parents,
'compare' => 'IN',
);
if( isset( $args['meta_query'] ) ){
$args['meta_query'][] = $meta_query;
}else{
$args['meta_query'] = array( $meta_query );
}
return $this->order_result(
$this->fetch( $args, $output ),
$collection,
isset( $args['disabled_fields'] ) ? $args['disabled_fields'] : false
);
}
/**
* Ordinate the result from fetch response if $collection has an ordination,
* fields not ordinated appear on the end of the list
*
*
* @param $result Response from method fetch
* @param Entities\Collection $collection
* @param bool $disabled_fields Disabled fields wont appear on list collection fields
* @return array or WP_Query ordinate
*/
public function order_result( $result, Entities\Collection $collection, $disabled_fields = false ){
$order = $collection->get_fields_order();
if($order) {
$order = ( is_array($order) ) ? $order : unserialize($order);
if ( is_array($result) ){
$result_ordinate = [];
$not_ordinate = [];
foreach ( $result as $item ) {
$id = $item->WP_Post->ID;
$index = array_search ( $id , array_column( $order , 'id') );
if( $index !== false ) {
// skipping fields disabled if the arg is set
if( $disabled_fields && !$order[$index]['enable'] ){
continue;
}
$result_ordinate[$index] = $item;
} else {
$not_ordinate[] = $item;
}
}
ksort ( $result_ordinate );
$result_ordinate = array_merge( $result_ordinate, $not_ordinate );
return $result_ordinate;
}
// if the result is a wp query object
else {
$posts = $result->posts;
$result_ordinate = [];
$not_ordinate = [];
foreach ( $posts as $item ) {
$id = $item->ID;
$index = array_search ( $id , array_column( $order , 'id') );
if( $index !== false ){
$result_ordinate[$index] = $item;
} else {
$not_ordinate[] = $item;
}
}
ksort ( $result_ordinate );
$result->posts = $result_ordinate;
$result->posts = array_merge( $result->posts, $not_ordinate );
return $result;
}
}
return $result;
}
/**
* @param $object
* @param $new_values
*
* @return mixed|string|Entities\Entity
* @throws \Exception
*/
public function update($object, $new_values = null){
return $this->insert($object);
}
public function delete($object){
}
/**
* fetch all registered field type classes
*
* Possible outputs are:
* CLASS (default) - returns the Class name of of field types registered
* NAME - return an Array of the names of field types registered
*
* @param $output string CLASS | NAME
* @return array of Entities\Field_Types\Field_Type classes path name
*/
public function fetch_field_types( $output = 'CLASS'){
$return = [];
do_action('register_field_types');
if( $output === 'NAME' ){
foreach ($this->field_types as $field_type) {
$return[] = str_replace('Tainacan\Field_Types\\','', $field_type);
}
return $return;
}
return $this->field_types;
}
/**
* @param Entities\Collection $collection
* @return array
* @throws \ErrorException
*/
public function register_core_fields( Entities\Collection $collection ){
$fields = $this->get_core_fields( $collection );
// TODO: create a better way to retrieve this data
$data_core_fields = [
'core_description' => [
'name' => 'Description',
'description' => 'description',
'collection_id' => $collection->get_id(),
'field_type' => 'Tainacan\Field_Types\Core_Description',
'status' => 'publish'
],
'core_title' => [
'name' => 'Title',
'description' => 'title',
'collection_id' => $collection->get_id(),
'field_type' => 'Tainacan\Field_Types\Core_Title',
'status' => 'publish'
]
];
if( $collection->get_parent() !== 0 ){
return false;
}
foreach ( $data_core_fields as $index => $data_core_field ) {
if( empty( $fields ) ){
$this->insert_array_field( $data_core_field );
} else {
$exists = false;
foreach ( $fields as $field ){
if ( $field->get_field_type() === $data_core_field['field_type'] ) {
$exists = true;
}
}
if( !$exists ){
$this->insert_array_field( $data_core_field );
}
}
}
}
/**
* block user from remove core fields
*
* @param $before wordpress pass a null value
* @param $post the post which is moving to trash
* @return null/bool
* @throws \ErrorException
*/
public function disable_delete_core_fields( $before, $post ){
$field = $this->fetch( $post->ID );
if ( $field && in_array( $field->get_field_type(), $this->core_fields ) && is_numeric($field->get_collection_id()) ) {
return false;
}
}
/**
* block user from remove core fields ( if use wp_delete_post)
*
* @param $before wordpress pass a null value
* @param $post the post which is deleting
* @param $force_delete a boolean that force the deleting
* @return null /bool
* @internal param The $post_id post ID which is deleting
*/
public function force_delete_core_fields( $before, $post, $force_delete ){
$field = $this->fetch( $post->ID );
if ( $field && in_array( $field->get_field_type(), $this->core_fields ) && is_numeric($field->get_collection_id()) ) {
return false;
}
}
/**
* returns all core items from a specific collection
*
* @param Entities\Collection $collection
* @return Array|\WP_Query
*/
public function get_core_fields( Entities\Collection $collection ){
$args = [];
$meta_query = array(
array(
'key' => 'collection_id',
'value' => $collection->get_id(),
'compare' => 'IN',
),
array(
'key' => 'field_type',
'value' => $this->core_fields,
'compare' => 'IN',
)
);
$args['meta_query'] = $meta_query;
return $this->fetch( $args, 'OBJECT' );
}
/**
* create a field entity and insert by an associative array ( attribute => value )
*
* @param Array $data the array of attributes to insert a field
* @return int the field id inserted
* @throws \ErrorException
*/
public function insert_array_field( $data ){
$field = new Entities\Field();
foreach ( $data as $attribute => $value ) {
$set_ = 'set_' . $attribute;
$field->$set_( $value );
}
if ( $field->validate( )) {
$field = $this->insert( $field );
return $field->get_id();
} else {
throw new \ErrorException('The entity wasn\'t validated.' . print_r( $field->get_errors(), true));
}
}
}

View File

@ -62,11 +62,11 @@ class Filters extends Repository {
'description'=> __('Filter color', 'tainacan'),
'validation' => ''
],
'metadata' => [
'field' => [
'map' => 'meta',
'title' => __('Metadata', 'tainacan'),
'title' => __('Field', 'tainacan'),
'type' => 'integer',
'description'=> __('Filter metadata', 'tainacan'),
'description'=> __('Filter field', 'tainacan'),
'validation' => ''
],
]);
@ -116,30 +116,30 @@ class Filters extends Repository {
/**
* @param Entities\Metadata $metadata
* @param Entities\Field $field
* @return int
*
public function insert($metadata) {
public function insert($field) {
// First iterate through the native post properties
$map = $this->get_map();
foreach ($map as $prop => $mapped) {
if ($mapped['map'] != 'meta' && $mapped['map'] != 'meta_multi') {
$metadata->WP_Post->{$mapped['map']} = $metadata->get_mapped_property($prop);
$field->WP_Post->{$mapped['map']} = $field->get_mapped_property($prop);
}
}
// save post and get its ID
$metadata->WP_Post->post_type = Entities\Filter::get_post_type();
$metadata->WP_Post->post_status = 'publish';
$id = wp_insert_post($metadata->WP_Post);
$metadata->WP_Post = get_post($id);
$field->WP_Post->post_type = Entities\Filter::get_post_type();
$field->WP_Post->post_status = 'publish';
$id = wp_insert_post($field->WP_Post);
$field->WP_Post = get_post($id);
// Now run through properties stored as postmeta
foreach ($map as $prop => $mapped) {
if ($mapped['map'] == 'meta') {
update_post_meta($id, $prop, $metadata->get_mapped_property($prop));
update_post_meta($id, $prop, $field->get_mapped_property($prop));
} elseif ($mapped['map'] == 'meta_multi') {
$values = $metadata->get_mapped_property($prop);
$values = $field->get_mapped_property($prop);
delete_post_meta($id, $prop);
@ -152,7 +152,7 @@ class Filters extends Repository {
}
// return a brand new object
return new Entities\Filter($metadata->WP_Post);
return new Entities\Filter($field->WP_Post);
}*/
/**
@ -168,20 +168,8 @@ class Filters extends Repository {
return new Entities\Filter(wp_trash_post($args[0]));
}
public function update($object){
$map = $this->get_map();
$entity = [];
foreach ($object as $key => $value) {
if($key != 'ID') {
$entity[$map[$key]['map']] = $value ;
} elseif ($key == 'ID'){
$entity[$key] = (int) $value;
}
}
return new Entities\Filter(wp_update_post($entity));
public function update($object, $new_values = null){
return $this->insert($object);
}
/**
@ -241,7 +229,7 @@ class Filters extends Repository {
/**
* fetch only supported filters for the type specified
*
* @param ( string || array ) $types Primitve types of metadata ( float, string, int)
* @param ( string || array ) $types Primitve types of field ( float, string, int)
* @return array Filters supported by the primitive types passed in $types
*/
public function fetch_supported_filter_types($types){

View File

@ -7,20 +7,27 @@ defined( 'ABSPATH' ) or die( 'No script kiddies please!' );
class Item_Metadata extends Repository {
public $entities_type = '\Tainacan\Entities\Item_Metadata_Entity';
public function insert($item_metadata) {
$unique = !$item_metadata->is_multiple();
if ($unique) {
add_post_meta($item_metadata->item->get_id(), $item_metadata->metadata->get_id(), wp_slash( $item_metadata->get_value() ) );
$field_type = $item_metadata->get_field()->get_field_type_object();
if ($field_type->core) {
$this->save_core_field_value($item_metadata);
} else {
update_post_meta($item_metadata->item->get_id(), $item_metadata->field->get_id(), wp_slash( $item_metadata->get_value() ) );
}
} else {
delete_post_meta($item_metadata->item->get_id(), $item_metadata->metadata->get_id());
delete_post_meta($item_metadata->item->get_id(), $item_metadata->field->get_id());
if (is_array($item_metadata->get_value())){
$values = $item_metadata->get_value();
foreach ($values as $value){
add_post_meta($item_metadata->item->get_id(), $item_metadata->metadata->get_id(), wp_slash( $value ));
add_post_meta($item_metadata->item->get_id(), $item_metadata->field->get_id(), wp_slash( $value ));
}
}
}
@ -28,34 +35,11 @@ class Item_Metadata extends Repository {
do_action('tainacan-insert', $item_metadata);
do_action('tainacan-insert-Item_Metadata_Entity', $item_metadata);
return new Entities\Item_Metadata_Entity($item_metadata->get_item(), $item_metadata->get_metadata());
}
public function update($item_metadata){
$unique = !$item_metadata->is_multiple();
if ($unique) {
update_post_meta($item_metadata->item->get_id(), $item_metadata->metadata->get_id(), wp_slash( $item_metadata->get_value() ) );
} else {
delete_post_meta($item_metadata->item->get_id(), $item_metadata->metadata->get_id());
if (is_array($item_metadata->get_value())){
$values = $item_metadata->get_value();
foreach ($values as $value){
update_post_meta($item_metadata->item->get_id(), $item_metadata->metadata->get_id(), wp_slash( $value ));
}
}
}
do_action('tainacan-update', $item_metadata);
do_action('tainacan-update-Item_Metadata_Entity', $item_metadata);
return new Entities\Item_Metadata_Entity($item_metadata->get_item(), $item_metadata->get_metadata());
return new Entities\Item_Metadata_Entity($item_metadata->get_item(), $item_metadata->get_field());
}
/**
* Delete Item Metadata
* Delete Item Field
*
* @param $item_metadata
*
@ -66,22 +50,40 @@ class Item_Metadata extends Repository {
// $values = $item_metadata->get_value();
//
// foreach ($values as $value){
// delete_post_meta($item_metadata->item->get_id(), $item_metadata->metadata->get_id(), wp_slash($value));
// delete_post_meta($item_metadata->item->get_id(), $item_metadata->field->get_id(), wp_slash($value));
// }
// } else {
// delete_post_meta($item_metadata->item->get_id(), $item_metadata->metadata->get_id(), wp_slash($item_metadata->get_value()));
// delete_post_meta($item_metadata->item->get_id(), $item_metadata->field->get_id(), wp_slash($item_metadata->get_value()));
// }
}
/**
* Fetch Item Metadata objects related to an Item
*
* @param Entities\Item $object
* @return array
*/
public function save_core_field_value(\Tainacan\Entities\Item_Metadata_Entity $item_metadata) {
$field_type = $item_metadata->get_field()->get_field_type_object();
if ($field_type->core) {
$item = $item_metadata->get_item();
$set_method = 'set_' . $field_type->related_mapped_prop;
$value = $item_metadata->get_value();
$item->$set_method( is_array( $value ) ? $value[0] : $value );
if ($item->validate_core_fields()) {
global $Tainacan_Items;
$Tainacan_Items->insert($item);
} else {
throw new \Exception('Item metadata should be validated beforehand');
}
}
}
/**
* Fetch Item Field objects related to an Item
*
* @param Entities\Item $object
*
* @return array
* @throws \Exception
*/
public function fetch($object, $output = null ){
if($object instanceof Entities\Item){
global $Tainacan_Items, $Tainacan_Metadatas;
global $Tainacan_Items, $Tainacan_Fields;
$collection = $object->get_collection();
@ -89,7 +91,7 @@ class Item_Metadata extends Repository {
return [];
}
$meta_list = $Tainacan_Metadatas->fetch_by_collection($collection, [], 'OBJECT' );
$meta_list = $Tainacan_Fields->fetch_by_collection($collection, [], 'OBJECT' );
$return = [];
@ -106,20 +108,38 @@ class Item_Metadata extends Repository {
}
/**
* Get the value for a Item metadata.
* Get the value for a Item field.
*
* @param Entities\Item_Metadata_Entity $item_metadata
* @return mixed
*/
public function get_value(Entities\Item_Metadata_Entity $item_metadata) {
$unique = ! $item_metadata->is_multiple();
return get_post_meta($item_metadata->item->get_id(), $item_metadata->metadata->get_id(), $unique);
$field_type = $item_metadata->get_field()->get_field_type_object();
if ($field_type->core) {
$item = $item_metadata->get_item();
$get_method = 'get_' . $field_type->related_mapped_prop;
return $item->$get_method();
} else {
return get_post_meta($item_metadata->item->get_id(), $item_metadata->field->get_id(), $unique);
}
}
public function register_post_type() { }
public function get_map() { return []; }
public function get_default_properties($map) { return []; }
/**
* @param $object
*
* @return mixed
*/
public function update( $object, $new_values = null ) {
return $this->insert($object);
}
}

View File

@ -16,8 +16,15 @@ class Items extends Repository {
'type' => 'string',
'description'=> __('Title of the item', 'tainacan'),
'on_error' => __('The title should be a text value and not empty', 'tainacan'),
'validation' => v::stringType()->notEmpty()
'validation' => v::stringType()->notEmpty(),
],
'status' => [
'map' => 'post_status',
'title' => __('Status', 'tainacan'),
'type' => 'string',
'default' => 'draft',
'description' => __('The posts status', 'tainacan')
],
'description' => [
'map' => 'post_content',
'title' => __('Description', 'tainacan'),
@ -33,8 +40,38 @@ class Items extends Repository {
'description'=> __('The collection ID', 'tainacan'),
'validation' => ''
],
'author_id' => [
'map' => 'post_author',
'title' => __('Author', 'tainacan'),
'type' => 'string',
'description' => __('The collection author\'s user ID (numeric string)', 'tainacan')
],
'creation_date' => [
'map' => 'post_date',
'title' => __('Creation Date', 'tainacan'),
'type' => 'string',
'description' => __('The collection creation date', 'tainacan')
],
'modification_date' => [
'map' => 'post_modified',
'title' => __('Modification Date', 'tainacan'),
'type' => 'string',
'description' => __('The collection modification date', 'tainacan')
],
'url' => [
'map' => 'guid',
'title' => __('Collection URL', 'tainacan'),
'type' => 'string',
'description' => __('The collection URL', 'tainacan')
],
'featured_image' => [
'map' => 'thumbnail',
'title' => __('Featured Image', 'tainacan'),
'type' => 'string',
'description' => __('The collection thumbnail URL')
],
//'collection' => 'relation...',
// metadata .. metadata...
// field .. field...
]);
}
@ -69,7 +106,7 @@ class Items extends Repository {
public function insert($item) {
global $Tainacan_Metadatas;
global $Tainacan_Fields;
$map = $this->get_map();
@ -91,7 +128,7 @@ class Items extends Repository {
// save post and geet its ID
$item->WP_Post->post_type = $cpt;
$item->WP_Post->post_status = 'publish';
//$item->WP_Post->post_status = 'publish';
$id = wp_insert_post($item->WP_Post);
$item->WP_Post = get_post($id);
@ -113,14 +150,6 @@ class Items extends Repository {
}
}
// save metadata
$metadata = $item->get_metadata();
global $Tainacan_Item_Metadata;
foreach ($metadata as $meta) {
$Tainacan_Item_Metadata->insert($meta);
}
do_action('tainacan-insert', $item);
do_action('tainacan-insert-Item', $item);
@ -165,36 +194,53 @@ class Items extends Repository {
if (is_numeric($collections)){
$collections = $Tainacan_Collections->fetch($collections);
}
$collections_objects = [];
$cpt = [];
if ($collections instanceof Entities\Collection) {
$cpt = $collections->get_db_identifier();
$collections_objects[] = $collections;
} elseif (is_array($collections)) {
$cpt = [];
foreach ($collections as $collection) {
if (is_numeric($collection)){
$collection = $Tainacan_Collections->fetch($collection);
foreach ($collections as $col) {
if (is_numeric($col)){
$col = $Tainacan_Collections->fetch($col);
}
if ($collection instanceof Entities\Collection){
$cpt[] = $collection->get_db_identifier();
if ($col instanceof Entities\Collection){
$collections_objects[] = $col;
}
}
} else {
return [];
}
foreach ($collections_objects as $collection) {
/**
* If no specific status is defined in the query, WordPress will fetch
* public items and private items for users withe the correct permission.
*
* If a collection is private, it must have the same behavior, despite its
* items are public or not.
*/
if (!isset($args['post_status'])) {
$status_obj = get_post_status_object( $collection->get_status() );
if ( $status_obj->public || current_user_can( $collection->cap->read_private_posts ) ) {
$cpt[] = $collection->get_db_identifier();
}
} else {
$cpt[] = $collection->get_db_identifier();
}
}
if (empty($cpt)){
return [];
$cpt[] = 'please-return-nothing';
}
//TODO: get collection order and order by options
$args = $this->parse_fetch_args($args);
$args = array_merge([
'post_status' => 'publish',
], $args);
$args['post_type'] = $cpt;
@ -202,20 +248,8 @@ class Items extends Repository {
return $this->fetch_output($wp_query, $output);
}
public function update($object){
$map = $this->get_map();
$entity = [];
foreach ($object as $key => $value) {
if($key != 'ID') {
$entity[$map[$key]['map']] = $value ;
} elseif ($key == 'ID'){
$entity[$key] = (int) $value;
}
}
return new Entities\Item(wp_update_post($entity));
public function update($object, $new_values = null){
return $this->insert($object);
}
/**

View File

@ -182,7 +182,7 @@ class Logs extends Repository {
}
public function update($object){
public function update($object, $new_values = null){
}

View File

@ -1,336 +0,0 @@
<?php
namespace Tainacan\Repositories;
use Tainacan\Entities;
defined( 'ABSPATH' ) or die( 'No script kiddies please!' );
use \Respect\Validation\Validator as v;
/**
* Class Metadatas
*/
class Metadatas extends Repository {
public $entities_type = '\Tainacan\Entities\Metadata';
protected $default_metadata = 'default';
public $field_types = [];
public function get_map() {
return apply_filters('tainacan-get-map-'.$this->get_name(), [
'name' => [
'map' => 'post_title',
'title' => __('Name', 'tainacan'),
'type' => 'string',
'description'=> __('Name of the metadata', 'tainacan'),
'on_error' => __('The name should be a text value and not empty', 'tainacan'),
'validation' => v::stringType()->notEmpty(),
],
'order' => [
'map' => 'menu_order',
'title' => __('Order', 'tainacan'),
'type' => 'string',
'description'=> __('Metadata order. Field used if collections are manually ordered', 'tainacan'),
'on_error' => __('The menu order should be a numeric value', 'tainacan'),
//'validation' => v::numeric(),
],
'parent' => [
'map' => 'parent',
'title' => __('Parent', 'tainacan'),
'type' => 'integer',
'description'=> __('Parent metadata', 'tainacan'),
//'on_error' => __('The Parent should be numeric value', 'tainacan'),
//'validation' => v::numeric(),
],
'description' => [
'map' => 'post_content',
'title' => __('Description', 'tainacan'),
'type' => 'string',
'description'=> __('The metadata description', 'tainacan'),
'default' => '',
//'on_error' => __('The description should be a text value', 'tainacan'),
//'validation' => v::stringType()->notEmpty(),
],
'field_type' => [
'map' => 'meta',
'title' => __('Type', 'tainacan'),
'type' => 'string',
'description'=> __('The metadata type', 'tainacan'),
'on_error' => __('Metadata type is empty', 'tainacan'),
'validation' => v::stringType()->notEmpty(),
],
'required' => [
'map' => 'meta',
'title' => __('Required', 'tainacan'),
'type' => 'string',
'description'=> __('The metadata is required', 'tainacan'),
'on_error' => __('Metadata required field is invalid', 'tainacan'),
'validation' => v::stringType()->in(['yes', 'no']), // yes or no
'default' => 'no'
],
'collection_key' => [
'map' => 'meta',
'title' => __('Collection key', 'tainacan'),
'type' => 'string',
'description'=> __('Metadata value should not be repeated', 'tainacan'),
'on_error' => __('Collection key is invalid', 'tainacan'),
'validation' => v::stringType()->in(['yes', 'no']), // yes or no
'default' => 'no'
],
'multiple' => [
'map' => 'meta',
'title' => __('Multiple', 'tainacan'),
'type' => 'string',
'description'=> __('Allow multiple fields for the metadata', 'tainacan'),
'on_error' => __('Multiple fields is invalid', 'tainacan'),
'validation' => v::stringType()->in(['yes', 'no']), // yes or no. It cant be multiple if its collection_key
'default' => 'no'
],
'cardinality' => [
'map' => 'meta',
'title' => __('Cardinality', 'tainacan'),
'type' => 'string',
'description'=> __('Number of multiples possible fields', 'tainacan'),
'on_error' => __('The number of fields not allowed', 'tainacan'),
'validation' => v::numeric()->positive(),
'default' => 1
],
'privacy' => [
'map' => 'meta',
'title' => __('Privacy', 'tainacan'),
'type' => 'string',
'description'=> __('The metadata should be omitted in item view', 'tainacan'),
'on_error' => __('Privacy is invalid', 'tainacan'),
'validation' => v::stringType()->in(['yes', 'no']), // yes or no. It cant be multiple if its collection_key
'default' => 'no'
],
'mask' => [
'map' => 'meta',
'title' => __('Mask', 'tainacan'),
'type' => 'string',
'description'=> __('The mask to be used in the metadata', 'tainacan'),
//'on_error' => __('Mask is invalid', 'tainacan'),
//'validation' => ''
],
'default_value' => [
'map' => 'meta',
'title' => __('Default value', 'tainacan'),
'type' => 'string',
'description'=> __('The value default fot the metadata', 'tainacan'),
],
'field_type_options' => [ // not showed in form
'map' => 'meta',
'title' => __('Field Type options', 'tainacan'),
'type' => 'string',
'description'=> __('Options specific for field type', 'tainacan'),
// 'validation' => ''
],
'collection_id' => [ // not showed in form
'map' => 'meta',
'title' => __('Collection', 'tainacan'),
'type' => 'integer',
'description'=> __('The collection ID', 'tainacan'),
//'validation' => ''
],
]);
}
public function register_post_type() {
$labels = array(
'name' => __('Metadata', 'tainacan'),
'singular_name' => __('Metadata', 'tainacan'),
'add_new' => __('Add new', 'tainacan'),
'add_new_item' => __('Add new Metadata', 'tainacan'),
'edit_item' => __('Edit Metadata', 'tainacan'),
'new_item' => __('New Metadata', 'tainacan'),
'view_item' => __('View Metadata', 'tainacan'),
'search_items' => __('Search Metadata', 'tainacan'),
'not_found' => __('No Metadata found ', 'tainacan'),
'not_found_in_trash' => __('No Metadata found in trash', 'tainacan'),
'parent_item_colon' => __('Parent Metadata:', 'tainacan'),
'menu_name' => __('Metadata', 'tainacan')
);
$args = array(
'labels' => $labels,
'hierarchical' => true,
//'supports' => array('title'),
//'taxonomies' => array(self::TAXONOMY),
'public' => true,
'show_ui' => tnc_enable_dev_wp_interface(),
'show_in_menu' => tnc_enable_dev_wp_interface(),
//'menu_position' => 5,
//'show_in_nav_menus' => false,
'publicly_queryable' => true,
'exclude_from_search' => true,
'has_archive' => true,
'query_var' => true,
'can_export' => true,
'rewrite' => true,
'map_meta_cap' => true,
'capability_type' => Entities\Metadata::get_post_type(),
'supports' => [
'title',
'editor',
'page-attributes'
]
);
register_post_type(Entities\Metadata::get_post_type(), $args);
}
/**
* constant used in default metadata in attribute collection_id
*
* @return string the value of constant
*/
public function get_default_metadata_attribute(){
return $this->default_metadata;
}
/**
* register field types class on array of types
*
* @param $class_name string | object The class name or the instance
*/
public function register_field_type( $class_name ){
if( is_object( $class_name ) ){
$class_name = get_class( $class_name );
}
if(!in_array( $class_name, $this->field_types)){
$this->field_types[] = $class_name;
}
}
/**
* register field types class on array of types
*
* @param $class_name string | object The class name or the instance
*/
public function unregister_field_type( $class_name ){
if( is_object( $class_name ) ){
$class_name = get_class( $class_name );
}
$key = array_search( $class_name, $this->field_types );
if($key !== false){
unset( $this->field_types[$key] );
}
}
/**
* fetch metadata based on ID or WP_Query args
*
* metadata are stored as posts. Check WP_Query docs
* to learn all args accepted in the $args parameter (@see https://developer.wordpress.org/reference/classes/wp_query/)
* You can also use a mapped property, such as name and description, as an argument and it will be mapped to the
* appropriate WP_Query argument
*
* @param array $args WP_Query args || int $args the metadata id
* @param string $output The desired output format (@see \Tainacan\Repositories\Repository::fetch_output() for possible values)
* @return \WP_Query|Array an instance of wp query OR array of entities;
*/
public function fetch( $args, $output = null ) {
if( is_numeric($args) ){
$existing_post = get_post($args);
if ($existing_post instanceof \WP_Post) {
return new Entities\Metadata($existing_post);
} else {
return [];
}
} elseif (is_array($args)) {
$args = array_merge([
'posts_per_page' => -1,
'post_status' => 'publish'
], $args);
$args['post_type'] = Entities\Metadata::get_post_type();
$wp_query = new \WP_Query($args);
return $this->fetch_output($wp_query, $output);
}
}
/**
* fetch metadata by collection, searches all metadata available
*
* @param Entities\Collection $collection
* @param array $args
* @param string $output The desired output format (@see \Tainacan\Repositories\Repository::fetch_output() for possible values)
* @return Array Entities\Metadata
*/
public function fetch_by_collection(Entities\Collection $collection, $args = [], $output = null){
$collection_id = $collection->get_id();
//get parent collections
$parents = get_post_ancestors( $collection_id );
//insert the actual collection
$parents[] = $collection_id;
//search for default metadata
$parents[] = $this->get_default_metadata_attribute();
$meta_query = array(
'key' => 'collection_id',
'value' => $parents,
'compare' => 'IN',
);
if( isset( $args['meta_query'] ) ){
$args['meta_query'][] = $meta_query;
}else{
$args['meta_query'] = array( $meta_query );
}
return $this->fetch( $args, $output );
}
public function update($object){
$map = $this->get_map();
$entity = [];
foreach ($object as $key => $value) {
if($key != 'ID') {
$entity[$map[$key]['map']] = $value ;
} elseif ($key == 'ID'){
$entity[$key] = (int) $value;
}
}
return new Entities\Metadata(wp_update_post($entity));
}
public function delete($object){
}
/**
* fetch all registered field type classes
*
* Possible outputs are:
* CLASS (default) - returns the Class name of of field types registered
* NAME - return an Array of the names of field types registered
*
* @param $output string CLASS | NAME
* @return array of Entities\Field_Types\Field_Type classes path name
*/
public function fetch_field_types( $output = 'CLASS'){
$return = [];
do_action('register_field_types');
if( $output === 'NAME' ){
foreach ($this->field_types as $field_type) {
$return[] = str_replace('Tainacan\Field_Types\\','', $field_type);
}
return $return;
}
return $this->field_types;
}
}

View File

@ -64,7 +64,7 @@ abstract class Repository {
*/
public function insert($obj) {
// validate
if ( in_array($obj->get_status(), apply_filters('tainacan-status-validation', ['publish','future','private'])) && !$obj->get_validated()){
if ( in_array($obj->get_status(), apply_filters('tainacan-status-require-validation', ['publish','future','private'])) && !$obj->get_validated()){
throw new \Exception('Entities must be validated before you can save them');
// TODO: Throw Warning saying you must validate object before insert()
}
@ -78,8 +78,11 @@ abstract class Repository {
}
}
$obj->WP_Post->post_type = $obj::get_post_type();
//$obj->WP_Post->post_status = 'publish';
if($obj instanceof Entities\Log) {
$obj->WP_Post->post_status = 'publish';
}
// TODO verificar se salvou mesmo
$id = wp_insert_post($obj->WP_Post);
@ -281,7 +284,11 @@ abstract class Repository {
} elseif ( $mapped == 'termmeta' ){
$property = get_term_meta($entity->WP_Term->term_id, $prop, true);
} elseif ( isset( $entity->WP_Post )) {
$property = isset($entity->WP_Post->$mapped) ? $entity->WP_Post->$mapped : null;
if($mapped == 'thumbnail'){
$property = get_the_post_thumbnail_url($entity->WP_Post->ID, 'full');
} else {
$property = isset($entity->WP_Post->$mapped) ? $entity->WP_Post->$mapped : null;
}
} elseif ( isset( $entity->WP_Term )) {
$property = isset($entity->WP_Term->$mapped) ? $entity->WP_Term->$mapped : null;
}
@ -347,8 +354,8 @@ abstract class Repository {
}
}
else {
global $Tainacan_Collections,$Tainacan_Metadatas, $Tainacan_Item_Metadata,$Tainacan_Filters,$Tainacan_Taxonomies,$Tainacan_Terms,$Tainacan_Logs;
$tnc_globals = [$Tainacan_Collections,$Tainacan_Metadatas, $Tainacan_Item_Metadata,$Tainacan_Filters,$Tainacan_Taxonomies,$Tainacan_Terms,$Tainacan_Logs];
global $Tainacan_Collections,$Tainacan_Fields, $Tainacan_Item_Metadata,$Tainacan_Filters,$Tainacan_Taxonomies,$Tainacan_Terms,$Tainacan_Logs;
$tnc_globals = [$Tainacan_Collections,$Tainacan_Fields, $Tainacan_Item_Metadata,$Tainacan_Filters,$Tainacan_Taxonomies,$Tainacan_Terms,$Tainacan_Logs];
foreach ($tnc_globals as $tnc_repository)
{
$entity_post_type = $tnc_repository->entities_type::get_post_type();
@ -377,7 +384,7 @@ abstract class Repository {
* @param $object
* @return mixed
*/
public abstract function update($object);
public abstract function update($object, $new_values = null);
/**
* @return mixed

View File

@ -23,13 +23,6 @@ class Taxonomies extends Repository {
'on_error' => __('The taxonomy should be a text value and not empty', 'tainacan'),
'validation' => v::stringType()->notEmpty(),
],
'parent' => [
'map' => 'parent',
'title' => __('Parent', 'tainacan'),
'type' => 'integer',
'description' => __('Parent taxonomy', 'tainacan'),
'validation' => ''
],
'description' => [
'map' => 'post_content',
'title' => __('Description', 'tainacan'),
@ -107,10 +100,11 @@ class Taxonomies extends Repository {
register_post_type(Entities\Taxonomy::get_post_type(), $args);
}
/**
* @param Entities\Taxonomy $taxonomy
* @return int
*/
/**
* @param Entities\Taxonomy $taxonomy
*
* @return Entities\Entity
*/
public function insert($taxonomy) {
$new_taxonomy = parent::insert($taxonomy);
@ -184,23 +178,8 @@ class Taxonomies extends Repository {
}
}
public function update($object){
$map = $this->get_map();
$entity = [];
foreach ($object as $key => $value) {
if($key != 'ID') {
$entity[$map[$key]['map']] = $value ;
} elseif ($key == 'ID'){
$entity[$key] = (int) $value;
}
}
$updated_taxonomy = new Entities\Taxonomy(wp_update_post($entity));
$updated_taxonomy->register_taxonomy();
return $updated_taxonomy;
public function update($object, $new_values = null){
return $this->insert($object);
}
public function delete($args){

View File

@ -61,6 +61,10 @@ class Terms extends Repository {
'on_error' => __('The user is empty or invalid', 'tainacan'),
'validation' => v::numeric()->positive(),
],
'hide_empty' => [
'map' => 'hide_empty',
'type' => 'bool'
]
]);
}
@ -155,21 +159,8 @@ class Terms extends Repository {
}
}
public function update($object){
$map = $this->get_map();
$entity = [];
foreach ($object[0] as $key => $value) {
if($key != 'ID') {
$entity[$map[$key]['map']] = $value ;
}
}
$term_tax_ids = wp_update_term($object[1]['term_id'], $object[1]['tax_name'], $entity);
$term_id = (int) $term_tax_ids['term_id'];
return new Entities\Term($term_id, $object[1]['tax_name']);
public function update($object, $tax_name = null){
return new Entities\Term($this->insert($object), $tax_name);
}
public function delete($args){

View File

@ -8,8 +8,10 @@ const FILTER_TYPES_DIR = __DIR__ . '/filter-types/';
const REPOSITORIES_DIR = __DIR__ . '/repositories/';
const TRAITS_DIR = __DIR__ . '/traits/';
const VENDOR_DIR = __DIR__ . '/../vendor/';
const TAPI_DIR = __DIR__ . '/../api/';
const ENDPOINTS_DIR = __DIR__ . '/../api/endpoints/';
const HELPERS_DIR = __DIR__ . '/../helpers/';
const HELPERS_DIR = __DIR__ . '/../helpers/';
const IMPORTER_DIR = __DIR__ . '/../importer/';
const DIRS = [
CLASSES_DIR,
@ -18,11 +20,14 @@ const DIRS = [
FILTER_TYPES_DIR,
REPOSITORIES_DIR,
TRAITS_DIR,
TAPI_DIR,
ENDPOINTS_DIR,
IMPORTER_DIR
];
require_once(VENDOR_DIR . 'autoload.php');
require_once(HELPERS_DIR . 'class-tainacan-helpers-html.php');
require_once(IMPORTER_DIR . 'class-tainacan-importer.php');
spl_autoload_register('tainacan_autoload');
@ -42,12 +47,14 @@ function tainacan_autoload($class_name){
elseif ($class_path[0] == 'Tainacan') {
$sliced = array_slice($class_path, 1, count($class_path) -2);
if($sliced) {
if( isset( $class_path[1] ) && $class_path[1] === 'Importer' ){
$dir = IMPORTER_DIR;
} else if($sliced) {
$lower = $sliced[0];
$sliced[0] = strtolower( $lower );
$dir = CLASSES_DIR . implode( DIRECTORY_SEPARATOR, $sliced ) . '/';
$dir = str_replace( '_', '-', $dir );
$dir = implode( DIRECTORY_SEPARATOR, $sliced ) . '/';
$dir = CLASSES_DIR . str_replace( '_', '-', $dir );
} else {
$dir = CLASSES_DIR;
}
@ -70,18 +77,18 @@ $Tainacan_Collections = new \Tainacan\Repositories\Collections();
global $Tainacan_Item_Metadata;
$Tainacan_Item_Metadata = new \Tainacan\Repositories\Item_Metadata();
global $Tainacan_Metadatas;
$Tainacan_Metadatas = new \Tainacan\Repositories\Metadatas();
global $Tainacan_Fields;
$Tainacan_Fields = new \Tainacan\Repositories\Fields();
//register field types
$Tainacan_Metadatas->register_field_type('Tainacan\Field_Types\Text');
$Tainacan_Metadatas->register_field_type('Tainacan\Field_Types\Textarea');
$Tainacan_Metadatas->register_field_type('Tainacan\Field_Types\Date');
$Tainacan_Metadatas->register_field_type('Tainacan\Field_Types\Numeric');
$Tainacan_Metadatas->register_field_type('Tainacan\Field_Types\Selectbox');
$Tainacan_Metadatas->register_field_type('Tainacan\Field_Types\Relationship');
$Tainacan_Metadatas->register_field_type('Tainacan\Field_Types\Radio');
$Tainacan_Metadatas->register_field_type('Tainacan\Field_Types\Checkbox');
$Tainacan_Fields->register_field_type('Tainacan\Field_Types\Text');
$Tainacan_Fields->register_field_type('Tainacan\Field_Types\Textarea');
$Tainacan_Fields->register_field_type('Tainacan\Field_Types\Date');
$Tainacan_Fields->register_field_type('Tainacan\Field_Types\Numeric');
$Tainacan_Fields->register_field_type('Tainacan\Field_Types\Selectbox');
$Tainacan_Fields->register_field_type('Tainacan\Field_Types\Relationship');
$Tainacan_Fields->register_field_type('Tainacan\Field_Types\Radio');
$Tainacan_Fields->register_field_type('Tainacan\Field_Types\Checkbox');
global $Tainacan_Filters;
$Tainacan_Filters = new \Tainacan\Repositories\Filters();

View File

@ -48,4 +48,12 @@ trait Entity_Collections_Relation {
$this->set_collections_ids($collections_ids);
}
public function set_collection($new_collection_id){
$this->collections = $this->get_mapped_property('collections_ids');
$collections[] = $new_collection_id;
$this->set_collections_ids($collections);
}
}

View File

@ -14,12 +14,13 @@ class DevInterface {
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_enqueue_scripts', array(&$this, 'add_admin_css'));
add_filter('post_type_link', array(&$this, 'permalink_filter'), 10, 3);
global $Tainacan_Collections, $Tainacan_Filters, $Tainacan_Logs, $Tainacan_Metadatas, $Tainacan_Taxonomies;
global $Tainacan_Collections, $Tainacan_Filters, $Tainacan_Logs, $Tainacan_Fields, $Tainacan_Taxonomies;
$repositories = [$Tainacan_Collections, $Tainacan_Filters, $Tainacan_Logs, $Tainacan_Metadatas, $Tainacan_Taxonomies];
$repositories = [$Tainacan_Collections, $Tainacan_Filters, $Tainacan_Logs, $Tainacan_Fields, $Tainacan_Taxonomies];
foreach ($repositories as $repo) {
$cpt = $repo->entities_type::get_post_type();
@ -32,8 +33,6 @@ class DevInterface {
global $TAINACAN_BASE_URL;
$components = ( has_filter( 'tainacan_register_web_components' ) ) ? apply_filters('tainacan_register_web_components') : [];
wp_enqueue_script('wp-settings',$TAINACAN_BASE_URL . '/js/wp-settings.js');
$settings = [
'root' => esc_url_raw( rest_url() ).'tainacan/v2',
'nonce' => wp_create_nonce( 'wp_rest' ),
@ -41,9 +40,13 @@ class DevInterface {
];
wp_localize_script( 'wp-settings', 'wp_settings', $settings );
wp_enqueue_script( 'custom-elements', $TAINACAN_BASE_URL . '/assets/customelements.min.js');
wp_enqueue_script( 'tainacan-dev-admin', $TAINACAN_BASE_URL . '/assets/dev_admin-components.js', [] , null, true);
}
function add_admin_css() {
global $TAINACAN_BASE_URL;
wp_enqueue_style('tainacan-dev-admin-styles', $TAINACAN_BASE_URL . '/assets/css/tainacan-admin.css' );
}
/**
* Filters the permalink for posts to:
@ -77,7 +80,7 @@ class DevInterface {
/**
* Run through all post types attributes and add metaboxes for them.
*
* Also run through all collections metadata and add metaboxes for its items post type
* Also run through all collections field and add metaboxes for its items post type
*
* @return void
*/
@ -102,8 +105,8 @@ class DevInterface {
foreach ($collections as $col) {
add_meta_box(
$col->get_db_identifier() . '_metadata',
__('Metadata', 'tainacan'),
$col->get_db_identifier() . '_field',
__('Field', 'tainacan'),
array(&$this, 'metadata_metabox'),
$col->get_db_identifier(), //post type
'normal'
@ -112,7 +115,7 @@ class DevInterface {
add_meta_box(
$col->get_db_identifier() . '_metadata_js',
__('Metadata Components', 'tainacan'),
__('Field Components', 'tainacan'),
array(&$this, 'metadata_components_metabox'),
$col->get_db_identifier(), //post type
'normal'
@ -135,9 +138,9 @@ class DevInterface {
global $Tainacan_Logs;
$this->properties_metabox($Tainacan_Logs);
}
function properties_metabox_Metadatas() {
global $Tainacan_Metadatas;
$this->properties_metabox($Tainacan_Metadatas);
function properties_metabox_Fields() {
global $Tainacan_Fields;
$this->properties_metabox($Tainacan_Fields);
}
function properties_metabox_Taxonomies() {
global $Tainacan_Taxonomies;
@ -231,7 +234,7 @@ class DevInterface {
if (!$entity->get_collection_id())
$entity->set_collection($current_collection);
$metadata = $Tainacan_Item_Metadata->fetch($entity, 'OBJECT');
$field = $Tainacan_Item_Metadata->fetch($entity, 'OBJECT');
wp_nonce_field( 'save_metadata_'.$typenow, $typenow.'_metadata_noncename' );
@ -244,14 +247,14 @@ class DevInterface {
<thead>
<tr>
<th class="left"><?php _e('Metadata', 'tainacan'); ?></th>
<th class="left"><?php _e('Field', 'tainacan'); ?></th>
<th><?php _e('Value', 'tainacan'); ?></th>
</tr>
</thead>
<tbody>
<?php foreach ($metadata as $item_meta): ?>
<?php foreach ($field as $item_meta): ?>
<?php
$value = $item_meta->get_value();
@ -259,11 +262,11 @@ class DevInterface {
?>
<tr>
<td>
<label><?php echo $item_meta->get_metadata()->get_name(); ?></label><br/>
<small><?php echo $item_meta->get_metadata()->get_description(); ?></small>
<label><?php echo $item_meta->get_field()->get_name(); ?></label><br/>
<small><?php echo $item_meta->get_field()->get_description(); ?></small>
</td>
<td>
<textarea name="tnc_metadata_<?php echo $item_meta->get_metadata()->get_id(); ?>"><?php echo htmlspecialchars($value); ?></textarea>
<textarea name="tnc_metadata_<?php echo $item_meta->get_field()->get_id(); ?>"><?php echo htmlspecialchars($value); ?></textarea>
</td>
</tr>
@ -301,7 +304,7 @@ class DevInterface {
if (!$entity->get_collection_id())
$entity->set_collection($current_collection);
$metadata = $Tainacan_Item_Metadata->fetch($entity, 'OBJECT');
$field = $Tainacan_Item_Metadata->fetch($entity, 'OBJECT');
wp_nonce_field( 'save_metadata_'.$typenow, $typenow.'_metadata_noncename' );
@ -314,14 +317,14 @@ class DevInterface {
<thead>
<tr>
<th class="left"><?php _e('Metadata', 'tainacan'); ?></th>
<th class="left"><?php _e('Field', 'tainacan'); ?></th>
<th><?php _e('Value', 'tainacan'); ?></th>
</tr>
</thead>
<tbody>
<?php foreach ($metadata as $item_meta): ?>
<?php foreach ($field as $item_meta): ?>
<?php
$value = $item_meta->get_value();
@ -329,12 +332,12 @@ class DevInterface {
?>
<tr>
<td>
<label><?php echo $item_meta->get_metadata()->get_name(); ?></label><br/>
<small><?php echo $item_meta->get_metadata()->get_description(); ?></small>
<label><?php echo $item_meta->get_field()->get_name(); ?></label><br/>
<small><?php echo $item_meta->get_field()->get_description(); ?></small>
</td>
<td>
<?php //echo '<tainacan-text name="'.$item_meta->get_metadata()->get_name().'"></tainacan-text>'; ?>
<?php echo $item_meta->get_metadata()->get_field_type_object()->render( $item_meta ); ?>
<?php //echo '<tainacan-text name="'.$item_meta->get_field()->get_name().'"></tainacan-text>'; ?>
<?php echo $item_meta->get_field()->get_field_type_object()->render( $item_meta ); ?>
</td>
</tr>
@ -351,7 +354,7 @@ class DevInterface {
function field_type_dropdown($id,$selected) {
global $Tainacan_Metadatas;
global $Tainacan_Fields;
$class = ( class_exists( $selected ) ) ? new $selected() : '';
@ -359,7 +362,7 @@ class DevInterface {
$selected = str_replace('Tainacan\Field_Types\\','', get_class( $class ) );
}
$field_types = $Tainacan_Metadatas->fetch_field_types('NAME');
$field_types = $Tainacan_Fields->fetch_field_types('NAME');
?>
<select name="tnc_prop_field_type">
<?php foreach ($field_types as $field_type): ?>
@ -448,7 +451,7 @@ class DevInterface {
// TODO: there should ve a method in the repository to find this out
// or I could try to initialize an entity and find out what type it is
global $Tainacan_Collections, $Tainacan_Items, $Tainacan_Metadatas, $Tainacan_Item_Metadata;
global $Tainacan_Collections, $Tainacan_Items, $Tainacan_Fields, $Tainacan_Item_Metadata;
$collections = $Tainacan_Collections->fetch([], 'OBJECT');
$cpts = [];
foreach($collections as $col) {
@ -466,10 +469,16 @@ class DevInterface {
}
$metalist = $Tainacan_Metadatas->fetch_by_collection($cpts[$post_type], [], 'OBJECT');
$metalist = $Tainacan_Fields->fetch_by_collection($cpts[$post_type], [], 'OBJECT');
foreach ($metalist as $meta) {
$item_meta = new \Tainacan\Entities\Item_Metadata_Entity($entity, $meta);
$pos = strpos($item_meta->get_field()->get_field_type(), 'Core');
if( $pos !== false ){
continue;
}
if (isset($_POST['tnc_metadata_' . $meta->get_id()])) {
$item_meta->set_value($_POST['tnc_metadata_' . $meta->get_id()]);
if ($item_meta->validate()) {

View File

@ -43,21 +43,21 @@ use Tainacan\Entities;
}
/**
* generates select field for all publish metadata for a single collection
* generates select field for all publish field for a single collection
*
* @param Entities\Collection | integer $collection The collection id or the collection object
* @param string $selected The collection id for the selected option
* @param string $name_field (optional) default 'tnc_prop_collections_id'
* @param array $args (optional) filter the metadata list
* @param array $args (optional) filter the field list
*/
public static function metadata_dropdown( $collection , $selected, $name_field = 'tnc_prop_metadata_id', $args = []){
global $Tainacan_Metadatas;
public static function metadata_dropdown( $collection , $selected, $name_field = 'tnc_prop_field_id', $args = []){
global $Tainacan_Fields;
$collection = ( is_numeric( $collection ) ) ? new Entities\Collection( $collection ) : $collection;
$metadata = $Tainacan_Metadatas->fetch_by_collection( $collection, $args, 'OBJECT');
$field = $Tainacan_Fields->fetch_by_collection( $collection, $args, 'OBJECT');
?>
<select name="<?php echo $name_field ?>">
<option value=""><?php echo __('Select an option','tainacan') ?>...</option>
<?php foreach ($metadata as $col): ?>
<?php foreach ($field as $col): ?>
<option value="<?php echo $col->get_id(); ?>" <?php selected($col->get_id(), $selected) ?>><?php echo $col->get_name(); ?></option>
<?php endforeach; ?>
</select>
@ -66,21 +66,21 @@ use Tainacan\Entities;
/**
* generates checkboxes fields for all publish metadata for a single collection
* generates checkboxes fields for all publish field for a single collection
*
* @param Entities\Collection | integer $collection The collection id or the collection object
* @param string(json) $selected string(json) | array json with an array or array of ids to be checked
* @param string $name_field (optional) default 'tnc_prop_tnc_metadata_ids[]' the checkbox field name
* @param array $args (optional) filter the metadata list
* @param string $name_field (optional) default 'tnc_prop_tnc_field_ids[]' the checkbox field name
* @param array $args (optional) filter the field list
*/
public static function metadata_checkbox_list( $collection , $selected,$name_field = 'tnc_prop_tnc_metadata_ids[]', $args = []) {
global $Tainacan_Metadatas;
public static function metadata_checkbox_list( $collection , $selected,$name_field = 'tnc_prop_tnc_field_ids[]', $args = []) {
global $Tainacan_Fields;
$collection = ( is_numeric( $collection ) ) ? new Entities\Collection( $collection ) : $collection;
$metadata = $Tainacan_Metadatas->fetch_by_collection( $collection, $args, 'OBJECT');
$field = $Tainacan_Fields->fetch_by_collection( $collection, $args, 'OBJECT');
$selected = ( is_array( $selected) ) ? $selected : json_decode($selected);
$selected = ( $selected ) ? $selected : [];
?>
<?php foreach ($metadata as $col): ?>
<?php foreach ($field as $col): ?>
<input type="checkbox" name="<?php echo $name_field ?>" value="<?php echo $col->get_id(); ?>" <?php checked(in_array($col->get_id(), $selected)); ?> style="width: 15px;">
<?php echo $col->get_name(); ?>
<br/>

View File

@ -0,0 +1,60 @@
<?php
namespace Tainacan\Importer;
use Tainacan;
class CSV extends Importer {
public $delimiter = ',';
public function __construct() {
parent::__construct();
}
/**
* @return string $delimiter value that divides each column
*/
public function get_delimiter(){
return $this->delimiter;
}
public function set_delimiter( $delimiter ){
$this->delimiter = $delimiter;
}
/**
* @inheritdoc
*/
public function get_fields_source(){
$file = new \SplFileObject( $this->tmp_file, 'r' );
$file->seek(0 );
return $file->fgetcsv( $this->get_delimiter() );
}
/**
*
*/
public function process_item(){
// TODO: process single item
}
/**
* @return mixed
*/
public function get_options(){
// TODO: Implement get_options() method.
}
public function get_total_items(){
if( isset( $this->total_items ) ){
return $this->total_items;
} else {
$file = new \SplFileObject( $this->tmp_file, 'r' );
$file->seek(PHP_INT_MAX);
// -1 removing header
return $this->total_items = $file->key() - 1;
}
}
}

View File

@ -0,0 +1,143 @@
<?php
namespace Tainacan\Importer;
use Tainacan;
abstract class Importer {
private $id;
public $collection;
public $mapping;
public $tmp_file;
public $total_items;
public $limit_query;
public function __construct() {
if (!session_id()) {
@session_start();
}
$this->id = uniqid();
$_SESSION['tainacan_importer'][$this->id] = $this;
}
/**
* @return string
*/
public function get_id(){
return $this->id;
}
/**
* @return array
*/
public function get_mapping(){
return $this->mapping;
}
/**
* @param Tainacan\Entities\Collection $collection
*/
public function set_collection( Tainacan\Entities\Collection $collection ){
$this->collection = $collection;
}
/**
* save an associative array with tainacan field id and field from source
*
* @param array $mapping Mapping importer-fields
*/
public function set_mapping( $mapping ){
$this->mapping = $mapping;
}
/**
* @param $file File to be managed by importer
* @return bool
*/
public function set_file( $file ){
$new_file = $this->upload_file( $file );
if ( is_numeric( $new_file ) ) {
$this->tmp_file = get_attached_file( $new_file );
} else {
return false;
}
}
/**
* internal function to upload the file
*
* @param $path_file
* @return array $response
*/
private function upload_file( $path_file ){
$name = basename( $path_file );
$file_array['name'] = $name;
$file_array['tmp_name'] = $path_file;
$file_array['size'] = filesize( $path_file );
return media_handle_sideload( $file_array, 0 );
}
/**
* get the content form url and creates a file
*
* @param $url
* @return array
*/
public function fetch_from_remote( $url ){
$tmp = wp_remote_get( $url );
if( isset( $tmp['body'] ) ){
$file = fopen( $this->get_id().'.txt', 'w' );
fwrite( $file, $tmp['body'] );
fclose( $file );
return $this->set_file( $this->get_id().'.txt' );
}
}
/**
* @return mixed
*/
public function get_collection_fields(){
return $this->collection;
}
/**
* get the fields of file/url to allow mapping
* should returns an array
*
* @return array $fields_source the fields from the source
*/
abstract public function get_fields_source();
/**
* get values for a single item
*/
abstract public function process_item();
/**
* @return mixed
*/
abstract public function get_options();
/**
* return the all items found
*
* @return int Total of items
*/
abstract public function get_total_items();
/**
* @param $start
* @param $end
*/
public function process( $start, $end ){
}
/**
*
*/
public function run(){
}
}

View File

@ -5,40 +5,68 @@ export const eventBus = new Vue({
store,
data: {
componentsTag: [],
errors : store.getters['item/getError']
errors : []
},
created(){
if( wp_settings.components ){
this.componentsTag = wp_settings.components;
}
this.$on('input', data => this.updateValue(data) );
},
methods : {
registerComponent( name ){
this.componentsTag.push( name );
if (this.componentsTag.indexOf(name) < 0) {
this.componentsTag.push( name );
}
},
listener(){
const components = this.getAllComponents();
for (let eventElement of components){
eventElement.addEventListener('changeValue', (event) => {
if ( event.detail[0] ){
const promisse = this.$store.dispatch('item/sendMetadata', event.detail[0] );
promisse.then( response => {
eventElement.errorsMsg = JSON.stringify( [] );
eventElement.value = response.value;
}, error => {
const metadata = this.errors.find(error => error.metadata_id === event.detail[0].metadata_id );
eventElement.errorsMsg = JSON.stringify( metadata.error );
eventElement.value = event.detail[0].values;
});
eventElement.addEventListener('input', (event) => {
if (event.detail && event.detail[0] ){
this.updateValue({
item_id: $(eventElement).attr("item_id"),
field_id: $(eventElement).attr("field_id"),
values: event.detail
})
}
});
}
},
updateValue(data){
if ( data.item_id ){
const promisse = this.$store.dispatch('item/updateMetadata',
{ item_id: data.item_id, field_id: data.field_id, values: data.values });
promisse.then( response => {
let index = this.errors.findIndex( errorItem => errorItem.field_id === data.field_id );
if ( index >= 0){
this.errors.splice( index, 1);
}
}, error => {
let index = this.errors.findIndex( errorItem => errorItem.field_id === data.field_id );
let messages = null;
for (let index in error) {
messages = error[index]
}
if ( index >= 0){
Vue.set( this.errors, index, { field_id: data.field_id, errors: messages });
}else{
this.errors.push( { field_id: data.field_id, errors: messages } );
}
});
}
},
getErrors(field_id){
let error = this.errors.find( errorItem => errorItem.field_id === field_id );
return ( error ) ? error.errors : false
},
setValues(){
const metadata = this.$store.getters['item/getMetadata'];
if( metadata ){
for(let singleMetadata of metadata){
const eventElement = this.getComponentById( singleMetadata.metadata_id );
const field = this.$store.getters['item/getMetadata'];
if( field ){
for(let singleMetadata of field){
const eventElement = this.getComponentById( singleMetadata.field_id );
eventElement.value = singleMetadata.values;
}
}
@ -53,20 +81,26 @@ export const eventBus = new Vue({
}
}
}
let elements = document.querySelectorAll('[web-component="true"]');
if( elements ) {
for (let eventElement of elements){
components.push( eventElement );
}
}
return components;
},
getComponentById( metadata_id ){
getComponentById( field_id ){
for( let component of this.componentsTag ){
const eventElements = document.getElementsByTagName( component );
if( eventElements ) {
for (let eventElement of eventElements){
if( eventElement.metadata_id === metadata_id ){
if( eventElement.field_id === field_id ){
return eventElement;
}
}
}
}
}
},
}
});

View File

@ -3,6 +3,10 @@ import Vue from 'vue'
// include vue-custom-element plugin to Vue
import VueCustomElement from 'vue-custom-element';
import { eventBus } from './event-bus-web-components';
import Buefy from 'buefy'
Vue.use(Buefy)
Vue.use(VueCustomElement);
@ -13,17 +17,31 @@ import Checkbox from '../classes/field-types/checkbox/Checkbox.vue';
import Radio from '../classes/field-types/radio/Radio.vue';
import Numeric from '../classes/field-types/numeric/Numeric.vue';
import Date from '../classes/field-types/date/Date.vue';
import Relationship from '../classes/field-types/relationship/Relationship.vue';
Vue.customElement('tainacan-text', Text);
eventBus.registerComponent( 'tainacan-text' );
Vue.customElement('tainacan-textarea', Textarea);
eventBus.registerComponent( 'tainacan-textarea' );
Vue.customElement('tainacan-selectbox', Selectbox);
eventBus.registerComponent( 'tainacan-selectbox' );
Vue.customElement('tainacan-checkbox', Checkbox);
eventBus.registerComponent( 'tainacan-checkbox' );
Vue.customElement('tainacan-radio', Radio);
eventBus.registerComponent( 'tainacan-radio' );
Vue.customElement('tainacan-numeric', Numeric);
eventBus.registerComponent( 'tainacan-numeric' );
Vue.customElement('tainacan-date', Date);
eventBus.registerComponent( 'tainacan-date' );
Vue.customElement('tainacan-relationship', Relationship);
eventBus.registerComponent( 'tainacan-relationship' );
eventBus.listener();

View File

@ -1,16 +1,117 @@
import axios from '../../../axios/axios';
export const fetchItems = ({ commit, state }) => {
axios.get('/')
.then(res => {})
.catch(error => console.log( error ));
export const fetchItems = ({ commit, state }, { collectionId, page, itemsPerPage }) => {
return new Promise ((resolve, reject) => {
axios.get('/collection/'+collectionId+'/items?paged='+page+'&perpage='+itemsPerPage)
.then(res => {
let items = res.data;
commit('setItems', items);
resolve({'items': items, 'total': res.headers['x-wp-total'] });
})
.catch(error => reject(error));
});
}
export const fetchCollections = ({ commit }) => {
axios.get('/collections')
export const deleteItem = ({ commit }, item_id ) => {
return new Promise((resolve, reject) => {
axios.delete('/items/' + item_id)
.then( res => {
commit('deleteItem', { id: item_id });
resolve( res );
}).catch((error) => {
reject( error );
});
});
};
export const fetchCollections = ({commit} , { page, collectionsPerPage }) => {
return new Promise((resolve, reject) => {
axios.get('/collections?paged='+page+'&perpage='+collectionsPerPage)
.then(res => {
let collections = res.data;
commit('setCollections', collections);
resolve({'collections': collections, 'total': res.headers['x-wp-total'] });
})
.catch(error => {
console.log(error);
reject(error);
});
});
}
export const fetchFields = ({ commit }, id) => {
return new Promise((resolve, reject) => {
axios.get('/collection/'+id+'/fields')
.then((res) => {
let fields= res.data;
commit('setFields', fields);
resolve (fields);
})
.catch(error => console.log(error));
}
.catch((error) => {
console.log(error);
reject(error);
});
});
}
export const fetchCollection = ({ commit }, id) => {
return new Promise((resolve, reject) =>{
axios.get('/collections/' + id)
.then(res => {
let collection = res.data;
commit('setCollection', collection);
resolve( res.data );
})
.catch(error => {
reject(error);
})
});
}
export const deleteCollection = ({ commit }, id) => {
return new Promise((resolve, reject) =>{
axios.delete('/collections/' + id)
.then(res => {
let collection = res.data;
commit('deleteCollection', collection);
resolve( res.data );
})
.catch(error => {
reject(error);
})
});
}
export const updateCollection = ({ commit }, { collection_id, name, description, status }) => {
return new Promise((resolve, reject) => {
axios.patch('/collections/' + collection_id, {
name: name,
description: description,
status: status
}).then( res => {
commit('setCollection', { id: collection_id, name: name, description: description, status: status });
resolve( res.data );
}).catch( error => {
reject( error.response );
});
});
}
export const sendCollection = ( { commit }, { name, description, status }) => {
return new Promise(( resolve, reject ) => {
axios.post('/collections/', {
name: name,
description: description,
status: status
})
.then( res => {
commit('setCollection', { name: name, description: description, status: status });
resolve( res.data );
})
.catch(error => {
reject( error.response );
});
});
};

View File

@ -4,4 +4,12 @@ export const getItems = state => {
export const getCollections = state => {
return state.collections;
}
export const getFields = state => {
return state.fields;
}
export const getCollection = state => {
return state.collection;
}

View File

@ -4,7 +4,9 @@ import * as mutations from './mutations';
const state = {
items: [],
collections: []
collections: [],
fields: [],
collection: null
};
export default {

View File

@ -1,7 +1,29 @@
export const setItems = ( state, items ) => {
state.item = items;
state.items = items;
}
export const deleteItem = ( state, item ) => {
let index = state.items.findIndex(deletedItem => deletedItem.id === item.id);
if (index >= 0) {
state.items.splice(index, 1);
}
}
export const deleteCollection = ( state, collection ) => {
let index = state.collections.findIndex(deletedCollection => deletedCollection.id === collection.id);
if (index >= 0) {
state.collections.splice(index, 1);
}
}
export const setCollections = (state, collections) => {
state.collections = collections;
}
export const setFields = (state, fields) => {
state.fields = fields;
}
export const setCollection = (state, collection) => {
state.collection = collection;
}

Some files were not shown because too many files have changed in this diff Show More