Merge branch 'feature/274' into develop

This commit is contained in:
leogermani 2019-12-16 16:00:53 -03:00
commit e17c5241cf
122 changed files with 7485 additions and 3373 deletions

View File

@ -13,25 +13,27 @@ sass -E 'UTF-8' --cache-location .tmp/sass-cache-1 src/scss/tainacan-embeds.scss
sass -E 'UTF-8' --cache-location .tmp/sass-cache-2 src/admin/scss/tainacan-admin.scss:src/assets/css/tainacan-admin.css
sass -E 'UTF-8' --cache-location .tmp/sass-cache-3 src/gutenberg-blocks/tainacan-collections/collections-list/collections-list.scss:src/assets/css/tainacan-gutenberg-block-collections-list.css
sass -E 'UTF-8' --cache-location .tmp/sass-cache-3 src/admin/scss/tainacan-roles.scss:src/assets/css/tainacan-roles.css
sass -E 'UTF-8' --cache-location .tmp/sass-cache-4 src/gutenberg-blocks/tainacan-collections/carousel-collections-list/carousel-collections-list.scss:src/assets/css/tainacan-gutenberg-block-carousel-collections-list.css
sass -E 'UTF-8' --cache-location .tmp/sass-cache-4 src/gutenberg-blocks/tainacan-collections/collections-list/collections-list.scss:src/assets/css/tainacan-gutenberg-block-collections-list.css
sass -E 'UTF-8' --cache-location .tmp/sass-cache-5 src/gutenberg-blocks/tainacan-items/items-list/items-list.scss:src/assets/css/tainacan-gutenberg-block-items-list.css
sass -E 'UTF-8' --cache-location .tmp/sass-cache-5 src/gutenberg-blocks/tainacan-collections/carousel-collections-list/carousel-collections-list.scss:src/assets/css/tainacan-gutenberg-block-carousel-collections-list.css
sass -E 'UTF-8' --cache-location .tmp/sass-cache-6 src/gutenberg-blocks/tainacan-items/dynamic-items-list/dynamic-items-list.scss:src/assets/css/tainacan-gutenberg-block-dynamic-items-list.css
sass -E 'UTF-8' --cache-location .tmp/sass-cache-6 src/gutenberg-blocks/tainacan-items/items-list/items-list.scss:src/assets/css/tainacan-gutenberg-block-items-list.css
sass -E 'UTF-8' --cache-location .tmp/sass-cache-7 src/gutenberg-blocks/tainacan-items/search-bar/search-bar.scss:src/assets/css/tainacan-gutenberg-block-search-bar.css
sass -E 'UTF-8' --cache-location .tmp/sass-cache-7 src/gutenberg-blocks/tainacan-items/dynamic-items-list/dynamic-items-list.scss:src/assets/css/tainacan-gutenberg-block-dynamic-items-list.css
sass -E 'UTF-8' --cache-location .tmp/sass-cache-8 src/gutenberg-blocks/tainacan-items/carousel-items-list/carousel-items-list.scss:src/assets/css/tainacan-gutenberg-block-carousel-items-list.css
sass -E 'UTF-8' --cache-location .tmp/sass-cache-8 src/gutenberg-blocks/tainacan-items/search-bar/search-bar.scss:src/assets/css/tainacan-gutenberg-block-search-bar.css
sass -E 'UTF-8' --cache-location .tmp/sass-cache-9 src/gutenberg-blocks/tainacan-items/carousel-items-list/carousel-items-list.scss:src/assets/css/tainacan-gutenberg-block-carousel-items-list.css
sass -E 'UTF-8' --cache-location .tmp/sass-cache-10 src/gutenberg-blocks/tainacan-terms/terms-list/terms-list.scss:src/assets/css/tainacan-gutenberg-block-terms-list.css
sass -E 'UTF-8' --cache-location .tmp/sass-cache-10 src/gutenberg-blocks/tainacan-items/carousel-items-list/carousel-items-list.scss:src/assets/css/tainacan-gutenberg-block-carousel-items-list.css
sass -E 'UTF-8' --cache-location .tmp/sass-cache-11 src/gutenberg-blocks/tainacan-facets/facets-list/facets-list.scss:src/assets/css/tainacan-gutenberg-block-facets-list.css
sass -E 'UTF-8' --cache-location .tmp/sass-cache-11 src/gutenberg-blocks/tainacan-terms/terms-list/terms-list.scss:src/assets/css/tainacan-gutenberg-block-terms-list.css
sass -E 'UTF-8' --cache-location .tmp/sass-cache-12 src/gutenberg-blocks/tainacan-terms/carousel-terms-list/carousel-terms-list.scss:src/assets/css/tainacan-gutenberg-block-carousel-terms-list.css
sass -E 'UTF-8' --cache-location .tmp/sass-cache-12 src/gutenberg-blocks/tainacan-facets/facets-list/facets-list.scss:src/assets/css/tainacan-gutenberg-block-facets-list.css
sass -E 'UTF-8' --cache-location .tmp/sass-cache-13 src/gutenberg-blocks/tainacan-terms/carousel-terms-list/carousel-terms-list.scss:src/assets/css/tainacan-gutenberg-block-carousel-terms-list.css
echo "Compilação do Sass Concluído!"
exit 0

View File

@ -7,7 +7,7 @@
convertWarningsToExceptions="true"
>
<testsuites>
<testsuite>
<testsuite name="default">
<directory prefix="test-" suffix=".php">./tests/</directory>
</testsuite>
</testsuites>

View File

@ -29,14 +29,11 @@ class Admin {
}
function add_admin_menu() {
$dummyEntity = new \Tainacan\Entities\Taxonomy();
// a capability everybody bu subscriber have.
// Maybe we will create a specific cap to view_admin later
$entity_cap = $dummyEntity->get_capabilities()->edit_posts;
$page_suffix = add_menu_page(
__( 'Tainacan', 'tainacan' ),
__( 'Tainacan', 'tainacan' ),
$entity_cap,
'read',
$this->menu_slug,
array( &$this, 'admin_page' ),
plugin_dir_url( __FILE__ ) . 'images/tainacan_logo_symbol.svg'
@ -51,7 +48,17 @@ class Admin {
array( &$this, 'systemcheck_page' )
);
$roles_page_suffix = add_submenu_page(
$this->menu_slug,
__('User Roles', 'tainacan'),
__('User Roles', 'tainacan'),
'tnc_rep_edit_users',
'tainacan_roles',
array( &$this, 'roles_page' )
);
add_action( 'load-' . $page_suffix, array( &$this, 'load_admin_page' ) );
add_action( 'load-' . $roles_page_suffix, array( &$this, 'load_roles_page' ) );
}
function load_admin_page() {
@ -60,6 +67,10 @@ class Admin {
add_action( 'admin_enqueue_scripts', array(&$this, 'add_theme_files') );
}
function load_roles_page() {
add_action( 'admin_enqueue_scripts', array( &$this, 'add_roles_css' ), 90 );
add_action( 'admin_enqueue_scripts', array( &$this, 'add_roles_js' ), 90 );
}
function login_styles_reset( $style ) {
if ( strpos( $style, 'wp-admin-css' ) !== false ) {
@ -82,6 +93,32 @@ class Admin {
wp_enqueue_script('underscore');
}
function add_roles_css() {
global $TAINACAN_BASE_URL;
wp_enqueue_style( 'tainacan-roles-page', $TAINACAN_BASE_URL . '/assets/css/tainacan-roles.css', [], TAINACAN_VERSION );
}
function add_roles_js() {
global $TAINACAN_BASE_URL;
wp_enqueue_script( 'tainacan-roles', $TAINACAN_BASE_URL . '/assets/roles-components.js', ['underscore', 'wp-i18n'], TAINACAN_VERSION, true );
$settings = $this->get_admin_js_localization_params();
wp_localize_script( 'tainacan-roles', 'tainacan_plugin', $settings );
wp_enqueue_script('underscore');
wp_enqueue_script('wp-i18n');
do_action('tainacan-enqueue-roles-scripts');
}
function roles_page() {
global $TAINACAN_BASE_URL;
// TODO move it to a separate file and start the Vue project
echo "<div id='tainacan-roles-app'></div>";
}
function add_admin_css() {
global $TAINACAN_BASE_URL;
@ -170,12 +207,9 @@ class Admin {
$user_caps = array();
$prefs = array();
if ( $cur_user instanceof \WP_User ) {
if ( is_array( $cur_user->allcaps ) ) {
foreach ( $cur_user->allcaps as $cap => $bool ) {
if ( $bool === true ) {
$user_caps[] = $cap;
}
}
$tainacan_caps = \tainacan_roles()->get_repository_caps_slugs();
foreach ($tainacan_caps as $tcap) {
$user_caps[$tcap] = current_user_can( $tcap );
}
$prefs = get_user_meta( $cur_user->ID, 'tainacan_prefs', true );
}
@ -341,6 +375,5 @@ class Admin {
$check = new System_Check();
$check->admin_page();
}
}

View File

@ -5,7 +5,7 @@
<tainacan-title
:bread-crumb-items="[{ path: '', label: $i18n.get('collection') }]"/>
<form
v-if="collection != null && collection != undefined"
v-if="collection != null && collection != undefined && ((isNewCollection && $userCaps.hasCapability('tnc_rep_edit_collections')) || (!isNewCollection && collection.current_user_can_edit))"
class="tainacan-form"
label-width="120px">
@ -256,6 +256,7 @@
</option>
</b-select>
</b-field>
<!-- Comment Status ------------------------ -->
<b-field
:addons="false"
@ -411,49 +412,6 @@
</b-select>
</b-field>
<!-- Moderators List -------------------------------- -->
<b-field
:addons="false"
:label="$i18n.get('label_moderators')"
:type="editFormErrors['moderators'] != undefined ? 'is-danger' : ''"
:message="editFormErrors['moderators'] != undefined ? editFormErrors['moderators'] : ''">
<help-button
:title="$i18n.getHelperTitle('collections', 'moderators_ids')"
:message="$i18n.getHelperMessage('collections', 'moderators_ids')"/>
<b-autocomplete
id="tainacan-text-moderators-input"
:placeholder="$i18n.get('instruction_moderators')"
:data="users"
@select="onAddModerator($event)"
:loading="isFetchingModerators"
@input="fecthModerators($event)"
@focus="clearErrors('moderators')">
<template slot-scope="props">
{{ props.option.name }}
</template>
<template slot="empty">{{ $i18n.get('info_no_user_found') }}</template>
</b-autocomplete>
<ul
class="selected-list-box"
v-if="moderators != undefined && moderators.length > 0">
<li
:key="index"
v-for="(moderator, index) of moderators">
<b-tag
attached
closable
@close="removeModerator(index)">
{{ moderator.name }}
</b-tag>
</li>
</ul>
<div
class="moderators-empty-list"
v-else>
{{ $i18n.get('info_no_moderator_on_collection') }}
</div>
</b-field>
<!-- Slug -------------------------------- -->
<b-field
:addons="false"
@ -498,14 +456,14 @@
style="margin-left: auto;"
class="control">
<button
v-if="isNewCollection"
v-if="isNewCollection && $userCaps.hasCapability('tnc_rep_edit_metadata')"
id="button-submit-goto-metadata"
@click.prevent="onSubmit('metadata')"
class="button is-secondary">{{ $i18n.get('label_save_goto_metadata') }}</button>
</div>
<div class="control">
<button
v-if="isNewCollection"
v-if="isNewCollection && $userCaps.hasCapability('tnc_rep_edit_metadata')"
id="button-submit-goto-filter"
@click.prevent="onSubmit('filters')"
class="button is-secondary">{{ $i18n.get('label_save_goto_filter') }}</button>
@ -520,6 +478,19 @@
<p class="help is-danger">{{ formErrorMessage }}</p>
</form>
<div v-if="!isLoading && ((isNewCollection && !$userCaps.hasCapability('tnc_rep_edit_collections')) || (!isNewCollection && collection && collection.current_user_can_edit != undefined && collection.current_user_can_edit == false))">
<section class="section">
<div class="content has-text-grey has-text-centered">
<p>
<span class="icon">
<i class="tainacan-icon tainacan-icon-30px tainacan-icon-items"/>
</span>
</p>
<p>{{ $i18n.get('info_can_not_edit_collection') }}</p>
</div>
</section>
</div>
<b-loading
:active.sync="isLoading"
:can-cancel="false"/>
@ -549,7 +520,6 @@ export default {
thumbnail: '',
header_image: '',
files:[],
moderators_ids: [],
enabled_view_modes: [],
default_view_mode: [],
allow_comments: ''
@ -568,9 +538,6 @@ export default {
mapper: false,
thumbPlaceholderPath: tainacan_plugin.base_url + '/admin/images/placeholder_square.png',
headerPlaceholderPath: tainacan_plugin.base_url + '/admin/images/placeholder_rectangle.png',
isFetchingModerators: false,
users: [],
moderators: [],
collections: [],
isFetchingCollections: true,
thumbnailMediaFrame: undefined,
@ -596,8 +563,7 @@ export default {
'updateHeaderImage',
'fetchPages',
'fetchPage',
'fetchUsers',
'fetchCollectionsForParent'
'fetchAllCollectionNames'
]),
updateSlug: _.debounce(function() {
if(!this.form.name || this.form.name.length <= 0){
@ -623,9 +589,6 @@ export default {
onSubmit(goTo) {
this.isLoading = true;
this.form.moderators_ids = [];
for (let moderator of this.moderators)
this.form.moderators_ids.push(moderator.id);
let data = {
collection_id: this.collectionId,
@ -635,7 +598,6 @@ export default {
cover_page_id: this.form.cover_page_id,
slug: this.form.slug,
status: this.form.status,
moderators_ids: this.form.moderators_ids,
parent: this.form.parent,
enabled_view_modes: this.form.enabled_view_modes,
default_view_mode: this.form.default_view_mode,
@ -712,7 +674,6 @@ export default {
this.form.parent = this.collection.parent;
this.form.default_view_mode = this.collection.default_view_mode;
this.form.enabled_view_modes = [];
this.moderators = [];
this.form.allow_comments = this.collection.allow_comments;
// Pre-fill status with publish to incentivate it
@ -720,7 +681,7 @@ export default {
// Generates options for parent collection
this.isFetchingCollections = true;
this.fetchCollectionsForParent()
this.fetchAllCollectionNames()
.then((collections) => {
this.collections = collections;
this.isFetchingCollections = false;
@ -733,7 +694,10 @@ export default {
this.isLoading = false;
})
.catch(error => this.$console.error(error));
.catch((error) => {
this.$console.error(error);
this.isLoading = false;
});
},
clearErrors(attribute) {
this.editFormErrors[attribute] = undefined;
@ -774,30 +738,6 @@ export default {
this.coverPageTitle = this.coverPage.title.rendered;
this.coverPageEditPath = tainacan_plugin.admin_url + 'post.php?post=' + selectedPage.id + '&action=edit';
},
fecthModerators(search) {
this.isFetchingModerators = true;
let exceptions = [];
for (let user of this.moderators)
exceptions.push(parseInt(user.id));
exceptions.push(this.collection.author_id);
this.fetchUsers({ search: search, exceptions: exceptions})
.then((users) => {
this.users = users;
this.isFetchingModerators = false;
})
.catch((error) => {
this.$console.error(error);
this.isFetchingPages = false;
});
},
onAddModerator(user) {
this.moderators.push({'id': user.id, 'name': user.name});
},
removeModerator(moderatorIndex) {
this.moderators.splice(moderatorIndex, 1);
},
removeCoverPage() {
this.coverPage = {};
this.coverPageTitle = '';
@ -878,6 +818,7 @@ export default {
// Obtains current Collection ID from URL
this.pathArray = this.$route.path.split("/").reverse();
this.collectionId = this.pathArray[1];
this.fetchCollection(this.collectionId).then(res => {
@ -902,7 +843,6 @@ export default {
this.form.parent = this.collection.parent;
this.form.default_view_mode = this.collection.default_view_mode;
this.form.enabled_view_modes = JSON.parse(JSON.stringify(this.collection.enabled_view_modes.reduce((result, viewMode) => { typeof viewMode == 'string' ? result.push(viewMode) : null; return result }, [])));
this.moderators = JSON.parse(JSON.stringify(this.collection.moderators));
this.form.allow_comments = this.collection.allow_comments;
// Generates CoverPage from current cover_page_id info
@ -925,7 +865,7 @@ export default {
// Generates options for parent collection
this.isFetchingCollections = true;
this.fetchCollectionsForParent()
this.fetchAllCollectionNames()
.then((resp) => {
resp.request.then((collections) => {
this.collections = collections;
@ -1087,10 +1027,6 @@ export default {
align-items: center;
}
}
.moderators-empty-list {
color: $gray4;
font-size: 0.85rem;
}
</style>

View File

@ -42,11 +42,13 @@
:placeholder="$i18n.get('instruction_select_a_target_collection')">
<option
v-for="collection of collections"
v-if="collection.current_user_can_edit_items"
:key="collection.id"
:value="collection.id">{{ collection.name }}
</option>
</b-select>
<router-link
v-if="$userCaps.hasCapability('tnc_rep_edit_collections')"
tag="a"
class="add-link"
:to="{ path: $routerHelper.getNewCollectionPath(), query: { fromImporter: true }}">
@ -216,7 +218,7 @@ export default {
'runImporter'
]),
...mapActions('collection', [
'fetchCollectionsForParent'
'fetchAllCollectionNames'
]),
createImporter() {
// Puts loading on Draft Importer creation
@ -386,7 +388,7 @@ export default {
loadCollections() {
// Generates options for target collection
this.isFetchingCollections = true;
this.fetchCollectionsForParent()
this.fetchAllCollectionNames()
.then((resp) => {
resp.request.then((collections) => {
this.collections = collections;
@ -403,7 +405,7 @@ export default {
},
onSelectCollection(collectionId) {
this.collectionId = collectionId;
this.mappedCollection['id'] = collectionId;
this.mappedCollection['id'] = collectionId;
}
},
created() {

View File

@ -62,7 +62,9 @@
<option :value="undefined">
{{ $i18n.get('label_select_metadatum') }}
</option>
<option :value="'create_metadata' + index">
<option
v-if="collection && collection.current_user_can_edit_metadata"
:value="'create_metadata' + index">
{{ $i18n.get('label_create_metadatum') }}
</option>
<option
@ -158,7 +160,7 @@
</div>
</b-modal>
<a
v-if="collectionId != null && collectionId != undefined && importerSourceInfo.source_metadata.length > 0"
v-if="collectionId != null && collectionId != undefined && importerSourceInfo.source_metadata.length > 0 && collection && collection.current_user_can_edit_metadata"
class="is-inline is-pulled-right add-link has-text-secondary"
@click="createNewMetadatum()">
<span class="icon">
@ -300,6 +302,7 @@ export default {
},
importerType: '',
importerName: '',
collection: undefined,
importerSourceInfo: null,
collections: [],
collectionMetadata: [],
@ -352,6 +355,9 @@ export default {
...mapGetters('bgprocess', [
'getProcess'
]),
...mapActions('collection', [
'fetchCollectionBasics'
]),
loadImporter() {
// Puts loading on Draft Importer creation
@ -589,6 +595,11 @@ export default {
});
this.loadImporter();
this.fetchCollectionBasics({ collectionId: this.collectionId, isContextEdit: true })
.then((collection) => {
this.collection = collection;
});
},
beforeDestroy() {
// Cancels previous Request

View File

@ -14,7 +14,7 @@
<hr>
</div>
<form
v-if="!isLoading"
v-if="!isLoading && collection && collection.current_user_can_bulk_edit"
class="tainacan-form"
label-width="120px">
@ -170,6 +170,18 @@
</div>
</footer>
</form>
<template v-else-if="!isLoading && collection && !collection.current_user_can_bulk_edit">
<section class="section">
<div class="content has-text-grey has-text-centered">
<p>
<span class="icon">
<i class="tainacan-icon tainacan-icon-30px tainacan-icon-collection"/>
</span>
</p>
<p>{{ $i18n.get('info_can_not_bulk_edit_items_collection') }}</p>
</div>
</section>
</template>
</div>
</template>
@ -194,6 +206,9 @@ export default {
computed: {
uploadedFileList() {
return this.getFiles();
},
collection() {
return this.getCollection()
}
},
methods: {
@ -204,6 +219,7 @@ export default {
]),
...mapGetters('collection', [
'getFiles',
'getCollection'
]),
...mapActions('item', [
'sendItem',

View File

@ -11,7 +11,7 @@
v-if="(item != null && item != undefined && item.status != undefined && !isLoading)"
class="status-tag">{{ $i18n.get('status_' + item.status) }}</span>
{{ $i18n.get('title_create_item_collection') + ' ' }}
<span style="font-weight: 600;">{{ collectionName }}</span>
<span style="font-weight: 600;">{{ collection && collection.name ? collection.name : '' }}</span>
</h1>
<h1 v-else>
<span
@ -31,7 +31,7 @@
mode="out-in"
:name="(isOnSequenceEdit && sequenceRightDirection != undefined) ? (sequenceRightDirection ? 'page-right' : 'page-left') : ''">
<form
v-if="!isLoading"
v-if="!isLoading && ((isCreatingNewItem && collection && collection.current_user_can_edit_items) || (!isCreatingNewItem && item && item.current_user_can_edit))"
class="tainacan-form"
label-width="120px">
<div class="columns">
@ -386,7 +386,7 @@
<span class="icon">
<i class="tainacan-icon tainacan-icon-collection"/>
</span>
{{ collectionName }}
{{ collection && collection.name ? collection.name : '' }}
</span>
</div>
</div>
@ -432,7 +432,7 @@
<!-- Comment Status ------------------------ -->
<div
class="column is-narrow"
v-if="collectionAllowComments == 'open'">
v-if="collection && collection.allow_comments && collection.allow_comments == 'open'">
<div class="section-label">
<label>{{ $i18n.get('label_comments') }}</label>
<help-button
@ -536,6 +536,21 @@
</div>
</form>
<!-- In case user enters this page whithout having permission -->
<template v-if="!isLoading && ((isCreatingNewItem && collection && collection.current_user_can_edit_items == false) || (!isCreatingNewItem && item && item.current_user_can_edit != undefined && collection && collection.current_user_can_edit_items == false))">
<section class="section">
<div class="content has-text-grey has-text-centered">
<p>
<span class="icon">
<i class="tainacan-icon tainacan-icon-30px tainacan-icon-items"/>
</span>
</p>
<p>{{ $i18n.get('info_can_not_edit_item') }}</p>
</div>
</section>
</template>
</transition>
<footer class="footer">
<!-- Sequence Progress -->
@ -572,6 +587,7 @@
class="form-submission-footer"
v-if="form.status == 'trash'">
<button
v-if="item && item.current_user_can_delete"
@click="onDeletePermanently()"
type="button"
class="button is-outlined">{{ $i18n.get('label_delete_permanently') }}</button>
@ -580,6 +596,7 @@
type="button"
class="button is-secondary">{{ $i18n.get('label_save_as_draft') }}</button>
<button
v-if="collection && collection.current_user_can_publish_items"
@click="onSubmit(visibility)"
type="button"
class="button is-success">{{ $i18n.get('label_publish') }}</button>
@ -598,7 +615,7 @@
<span>{{ $i18n.get('previous') }}</span>
</button>
<button
v-if="form.status == 'draft' && !isOnSequenceEdit"
v-if="form.status == 'draft' && !isOnSequenceEdit && item && item.current_user_can_delete"
@click="onSubmit('trash')"
type="button"
class="button is-outlined">{{ $i18n.get('label_send_to_trash') }}</button>
@ -622,21 +639,35 @@
<i class="tainacan-icon tainacan-icon-20px tainacan-icon-next"/>
</span>
</button>
<button
v-if="!isOnSequenceEdit || (group != null && group.items_count != undefined && group.items_count == itemPosition)"
@click="onSubmit(visibility)"
type="button"
class="button is-success">{{ $i18n.get('label_publish') }}</button>
<button
v-else
@click="onSubmit(visibility, 'next')"
type="button"
class="button is-success">
<span>{{ $i18n.get('label_publish') }}</span>
<span class="icon is-large">
<i class="tainacan-icon tainacan-icon-20px tainacan-icon-next"/>
</span>
</button>
<template v-if="collection && collection.current_user_can_publish_items">
<button
v-if="!isOnSequenceEdit || (group != null && group.items_count != undefined && group.items_count == itemPosition)"
@click="onSubmit(visibility)"
type="button"
class="button is-success">{{ $i18n.get('label_publish') }}</button>
<button
v-else
@click="onSubmit(visibility, 'next')"
type="button"
class="button is-success">
<span>{{ $i18n.get('label_publish') }}</span>
<span class="icon is-large">
<i class="tainacan-icon tainacan-icon-20px tainacan-icon-next"/>
</span>
</button>
</template>
<template v-else>
<button
v-if="isOnSequenceEdit && (group != null && group.items_count != undefined && group.items_count < itemPosition)"
@click="onNextInSequence()"
type="button"
class="button is-success">
<span>{{ $i18n.get('label_next') }}</span>
<span class="icon is-large">
<i class="tainacan-icon tainacan-icon-20px tainacan-icon-next"/>
</span>
</button>
</template>
<button
v-if="isOnSequenceEdit && (group != null && group.items_count != undefined && group.items_count == itemPosition)"
@click="$router.push($routerHelper.getCollectionPath(form.collectionId))"
@ -662,7 +693,7 @@
<span>{{ $i18n.get('previous') }}</span>
</button>
<button
v-if="!isOnSequenceEdit"
v-if="!isOnSequenceEdit && item && item.current_user_can_delete"
@click="onSubmit('trash')"
type="button"
class="button is-outlined">{{ $i18n.get('label_send_to_trash') }}</button>
@ -681,23 +712,38 @@
<i class="tainacan-icon tainacan-icon-20px tainacan-icon-next"/>
</span>
</button>
<button
v-if="!isOnSequenceEdit || (group != null && group.items_count != undefined && group.items_count == itemPosition)"
:disabled="formErrorMessage != undefined && formErrorMessage != ''"
@click="onSubmit(visibility)"
type="button"
class="button is-success">{{ $i18n.get('label_update') }}</button>
<button
v-else
:disabled="formErrorMessage != undefined && formErrorMessage != ''"
@click="onSubmit(visibility, 'next')"
type="button"
class="button is-success">
<span>{{ $i18n.get('label_update') }}</span>
<span class="icon is-large">
<i class="tainacan-icon tainacan-icon-20px tainacan-icon-next"/>
</span>
</button>
<template v-if="collection && collection.current_user_can_publish_items">
<button
v-if="!isOnSequenceEdit || (group != null && group.items_count != undefined && group.items_count == itemPosition)"
:disabled="formErrorMessage != undefined && formErrorMessage != ''"
@click="onSubmit(visibility)"
type="button"
class="button is-success">{{ $i18n.get('label_update') }}</button>
<button
v-else
:disabled="formErrorMessage != undefined && formErrorMessage != ''"
@click="onSubmit(visibility, 'next')"
type="button"
class="button is-success">
<span>{{ $i18n.get('label_update') }}</span>
<span class="icon is-large">
<i class="tainacan-icon tainacan-icon-20px tainacan-icon-next"/>
</span>
</button>
</template>
<template v-else>
<button
v-if="isOnSequenceEdit && (group != null && group.items_count != undefined && group.items_count < itemPosition)"
:disabled="formErrorMessage != undefined && formErrorMessage != ''"
@click="onNextInSequence()"
type="button"
class="button is-success">
<span>{{ $i18n.get('label_next') }}</span>
<span class="icon is-large">
<i class="tainacan-icon tainacan-icon-20px tainacan-icon-next"/>
</span>
</button>
</template>
<button
v-if="isOnSequenceEdit && (group != null && group.items_count != undefined && group.items_count == itemPosition)"
@click="$router.push($routerHelper.getCollectionPath(form.collectionId))"
@ -759,15 +805,15 @@ export default {
isTextModalActive: false,
textLink: '',
isUpdatingValues: false,
collectionName: '',
collectionAllowComments: '',
entityName: 'item',
activeTab: 0,
isLoadingAttachments: false,
collectionNameSearchCancel: undefined
}
},
computed: {
collection() {
return this.getCollection()
},
metadatumList() {
return JSON.parse(JSON.stringify(this.getMetadata()));
},
@ -843,10 +889,11 @@ export default {
'getLastUpdated'
]),
...mapActions('collection', [
'fetchCollectionName',
'fetchCollectionAllowComments',
'deleteItem',
]),
...mapGetters('collection', [
'getCollection',
]),
...mapActions('bulkedition', [
'fetchItemIdInSequence',
'fetchGroup'
@ -908,9 +955,8 @@ export default {
if (errors.errors) {
for (let error of errors.errors) {
for (let metadatum of Object.keys(error)){
eventBus.errors.push({ metadatum_id: metadatum, errors: error[metadatum]});
}
eventBus.errors.push({ metadatum_id: metadatum, errors: error[metadatum]});
}
}
this.formErrorMessage = errors.error_message;
}
@ -960,7 +1006,10 @@ export default {
.catch(() => this.isLoadingAttachments = false);
})
.catch(error => this.$console.error(error));
.catch((error) => {
this.$console.error(error);
this.isLoading = false;
});
},
loadMetadata() {
// Obtains Item Metadatum
@ -1326,30 +1375,6 @@ export default {
this.fetchGroup({ collectionId: this.collectionId, groupId: this.sequenceId });
}
// Obtains collection name
if (!this.isRepositoryLevel) {
// Cancels previous collection name Request
if (this.collectionNameSearchCancel != undefined)
this.collectionNameSearchCancel.cancel('Collection name search Canceled.');
this.fetchCollectionName(this.collectionId)
.then((resp) => {
resp.request
.then((collectionName) => {
this.collectionName = collectionName;
});
// Search Request Token for cancelling
this.collectionNameSearchCancel = resp.source;
})
}
// Obtains if collection allow items comments
this.fetchCollectionAllowComments(this.collectionId).then((collectionAllowComments) => {
this.collectionAllowComments = collectionAllowComments;
});
// Sets feedback variables
eventBus.$on('isUpdatingValue', (status) => {
this.isUpdatingValues = status;
@ -1365,10 +1390,6 @@ export default {
beforeDestroy () {
eventBus.$off('isUpdatingValue');
eventBus.$off('hasErrorsOnForm');
// Cancels previous collection name Request
if (this.collectionNameSearchCancel != undefined)
this.collectionNameSearchCancel.cancel('Collection name search Canceled.');
},
beforeRouteLeave ( to, from, next ) {
if (this.item.status == 'auto-draft') {

View File

@ -14,6 +14,7 @@
<hr>
</div>
<form
v-if="collection && collection.current_user_can_bulk_edit"
class="tainacan-form"
label-width="120px">
@ -183,6 +184,18 @@
</div>
</footer>
</form>
<template v-if="collection && !collection.current_user_can_bulk_edit">
<section class="section">
<div class="content has-text-grey has-text-centered">
<p>
<span class="icon">
<i class="tainacan-icon tainacan-icon-30px tainacan-icon-collection"/>
</span>
</p>
<p>{{ $i18n.get('info_can_not_bulk_edit_items_collection') }}</p>
</div>
</section>
</template>
</div>
</template>
@ -231,6 +244,9 @@ export default {
},
showLoading() {
return this.isLoadingMetadata || this.isLoadingItemMetadata;
},
collection() {
return this.getCollection()
}
},
methods: {
@ -244,6 +260,9 @@ export default {
...mapGetters('metadata', [
'getMetadata',
]),
...mapGetters('collection', [
'getCollection',
]),
...mapActions('bulkedition', [
'setValueInBulk',
'addValueInBulk',
@ -432,7 +451,8 @@ export default {
this.itemMetadata = metadata;
this.isLoadingItemMetadata = false;
});
});
})
.catch(() => this.isLoadingItemMetadata = false);
}
},
created() {
@ -459,7 +479,8 @@ export default {
this.metadatumCollapses[i] = true;
}
this.loadItemMetadata();
});
})
.catch(() => this.isLoadingMetadata = false);
this.isLoadingGroupInfo = true;
this.fetchGroup({ collectionId: this.collectionId, groupId: this.groupID })

View File

@ -4,14 +4,14 @@
<tainacan-title
:bread-crumb-items="[
{ path: $routerHelper.getTaxonomiesPath(), label: $i18n.get('taxonomies') },
{ path: '', label: (taxonomy!= null && taxonomy.name != undefined) ? taxonomy.name : $i18n.get('taxonomy') }
{ path: '', label: (taxonomy != null && taxonomy.name != undefined) ? taxonomy.name : $i18n.get('taxonomy') }
]"/>
<b-tabs
@change="onChangeTab($event)"
v-model="tabIndex">
<b-tab-item :label="$i18n.get('taxonomy')">
<form
v-if="taxonomy != null && taxonomy != undefined"
v-if="taxonomy != null && taxonomy != undefined && (($route.name == 'TaxonomyCreationForm' && $userCaps.hasCapability('tnc_rep_edit_taxonomies')) || ($route.name == 'TaxonomyEditionForm' && taxonomy.current_user_can_edit))"
class="tainacan-form"
label-width="120px">
<div class="columns">
@ -193,6 +193,20 @@
</div>
<p class="help is-danger">{{ formErrorMessage }}</p>
</form>
<div v-if="!isLoading && (($route.name == 'TaxonomyCreationForm' && !$userCaps.hasCapability('tnc_rep_edit_taxonomies')) || ($route.name == 'TaxonomyEditionForm' && taxonomy && taxonomy.current_user_can_edit != undefined && !taxonomy.current_user_can_edit))">
<section class="section">
<div class="content has-text-grey has-text-centered">
<p>
<span class="icon">
<i class="tainacan-icon tainacan-icon-30px tainacan-icon-taxonomies"/>
</span>
</p>
<p>{{ $i18n.get('info_can_not_edit_taxonomy') }}</p>
</div>
</section>
</div>
</b-tab-item>
<b-tab-item :label="$i18n.get('terms')">
@ -200,7 +214,8 @@
<terms-list
:key="shouldReloadTermsList ? 'termslistreloaded' : 'termslist'"
@isEditingTermUpdate="isEditingTermUpdate"
:taxonomy-id="taxonomyId"/>
:taxonomy-id="taxonomyId"
:current-user-can-edit-taxonomy="taxonomy ? taxonomy.current_user_can_edit : false"/>
</b-tab-item>
<b-loading
@ -250,20 +265,22 @@
beforeRouteLeave( to, from, next ) {
let formNotSaved = false;
if (this.taxonomy.name != this.form.name)
formNotSaved = true;
if (this.taxonomy.description != this.form.description)
formNotSaved = true;
if (this.taxonomy.slug != this.form.slug)
formNotSaved = true;
if (this.taxonomy.allow_insert != this.form.allowInsert)
formNotSaved = true;
if (this.taxonomy.status != this.form.status)
formNotSaved = true;
if (this.taxonomy.enabled_post_types != this.form.enabledPostTypes)
formNotSaved = true;
if (this.taxonomy) {
if (this.taxonomy.name != this.form.name)
formNotSaved = true;
if (this.taxonomy.description != this.form.description)
formNotSaved = true;
if (this.taxonomy.slug != this.form.slug)
formNotSaved = true;
if (this.taxonomy.allow_insert != this.form.allowInsert)
formNotSaved = true;
if (this.taxonomy.status != this.form.status)
formNotSaved = true;
if (this.taxonomy.enabled_post_types != this.form.enabledPostTypes)
formNotSaved = true;
}
if (formNotSaved) {
if (formNotSaved && this.taxonomy) {
this.$buefy.modal.open({
parent: this,
component: CustomDialog,
@ -324,14 +341,14 @@
slug: this.form.slug ? this.form.slug : '',
status: this.form.status,
allow_insert: this.form.allowInsert,
enabled_post_types: this.form.enabledPostTypes
enabled_post_types: this.form.enabledPostTypes,
context: 'edit'
};
this.fillExtraFormData(data);
this.updateTaxonomy(data)
.then(updatedTaxonomy => {
this.taxonomy = updatedTaxonomy;
// Fills hook forms with it's real values
this.updateExtraFormData(this.taxonomy);
@ -346,7 +363,6 @@
this.isLoadingTaxonomy = false;
this.formErrorMessage = '';
this.editFormErrors = {};
// Updates saved at message
let now = new Date();
this.updatedAt = now.toLocaleString();
@ -420,7 +436,10 @@
this.shouldReloadTermsList = false;
})
.catch(error => this.$console.error(error));
.catch((error) => {
this.$console.error(error)
this.isLoadingTaxonomy = false;
});
},
clearErrors(attribute) {
this.editFormErrors[attribute] = undefined;
@ -474,25 +493,27 @@
this.pathArray = this.$route.fullPath.split("/").reverse();
this.taxonomyId = this.pathArray[1];
this.fetchTaxonomy(this.taxonomyId).then(res => {
this.taxonomy = res.taxonomy;
this.fetchTaxonomy({ taxonomyId: this.taxonomyId, isContextEdit: true })
.then(res => {
this.taxonomy = res.taxonomy;
// Fills hook forms with it's real values
this.$nextTick()
.then(() => {
this.updateExtraFormData(this.taxonomy);
});
// Fills hook forms with it's real values
this.$nextTick()
.then(() => {
this.updateExtraFormData(this.taxonomy);
});
// Fill this.form data with current data.
this.form.name = this.taxonomy.name;
this.form.description = this.taxonomy.description;
this.form.slug = this.taxonomy.slug;
this.form.status = this.taxonomy.status;
this.form.allowInsert = this.taxonomy.allow_insert;
this.form.enabledPostTypes = this.taxonomy.enabled_post_types;
// Fill this.form data with current data.
this.form.name = this.taxonomy.name;
this.form.description = this.taxonomy.description;
this.form.slug = this.taxonomy.slug;
this.form.status = this.taxonomy.status;
this.form.allowInsert = this.taxonomy.allow_insert;
this.form.enabledPostTypes = this.taxonomy.enabled_post_types;
this.isLoadingTaxonomy = false;
});
this.isLoadingTaxonomy = false;
})
.catch(() => this.isLoadingTaxonomy = false);
}
}
}

View File

@ -114,9 +114,11 @@
<div v-if="(totalActivities <= 0 || !totalActivities) && !isLoading">
<section class="section">
<div class="content has-text-grey has-text-centered">
<span class="icon">
<i class="tainacan-icon tainacan-icon-20px tainacan-icon-activities"/>
</span>
<p>
<span class="icon">
<i class="tainacan-icon tainacan-icon-30px tainacan-icon-activities"/>
</span>
</p>
<p>{{ $i18n.get('info_no_activities') }}</p>
</div>
</section>

View File

@ -40,9 +40,11 @@
<div v-if="(totalAttachments <= 0 || !totalAttachments) && !isLoading">
<section class="section">
<div class="content has-text-grey has-text-centered">
<span class="icon">
<i class="tainacan-icon tainacan-icon-20px tainacan-icon-attachments"/>
</span>
<p>
<span class="icon">
<i class="tainacan-icon tainacan-icon-30px tainacan-icon-attachments"/>
</span>
</p>
<p>{{ $i18n.get('info_no_attachments_on_item_yet') }}</p>
</div>
</section>

View File

@ -21,6 +21,7 @@
</span>
</span>
<span
v-if="currentUserCanEditTaxonomy"
class="controls"
:class="{'is-disabled': isEditingTerm}">
<a
@ -60,7 +61,7 @@ export default {
data(){
return {
isLoadingTerms: false,
isEditingTerm: false,
isEditingTerm: false
}
},
props: {
@ -68,6 +69,7 @@ export default {
index: Number,
taxonomyId: Number,
order: String,
currentUserCanEditTaxonomy: Boolean
},
methods: {
...mapActions('taxonomy', [

View File

@ -0,0 +1,312 @@
<template>
<div class="table-container">
<div class="table-wrapper">
<table class="tainacan-table is-narrow">
<thead>
<tr>
<!-- Name -->
<th>
<div class="th-wrap">{{ $i18n.get('label_name') }}</div>
</th>
<!-- Description -->
<th>
<div class="th-wrap">{{ $i18n.get('label_description') }}</div>
</th>
<!-- Capability date -->
<th>
<div class="th-wrap">{{ $i18n.get('label_user_roles') }}</div>
</th>
<!-- Actions -->
<th class="actions-header">
&nbsp;
<!-- nothing to show on header for actions cell-->
</th>
</tr>
</thead>
<tbody v-if="!isLoading">
<template v-for="(capability, index) of capabilities">
<tr
:key="index"
:style="index == editingCapability ? 'background-color: #f2f2f2' : ''">
<!-- Name -->
<td
class="column-default-width column-main-content"
:label="$i18n.get('label_name')"
:aria-label="$i18n.get('label_name') + ': ' + capability.display_name">
<p
v-tooltip="{
delay: {
show: 500,
hide: 120,
},
content: capability.display_name,
autoHide: false,
classes: ['tooltip', 'repository-tooltip'],
placement: 'auto-start'
}">
{{ capability.display_name }}
</p>
</td>
<!-- Description -->
<td
class="table-creation column-large-width"
:label="$i18n.get('label_description')"
:aria-label="$i18n.get('label_description') + ': ' + capability.description">
<p
v-tooltip="{
delay: {
show: 500,
hide: 120,
},
content: capability.description,
autoHide: false,
classes: ['tooltip', 'repository-tooltip'],
placement: 'auto-start'
}"
v-html="capability.description"/>
</td>
<!-- Associated Roles -->
<complete-roles-list
v-if="capability.roles"
:complete-roles-list="getCompleteRolesList(capability.roles, capability.roles_inherited)">
<td
slot-scope="props"
class="table-creation column-small-width"
:label="$i18n.get('label_associated_roles')"
:aria-label="$i18n.get('label_associated_roles') + ': ' + props['complete-roles-list']">
<p
v-tooltip="{
delay: {
show: 500,
hide: 120,
},
content: props['complete-roles-list'],
autoHide: false,
classes: ['tooltip', 'repository-tooltip'],
placement: 'auto-start'
}"
v-html="props['complete-roles-list']"/>
</td>
</complete-roles-list>
<!-- Actions -->
<td
class="actions-cell column-default-width"
:label="$i18n.get('label_actions')">
<div
class="actions-container"
:style="index == editingCapability ? 'background-color: #dbdbdb' : ''">
<a
id="button-edit"
:aria-label="$i18n.get('edit')"
@click="toggleEditForm(index)">
<span
v-tooltip="{
content: $i18n.get('edit'),
autoHide: true,
classes: ['tooltip', 'repository-tooltip'],
placement: 'auto'
}"
class="icon">
<i class="tainacan-icon tainacan-icon-20px tainacan-icon-edit"/>
</span>
</a>
</div>
</td>
</tr>
<tr
:key="index + '-form'"
class="capabilities-edit-form">
<transition name="form-capabilities">
<td
v-if="index == editingCapability"
class="tainacan-form"
colspan="5">
<div>
<template v-if="existingRoles && Object.values(existingRoles).length && capability.roles">
<b-field :addons="false">
<label class="label is-inline-block">
{{ $i18n.get('label_inherited_roles') }}
<help-button
:title="$i18n.get('label_inherited_roles')"
:message="$i18n.get('info_inherited_roles')"/>
</label>
<div class="roles-list">
<b-checkbox
v-if="capability.roles_inherited[role.slug]"
v-for="(role, roleIndex) of existingRoles"
:key="roleIndex"
size="is-small"
:value="capability.roles[role.slug] || capability.roles_inherited[role.slug] ? true : false"
name="roles_inherited"
disabled>
{{ role.name }}
</b-checkbox>
</div>
</b-field>
</template>
<p
v-else
class="is-italic has-text-gray">
{{ $i18n.get('info_no_role_associated_capability') }}
</p>
</div>
<div>
<template v-if="existingRoles && Object.values(existingRoles).length && capability.roles">
<b-field :addons="false">
<label class="label is-inline-block">
{{ $i18n.get('label_associated_roles') }}
<help-button
:title="$i18n.get('label_associated_roles')"
:message="$i18n.get('info_associated_roles')"/>
</label>
<div class="roles-list">
<b-checkbox
v-if="!capability.roles_inherited[role.slug]"
v-for="(role, roleIndex) of existingRoles"
:key="roleIndex"
size="is-small"
:value="capability.roles[role.slug] || capability.roles_inherited[role.slug] ? true : false"
@input="($event) => updateRole(role.slug, index, $event)"
name="roles">
{{ role.name }}
</b-checkbox>
</div>
</b-field>
</template>
<p
v-else
class="is-italic has-text-gray">
{{ $i18n.get('info_no_role_associated_capability') }}
</p>
</div>
</td>
</transition>
</tr>
</template>
</tbody>
</table>
</div>
</div>
</template>
<script>
import { mapActions } from 'vuex';
// Auxiliary component for avoinding multiple calls to getCompleteRolesList
const CompleteRolesList = {
render() {
return this.$scopedSlots.default(this.$attrs)
}
}
export default {
name: 'CapabilitiesList',
props: {
isLoading: false,
capabilities: Array,
editingCapability: ''
},
components: {
CompleteRolesList
},
data() {
return {
existingRoles: []
}
},
methods: {
...mapActions('capability', [
'fetchRoles',
'addCapabilityToRole',
'removeCapabilityFromRole'
]),
toggleEditForm(capabilityKey) {
if (this.editingCapability == capabilityKey)
this.editingCapability = ''
else
this.editingCapability = capabilityKey;
},
updateRole(role, capabilityKey, value) {
if (value)
this.addCapabilityToRole({ capabilityKey: capabilityKey.replace('%d', 'all'), role: role })
else
this.removeCapabilityFromRole({ capabilityKey: capabilityKey.replace('%d', 'all'), role: role })
},
getCompleteRolesList(roles, rolesInherited) {
const rolesArray = roles && !Array.isArray(roles) ? Object.values(roles) : [];
const rolesInheritedArray = rolesInherited && !Array.isArray(rolesInherited) ? Object.values(rolesInherited) : [];
if (rolesArray.length || rolesInheritedArray.length) {
const completeRoles = rolesArray.map(role => role.name).concat(rolesInheritedArray.map(roleInherited => roleInherited.name))
let completeRolesString = '';
for (let i = 0; i < completeRoles.length; i++) {
completeRolesString += completeRoles[i];
if (completeRoles.length > 2 && i < completeRoles.length - 1) {
if (i < completeRoles.length - 2)
completeRolesString += ', '
else
completeRolesString += ' ' + this.$i18n.get('label_and') + ' ';
} else if (completeRoles.length == 2 && i == 0) {
completeRolesString += ' ' + this.$i18n.get('label_and') + ' ';
}
}
return completeRolesString
}
else
return '<span class=is-italic>' + this.$i18n.get('info_no_associated_role') + '</span>';
}
},
created() {
this.fetchRoles().then(roles => this.existingRoles = roles);
}
}
</script>
<style scoped lang="scss">
.table-container .table-wrapper table.tainacan-table tbody tr {
cursor: default;
&.capabilities-edit-form td {
min-height: 120px;
padding: 12px 24px;
background-color: #f6f6f6;
vertical-align: top;
&>div {
display: table-cell;
padding: 12px 24px;
&:last-of-type {
border-left: 1px solid #cbcbcb;
}
}
.roles-list {
column-count: 3;
break-inside: avoid;
label {
margin-left: 12px;
}
}
@media screen and (max-width: 1280px) {
.roles-list {
column-count: 2;
}
}
@media screen and (max-width: 1024px) {
.roles-list {
column-count: 1;
}
&>div:last-of-type {
border-left: 0px solid #cbcbcb;
border-top: 0px solid #cbcbcb;
}
}
}
&.capabilities-edit-form:hover {
background-color: #f6f6f6 !important;
}
}
</style>

View File

@ -2,7 +2,7 @@
<div
style="min-height: initial; position: relative"
class="tainacan-cards-container">
<template v-if="collections.length <= 0 && !isLoading">
<template v-if="collections.length <= 0 && !isLoading && $userCaps.hasCapability('tnc_rep_edit_collections')">
<ul class="new-collection-menu">
<li>
<router-link
@ -76,6 +76,7 @@
:gutter="25"
style="width:100%;">
<router-link
v-if="$userCaps.hasCapability('tnc_rep_edit_collections')"
tag="a"
:to="$routerHelper.getNewCollectionPath()"
class="tainacan-card new-card">
@ -170,7 +171,7 @@
<!-- <span class="menu-text">{{ $i18n.get('label_settings') }}</span> -->
</router-link>
</li>
<li>
<li v-if="collection.current_user_can_edit_metadata">
<router-link
tag="a"
:to="{ path: $routerHelper.getCollectionMetadataPath(collection.id) }"
@ -185,7 +186,7 @@
<!-- <span class="menu-text">{{ $i18n.getFrom('metadata', 'name') }}</span> -->
</router-link>
</li>
<li>
<li v-if="collection.current_user_can_edit_filters">
<router-link
tag="a"
:to="{ path: $routerHelper.getCollectionFiltersPath(collection.id) }"
@ -201,7 +202,7 @@
<!-- <span class="menu-text">{{ $i18n.getFrom('filters', 'name') }}</span> -->
</router-link>
</li>
<li>
<li v-if="$userCaps.hasCapability('tnc_rep_read_logs')">
<router-link
tag="a"
:to="{ path: $routerHelper.getCollectionActivitiesPath(collection.id) }"

View File

@ -2,7 +2,9 @@
<div
v-if="collections.length > 0 && !isLoading"
class="table-container">
<div class="selection-control">
<div
v-if="$userCaps.hasCapability('tnc_rep_delete_collections')"
class="selection-control">
<div class="field select-all is-pulled-left">
<span>
<b-checkbox
@ -13,7 +15,6 @@
<div class="field is-pulled-right">
<b-dropdown
position="is-bottom-left"
v-if="$userCaps.hasCapability('delete_tainacan-collections')"
:disabled="!isSelectingCollections"
id="bulk-actions-dropdown"
aria-role="list"
@ -74,22 +75,21 @@
</b-dropdown-item>
<b-dropdown-item
@click="goToCollectionEditPage(contextMenuCollection)"
v-if="contextMenuCollection != null">
v-if="contextMenuCollection != null && (collections[contextMenuIndex] && collections[contextMenuIndex].current_user_can_edit)">
{{ $i18n.getFrom('collections', 'edit_item') }}
</b-dropdown-item>
<b-dropdown-item
@click="deleteOneCollection(contextMenuCollection)"
v-if="contextMenuCollection != null">
v-if="contextMenuCollection != null && (collections[contextMenuIndex] && collections[contextMenuIndex].current_user_can_delete)">
{{ $i18n.get('label_delete_collection') }}
</b-dropdown-item>
</b-dropdown>
</div>
<table class="tainacan-table">
<thead>
<tr>
<!-- Checking list -->
<th>
<th v-if="$userCaps.hasCapability('tnc_rep_delete_collections')">
&nbsp;
<!-- nothing to show on header -->
</th>
@ -117,7 +117,9 @@
<th v-if="!isOnTrash">
<div class="th-wrap total-items-header">{{ $i18n.get('label_total_items') }}</div>
</th>
<th class="actions-header">
<th
v-if="collections.findIndex((collection) => collection.current_user_can_edit || collection.current_user_can_delete) >= 0"
class="actions-header">
&nbsp;
<!-- nothing to show on header for actions cell-->
</th>
@ -130,6 +132,7 @@
v-for="(collection, index) of collections">
<!-- Checking list -->
<td
v-if="$userCaps.hasCapability('tnc_rep_delete_collections')"
:class="{ 'is-selecting': isSelectingCollections }"
class="checkbox-cell">
<b-checkbox
@ -253,8 +256,10 @@
</td>
<!-- Actions -->
<td
v-if="collection.current_user_can_edit || collection.current_user_can_delete"
@click="onClickCollection($event, collection.id, index)"
class="actions-cell column-default-width"
class="column-default-width"
:class="{ 'actions-cell': collection.current_user_can_edit || collection.current_user_can_delete }"
:label="$i18n.get('label_actions')">
<div class="actions-container">
<a

View File

@ -6,7 +6,7 @@
class="tainacan-page-title">
<h1>
{{ $i18n.get('title_collection_filters_edition') + ' ' }}
<span style="font-weight: 600;">{{ collectionName }}</span>
<span style="font-weight: 600;">{{ collection && collection.name ? collection.name : '' }}</span>
</h1>
<a
@click="$router.go(-1)"
@ -18,6 +18,7 @@
<p v-if="isRepositoryLevel">{{ $i18n.get('info_repository_filters_inheritance') }}</p>
<br>
<div
v-if="(isRepositoryLevel && $userCaps.hasCapability('tnc_rep_edit_filters') || (!isRepositoryLevel && collection && collection.current_user_can_edit_filters))"
:style="{ height: activeFilterList.length <= 0 && !isLoadingFilters ? 'auto' : 'calc(100vh - 6px - ' + columnsTopY + 'px)'}"
class="columns"
ref="filterEditionPageColumns">
@ -49,7 +50,7 @@
<div
class="active-filter-item"
:class="{
'not-sortable-item': (isSelectingFilterType || filter.id == undefined || openedFilterId != '' || choosenMetadatum.name == filter.name || isUpdatingFiltersOrder == true || isRepositoryLevel),
'not-sortable-item': (isSelectingFilterType || filter.id == undefined || openedFilterId != '' || choosenMetadatum.name == filter.name || isUpdatingFiltersOrder == true),
'not-focusable-item': openedFilterId == filter.id,
'disabled-filter': filter.enabled == false,
'inherited-filter': filter.collection_id != collectionId || isRepositoryLevel
@ -60,7 +61,7 @@
<span
v-if="!(isSelectingFilterType || filter.id == undefined || openedFilterId != '' || choosenMetadatum.name == filter.name || isUpdatingFiltersOrder == true || isRepositoryLevel)"
v-tooltip="{
content: $i18n.get('instruction_drag_and_drop_filter_sort'),
content: (isSelectingFilterType || filter.id == undefined || openedFilterId != '' || choosenMetadatum.name == filter.name || isUpdatingFiltersOrder == true) ? $i18n.get('info_not_allowed_change_order_filters') : $i18n.get('instruction_drag_and_drop_filter_sort'),
autoHide: true,
classes: ['tooltip', isRepositoryLevel ? 'repository-tooltip' : ''],
placement: 'auto-start'
@ -114,6 +115,7 @@
:value="filter.enabled"
@input="onChangeEnable($event, index)"/>
<a
v-if="filter.current_user_can_delete"
:style="{ visibility: filter.collection_id != collectionId && !isRepositoryLevel? 'hidden' : 'visible' }"
@click.prevent="toggleFilterEdition(filter.id)">
<span
@ -128,6 +130,7 @@
</span>
</a>
<a
v-if="filter.current_user_can_delete"
:style="{ visibility: filter.collection_id != collectionId && !isRepositoryLevel ? 'hidden' : 'visible' }"
@click.prevent="removeFilter(filter)">
<span
@ -157,7 +160,9 @@
</div>
</draggable>
</div>
<div class="column available-metadata-area">
<div
v-if="(isRepositoryLevel && $userCaps.hasCapability('tnc_rep_edit_filters') || !isRepositoryLevel)"
class="column available-metadata-area">
<div class="field" >
<h3 class="label has-text-secondary"> {{ $i18n.get('label_available_metadata') }}</h3>
<draggable
@ -227,6 +232,21 @@
</div>
</div>
</div>
<section
v-else
class="section">
<div class="content has-text-grey has-text-centered">
<p>
<span class="icon">
<i class="tainacan-icon tainacan-icon-30px tainacan-icon-filters"/>
</span>
</p>
<p>{{ $i18n.get('info_can_not_edit_filters') }}</p>
</div>
</section>
<b-modal
ref="filterTypeModal"
:width="680"
@ -318,7 +338,6 @@ export default {
data(){
return {
collectionId: '',
collectionName: '',
isRepositoryLevel: false,
isDraggingFromAvailable: false,
isLoadingMetadatumTypes: true,
@ -339,8 +358,7 @@ export default {
currentFilterTypePreview: undefined,
columnsTopY: 0,
filtersSearchCancel: undefined,
metadataSearchCancel: undefined,
collectionNameSearchCancel: undefined
metadataSearchCancel: undefined
}
},
computed: {
@ -351,6 +369,9 @@ export default {
set(value) {
this.updateFilters(value);
}
},
collection() {
return this.getCollection();
}
},
components: {
@ -414,8 +435,8 @@ export default {
...mapGetters('metadata', [
'getMetadata',
]),
...mapActions('collection', [
'fetchCollectionName'
...mapGetters('collection', [
'getCollection',
]),
handleChangeOnFilter($event) {
if ($event.added) {
@ -680,7 +701,7 @@ export default {
this.$root.$emit('onCollectionBreadCrumbUpdate', [{ path: '', label: this.$i18n.get('filter') }]);
this.$nextTick(() => {
this.columnsTopY = this.$refs.filterEditionPageColumns.getBoundingClientRect().top;
this.columnsTopY = this.$refs.filterEditionPageColumns ? this.$refs.filterEditionPageColumns.getBoundingClientRect().top : 0;
});
this.isRepositoryLevel = this.$route.name == 'FiltersPage' ? true : false;
@ -707,25 +728,6 @@ export default {
// Loads Filters
this.refreshFilters();
// Obtains collection name
if (!this.isRepositoryLevel) {
// Cancels previous collection name Request
if (this.collectionNameSearchCancel != undefined)
this.collectionNameSearchCancel.cancel('Collection name search Canceled.');
this.fetchCollectionName(this.collectionId)
.then((resp) => {
resp.request
.then((collectionName) => {
this.collectionName = collectionName;
});
// Search Request Token for cancelling
this.collectionNameSearchCancel = resp.source;
})
}
// Sets modal callback function
this.$refs.filterTypeModal.onCancel = () => {
@ -741,10 +743,6 @@ export default {
// Cancels previous metadata Request
if (this.metadataSearchCancel != undefined)
this.metadataSearchCancel.cancel('Metadata search Canceled.');
// Cancels previous collection name Request
if (this.collectionNameSearchCancel != undefined)
this.collectionNameSearchCancel.cancel('Collection name search Canceled.');
}
}
</script>

View File

@ -3,7 +3,7 @@
<!-- TODO: Remove v-if="collectionId" from this element when the bulk edit in repository is done -->
<div
v-if="collectionId"
v-if="collectionId && collection && collection.current_user_can_edit_items && collection.current_user_can_bulk_edit"
class="selection-control">
<div class="field select-all is-pulled-left">
<span>
@ -28,7 +28,7 @@
<b-dropdown
:mobile-modal="true"
position="is-bottom-left"
v-if="items.length > 0 && items[0].current_user_can_edit"
v-if="items.length > 0"
:disabled="selectedItems.length <= 1"
id="bulk-actions-dropdown"
aria-role="list"
@ -43,19 +43,19 @@
</button>
<b-dropdown-item
v-if="$route.params.collectionId && $userCaps.hasCapability('edit_others_posts') && !isOnTrash"
v-if="$route.params.collectionId && !isOnTrash"
@click="openBulkEditionModal()"
aria-role="listitem">
{{ $i18n.get('label_bulk_edit_selected_items') }}
</b-dropdown-item>
<b-dropdown-item
v-if="$route.params.collectionId && $userCaps.hasCapability('edit_others_posts') && !isOnTrash"
v-if="$route.params.collectionId && !isOnTrash"
@click="sequenceEditSelectedItems()"
aria-role="listitem">
{{ $i18n.get('label_sequence_edit_selected_items') }}
</b-dropdown-item>
<b-dropdown-item
v-if="collectionId"
v-if="collectionId && collection && collection.current_user_can_delete_items"
@click="deleteSelectedItems()"
id="item-delete-selected-items"
aria-role="listitem">
@ -83,7 +83,7 @@
@click.left="clearContextMenu()"
@click.right="clearContextMenu()"
class="context-menu-backdrop" />
<b-dropdown
inline
:style="{ top: cursorPosY + 'px', left: cursorPosX + 'px' }"
@ -136,7 +136,7 @@
<!-- Checkbox -->
<!-- TODO: Remove v-if="collectionId" from this element when the bulk edit in repository is done -->
<div
v-if="collectionId && !$route.query.readmode"
v-if="collectionId && !$route.query.readmode && ($route.query.iframemode || collection && collection.current_user_can_bulk_edit)"
:class="{ 'is-selecting': isSelectingItems }"
class="grid-item-checkbox">
<b-checkbox
@ -147,7 +147,7 @@
<!-- Title -->
<div
:style="{ 'padding-left': !collectionId ? '0.5rem !important' : '2.75rem' }"
:style="{ 'padding-left': !collectionId || !($route.query.iframemode || collection && collection.current_user_can_bulk_edit) || $route.query.readmode ? '0.5rem !important' : '2.75rem' }"
class="metadata-title">
<p
v-tooltip="{
@ -213,7 +213,7 @@
</span>
</a>
<a
v-if="collectionId"
v-if="item.current_user_can_delete"
id="button-delete"
:aria-label="$i18n.get('label_button_delete')"
@click.prevent.stop="deleteOneItem(item.id)">
@ -253,7 +253,7 @@
<!-- Checkbox -->
<!-- TODO: Remove v-if="collectionId" from this element when the bulk edit in repository is done -->
<div
v-if="collectionId && !$route.query.readmode"
v-if="collectionId && !$route.query.readmode && ($route.query.iframemode || collection && collection.current_user_can_bulk_edit)"
:class="{ 'is-selecting': isSelectingItems }"
class="masonry-item-checkbox">
<label
@ -271,7 +271,7 @@
<!-- Title -->
<div
:style="{
'padding-left': !collectionId ? '0 !important' : '1rem'
'padding-left': !collectionId || !($route.query.iframemode || collection && collection.current_user_can_bulk_edit) || $route.query.readmode ? '0 !important' : '1rem'
}"
@click.left="onClickItem($event, item)"
@click.right="onRightClickItem($event, item)"
@ -326,7 +326,7 @@
</span>
</a>
<a
v-if="collectionId"
v-if="item.current_user_can_delete"
id="button-delete"
:aria-label="$i18n.get('label_button_delete')"
@click.prevent.stop="deleteOneItem(item.id)">
@ -361,7 +361,7 @@
<!-- Checkbox -->
<!-- TODO: Remove v-if="collectionId" from this element when the bulk edit in repository is done -->
<div
v-if="collectionId && !$route.query.readmode"
v-if="collectionId && !$route.query.readmode && ($route.query.iframemode || collection && collection.current_user_can_bulk_edit)"
:class="{ 'is-selecting': isSelectingItems }"
class="card-checkbox">
<b-checkbox
@ -372,7 +372,10 @@
<!-- Title -->
<div
:style="{ 'padding-left': !collectionId ? '0.5rem !important' : '2.75rem' }"
:style="{
'padding-left': !collectionId || $route.query.readmode || !($route.query.iframemode || collection && collection.current_user_can_bulk_edit) ? '0.5rem !important' : '2.75rem',
'margin-bottom': item.current_user_can_edit && !$route.query.iframemode ? '-26px' : '0px'
}"
class="metadata-title">
<p
v-tooltip="{
@ -425,7 +428,7 @@
</span>
</a>
<a
v-if="collectionId"
v-if="item.current_user_can_delete"
id="button-delete"
:aria-label="$i18n.get('label_button_delete')"
@click.prevent.stop="deleteOneItem(item.id)">
@ -525,7 +528,7 @@
<!-- Checkbox -->
<!-- TODO: Remove v-if="collectionId" from this element when the bulk edit in repository is done -->
<div
v-if="collectionId && !$route.query.readmode"
v-if="collectionId && !$route.query.readmode && ($route.query.iframemode || collection && collection.current_user_can_bulk_edit)"
:class="{ 'is-selecting': isSelectingItems }"
class="record-checkbox">
<label
@ -543,7 +546,10 @@
<!-- Title -->
<div
class="metadata-title"
:style="{ 'padding-left': !collectionId ? '1.5rem !important' : '2.75rem' }">
:style="{
'padding-left': !collectionId || !($route.query.iframemode || collection && collection.current_user_can_bulk_edit) || $route.query.readmode ? '1.5rem !important' : '2.75rem',
'margin-bottom': item.current_user_can_edit || $route.query.iframemode ? '-27px' : '0px'
}">
<p
v-tooltip="{
delay: {
@ -614,7 +620,7 @@
</span>
</a>
<a
v-if="collectionId"
v-if="item.current_user_can_delete"
id="button-delete"
:aria-label="$i18n.get('label_button_delete')"
@click.prevent.stop="deleteOneItem(item.id)">
@ -687,7 +693,7 @@
<tr>
<!-- Checking list -->
<th
v-if="collectionId && !$route.query.readmode">
v-if="collectionId && !$route.query.readmode && ($route.query.iframemode || collection && collection.current_user_can_bulk_edit)">
&nbsp;
<!-- nothing to show on header for checkboxes -->
</th>
@ -709,7 +715,9 @@
}">
<div class="th-wrap">{{ column.name }}</div>
</th>
<th class="actions-header">
<th
v-if="items.findIndex((item) => item.current_user_can_edit || item.current_user_can_delete) >= 0"
class="actions-header">
&nbsp;
<!-- nothing to show on header for actions cell-->
</th>
@ -726,7 +734,7 @@
<!-- Checking list -->
<!-- TODO: Remove v-if="collectionId" from this element when the bulk edit in repository is done -->
<td
v-if="collectionId && !$route.query.readmode"
v-if="collectionId && !$route.query.readmode && ($route.query.iframemode || collection && collection.current_user_can_bulk_edit)"
:class="{ 'is-selecting': isSelectingItems }"
class="checkbox-cell">
<b-checkbox
@ -847,7 +855,7 @@
<!-- Actions -->
<td
v-if="item.current_user_can_edit && !$route.query.iframemode"
v-if="(item.current_user_can_edit || item.current_user_can_delete) && !$route.query.iframemode"
class="actions-cell"
:label="$i18n.get('label_actions')">
<div class="actions-container">
@ -881,7 +889,7 @@
</span>
</a>
<a
v-if="collectionId"
v-if="item.current_user_can_delete"
id="button-delete"
:aria-label="$i18n.get('label_button_delete')"
@click.prevent.stop="deleteOneItem(item.id)">
@ -943,6 +951,9 @@ export default {
setTimeout(() => this.$eventBusSearch.highlightsItem(null), 3000);
},
computed: {
collection() {
return this.getCollection();
},
highlightedItem () {
return this.getHighlightedItem();
},
@ -987,6 +998,9 @@ export default {
...mapActions('collection', [
'deleteItem',
]),
...mapGetters('collection', [
'getCollection',
]),
...mapActions('bulkedition', [
'createEditGroup',
'trashItemsInBulk',

View File

@ -2,12 +2,12 @@
<div class="metadata-list-page">
<b-loading :active.sync="isLoadingMetadatumTypes"/>
<b-loading :active.sync="isLoadingMetadatumMappers"/>
<div
<div
v-if="!isRepositoryLevel"
class="tainacan-page-title">
<h1>
{{ $i18n.get('title_collection_metadata_edition') + ' ' }}
<span style="font-weight: 600;">{{ collectionName }}</span>
<span style="font-weight: 600;">{{ collection && collection.name ? collection.name : '' }}</span>
</h1>
<a
@click="$router.go(-1)"
@ -18,7 +18,9 @@
</div>
<p v-if="isRepositoryLevel">{{ $i18n.get('info_repository_metadata_inheritance') }}</p>
<br>
<b-tabs v-model="activeTab">
<b-tabs
v-if="(isRepositoryLevel && $userCaps.hasCapability('tnc_rep_edit_metadata') || (!isRepositoryLevel && collection && collection.current_user_can_edit_metadata))"
v-model="activeTab">
<b-tab-item :label="$i18n.get('metadata')">
<div
:style="{ height: activeMetadatumList.length <= 0 && !isLoadingMetadata ? 'auto' : 'calc(100vh - 6px - ' + columnsTopY + 'px)'}"
@ -64,7 +66,7 @@
<span
v-if="!(isRepositoryLevel || metadatum.id == undefined || openedMetadatumId != '' || isUpdatingMetadataOrder)"
v-tooltip="{
content: $i18n.get('instruction_drag_and_drop_metadatum_sort'),
content: isRepositoryLevel || metadatum.id == undefined || openedMetadatumId != '' || isUpdatingMetadataOrder ? $i18n.get('info_not_allowed_change_order_metadata') : $i18n.get('instruction_drag_and_drop_metadatum_sort'),
autoHide: true,
classes: ['tooltip', isRepositoryLevel ? 'repository-tooltip' : ''],
placement: 'auto-start'
@ -129,6 +131,7 @@
:value="metadatum.enabled"
@input="onChangeEnable($event, index)"/>
<a
v-if="metadatum.current_user_can_edit"
:style="{ visibility:
metadatum.collection_id != collectionId
? 'hidden' : 'visible'
@ -146,6 +149,7 @@
</span>
</a>
<a
v-if="metadatum.current_user_can_delete"
:style="{ visibility:
metadatum.collection_id != collectionId ||
metadatum.metadata_type_object.related_mapped_prop == 'title' ||
@ -183,7 +187,9 @@
</draggable>
</div>
<div class="column available-metadata-area" >
<div
v-if="(isRepositoryLevel && $userCaps.hasCapability('tnc_rep_edit_metadata')) || !isRepositoryLevel"
class="column available-metadata-area" >
<div class="field">
<h3 class="label has-text-secondary">{{ $i18n.get('label_available_metadata_types') }}</h3>
<draggable
@ -230,7 +236,9 @@
</b-tab-item>
<!-- Mapping --------------- -->
<b-tab-item :label="$i18n.get('mapping')">
<b-tab-item
v-if="(isRepositoryLevel && $userCaps.hasCapability('tnc_rep_edit_metadata') || !isRepositoryLevel)"
:label="$i18n.get('mapping')">
<div>
<section
v-if="activeMetadatumList.length <= 0 && !isLoadingMetadata"
@ -428,6 +436,18 @@
</div>
</b-tab-item>
</b-tabs>
<section
v-else
class="section">
<div class="content has-text-grey has-text-centered">
<p>
<span class="icon">
<i class="tainacan-icon tainacan-icon-30px tainacan-icon-metadata"/>
</span>
</p>
<p>{{ $i18n.get('info_can_not_edit_metadata') }}</p>
</div>
</section>
</div>
</template>
@ -462,14 +482,16 @@ export default {
new_metadata_uri: '',
new_metadata_slug: '',
columnsTopY: 0,
metadataSearchCancel: undefined,
collectionNameSearchCancel: undefined
metadataSearchCancel: undefined
}
},
components: {
MetadatumEditionForm
},
computed: {
collection() {
return this.getCollection();
},
availableMetadatumList: {
get() {
return this.getMetadatumTypes();
@ -552,8 +574,8 @@ export default {
'getMetadata',
'getMetadatumMappers'
]),
...mapActions('collection', [
'fetchCollectionName'
...mapGetters('collection', [
'getCollection',
]),
handleChange(event) {
if (event.added) {
@ -921,7 +943,7 @@ export default {
this.$root.$emit('onCollectionBreadCrumbUpdate', [{ path: '', label: this.$i18n.get('metadata') }]);
this.$nextTick(() => {
this.columnsTopY = this.$refs.metadataEditionPageColumns.getBoundingClientRect().top;
this.columnsTopY = this.$refs.metadataEditionPageColumns ? this.$refs.metadataEditionPageColumns.getBoundingClientRect().top : 0;
});
this.cleanMetadata();
@ -943,35 +965,12 @@ export default {
.catch(() => {
this.isLoadingMetadatumMappers = false;
});
// Obtains collection name
if (!this.isRepositoryLevel) {
// Cancels previous collection name Request
if (this.collectionNameSearchCancel != undefined)
this.collectionNameSearchCancel.cancel('Collection name search Canceled.');
this.fetchCollectionName(this.collectionId)
.then((resp) => {
resp.request
.then((collectionName) => {
this.collectionName = collectionName;
});
// Search Request Token for cancelling
this.collectionNameSearchCancel = resp.source;
})
}
},
beforeDestroy() {
// Cancels previous Request
if (this.metadataSearchCancel != undefined)
this.metadataSearchCancel.cancel('Metadata search Canceled.');
// Cancels previous collection name Request
if (this.collectionNameSearchCancel != undefined)
this.collectionNameSearchCancel.cancel('Collection name search Canceled.');
}
}

View File

@ -49,7 +49,8 @@
v-if="term.total_children > 0">
<span>{{ term.total_children + ' ' + $i18n.get('label_children_terms') }}</span>
</span>
<span
<span
v-if="currentUserCanEditTaxonomy"
class="controls"
:class="{'is-disabled': isEditingTerm}">
<a @click="addNewChildTerm(term, index)">
@ -111,7 +112,8 @@
:term="childTerm"
:index="childIndex"
:taxonomy-id="taxonomyId"
:order="order"/>
:order="order"
:current-user-can-edit-taxonomy="currentUserCanEditTaxonomy"/>
</div>
</transition-group>
<a
@ -147,6 +149,7 @@ export default {
index: Number,
taxonomyId: Number,
order: String,
currentUserCanEditTaxonomy: Boolean
},
components: {
RecursiveTermItem,

View File

@ -3,7 +3,9 @@
v-if="taxonomies.length > 0 && !isLoading"
class="table-container">
<div class="selection-control">
<div
v-if="$userCaps.hasCapability('tnc_rep_delete_taxonomies')"
class="selection-control">
<div class="field select-all is-pulled-left">
<span>
<b-checkbox
@ -14,7 +16,6 @@
<div class="field is-pulled-right">
<b-dropdown
position="is-bottom-left"
v-if="$userCaps.hasCapability('delete_tainacan-taxonomies')"
:disabled="!isSelecting"
id="bulk-actions-dropdown"
aria-role="list"
@ -47,7 +48,7 @@
<thead>
<tr>
<!-- Checking list -->
<th>
<th v-if="$userCaps.hasCapability('tnc_rep_delete_taxonomies')">
&nbsp;
<!-- nothing to show on header -->
</th>
@ -64,7 +65,9 @@
<div class="th-wrap">{{ $i18n.get('label_collections_using') }}</div>
</th>
<!-- Actions -->
<th class="actions-header">
<th
v-if="taxonomies.findIndex((taxonomy) => taxonomy.current_user_can_edit || taxonomy.current_user_can_delete) >= 0"
class="actions-header">
&nbsp;
<!-- nothing to show on header for actions cell-->
</th>
@ -77,6 +80,7 @@
v-for="(taxonomy, index) of taxonomies">
<!-- Checking list -->
<td
v-if="$userCaps.hasCapability('tnc_rep_delete_taxonomies')"
:class="{ 'is-selecting': isSelecting }"
class="checkbox-cell">
<b-checkbox
@ -140,12 +144,15 @@
</td>
<!-- Actions -->
<td
v-if="taxonomy.current_user_can_edit || taxonomy.current_user_can_delete"
@click="onClickTaxonomy($event, taxonomy.id, index)"
class="actions-cell column-default-width"
class="column-default-width"
:class="{ 'actions-cell': taxonomy.current_user_can_edit || taxonomy.current_user_can_delete }"
:label="$i18n.get('label_actions')">
<div class="actions-container">
<a
id="button-edit"
v-if="taxonomy.current_user_can_edit"
:aria-label="$i18n.getFrom('taxonomies','edit_item')"
@click="onClickTaxonomy($event, taxonomy.id, index)">
<span
@ -161,6 +168,7 @@
</a>
<a
id="button-delete"
v-if="taxonomy.current_user_can_delete"
:aria-label="$i18n.get('label_button_delete')"
@click.prevent.stop="deleteOneTaxonomy(taxonomy.id)">
<span
@ -323,7 +331,7 @@
for (let i = 0; i < collections.length; i++) {
htmlList += `<a target="_blank" href=${ this.adminUrl + 'admin.php?page=tainacan_admin#' + this.$routerHelper.getCollectionPath(collections[i].id)}>${collections[i].name}</a>`;
if (collections.length > 2 && i > 0 && i < collections.length - 1) {
if (collections.length > 2 && i < collections.length - 1) {
if (i < collections.length - 2)
htmlList += ', '
else

View File

@ -4,6 +4,7 @@
v-if="(termsList.length > 0 || searchQuery != '') && !isLoadingTerms"
class="terms-list-header">
<button
v-if="currentUserCanEditTaxonomy"
class="button is-secondary"
type="button"
@click="addNewTerm(0)"
@ -96,7 +97,8 @@
:term="term"
:index="index"
:taxonomy-id="taxonomyId"
:order="order"/>
:order="order"
:current-user-can-edit-taxonomy="currentUserCanEditTaxonomy"/>
</div>
<a
class="view-more-terms-level-0"
@ -117,7 +119,8 @@
:term="term"
:index="index"
:taxonomy-id="taxonomyId"
:order="order"/>
:order="order"
:current-user-can-edit-taxonomy="currentUserCanEditTaxonomy"/>
</div>
<a
class="view-more-terms-level-0"
@ -145,12 +148,12 @@
<div class="content has-text-grey has-text-centered">
<p>
<span class="icon is-medium">
<i class="tainacan-icon tainacan-icon-36px tainacan-icon-terms"/>
<i class="tainacan-icon tainacan-icon-30px tainacan-icon-terms"/>
</span>
</p>
<p>{{ searchQuery != '' ? $i18n.get('info_no_terms_found') : $i18n.get('info_no_terms_created_on_taxonomy') }}</p>
<button
v-if="searchQuery == ''"
v-if="searchQuery == '' && currentUserCanEditTaxonomy"
id="button-create-term"
class="button is-secondary"
@click="addNewTerm(0)">
@ -195,6 +198,7 @@ export default {
},
props: {
taxonomyId: String,
currentUserCanEditTaxonomy: Boolean
},
computed: {
termsList() {

View File

@ -48,7 +48,7 @@
</router-link>
</li>
<li class="separator"/>
<li>
<li v-if="$userCaps.hasCapability('tnc_rep_edit_metadata')">
<router-link
tag="a"
to="/metadata"
@ -67,7 +67,7 @@
<span class="menu-text">{{ $i18n.get('metadata') }}</span>
</router-link>
</li>
<li>
<li v-if="$userCaps.hasCapability('tnc_rep_edit_filters')">
<router-link
tag="a"
to="/filters"
@ -124,6 +124,25 @@
<span class="menu-text">{{ $i18n.get('activities') }}</span>
</router-link>
</li>
<li v-if="$userCaps.hasCapability('tnc_rep_edit_users')">
<router-link
tag="a"
:to="this.$routerHelper.getCapabilitiesPath()"
:class="activeRoute == 'CapabilitiesPage' ? 'is-active':''">
<span
v-tooltip="{
offset: 4,
content: isMenuCompressed ? $i18n.get('capabilities') : '',
autoHide: true,
classes: ['tooltip', 'repository-tooltip'],
placement: 'auto'
}"
class="icon">
<i class="tainacan-icon tainacan-icon-20px tainacan-icon-user"/>
</span>
<span class="menu-text">{{ $i18n.get('capabilities') }}</span>
</router-link>
</li>
<li>
<router-link
tag="a"

View File

@ -67,7 +67,7 @@
}">
<router-link
tag="a"
:to="{ path: $routerHelper.getCollectionItemsPath(id, '') }"
:to="{ path: collection && collection.id ? $routerHelper.getCollectionItemsPath(collection.id, '') : '' }"
:aria-label="$i18n.get('label_collection_items')">
<span class="icon">
<i class="tainacan-icon tainacan-icon-20px tainacan-icon-items"/>
@ -76,7 +76,7 @@
</router-link>
</li>
<li
v-if="currentUserCanEdit"
v-if="collection && collection.current_user_can_edit"
:class="activeRoute == 'CollectionEditionForm' ? 'is-active':''"
class="level-item"
v-tooltip="{
@ -91,7 +91,7 @@
}">
<router-link
tag="a"
:to="{ path: $routerHelper.getCollectionEditPath(id) }"
:to="{ path: collection && collection.id ? $routerHelper.getCollectionEditPath(collection.id) : '' }"
:aria-label="$i18n.get('label_settings')">
<span class="icon">
<i class="tainacan-icon tainacan-icon-20px tainacan-icon-settings"/>
@ -101,7 +101,7 @@
</router-link>
</li>
<li
v-if="currentUserCanEdit"
v-if="collection && collection.current_user_can_edit_metadata"
:class="activeRoute == 'MetadataList' ? 'is-active':''"
class="level-item"
v-tooltip="{
@ -116,7 +116,7 @@
}">
<router-link
tag="a"
:to="{ path: $routerHelper.getCollectionMetadataPath(id) }"
:to="{ path: collection && collection.id ? $routerHelper.getCollectionMetadataPath(collection.id) : '' }"
:aria-label="$i18n.get('label_collection_metadata')">
<span class="icon">
<i class="tainacan-icon tainacan-icon-20px tainacan-icon-metadata"/>
@ -125,7 +125,7 @@
</router-link>
</li>
<li
v-if="currentUserCanEdit"
v-if="collection && collection.current_user_can_edit_filters"
:class="activeRoute == 'FiltersList' ? 'is-active':''"
class="level-item"
v-tooltip="{
@ -140,7 +140,7 @@
}">
<router-link
tag="a"
:to="{ path: $routerHelper.getCollectionFiltersPath(id) }"
:to="{ path: collection && collection.id ? $routerHelper.getCollectionFiltersPath(collection.id) : ''}"
:aria-label="$i18n.get('label_collection_filters')">
<span class="icon">
<i class="tainacan-icon tainacan-icon-20px tainacan-icon-filters"/>
@ -149,6 +149,7 @@
</router-link>
</li>
<li
v-if="$userCaps.hasCapability('tnc_rep_read_logs')"
:class="activeRoute == 'CollectionActivitiesPage' ? 'is-active':''"
class="level-item"
v-tooltip="{
@ -163,7 +164,7 @@
}">
<router-link
tag="a"
:to="{ path: $routerHelper.getCollectionActivitiesPath(id) }"
:to="{ path: collection && collection.id ? $routerHelper.getCollectionActivitiesPath(collection.id) : '' }"
:aria-label="$i18n.get('label_collection_activities')">
<span class="icon">
<i class="tainacan-icon tainacan-icon-20px tainacan-icon-activities"/>
@ -171,6 +172,30 @@
<!-- <span class="menu-text">{{ $i18n.get('activities') }}</span> -->
</router-link>
</li>
<li
v-if="collection && collection.current_user_can_edit_users"
:class="activeRoute == 'CollectionCapabilitiesPage' ? 'is-active':''"
class="level-item"
v-tooltip="{
delay: {
show: 300,
hide: 100,
},
content: $i18n.get('capabilities'),
autoHide: false,
placement: 'bottom-start',
classes: ['header-tooltips']
}">
<router-link
tag="a"
:to="{ path: collection && collection.id ? $routerHelper.getCollectionCapabilitiesPath(collection.id) : '' }"
:aria-label="$i18n.get('label_collection_capabilities')">
<span class="icon">
<i class="tainacan-icon tainacan-icon-20px tainacan-icon-user"/>
</span>
<!-- <span class="menu-text">{{ $i18n.get('activities') }}</span> -->
</router-link>
</li>
</ul>
</div>
@ -178,7 +203,7 @@
</template>
<script>
import { mapActions } from 'vuex';
import { mapGetters } from 'vuex';
export default {
name: 'TainacanCollectionSubheader',
@ -187,27 +212,31 @@ export default {
activeRoute: 'ItemsList',
pageTitle: '',
activeRouteName: '',
collectionNameRequestCancel: undefined,
collectionBreadCrumbItem: {},
childrenBreadCrumbItems: []
}
},
props: {
id: Number,
currentUserCanEdit: Boolean
computed: {
collection() {
return this.getCollection();
},
collectionBreadCrumbItem() {
return {
url: this.collection && this.collection.id ? this.$routerHelper.getCollectionPath(this.collection.id) : '',
name: this.collection && this.collection.name ? this.collection.name : ''
};
}
},
watch: {
'$route' (to, from) {
if (to.path != from.path) {
this.activeRoute = to.name;
this.pageTitle = this.$route.meta.title;
}
}
},
methods: {
...mapActions('collection', [
'fetchCollectionNameAndURL'
...mapGetters('collection', [
'getCollection'
]),
collectionBreadCrumbUpdate(breadCrumbItems) {
this.childrenBreadCrumbItems = breadCrumbItems;
@ -220,28 +249,7 @@ export default {
this.$root.$on('onCollectionBreadCrumbUpdate', this.collectionBreadCrumbUpdate);
},
mounted() {
// Cancels previous Request
if (this.collectionNameRequestCancel != undefined)
this.collectionNameRequestCancel.cancel('Collection name Canceled.');
this.fetchCollectionNameAndURL(this.id)
.then((resp) => {
resp.request
.then(collection => this.collectionBreadCrumbItem = { url: this.$routerHelper.getCollectionPath(this.id), name: collection.name })
.catch((error) => this.$console.error(error));
this.collectionNameRequestCancel = resp.source;
})
.catch((error) => this.$console.error(error));
},
beforeDestroy() {
// Cancels previous Request
if (this.collectionNameRequestCancel != undefined)
this.collectionNameRequestCancel.cancel('Collection name Canceled.');
this.$root.$on('onCollectionBreadCrumbUpdate', this.collectionBreadCrumbUpdate);
}
}
@ -252,15 +260,15 @@ export default {
@import "../../scss/_variables.scss";
.header-tooltips .tooltip-inner {
color: white;
text-shadow: 1px 1px $turquoise4;
background-color: $turquoise3;
color: turquoise5;
text-shadow: none;
background-color: $turquoise2;
font-size: 0.75rem;
font-weight: 400;
padding: 10px 14px;
}
.header-tooltips .tooltip-arrow {
border-color: $turquoise3;
border-color: $turquoise2;
}
// Tainacan Header

View File

@ -4,7 +4,7 @@
class="level secondary-page"
:class="{'is-menu-compressed': isMenuCompressed, 'is-repository-level' : isRepositoryLevel}">
<h1 v-if="isRepositoryLevel">{{ repositoryName }}</h1>
<h1 v-else>{{ $i18n.get('collection') + '' }} <span class="has-text-weight-bold">{{ collectionName }}</span></h1>
<h1 v-else>{{ $i18n.get('collection') + '' }} <span class="has-text-weight-bold">{{ collection && collection.name ? collection.name : '' }}</span></h1>
<ul class="repository-subheader-icons">
<li
@ -40,9 +40,9 @@
classes: ['header-tooltips']
}">
<a
:href="collectionURL"
:href="collection && collection.url ? collection.url : ''"
target="_blank"
v-if="!isRepositoryLevel"
v-if="!isRepositoryLevel && collection && collection.url"
class="button"
id="view-collection-button">
<span class="icon">
@ -81,7 +81,7 @@
</template>
<script>
import { mapActions, mapGetters } from 'vuex';
import { mapGetters } from 'vuex';
import AvailableExportersModal from '../other/available-exporters-modal.vue';
export default {
@ -98,28 +98,13 @@ export default {
isRepositoryLevel: true
},
computed: {
collectionName() {
return this.getCollectionName();
},
collectionURL() {
return this.getCollectionURL();
}
},
watch: {
'$route' (to, from) {
if (!this.isRepositoryLevel && from.path != undefined && to.path != from.path) {
this.collectionId = this.$route.params.collectionId;
this.fetchCollectionNameAndURL(this.collectionId);
}
collection() {
return this.getCollection();
}
},
methods: {
...mapActions('collection', [
'fetchCollectionNameAndURL'
]),
...mapGetters('collection', [
'getCollectionName',
'getCollectionURL'
'getCollection'
]),
openAvailableExportersModal(){

View File

@ -23,6 +23,7 @@
class="collection-type"
v-for="(collection, index) in collections"
:key="index"
v-if="collection && collection.current_user_can_edit_items"
@click="onSelectCollection(collection)">
<h4>{{ collection.name }}</h4>
<p>{{ collection.description.length > 200 ? (collection.description.substring(0,197) + '...') : collection.description }}</p>

View File

@ -266,7 +266,8 @@ export default {
selectedExposer: undefined,
selectedExposerMappers: [],
maxItemsPerPage: tainacan_plugin.api_max_items_per_page,
shouldRespectFetchOnly: false
shouldRespectFetchOnly: false,
collectionURL: undefined
}
},
computed: {
@ -302,15 +303,6 @@ export default {
return tainacan_plugin.tainacan_api_url + baseURL + '?' + qs.stringify(currentParams);
},
collectionName() {
return this.getCollectionName();
},
collectionURL() {
if (this.collectionId != undefined)
return this.getCollectionURL();
else
return tainacan_plugin.theme_items_list_url;
},
availableExposers() {
let exposers = this.getAvailableExposers();
@ -336,11 +328,7 @@ export default {
'getAvailableExposers'
]),
...mapActions('collection', [
'fetchCollectionNameAndURL'
]),
...mapGetters('collection', [
'getCollectionName',
'getCollectionURL'
'fetchCollectionForExposer'
]),
collapse(index) {
let exposerMapper = this.selectedExposerMappers[index];
@ -467,8 +455,10 @@ export default {
});
if (this.collectionId != undefined) {
this.fetchCollectionNameAndURL(this.collectionId);
}
this.fetchCollectionForExposer(this.collectionId)
.then((collection) => this.collectionURL = collection.url);
} else
this.collectionURL = tainacan_plugin.theme_items_list_url;
if (this.itemId)
this.shouldRespectFetchOnly = false;

View File

@ -64,19 +64,23 @@ export default {
opacity: 0;
.help-tooltip-header {
padding: 0.8em 0.8em 0em 0.8em;
padding: 0.8rem 0.8rem 0rem 0.8rem;
h5 {
font-size: 14px;
font-size: 0.875rem;
font-weight: bold;
}
}
.help-tooltip-body {
padding: 0.8em 1.0em 1.0em 1.0em;
font-size: 11px;
font-weight: normal;
white-space: normal;
padding: 0.5em 1.0rem 1.0rem 1.0rem;
p {
font-size: 0.875rem !important;
font-weight: normal !important;
white-space: normal !important;
overflow: visible;
}
}
&:before {

View File

@ -221,28 +221,21 @@
if (this.taxonomyFilters != undefined) {
this.$set(this.taxonomyFiltersCollectionNames, 'repository-filters', this.$i18n.get('repository'));
for (let taxonomyFilter of Object.keys(this.taxonomyFilters)) {
if (taxonomyFilter != 'repository-filters') {
// Cancels previous collection name Request
if (this.collectionNameSearchCancel != undefined)
this.collectionNameSearchCancel.cancel('Collection name search Canceled.');
// Cancels previous collection name Request
if (this.collectionNameSearchCancel != undefined)
this.collectionNameSearchCancel.cancel('Collection name search Canceled.');
this.fetchCollectionName(taxonomyFilter)
.then((resp) => {
resp.request
.then((collectionName) => {
this.$nextTick(() => {
this.$set(this.taxonomyFiltersCollectionNames, taxonomyFilter, collectionName);
});
});
// Search Request Token for cancelling
this.collectionNameSearchCancel = resp.source;
})
}
}
this.fetchAllCollectionNames(Object.keys(this.taxonomyFilters))
.then((resp) => {
resp.request
.then((collections) => {
for (let collection of collections)
this.$set(this.taxonomyFiltersCollectionNames, '' + collection.id, collection.name);
});
// Search Request Token for cancelling
this.collectionNameSearchCancel = resp.source;
});
}
},
repositoryCollectionFilters() {
@ -250,28 +243,21 @@
this.$set(this.repositoryCollectionNames, 'repository-filters', this.$i18n.get('repository'));
for (let repositoryCollectionFilter of Object.keys(this.repositoryCollectionFilters)) {
if (repositoryCollectionFilter != 'repository-filters') {
// Cancels previous collection name Request
if (this.collectionNameSearchCancel != undefined)
this.collectionNameSearchCancel.cancel('Collection name search Canceled.');
// Cancels previous collection name Request
if (this.collectionNameSearchCancel != undefined)
this.collectionNameSearchCancel.cancel('Collection name search Canceled.');
this.fetchCollectionName(repositoryCollectionFilter)
.then((resp) => {
resp.request
.then((collectionName) => {
this.$nextTick(() => {
this.$set(this.repositoryCollectionNames, '' + repositoryCollectionFilter, collectionName);
});
});
// Search Request Token for cancelling
this.collectionNameSearchCancel = resp.source;
})
}
}
}
this.fetchAllCollectionNames()
.then((resp) => {
resp.request
.then((collections) => {
for (let collection of collections)
this.$set(this.repositoryCollectionNames, '' + collection.id, collection.name);
});
// Search Request Token for cancelling
this.collectionNameSearchCancel = resp.source;
});
}
}
},
methods: {
@ -279,7 +265,7 @@
'getPostQuery'
]),
...mapActions('collection',[
'fetchCollectionName'
'fetchAllCollectionNames'
]),
},
computed: {
@ -287,7 +273,7 @@
return this.getPostQuery();
},
taxonomyId () {
let taxonomyArray = this.taxonomy.split("_");
const taxonomyArray = this.taxonomy.split("_");
return taxonomyArray[taxonomyArray.length - 1];
}
},

View File

@ -0,0 +1,25 @@
import Vue from 'vue';
import store from '../../js/store/store';
import router from './roles-router';
import VTooltip from 'v-tooltip';
import { I18NPlugin } from './wp-i18n-plugin';
import RolesPage from '../roles.vue';
Vue.use(I18NPlugin);
Vue.use(VTooltip);
// Changing title of pages
router.beforeEach((to, from, next) => {
document.title = to.meta.title;
if (next() != undefined)
next();
});
new Vue({
el: '#tainacan-roles-app',
store,
router,
render: h => h(RolesPage)
});

View File

@ -0,0 +1,31 @@
import Vue from 'vue';
import VueRouter from 'vue-router'
import qs from 'qs';
import RolesList from '../roles/roles-list.vue';
import RoleEditionForm from '../roles/role-edition-form.vue'
const { __ } = wp.i18n;
Vue.use(VueRouter);
const routes = [
{ path: '/', redirect:'/roles' },
{ path: '/roles', name: 'RolesList', component: RolesList, meta: { title: __('Tainacan User Roles') } },
{ path: '/roles/:roleSlug', name: 'RoleEditionForm', component: RoleEditionForm, meta: { title: __('Editing User Role') } },
{ path: '*', redirect: '/'}
];
export default new VueRouter ({
routes,
// set custom query resolver
parseQuery(query) {
return qs.parse(query);
},
stringifyQuery(query) {
let result = qs.stringify(query);
return result ? ('?' + result) : '';
}
});

View File

@ -10,10 +10,11 @@ import ItemsPage from '../pages/lists/items-page.vue'
import ItemPage from '../pages/singles/item-page.vue'
import MetadataPage from '../pages/lists/metadata-page.vue'
import FiltersPage from '../pages/lists/filters-page.vue'
import Page from '../pages/lists/taxonomies-page.vue'
import TaxonomyPage from '../pages/lists/taxonomies-page.vue'
import ActivitiesPage from '../pages/lists/activities-page.vue'
import AvailableExportersPage from '../pages/lists/available-exporters-page.vue'
import AvailableImportersPage from '../pages/lists/available-importers-page.vue'
import CapabilitiesPage from '../pages/lists/capabilities-page.vue'
// Edition Form Components
import CollectionEditionForm from '../components/edition/collection-edition-form.vue'
@ -40,49 +41,52 @@ const routes = [
{ path: '/', redirect:'/home' },
{ path: '/home', name: 'HomePage', component: HomePage, meta: {title: 'Tainacan'} },
{ path: '/collections', name: 'CollectionsPage', component: CollectionsPage, meta: {title: i18nGet('title_repository_collections_page'), icon: 'folder-multiple'} },
{ path: '/collections/new', name: 'CollectionCreationForm', component: CollectionEditionForm, meta: {title: i18nGet('title_create_collection'), icon: 'folder-multiple'} },
{ path: '/collections/new/:mapper', name: 'MappedCollectionCreationForm', component: CollectionEditionForm, meta: {title: i18nGet('title_create_collection'), icon: 'folder-multiple'} },
{ path: '/collections', name: 'CollectionsPage', component: CollectionsPage, meta: { title: i18nGet('title_repository_collections_page') } },
{ path: '/collections/new', name: 'CollectionCreationForm', component: CollectionEditionForm, meta: {title: i18nGet('title_create_collection') } },
{ path: '/collections/new/:mapper', name: 'MappedCollectionCreationForm', component: CollectionEditionForm, meta: {title: i18nGet('title_create_collection') } },
{ path: '/collections/:collectionId', component: CollectionPage, meta: {title: i18nGet('title_collection_page'), icon: 'folder-multiple'},
{ path: '/collections/:collectionId', component: CollectionPage, meta: {title: i18nGet('title_collection_page') },
children: [
{ path: '', redirect: 'items'},
{ path: 'items', component: ItemsPage, name: 'CollectionItemsPage', meta: {title: i18nGet('title_collection_page'), icon: 'folder-multiple'} },
{ path: 'items/:itemId/edit', name: 'ItemEditionForm', component: ItemEditionForm, meta: {title: i18nGet('title_edit_item'), icon: 'folder-multiple'} },
{ path: 'items/new', name: 'CollectionItemCreatePage', component: ItemEditionForm, meta: {title: i18nGet('title_create_item_collection'), icon: 'folder-multiple'} },
{ path: 'items/:itemId', name: 'ItemPage', component: ItemPage, meta: {title: i18nGet('title_item_page'), icon: 'folder-multiple'} },
{ path: 'bulk-add', name: 'CollectionItemBulkAddPage', component: ItemBulkEditionForm, meta: {title: i18nGet('title_item_bulk_add'), icon: 'folder-multiple'} },
{ path: 'bulk-add/:groupId', name: 'CollectionItemBulkAddMetadataPage', component: ItemMetadataBulkEditionForm, meta: {title: i18nGet('title_item_bulk_add'), icon: 'folder-multiple'} },
{ path: 'settings', component: CollectionEditionForm, name: 'CollectionEditionForm', meta: {title: i18nGet('title_collection_settings'), icon: 'folder-multiple'} },
{ path: 'metadata', component: MetadataList, name: 'MetadataList', meta: {title: i18nGet('title_collection_metadata_edition'), icon: 'folder-multiple'} },
{ path: 'filters', component: FiltersList, name: 'FiltersList', meta: {title: i18nGet('title_collection_filters_edition'), icon: 'folder-multiple'} },
{ path: 'activities', component: ActivitiesPage, name: 'CollectionActivitiesPage', meta: {title: i18nGet('title_collection_activities'), icon: 'flash'} },
{ path: 'sequence/:sequenceId', name: 'SavedSequenceEditionForm', component: ItemEditionForm, meta: {title: i18nGet('title_edit_item'), icon: 'folder-multiple'} },
{ path: 'sequence/:sequenceId/:itemPosition', name: 'SequenceEditionForm', component: ItemEditionForm, meta: {title: i18nGet('title_edit_item'), icon: 'folder-multiple'} },
{ path: 'items', component: ItemsPage, name: 'CollectionItemsPage', meta: {title: i18nGet('title_collection_page') }, props: { isOnTheme: false } },
{ path: 'items/:itemId/edit', name: 'ItemEditionForm', component: ItemEditionForm, meta: {title: i18nGet('title_edit_item') } },
{ path: 'items/new', name: 'CollectionItemCreatePage', component: ItemEditionForm, meta: {title: i18nGet('title_create_item_collection') } },
{ path: 'items/:itemId', name: 'ItemPage', component: ItemPage, meta: {title: i18nGet('title_item_page') } },
{ path: 'bulk-add', name: 'CollectionItemBulkAddPage', component: ItemBulkEditionForm, meta: {title: i18nGet('title_item_bulk_add') } },
{ path: 'bulk-add/:groupId', name: 'CollectionItemBulkAddMetadataPage', component: ItemMetadataBulkEditionForm, meta: {title: i18nGet('title_item_bulk_add') } },
{ path: 'settings', component: CollectionEditionForm, name: 'CollectionEditionForm', meta: {title: i18nGet('title_collection_settings') } },
{ path: 'metadata', component: MetadataList, name: 'MetadataList', meta: {title: i18nGet('title_collection_metadata_edition') } },
{ path: 'filters', component: FiltersList, name: 'FiltersList', meta: {title: i18nGet('title_collection_filters_edition') } },
{ path: 'activities', component: ActivitiesPage, name: 'CollectionActivitiesPage', meta: {title: i18nGet('title_collection_activities') } },
{ path: 'capabilities', component: CapabilitiesPage, name: 'CollectionCapabilitiesPage', meta: {title: i18nGet('title_collection_capabilities') } },
{ path: 'sequence/:sequenceId', name: 'SavedSequenceEditionForm', component: ItemEditionForm, meta: {title: i18nGet('title_edit_item') } },
{ path: 'sequence/:sequenceId/:itemPosition', name: 'SequenceEditionForm', component: ItemEditionForm, meta: {title: i18nGet('title_edit_item') } },
]
},
{ path: '/items', name: 'ItemsPage', component: ItemsPage, meta: {title: i18nGet('title_items_page'), icon: 'file-multiple'} },
{ path: '/items/new', name: 'ItemCreationForm', component: ItemEditionForm, meta: {title: i18nGet('title_create_item'), icon: 'file-multiple'} },
{ path: '/items', name: 'ItemsPage', component: ItemsPage, meta: {title: i18nGet('title_items_page') } },
{ path: '/items/new', name: 'ItemCreationForm', component: ItemEditionForm, meta: {title: i18nGet('title_create_item') } },
{ path: '/filters', name: 'FiltersPage', component: FiltersPage, meta: {title: i18nGet('title_repository_filters_page'), icon: 'filter'} },
{ path: '/filters', name: 'FiltersPage', component: FiltersPage, meta: {title: i18nGet('title_repository_filters_page') } },
{ path: '/metadata', name: 'MetadataPage', component: MetadataPage, meta: {title: i18nGet('title_repository_metadata_page'), icon: 'format-list-checks'} },
{ path: '/metadata', name: 'MetadataPage', component: MetadataPage, meta: {title: i18nGet('title_repository_metadata_page') } },
{ path: '/taxonomies', name: 'Page', component: Page, meta: {title: i18nGet('title_taxonomies_page'), icon: 'shape'} },
{ path: '/taxonomies/new', name: 'TaxonomyCreationForm', component: TaxonomyEditionForm, meta: {title: i18nGet('title_create_taxonomy_page'), icon: 'shape'} },
{ path: '/taxonomies/:taxonomyId/edit', name: 'TaxonomyEditionForm', component: TaxonomyEditionForm, meta: {title: i18nGet('title_taxonomy_edition_page'), icon: 'shape'} },
{ path: '/taxonomies', name: 'TaxonomyPage', component: TaxonomyPage, meta: {title: i18nGet('title_taxonomies_page') } },
{ path: '/taxonomies/new', name: 'TaxonomyCreationForm', component: TaxonomyEditionForm, meta: {title: i18nGet('title_create_taxonomy_page') } },
{ path: '/taxonomies/:taxonomyId/edit', name: 'TaxonomyEditionForm', component: TaxonomyEditionForm, meta: {title: i18nGet('title_taxonomy_edition_page') } },
{ path: '/taxonomies/:taxonomyId', redirect: '/taxonomies/:taxonomyId/edit' },
{ path: '/activities', name: 'ActivitiesPage', component: ActivitiesPage, meta: {title: i18nGet('title_repository_activities_page'), icon: 'flash'} },
{ path: '/activities', name: 'ActivitiesPage', component: ActivitiesPage, meta: {title: i18nGet('title_repository_activities_page') } },
{ path: '/importers/', name: 'AvailableImportersPage', component: AvailableImportersPage, meta: {title: i18nGet('title_importers_page'), icon: 'import'} },
{ path: '/importers/:importerSlug', name: 'ImporterEditionForm', component: ImporterEditionForm, meta: {title: i18nGet('title_importer_page'), icon: 'import'} },
{ path: '/importers/:importerSlug/:sessionId', name: 'ImporterCreationForm', component: ImporterEditionForm, meta: { title: i18nGet('title_importer_page'), icon: 'import' } },
{ path: '/importers/:importerType/:sessionId/mapping/:collectionId', name: 'ImporterMappingForm', component: ImporterMappingForm, meta: {title: i18nGet('title_importer_mapping_page'), icon: 'import'} },
{ path: '/capabilities', component: CapabilitiesPage, name: 'CapabilitiesPage', meta: {title: i18nGet('title_repository_capabilities') } },
{ path: '/exporters/', name: 'ExportersPage', component: AvailableExportersPage, meta: {title: i18nGet('title_exporters_page'), icon: 'export'} },
{ path: '/exporters/:exporterSlug', name: 'ExporterEditionForm', component: ExporterEditionForm, meta: {title: i18nGet('title_exporter_page'), icon: 'export'}},
{ path: '/importers/', name: 'AvailableImportersPage', component: AvailableImportersPage, meta: {title: i18nGet('title_importers_page') } },
{ path: '/importers/:importerSlug', name: 'ImporterEditionForm', component: ImporterEditionForm, meta: {title: i18nGet('title_importer_page') } },
{ path: '/importers/:importerSlug/:sessionId', name: 'ImporterCreationForm', component: ImporterEditionForm, meta: { title: i18nGet('title_importer_page') } },
{ path: '/importers/:importerType/:sessionId/mapping/:collectionId', name: 'ImporterMappingForm', component: ImporterMappingForm, meta: {title: i18nGet('title_importer_mapping_page') } },
{ path: '/exporters/', name: 'ExportersPage', component: AvailableExportersPage, meta: {title: i18nGet('title_exporters_page') } },
{ path: '/exporters/:exporterSlug', name: 'ExporterEditionForm', component: ExporterEditionForm, meta: {title: i18nGet('title_exporter_page') }},
{ path: '*', redirect: '/'}
];

View File

@ -215,6 +215,9 @@ RouterHelperPlugin.install = function (Vue, options = {}) {
getCollectionActivitiesPath(collectionId) {
return '/collections/'+ collectionId + '/activities/';
},
getCollectionCapabilitiesPath(collectionId) {
return '/collections/'+ collectionId + '/capabilities/';
},
getItemsPath(query) {
return '/items/?' + qs.stringify(query);
},
@ -230,6 +233,9 @@ RouterHelperPlugin.install = function (Vue, options = {}) {
getActivitiesPath(query) {
return '/activities/?' + qs.stringify(query);
},
getCapabilitiesPath() {
return '/capabilities';
},
getAvailableImportersPath() {
return '/importers';
},
@ -341,10 +347,7 @@ UserCapabilitiesPlugin.install = function (Vue, options = {}) {
Vue.prototype.$userCaps = {
hasCapability(key) {
for (let i = 0; i < tainacan_plugin.user_caps.length; i++)
if (tainacan_plugin.user_caps[i] == key)
return true;
return false;
return tainacan_plugin.user_caps[key];
}
}
};

View File

@ -0,0 +1,29 @@
const { __, _x, _n, _nx } = wp.i18n;
/**
I18N PLUGIN - Allows access to Wordpress translation functions.
__( '__', 'my-domain' );
_x( '_x', '_x_context', 'my-domain' );
_n( '_n_single', '_n_plural', number, 'my-domain' );
_nx( '_nx_single', '_nx_plural', number, '_nx_context', 'my-domain' );
**/
export const I18NPlugin = {};
I18NPlugin.install = function (Vue, options = {}) {
Vue.prototype.$i18n = {
get(key) {
return __(key, 'tainacan');
},
getWithContext(key, keyContext) {
return _x(key, keyContext, 'tainacan');
},
getWithNumber(keySingle, keyPlural, number) {
return _n(keySingle, keyPlural, number, 'tainacan');
},
getWithNumberAndContext(keySingle, keyPlural, number, keyContext) {
return _nx(keySingle, keyPlural, number, keyContext, 'tainacan');
},
}
};

View File

@ -44,7 +44,7 @@
<span class="menu-text">{{ $i18n.get('label_all_published_items') }}</span>
</router-link>
</li> -->
<li>
<li v-if="$userCaps.hasCapability('tnc_rep_edit_metadata')">
<router-link
tag="a"
to="/metadata">
@ -54,7 +54,7 @@
<span class="menu-text">{{ $i18n.get('title_repository_metadata_page' ) }}</span>
</router-link>
</li>
<li>
<li v-if="$userCaps.hasCapability('tnc_rep_edit_filters')">
<router-link
tag="a"
to="/filters">

View File

@ -132,12 +132,24 @@
</div>
<activities-list
v-if="tab != 'processes'"
v-if="tab != 'processes' && $userCaps.hasCapability('tnc_rep_read_logs')"
:is-loading="isLoading"
:total-activities="totalActivities"
:page="activitiesPage"
:activities-per-page="activitiesPerPage"
:activities="activities"/>
<template v-if="tab != 'processes' && !$userCaps.hasCapability('tnc_rep_read_logs')">
<section class="section">
<div class="content has-text-grey has-text-centered">
<p>
<span class="icon">
<i class="tainacan-icon tainacan-icon-30px tainacan-icon-activities"/>
</span>
</p>
<p>{{ $i18n.get('info_can_not_read_activities') }}</p>
</div>
</section>
</template>
<processes-list
v-if="tab == 'processes'"
@ -153,7 +165,7 @@
<div class="content has-text-grey has-text-centered">
<p>
<span class="icon">
<i class="tainacan-icon tainacan-icon-activities"/>
<i class="tainacan-icon tainacan-icon-30px tainacan-icon-activities"/>
</span>
</p>
<p v-if="status == undefined || status == ''">{{ $i18n.get('info_no_process') }}</p>

View File

@ -0,0 +1,276 @@
<template>
<div
:class="{
'repository-level-page': isRepositoryLevel,
'page-container': isRepositoryLevel
}">
<tainacan-title
:bread-crumb-items="[{ path: '', label: this.$i18n.get('capabilities') }]"/>
<div class="sub-header">
<b-field
style="margin-left: auto; margin-right: 0;"
class="header-item">
<div class="control has-icons-right is-small is-clearfix">
<b-autocomplete
v-model="filteredRole"
:data="filteredRoles"
:placeholder="$i18n.get('instruction_type_search_roles_filter')"
keep-first
open-on-focus
:loading="isFetchingRoles"
field="name"
icon="magnify" />
</div>
</b-field>
</div>
<div>
<b-loading
:is-full-page="true"
:active.sync="isLoading"
:can-cancel="false"/>
<capabilities-list
v-if="(isRepositoryLevel && $userCaps.hasCapability('tnc_rep_edit_users')) || (!isRepositoryLevel && collection && collection.current_user_can_edit_users)"
:is-loading="isLoading || isFetchingRoles"
:capabilities="capabilities"/>
<template v-else-if="(isRepositoryLevel && !$userCaps.hasCapability('tnc_rep_edit_users')) || (!isRepositoryLevel && collection && !collection.current_user_can_edit_users)">
<section class="section">
<div class="content has-text-grey has-text-centered">
<p>
<span class="icon">
<i class="tainacan-icon tainacan-icon-30px tainacan-icon-user"/>
</span>
</p>
<p>{{ $i18n.get('info_can_not_edit_capabilities') }}</p>
</div>
</section>
</template>
<!-- Empty state -->
<div v-if="capabilities.length <= 0 && !isLoading && $userCaps.hasCapability('tnc_rep_edit_users')">
<section class="section">
<div class="content has-text-grey has-text-centered">
<p>
<span class="icon is-medium">
<i class="tainacan-icon tainacan-icon-30px tainacan-icon-user"/>
</span>
</p>
<p>
{{ $i18n.get('info_no_capabilities_found') }}
</p>
</div>
</section>
</div>
<!-- Footer -->
<div
class="pagination-area"
v-if="capabilities.length > 0">
<div class="shown-items">
{{
$i18n.get('info_showing_capabilities') +
(capabilitiesPerPage * (page - 1) + 1) +
$i18n.get('info_to') +
capabilities.length +
$i18n.get('info_of') + total + '.'
}}
</div>
</div>
</div>
</div>
</template>
<script>
import CapabilitiesList from "../../components/lists/capabilities-list.vue";
import { mapActions, mapGetters } from 'vuex';
export default {
name: 'CapabilitiesPage',
data() {
return {
isRepositoryLevel: false,
isLoading: false,
roles: [],
isFetchingRoles: false,
filteredRole: ''
}
},
computed: {
capabilities() {
const capabilities = this.getCapabilities()
if (capabilities) {
if (this.filteredRole) {
let filteredCapabilities = {};
for (let [capabilitySlug, capability] of Object.entries(capabilities)) {
const rolesArray = capability.roles && !Array.isArray(capability.roles) ? Object.values(capability.roles) : [];
const rolesInheritedArray = capability.roles_inherited && !Array.isArray(capability.roles_inherited) ? Object.values(capability.roles_inherited) : [];
const completeRoles = rolesArray.map(role => role.name).concat(rolesInheritedArray.map(roleInherited => roleInherited.name))
if (completeRoles.toString().search(this.filteredRole) >= 0)
filteredCapabilities[capabilitySlug] = capability;
}
return Object.keys(filteredCapabilities).length === 0 ? [] : filteredCapabilities;
} else {
return capabilities;
}
} else {
return []
}
},
filteredRoles() {
if (this.roles && this.roles.length) {
return this.roles
.filter((option) => {
if (option) {
return option.name
.toString()
.toLowerCase()
.indexOf(this.filteredRole.toLowerCase()) >= 0
} else {
return false
}
});
} else {
return []
}
},
collection() {
return this.getCollection();
}
},
components: {
CapabilitiesList
},
methods: {
...mapActions('capability', [
'fetchCapabilities',
'fetchRoles'
]),
...mapGetters('capability', [
'getCapabilities'
]),
...mapGetters('collection', [
'getCollection'
]),
loadCapabilities() {
this.isLoading = true;
this.fetchCapabilities({ collectionId: this.$route.params.collectionId })
.then(() => {
this.isLoading = false;
})
.catch(() => {
this.isLoading = false;
});
},
fetchRolesForFiltering() {
this.isFetchingRoles = true;
this.fetchRoles()
.then((roles) => {
this.roles = Object.values(roles);
this.isFetchingRoles = false;
})
.catch((error) => {
this.$console.error(error);
this.isFetchingRoles = false;
});
}
},
mounted() {
this.loadCapabilities();
this.fetchRolesForFiltering();
},
created() {
this.isRepositoryLevel = (this.$route.params.collectionId === undefined);
}
}
</script>
<style lang="scss" scoped>
@import '../../scss/_variables.scss';
.sub-header {
min-height: $subheader-height;
height: $header-height;
padding-left: 0;
padding-right: 0;
border-bottom: 1px solid #ddd;
display: inline-flex;
justify-content: space-between;
align-items: center;
flex-wrap: wrap;
width: 100%;
.header-item {
margin-bottom: 0 !important;
&:first-child {
margin-right: auto;
}
&:not(:last-child) {
padding-right: 0.5em;
}
.label {
font-size: 0.875rem;
font-weight: normal;
margin-top: 3px;
margin-bottom: 2px;
cursor: default;
}
&:not(:first-child) {
.button {
display: flex;
align-items: center;
border-radius: 0 !important;
height: 1.95rem !important;
}
}
.field {
align-items: center;
}
.gray-icon, .gray-icon .icon {
color: $gray4 !important;
padding-right: 10px;
}
.gray-icon .icon i::before,
.gray-icon i::before {
font-size: 1.3125rem !important;
max-width: 26px;
}
.icon {
pointer-events: all;
cursor: pointer;
color: $blue5;
height: 27px;
font-size: 18px !important;
height: 1.75rem !important;
}
}
@media screen and (max-width: 769px) {
height: 160px;
margin-top: -0.5em;
padding-top: 0.9em;
.header-item:not(:last-child) {
padding-right: 0.2em;
}
}
}
.above-subheader {
margin-bottom: 0;
margin-top: 0;
height: auto;
}
</style>

View File

@ -3,12 +3,12 @@
<b-loading :active.sync="isLoading"/>
<tainacan-title
:bread-crumb-items="[{ path: '', label: this.$i18n.get('collections') }]"/>
<div
class="sub-header"
v-if="$userCaps.hasCapability('edit_tainacan-collections')">
<div class="sub-header">
<!-- New Collection button -->
<div class="header-item">
<div
v-if="$userCaps.hasCapability('tnc_rep_edit_collections')"
class="header-item">
<b-dropdown
aria-role="list"
id="collection-creation-options-dropdown"
@ -162,6 +162,7 @@
<li
v-for="(statusOption, index) of $statusHelper.getStatuses().filter((status) => status.slug != 'draft')"
:key="index"
v-if="statusOption.slug != 'private' || (statusOption.slug == 'private' && $userCaps.hasCapability('tnc_rep_read_private_collections'))"
@click="onChangeTab(statusOption.slug)"
:class="{ 'is-active': status == statusOption.slug}"
:style="{ marginRight: statusOption.slug == 'private' ? 'auto' : '', marginLeft: statusOption.slug == 'trash' ? 'auto' : '' }"
@ -201,7 +202,7 @@
<div class="content has-text-grey has-text-centered">
<p>
<span class="icon is-large">
<i class="tainacan-icon tainacan-icon-36px tainacan-icon-collections" />
<i class="tainacan-icon tainacan-icon-30px tainacan-icon-collections" />
</span>
</p>
<p v-if="status == undefined || status == ''">{{ $i18n.get('info_no_collection_created') }}</p>
@ -212,7 +213,7 @@
{{ $i18n.get('info_no_collections_' + statusOption.slug) }}
</p>
<div v-if="$userCaps.hasCapability('edit_tainacan-collections') && status == undefined || status == ''">
<div v-if="$userCaps.hasCapability('tnc_rep_edit_collections') && status == undefined || status == ''">
<b-dropdown
:disabled="isLoadingMetadatumMappers"
id="collection-creation-options-dropdown"

View File

@ -2,6 +2,7 @@
<div class="repository-level-page page-container">
<tainacan-title
:bread-crumb-items="[{ path: '', label: this.$i18n.get('filters') }]"/>
<filters-list/>
</div>
</template>

View File

@ -228,8 +228,10 @@
class="search-control-item"
v-if="!isOnTheme &&
!$route.query.iframemode &&
!openAdvancedSearch">
<b-dropdown
!openAdvancedSearch &&
collection &&
collection.current_user_can_edit_items">
<b-dropdown
:mobile-modal="true"
id="item-creation-options-dropdown"
aria-role="list"
@ -641,7 +643,7 @@
</li>
<li
v-for="(statusOption, index) of $statusHelper.getStatuses()"
v-if="(isRepositoryLevel || statusOption.slug != 'private') || (statusOption.slug == 'private' && $userCaps.hasCapability('read_private_tnc_col_' + collectionId + '_items'))"
v-if="(isRepositoryLevel || statusOption.slug != 'private') || (statusOption.slug == 'private' && collection && collection.current_user_can_read_private_items)"
:key="index"
@click="onChangeTab(statusOption.slug)"
:class="{ 'is-active': status == statusOption.slug}"
@ -779,7 +781,7 @@
<div class="content has-text-grey has-text-centered">
<p>
<span class="icon is-large">
<i class="tainacan-icon tainacan-icon-36px tainacan-icon-items" />
<i class="tainacan-icon tainacan-icon-30px tainacan-icon-items" />
</span>
</p>
<p v-if="status == undefined || status == ''">{{ hasFiltered ? $i18n.get('info_no_item_found_filter') : (isSortingByCustomMetadata ? $i18n.get('info_no_item_found') : $i18n.get('info_no_item_created')) }}</p>
@ -919,7 +921,6 @@
hasFiltered: false,
isFiltersMenuCompressed: false,
collapseAll: true,
isOnTheme: false,
futureSearchQuery: '',
localDisplayedMetadata: [],
registeredViewModes: tainacan_plugin.registered_view_modes,
@ -930,7 +931,6 @@
searchControlHeight: 0,
sortingMetadata: [],
isFilterModalActive: false,
collection: undefined,
hasAnOpenModal: false,
hasAnOpenAlert: true,
repositoryFiltersSearchCancel: undefined,
@ -941,13 +941,14 @@
props: {
collectionId: Number,
defaultViewMode: String, // Used only on theme
enabledViewModes: Object // Used only on theme
enabledViewModes: Object, // Used only on theme
isOnTheme: Boolean
},
computed: {
isSortingByCustomMetadata() {
return (this.orderBy != undefined && this.orderBy != '' && this.orderBy != 'title' && this.orderBy != 'date');
},
repositoryTotalItems(){
repositoryTotalItems() {
let collections = this.getCollections();
let total_items = {
@ -985,6 +986,9 @@
metadata() {
return this.getMetadata();
},
collection() {
return this.getCollection();
},
searchQuery() {
return this.getSearchQuery();
},
@ -1062,13 +1066,14 @@
}
},
methods: {
...mapActions('collection', [
'fetchCollectionTotalItems'
]),
...mapGetters('collection', [
'getItems',
'getItemsListTemplate',
'getCollections'
'getCollections',
'getCollection'
]),
...mapActions('collection', [
'fetchCollectionBasics'
]),
...mapActions('metadata', [
'fetchMetadata'
@ -1485,11 +1490,9 @@
.catch(() => this.isLoadingMetadata = false);
},
updateCollectionInfo () {
if (this.collectionId) {
this.fetchCollectionTotalItems(this.collectionId)
.then((data) => {
this.collection = data;
})
// Only needed for displayting totalItems on tabs.
if (this.collectionId && !this.isOnTheme) {
this.fetchCollectionBasics({ collectionId: this.collectionId, isContextEdit: true });
}
},
showItemsHiddingDueSortingDialog() {
@ -1545,8 +1548,6 @@
},
created() {
this.isOnTheme = (this.$route.name === null);
this.isRepositoryLevel = (this.collectionId === undefined);
this.$eventBusSearch.setCollectionId(this.collectionId);

View File

@ -1,9 +1,10 @@
<template>
<div class="repository-level-page page-container">
<tainacan-title
:bread-crumb-items="[{ path: '', label: this.$i18n.get('metadata') }]"/>
<metadata-list/>
</div>
<div class="repository-level-page page-container">
<tainacan-title
:bread-crumb-items="[{ path: '', label: this.$i18n.get('metadata') }]"/>
<metadata-list/>
</div>
</template>
<script>

View File

@ -3,12 +3,12 @@
<div class="repository-level-page page-container">
<tainacan-title
:bread-crumb-items="[{ path: '', label: this.$i18n.get('taxonomies') }]"/>
<div
class="sub-header"
v-if="$userCaps.hasCapability('edit_tainacan-taxonomies')">
<div class="sub-header">
<!-- New Taxonomy Button ---- -->
<div class="header-item">
<div
v-if="$userCaps.hasCapability('tnc_rep_edit_taxonomies')"
class="header-item">
<router-link
id="button-create-taxonomy"
tag="button"
@ -128,6 +128,7 @@
<li
v-for="(statusOption, index) of $statusHelper.getStatuses()"
:key="index"
v-if="statusOption.slug != 'private' || (statusOption.slug == 'private' && $userCaps.hasCapability('tnc_rep_read_private_taxonomies'))"
@click="onChangeTab(statusOption.slug)"
:class="{ 'is-active': status == statusOption.slug}"
:style="{ marginRight: statusOption.slug == 'private' ? 'auto' : '', marginLeft: statusOption.slug == 'draft' ? 'auto' : '' }"
@ -164,9 +165,11 @@
<div v-if="taxonomies.length <= 0 && !isLoading">
<section class="section">
<div class="content has-text-grey has-text-centered">
<span class="icon is-medium">
<i class="tainacan-icon tainacan-icon-36px tainacan-icon-terms"/>
</span>
<p>
<span class="icon is-medium">
<i class="tainacan-icon tainacan-icon-30px tainacan-icon-terms"/>
</span>
</p>
<p v-if="status == undefined || status == ''">{{ $i18n.get('info_no_taxonomy_created') }}</p>
<p
v-for="(statusOption, index) of $statusHelper.getStatuses()"
@ -240,7 +243,7 @@
//import moment from 'moment'
export default {
name: 'Page',
name: 'TaxonomyPage',
data(){
return {
isLoading: false,

View File

@ -756,7 +756,7 @@
<div class="content has-text-grey has-text-centered">
<p>
<span class="icon is-large">
<i class="tainacan-icon tainacan-icon-36px tainacan-icon-items" />
<i class="tainacan-icon tainacan-icon-30px tainacan-icon-items" />
</span>
</p>
<p v-if="status == undefined || status == ''">{{ hasFiltered ? $i18n.get('info_no_item_found_filter') : (isSortingByCustomMetadata ? $i18n.get('info_no_item_found') : $i18n.get('info_no_item_created')) }}</p>
@ -889,7 +889,6 @@
hasFiltered: false,
isFiltersMenuCompressed: false,
collapseAll: true,
isOnTheme: false,
futureSearchQuery: '',
localDisplayedMetadata: [],
registeredViewModes: tainacan_plugin.registered_view_modes,
@ -912,7 +911,8 @@
termId: Number,
taxonomy: String,
defaultViewMode: String, // Used only on theme
enabledViewModes: Object // Used only on theme,
enabledViewModes: Object, // Used only on theme
isOnTheme: Boolean
},
computed: {
isSortingByCustomMetadata() {
@ -1471,8 +1471,6 @@
},
created() {
this.isOnTheme = (this.$route.name === null);
this.isRepositoryLevel = (this.collectionId === undefined);
if (this.collectionId != undefined)

View File

@ -1,10 +1,8 @@
<template>
<div class="columns is-fullheight">
<section class="column is-secondary-content">
<tainacan-collection-subheader
:current-user-can-edit="currentUserCanEdit"
:id="collectionId"/>
<tainacan-collection-subheader />
<router-view
id="collection-page-container"
:collection-id="collectionId"
@ -15,14 +13,22 @@
<script>
import TainacanCollectionSubheader from '../../components/navigation/tainacan-collection-subheader.vue';
import { mapActions, mapGetters } from 'vuex';
import { mapActions } from 'vuex';
export default {
name: 'CollectionPage',
data(){
return {
collectionId: Number,
currentUserCanEdit: Boolean
collectionId: Number
}
},
watch: {
'$route' (to, from) {
if (!this.isRepositoryLevel && from.path != undefined && to.path != from.path && this.collectionId != this.$route.params.collectionId) {
this.collectionId = this.$route.params.collectionId;
this.fetchCollectionBasics({ collectionId: this.collectionId, isContextEdit: true })
.catch((error) => this.$console.error(error));
}
}
},
components: {
@ -30,25 +36,17 @@ export default {
},
methods: {
...mapActions('collection', [
'fetchCollectionUserCanEdit'
]),
...mapGetters('collection', [
'getCollection'
'fetchCollectionBasics'
])
},
created(){
this.collectionId = parseInt(this.$route.params.collectionId);
this.collectionId = this.$route.params.collectionId;
this.$eventBusSearch.setCollectionId(this.collectionId);
},
mounted() {
let storedCollection = this.getCollection();
if (storedCollection != undefined && storedCollection.id == this.collectionId && storedCollection.currentUserCanEdit != undefined)
this.currentUserCanEdit = storedCollection.currentUserCanEdit;
else {
this.fetchCollectionUserCanEdit(this.collectionId).then((caps) => {
this.currentUserCanEdit = caps;
}).catch((error) => this.$console.error(error));
}
// Loads to store basic collection info such as name, url, current_user_can_edit... etc.
this.fetchCollectionBasics({ collectionId: this.collectionId, isContextEdit: true })
.catch((error) => this.$console.error(error));
}
}
</script>

View File

@ -134,7 +134,7 @@
<span class="icon">
<i class="tainacan-icon tainacan-icon-collection"/>
</span>
{{ collectionName }}
{{ collection && collection.name ? collection.name : '' }}
</span>
</div>
</div>
@ -164,7 +164,7 @@
<!-- Comment Status ------------------------ -->
<div
v-if="collectionAllowComments == 'open'"
v-if="collection && collection.allow_comments && collection.allow_comments == 'open'"
class="column is-narrow">
<div class="section-label">
<label>{{ $i18n.get('label_comments') }}</label>
@ -331,16 +331,16 @@
itemId: Number,
isLoading: false,
open: true,
collectionName: '',
thumbPlaceholderPath: tainacan_plugin.base_url + '/admin/images/placeholder_square.png',
urls_open: false,
collectionAllowComments: '',
activeTab: 0,
isLoadingAttachments: false,
collectionNameSearchCancel: undefined
isLoadingAttachments: false
}
},
computed: {
collection() {
return this.getCollection();
},
item() {
// Fills hook forms with it's real values
this.updateExtraFormData(this.getItem());
@ -359,15 +359,14 @@
'fetchItem',
'fetchMetadata',
]),
...mapActions('collection', [
'fetchCollectionName',
'fetchCollectionAllowComments'
]),
...mapGetters('item', [
'getItem',
'getMetadata',
'getTotalAttachments'
]),
...mapGetters('collection', [
'getCollection'
]),
loadMetadata() {
// Obtains Item Metadatum
this.fetchMetadata(this.itemId).then(() => {
@ -411,34 +410,6 @@
this.loadMetadata();
});
// Obtains collection name
if (!this.isRepositoryLevel) {
// Cancels previous collection name Request
if (this.collectionNameSearchCancel != undefined)
this.collectionNameSearchCancel.cancel('Collection name search Canceled.');
this.fetchCollectionName(this.collectionId)
.then((resp) => {
resp.request
.then((collectionName) => {
this.collectionName = collectionName;
});
// Search Request Token for cancelling
this.collectionNameSearchCancel = resp.source;
})
}
// Obtains collection Comment Status
this.fetchCollectionAllowComments(this.collectionId).then((collectionAllowComments) => {
this.collectionAllowComments = collectionAllowComments;
});
},
beforeDestroy() {
// Cancels previous collection name Request
if (this.collectionNameSearchCancel != undefined)
this.collectionNameSearchCancel.cancel('Collection name search Canceled.');
}
}
</script>

118
src/admin/roles.vue Normal file
View File

@ -0,0 +1,118 @@
<template>
<div
id="tainacan-roles-app"
class="wrap">
<router-view />
</div>
</template>
<script>
export default {
name: "RolesPage"
}
</script>
<style lang="scss">
.tainacan_page_tainacan_roles #wpbody {
overflow-x: hidden;
}
#tainacan-roles-app {
margin-top: 42px;
a:hover {
cursor: pointer;
}
}
.tainacan-roles-tooltip {
display: block !important;
z-index: 10000;
max-width: 300px;
.tooltip-inner {
background: black;
padding: 0.35rem 0.45rem;
color: white;
text-align: center;
}
.tooltip-arrow {
width: 0;
height: 0;
border-style: solid;
position: absolute;
margin: 5px;
border-color: black;
z-index: 1;
}
&[x-placement^="top"] {
margin-bottom: 5px;
.tooltip-arrow {
border-width: 5px 5px 0 5px;
border-left-color: transparent !important;
border-right-color: transparent !important;
border-bottom-color: transparent !important;
bottom: -5px;
left: calc(50% - 5px);
margin-top: 0;
margin-bottom: 0;
}
}
&[x-placement^="bottom"] {
margin-top: 5px;
.tooltip-arrow {
border-width: 0 5px 5px 5px;
border-left-color: transparent !important;
border-right-color: transparent !important;
border-top-color: transparent !important;
top: -5px;
left: calc(50% - 5px);
margin-top: 0;
margin-bottom: 0;
}
}
&[x-placement^="right"] {
margin-left: 5px;
.tooltip-arrow {
border-width: 5px 5px 5px 0;
border-left-color: transparent !important;
border-top-color: transparent !important;
border-bottom-color: transparent !important;
left: -5px;
top: calc(50% - 5px);
margin-left: 0;
margin-right: 0;
}
}
&[x-placement^="left"] {
margin-right: 5px;
.tooltip-arrow {
border-width: 5px 0 5px 5px;
border-top-color: transparent !important;
border-right-color: transparent !important;
border-bottom-color: transparent !important;
right: -5px;
top: calc(50% - 5px);
margin-left: 0;
margin-right: 0;
}
}
&[aria-hidden='true'] {
visibility: hidden;
opacity: 0;
transition: opacity .15s, visibility .15s;
}
&[aria-hidden='false'] {
visibility: visible;
opacity: 1;
transition: opacity .15s;
}
}
</style>

View File

@ -0,0 +1,494 @@
<template>
<form @submit="onSubmit">
<h1
v-if="this.roleSlug !== 'new'"
class="wp-heading-inline">
{{ $route.meta.title }}&nbsp;<strong>{{ role.name ? role.name : '' }}</strong>
</h1>
<h1
v-else
class="wp-heading-inline">
{{ $i18n.get('Add new role') }}
</h1>
<transition name="appear-from-right">
<div
v-if="showNotice"
class="notice notice-success notice-alt">
<p>{{ $i18n.get('User Role Saved') }}</p>
</div>
</transition>
<hr class="wp-header-end">
<br>
<template v-if="!isLoadingRole">
<div class="name-edition-box">
<label for="role-name-input">{{ $i18n.get('Role name') + ':' }}</label>
<input
type="text"
id="role-name-input"
name="name"
@input="showNotice = false"
v-model="role.name"
:placeholder="$i18n.get('Insert the role name...')">
</div>
</template>
<span
v-if="isLoadingRole || isLoadingCapabilities"
class="spinner is-active"
style="float: none; margin: 0 auto; width: 100%; display: block;" />
<template v-if="!isLoadingRole && !isLoadingCapabilities">
<br>
<div id="capabilities-tabs">
<h2 class="nav-tab-wrapper">
<a
class="nav-tab"
:class="{ 'nav-tab-active': capabilitiesTab == 'repository'}"
@click="capabilitiesTab = 'repository'">
{{ $i18n.get('Repository') }}
</a>
<a
class="nav-tab"
:class="{ 'nav-tab-active': capabilitiesTab == 'collections'}"
@click="capabilitiesTab = 'collections'">
{{ $i18n.get('Collections') }}
</a>
</h2>
<div
class="tabs-content"
v-if="capabilitiesTab === 'repository'"
id="tab-repository">
<!-- <h3>{{ $i18n.get('Role\'s Repository Related Capabilities List') }}</h3> -->
<div class="capabilities-list">
<div
class="capability-group"
v-for="(group, groupIndex) of groupedRepositoryCapabilities"
:key="groupIndex">
<h3>{{ groupIndex }}</h3>
<ul>
<template v-for="(capability, index) of group">
<li
v-tooltip="{
content: repositoryCapabilities[capability].description,
autoHide: true,
delay: 0,
placement: 'bottom',
classes: ['tainacan-roles-tooltip']
}"
:key="index"
:id="'capability-' + capability">
<span class="check-column">
<label
class="screen-reader-text"
:for="'capability_' + capability">
{{ $i18n.get('Selecionar') + ' ' + repositoryCapabilities[capability].display_name }}
</label>
<input
type="checkbox"
name="capabilities[]"
:id="'capability_'+ capability"
:disabled="repositoryCapabilities[capability].supercaps.length > 0 && repositoryCapabilities[capability].supercaps.findIndex((supercap) => role.capabilities[supercap] == true) >= 0"
:checked="role.capabilities[capability] || (repositoryCapabilities[capability].supercaps.length > 0 && repositoryCapabilities[capability].supercaps.findIndex((supercap) => role.capabilities[supercap] == true) >= 0)"
@input="onUpdateCapability($event.target.checked, capability)">
</span>
<span
class="name column-name"
:data-colname="$i18n.get('Capability name')">
{{ repositoryCapabilities[capability].display_name }}
</span>
</li>
<br :key="index">
</template>
</ul>
</div>
</div>
<p><span class="dashicons dashicons-info" />&nbsp; {{ $i18n.get('The capability "Manage Tainacan" may affect other capabilities related to repository and collections.') }}</p>
</div> <!-- End of Repository Tab -->
<div
class="tabs-content"
v-else-if="capabilitiesTab === 'collections'"
id="tab-collections">
<span
v-if="isLoadingCollections"
class="spinner is-active"
style="float: none; margin: 0 auto;" />
<template v-if="!isLoadingCollections">
<!-- <h3>{{ $i18n.get('Role\'s Collection Related Capabilities List') }}</h3> -->
<div class="tablenav top">
<div class="alignleft collection-selector">
<label
for="bulk-action-selector-top"
class="screen-reader-text">
{{ $i18n.get('Select the collection to change capabilities') }}
</label>
<select
name="collection"
id="collection-select"
:value="selectedCollection"
@input="selectedCollection = $event.target.value">
<option value="all">{{ $i18n.get('All Collections') }}</option>
<option
:key="index"
v-for="(collection, index) of collections"
:value="collection.id">
{{ collection.name }}
</option>
</select>
</div>
<br class="clear">
</div>
<div class="capabilities-list">
<div
class="capability-group"
v-for="(group, groupIndex) of groupedCollectionCapabilities"
:key="groupIndex">
<h3>{{ groupIndex }}</h3>
<ul>
<template v-for="(capability, index) of group">
<li
v-tooltip="{
content: collectionCapabilities[capability].description,
autoHide: true,
delay: 0,
placement: 'bottom',
classes: ['tainacan-roles-tooltip']
}"
:key="index"
:id="'capability-' + capability.replace('%d', selectedCollection)">
<span class="check-column">
<label
class="screen-reader-text"
:for="'capability_' + capability.replace('%d', selectedCollection)">
{{ $i18n.get('Selecionar') + ' ' + collectionCapabilities[capability].display_name }}
</label>
<input
type="checkbox"
name="roles[]"
:id="'capability_'+ capability.replace('%d', selectedCollection)"
:disabled="collectionCapabilities[capability].supercaps.length > 0 && collectionCapabilities[capability].supercaps.filter((supercap) => supercap.replace('%d', selectedCollection) != capability.replace('%d', selectedCollection)).findIndex((supercap) => role.capabilities[supercap.replace('%d', selectedCollection)] == true) >= 0"
:checked="role.capabilities[capability.replace('%d', selectedCollection)] || (collectionCapabilities[capability].supercaps.length > 0 && collectionCapabilities[capability].supercaps.findIndex((supercap) => role.capabilities[supercap.replace('%d', selectedCollection)] == true) >= 0)"
@input="onUpdateCapability($event.target.checked, capability.replace('%d', selectedCollection))">
</span>
<span
class="name column-name"
:data-colname="$i18n.get('Capability name')">
{{ collectionCapabilities[capability].display_name }}
</span>
</li>
<br :key="index">
</template>
</ul>
</div>
</div>
</template>
<p><span class="dashicons dashicons-info" />&nbsp; {{ $i18n.get('The capability "Manage Tainacan" may affect other capabilities related to repository and collections.') }}</p>
<p><span class="dashicons dashicons-info" />&nbsp; {{ $i18n.get('Capabilities related to All Collections shall affect other Collections capabilities.') }}</p>
</div> <!-- End of Collections Tab -->
</div> <!-- End of Tabs-->
</template>
<div class="form-submit">
<p class="cancel">
<input
type="button"
name="cancel"
@click="$router.go(-1)"
id="cancel"
class="button"
:value="$i18n.get('Cancel')">
</p>
<p class="submit">
<span
v-if="isUpdatingRole"
class="spinner is-active"
style="float: none;" />
<input
type="submit"
name="submit"
id="submit"
:disabled="!role.name || showNotice"
class="button button-primary"
:value="this.roleSlug === 'new' ? $i18n.get('Create Role') : $i18n.get('Save Changes')">
</p>
</div>
</form>
</template>
<script>
import { mapActions, mapGetters } from 'vuex';
export default {
data() {
return {
isUpdatingRole: false,
isLoadingRole: false,
isLoadingCapabilities: false,
selectedCollection: 'all',
collections: [],
isLoadingCollections: false,
role: {
name: '',
capabilities: {}
},
capabilitiesTab: 'repository',
showNotice: false
}
},
computed: {
originalRole() {
return this.getRole()
},
capabilities() {
return this.getCapabilities();
},
collectionCapabilities() {
let collectionCapabilities = {}
for (let [capabilityKey, capability] of Object.entries(this.capabilities)) {
if (capability.scope === 'collection')
collectionCapabilities[capabilityKey] = capability;
}
return collectionCapabilities;
},
repositoryCapabilities() {
let repositoryCapabilities = {}
for (let [capabilityKey, capability] of Object.entries(this.capabilities)) {
if (capability.scope === 'repository')
repositoryCapabilities[capabilityKey] = capability;
}
return repositoryCapabilities;
},
groupedCollectionCapabilities() {
return _.groupBy(Object.keys(this.collectionCapabilities), this.getCapabilityRelatedEntity);
},
groupedRepositoryCapabilities() {
return _.groupBy(Object.keys(this.repositoryCapabilities), this.getCapabilityRelatedEntity);
}
},
methods: {
...mapActions('collection', [
'fetchAllCollectionNames'
]),
...mapActions('capability', [
'createRole',
'updateRole',
'fetchRole',
'fetchCapabilities'
]),
...mapGetters('collection', [
'getCollections'
]),
...mapGetters('capability', [
'getRole',
'getCapabilities'
]),
onUpdateCapability(value, capabilityKey) {
this.showNotice = false;
const capabilities = this.role.capabilities && Object.keys(this.role.capabilities).length ? this.role.capabilities : {};
this.$set(capabilities, capabilityKey, value);
this.$set(this.role, 'capabilities', capabilities);
},
onSubmit(event) {
event.preventDefault();
this.isUpdatingRole = true;
if (this.roleSlug === 'new') {
this.createRole(this.role)
.then((createdRole) => {
this.roleSlug = createdRole.slug;
this.$router.push('/roles/' + this.roleSlug);
this.isUpdatingRole = false;
this.showNotice = true;
})
.catch(() => {
this.isUpdatingRole = false;
});
} else {
this.updateRole(this.role)
.then(() => {
this.isUpdatingRole = false;
this.showNotice = true;
})
.catch(() => {
this.isUpdatingRole = false;
});
}
},
getCapabilityRelatedEntity(capabilitySlug) {
if (capabilitySlug.match('collection'))
return this.$i18n.get('Collection')
else if (capabilitySlug.match('metadata') || capabilitySlug.match('metadatum'))
return this.$i18n.get('Metadata')
else if (capabilitySlug.match('filter'))
return this.$i18n.get('Filters')
else if (capabilitySlug.match('log'))
return this.$i18n.get('Activities')
else if (capabilitySlug.match('taxonomy') || capabilitySlug.match('taxonomies'))
return this.$i18n.get('Taxonomies')
else if (capabilitySlug.match('item'))
return this.$i18n.get('Items')
else if (capabilitySlug.match('%d'))
return this.$i18n.get('Collection')
else
return this.$i18n.get('Repository')
}
},
created() {
this.roleSlug = this.$route.params.roleSlug;
if (this.roleSlug !== 'new') {
this.isLoadingRole = true;
this.fetchRole(this.roleSlug)
.then((originalRole) => {
this.role = JSON.parse(JSON.stringify(originalRole));
this.isLoadingRole = false;
}).catch(() => {
this.isLoadingRole = false;
});
} else if (this.roleSlug === 'new' && this.$route.query.template) {
this.isLoadingRole = true;
this.fetchRole(this.$route.query.template)
.then((originalRole) => {
this.role = JSON.parse(JSON.stringify(originalRole));
this.role.name = this.role.name + ' ' + this.$i18n.get('(Copy)');
this.role.slug = undefined;
this.isLoadingRole = false;
}).catch(() => {
this.isLoadingRole = false;
});
} else {
this.role = {
name: '',
capabilities: {}
}
}
this.isLoadingCapabilities = true;
this.fetchCapabilities({ collectionId: undefined })
.then(() => {
this.isLoadingCapabilities = false;
}).catch(() => {
this.isLoadingCapabilities = false;
});
this.isLoadingCollections = true;
this.fetchAllCollectionNames()
.then((resp) => {
resp.request
.then((collections) => {
this.collections = collections;
this.isLoadingCollections = false;
}).catch(() => {
this.isLoadingCollections = false;
});
})
.catch(() => {
this.isLoadingCollections = false;
});
}
}
</script>
<style lang="scss" scoped>
@keyframes appear-from-right {
from {
right: -100%;
opacity: 0.5;
}
to {
right: 0;
opacity: 1;
}
}
.appear-from-right-enter-active {
animation: appear-from-right 0.8s;
}
.appear-from-right-leave-active {
animation: appear-from-right 0.8s reverse;
}
.notice {
position: relative;
float: right;
}
#role-name-input {
min-width: 200px;
}
.form-submit {
display: flex;
justify-content: space-between;
align-content: center;
margin: 2rem 0 1rem 0;
p {
margin: 0;
padding: 0;
}
.button {
padding: 2px 16px;
}
}
.name-edition-box label {
margin-right: 2rem;
padding-bottom: 3px;
font-size: 1rem;
}
.nav-tab {
background-color: #faf9f9;
border-bottom-color: #faf9f9;
padding: 5px 24px;
}
.tabs-content {
background-color: #faf9f9;
border: 1px solid #ccc;
border-top: none;
padding: 1rem 2rem;
}
.dashicons-info {
color: #e69810;
}
.capabilities-list {
padding: 1rem;
break-inside: avoid;
column-count: 5;
.capability-group {
break-inside: avoid;
h3 {
margin-top: 0;
margin-bottom: 1rem;
font-size: 1rem;
font-weight: bold;
color: #0073aa;
}
ul {
padding-bottom: 1rem;
li {
margin: 0 0.5rem 0.5rem;
display: inline-block;
}
}
}
@media only screen and (max-width: 1600px) {
column-count: 4;
}
@media only screen and (max-width: 1400px) {
column-count: 3;
}
@media only screen and (max-width: 962px) {
column-count: 2;
}
@media only screen and (max-width: 568px) {
column-count: 1;
}
}
@media only screen and (max-width: 783px) {
#collection-select {
width: 100%;
}
.nav-tab-wrapper {
border-bottom: 1px solid #ccc;
}
}
</style>

View File

@ -0,0 +1,466 @@
<template>
<div>
<h1 class="wp-heading-inline">{{ $route.meta.title }}</h1>
<div class="dropdown-new-role">
<router-link
to="/roles/new"
class="page-title-action">
{{ $i18n.get('New role') }}
</router-link>
<button
v-tooltip="{
content: $i18n.get('Create a role based on: '),
autoHide: true,
placement: 'top',
classes: ['tainacan-roles-tooltip']
}"
@click="showDropdownMenu = !showDropdownMenu"
class="button button-secondary"
aria-haspopup="true"
aria-controls="dropdown-menu"
:aria-expanded="showDropdownMenu">
<span class="dashicons dashicons-arrow-down-alt2" />
</button>
<div
:class="{ 'show': showDropdownMenu }"
class="dropdown-menu"
id="dropdown-menu"
:aria-hidden="showDropdownMenu">
<!-- <p class="dropdown-menu-intro">{{ $i18n.get('Create a new role based on: ') }}</p> -->
<ul>
<li
v-for="role of roles"
:key="role.slug"
v-if="role.slug.match('tainacan')">
<router-link :to="'/roles/new?template=' + role.slug">
{{ role.name }}
</router-link>
</li>
<li><router-link to="/roles/new"><em>{{ $i18n.get('Blank') }}</em></router-link></li>
</ul>
</div>
</div>
<hr class="wp-header-end">
<h2 class="screen-reader-text">{{ $i18n.get('Roles list') }}</h2>
<p class="search-box">
<label
class="screen-reader-text"
for="roles-search-input">
{{ $i18n.get('Type to search by Role Name') }}
</label>
<input
type="search"
id="roles-search-input"
:placeholder="$i18n.get('Type to search by Role Name')"
v-model="searchString">
</p>
<div class="tablenav top">
<div class="align-left actions">
<p>{{ $i18n.get('Create and edit roles for users') }}</p>
</div>
<div class="tablenav-pages one-page">
<span class="displaying-num">{{ Object.keys(roles).length + ' ' + $i18n.getWithNumber('item', 'items', Object.keys(roles).length) }}</span>
</div>
</div>
<table
v-if="!isLoadingRoles"
class="wp-list-table widefat fixed striped roles">
<thead>
<tr>
<!-- <td class="manage-column column-cb check-column">
<label
class="screen-reader-text"
for="cb-select-all">
{{ $i18n.get('Selecionar Todos') }}
</label>
<input
id="cb-select-all"
type="checkbox">
</td> -->
<th
scope="col"
id="name"
class="manage-column column-name">
{{ $i18n.get('Role\'s Name') }}
</th>
<!-- <th
scope="col"
id="role"
class="manage-column column-slug">
{{ $i18n.get('Slug') }}
</th> -->
<th
scope="col"
id="capabilities-number"
class="manage-column column-capabilities num">
{{ $i18n.get('Number of Capabilities') }}
</th>
</tr>
</thead>
<tbody data-wp-lists="list:roles">
<tr
v-for="role of roles"
:key="role.slug"
:id="role.slug">
<!-- <th
scope="row"
class="check-column">
<label
class="screen-reader-text"
:for="'role_' + role.slug">
{{ $i18n.get('Selecionar') + ' ' + role.name }}
</label>
<input
type="checkbox"
name="roles[]"
:id="'role_'+ role.slug"
:value="role.slug">
</th> -->
<td
class="name column-name has-row-actions column-primary"
:data-colname="$i18n.get('Role name')">
<strong>
<router-link
:to="'/roles/' + role.slug"
class="submitdelete">
{{ role.name }}
</router-link>
</strong>
<br>
<div class="row-actions">
<span class="edit">
<router-link :to="'/roles/' + role.slug">
{{ $i18n.get('Edit') }}
</router-link>
</span>
<span
v-if="role.slug.match('tainacan')"
class="delete">
&nbsp;|&nbsp;
<a
@click="removeRole(role.slug)"
class="submitdelete">
{{ $i18n.get('Delete') }}
</a>
</span>
</div>
</td>
<!-- <td
class="slug column-slug"
:data-colname="$i18n.get('Slug')">
{{ role.slug }}
</td> -->
<td
class="capabilities column-capabilities num column-primary"
:data-colname="$i18n.get('Number of capabilities')">
{{ Object.values(role.capabilities).filter((capability) => capability == true).length }}
</td>
</tr>
</tbody>
<tfoot>
<tr>
<!-- <td class="manage-column column-cb check-column">
<label
class="screen-reader-text"
for="cb-select-all-2">
{{ $i18n.get('Selecionar Todos') }}
</label>
<input
id="cb-select-all-2"
type="checkbox">
</td> -->
<th
scope="col"
id="name"
class="manage-column column-name column-primary">
{{ $i18n.get('Role\'s Name') }}
</th>
<!-- <th
scope="col"
id="role"
class="manage-column column-slug">
{{ $i18n.get('Slug') }}
</th> -->
<th
scope="col"
id="capabilities-number"
class="manage-column column-capabilities num">
{{ $i18n.get('Number of Capabilities') }}
</th>
</tr>
</tfoot>
</table>
<div class="tablenav bottom">
<div
style="margin-left: auto;"
class="tablenav-pages one-page">
<span class="displaying-num">
{{ Object.keys(roles).length + ' ' + $i18n.getWithNumber('item', 'items', Object.keys(roles).length) }}
</span>
</div>
</div>
</div>
</template>
<script>
import { mapActions, mapGetters } from 'vuex';
export default {
name: "RolesList",
data() {
return {
isLoadingRoles: false,
relatedEntities: [],
currentRelatedEntity: '',
showDropdownMenu: false,
searchString: ''
}
},
computed: {
roles() {
let roles = this.getRoles();
if (this.searchString) {
let searchedRoles = {}
for (let [roleKey, role] of Object.entries(roles)) {
if (role.name.toLowerCase().match(this.searchString))
searchedRoles[roleKey] = role;
}
roles = searchedRoles;
}
if (this.relatedEntities.length) {
let filteredRoles = {};
for (let [roleKey, role] of Object.entries(roles)) {
for (let entity of this.relatedEntities) {
const existingIndex = Object.entries(role.capabilities).findIndex((capability) => capability[1] ? capability[0].match(entity) : false)
if (existingIndex >= 0) {
filteredRoles[roleKey] = role;
break;
}
}
}
roles = filteredRoles;
}
return roles;
}
},
methods: {
...mapActions('capability', [
'fetchRoles',
'deleteRole'
]),
...mapGetters('capability', [
'getRoles'
]),
filteByCapabilitiesRelatedTo(entityName) {
this.currentRelatedEntity = entityName;
switch(entityName) {
case 'repository':
this.relatedEntities = ['repository', 'manage-tainacan', 'manage-users', 'users'];
break;
case 'taxonomy':
this.relatedEntities = ['taxonomy', 'taxonomies'];
break;
case 'collection':
this.relatedEntities = ['collection', 'item'];
break;
case 'metadata':
this.relatedEntities = ['metadata', 'metadatum'];
break;
case 'filter':
this.relatedEntities = ['filter'];
break;
case 'activity':
this.relatedEntities = ['log'];
break;
default:
this.relatedEntities = [];
}
},
removeRole(roleSlug) {
this.deleteRole(roleSlug)
.then(() => {
this.$forceUpdate();
})
}
},
created() {
this.isLoadingRoles = true;
this.fetchRoles()
.then(() => {
this.isLoadingRoles = false;
}).catch(() => {
this.isLoadingRoles = false;
});
}
}
</script>
<style lang="scss" scoped>
.tablenav {
display: flex;
justify-content: space-between;
align-items: flex-end;
height: auto;
}
.search-box {
margin-top: -34px;
#roles-search-input {
min-width: 300px;
}
}
.dropdown-new-role {
display: inline-flex;
align-items: center;
position: relative;
a:first-child {
border-top-right-radius: 0;
border-bottom-right-radius: 0;
margin-right: -1px;
font-weight: normal;
}
.button {
top: -3px;
position: relative;
border-top-left-radius: 0;
border-bottom-left-radius: 0;
border-top-right-radius: 2px;
border-bottom-right-radius: 2px;
padding: 0px 6px;
line-height: 1rem;
height: 15px;
font-size: 16px;
}
.dropdown-menu {
display: none;
opacity: 0;
visibility: hidden;
position: absolute;
top: 50%;
left: calc(100% - 42px);
background:white;
border: 1px solid#ccc;
z-index: 9;
transition: top 0.3s ease, opacity 0.3s ease, display 0.3s ease;
text-align: center;
&:after {
content: "";
display: block;
position: absolute;
right: calc(100% - 32px);
width: 0;
height: 0;
border-style: solid;
border-color: transparent transparent #ccc transparent;
border-right-width: 8px;
border-bottom-width: 8px;
border-left-width: 8px;
top: -12px;
}
&:before {
content: "";
display: block;
position: absolute;
right: calc(100% - 32px);
width: 0;
height: 0;
border-style: solid;
border-color: transparent transparent white transparent;
border-right-width: 8px;
border-bottom-width: 8px;
border-left-width: 8px;
top: -11px;
z-index: 9;
}
&.show {
display: block;
opacity: 1;
visibility: visible;
top: calc(100% + 6px);
}
.dropdown-menu-intro {
color: #898d8f;
font-size: 0.75rem;
font-style: italic;
padding: 0.75rem 1rem 0 0.75rem;
white-space: nowrap;
margin: 0;
}
ul {
margin: 0.5rem 0;
}
li>a {
display: block;
margin: 0;
padding: 0.25rem 0.75rem;
white-space: nowrap;
cursor: pointer;
color: #32373c;
text-decoration: none;
&:hover {
background-color: #0073aa;;
color: white;
}
}
}
@media only screen and (max-width: 782px) {
.button {
padding: 1.24rem 0.5rem;
top: -1px;
font-size: 1rem;
.dashicons {
margin-top: -0.5rem;
}
}
.dropdown-menu {
left: -50%;
&::before,
&::after {
right: 16px;
}
}
}
}
.selected-entity {
margin-top: -4px;
padding: 2px 0;
a {
font-weight: bold;
color: black;
}
}
table {
table-layout: auto;
&.widefat td,
&.widefat th {
padding: 8px 18px;
}
}
.column-capabilities {
width: 1px;
white-space: nowrap;
}
</style>

View File

@ -189,4 +189,4 @@
margin: 1rem auto;
}
}
}
}

View File

@ -394,6 +394,62 @@ $modal-z: 9999999;
animation-timing-function: ease;
}
// Capababilities Form Collapse
@keyframes form-capabilities-in {
from {
opacity: 0;
height: 0px;
max-height: 0px;
min-height: 0px;
// -ms-transform: translate(0%, -30%); /* IE 9 */
// -webkit-transform: translate(0%, -30%); /* Safari */
// transform: translate(0%, -30%);
}
to {
height: 120px;
max-height: 120px;
min-height: 120px;
opacity: 1;
// -ms-transform: translate(0, 0); /* IE 9 */
// -webkit-transform: translate(0, 0); /* Safari */
// transform: translate(0, 0);
}
}
@keyframes form-capabilities-out {
from {
height: 120px;
max-height: 120px;
min-height: 120px;
//opacity: 1;
// -ms-transform: translate(0, 0); /* IE 9 */
// -webkit-transform: translate(0, 0); /* Safari */
// transform: translate(0, 0);
}
to {
height: 0px;
max-height: 0px;
min-height: 0px;
//opacity: 0;
// -ms-transform: translate(0%, -30%); /* IE 9 */
// -webkit-transform: translate(0%, -30%); /* Safari */
// transform: translate(0%, -30%);
}
}
.form-capabilities-enter-active {
overflow: hidden;
animation-name: form-capabilities-in;
animation-duration: 0.2s;
animation-timing-function: ease;
}
.form-capabilities-leave-active {
overflow: hidden;
animation-name: form-capabilities-out;
animation-duration: 0.15s;
animation-timing-function: ease;
}
// Filters menu
@keyframes filters-menu-in {
from {

View File

@ -0,0 +1,3 @@
#tainacan-roles-app {
}

View File

@ -37,6 +37,7 @@ return apply_filters( 'tainacan-admin-i18n', [
'processes' => __( 'Processes', 'tainacan' ),
'sequence' => __( 'Sequence', 'tainacan' ),
'exporters' => __( 'Exporters', 'tainacan' ),
'capabilities' => __( 'Capabilities', 'tainacan' ),
// Actions
'close' => __( 'Close', 'tainacan' ),
@ -132,6 +133,8 @@ return apply_filters( 'tainacan-admin-i18n', [
'title_processes_page' => __( 'Processes', 'tainacan' ),
'title_item_bulk_add' => __( 'Bulk Add Items', 'tainacan' ),
'title_exporter_page' => __( 'Exporter', 'tainacan'),
'title_collection_capabilities' => __( 'Capabilities Related to the Collection', 'tainacan'),
'title_repository_capabilities' => __( 'Capabilities Related to the Repository', 'tainacan'),
// Labels (used mainly on Aria Labels and Inputs)
'label' => __( 'Label', 'tainacan' ),
@ -156,7 +159,6 @@ return apply_filters( 'tainacan-admin-i18n', [
'label_thumbnail' => __( 'Thumbnail', 'tainacan' ),
'label_empty_thumbnail' => __( 'Empty Thumbnail', 'tainacan' ),
'label_empty_term_image' => __( 'Empty Term Image', 'tainacan' ),
'label_moderators' => __( 'Moderators', 'tainacan' ),
'label_parent_collection' => __( 'Parent collection', 'tainacan' ),
'label_no_parent_collection' => __( 'No parent collection', 'tainacan' ),
'label_button_view' => __( 'Button View', 'tainacan' ),
@ -407,20 +409,21 @@ return apply_filters( 'tainacan-admin-i18n', [
'label_previous_page' => __( 'Previous page', 'tainacan' ),
'label_page' => __( 'Page', 'tainacan' ),
'label_current_page' => __( 'Current page', 'tainacan' ),
'label_shrink_menu' => __( 'Shrink menu', 'tainacan' ),
'label_document_uploaded' => __( 'Document uploaded', 'tainacan' ),
'label_repository_filter' => __( 'Repository filter', 'tainacan' ),
'label_repository_metadatum' => __( 'Repository metadatum', 'tainacan' ),
'label_collection_filter' => __( 'Collection filter', 'tainacan' ),
'label_collection_metadatum' => __( 'Collection metadatum', 'tainacan' ),
'label_recover_from_trash' => __( 'Recover from trash', 'tainacan' ),
'label_show_children_terms' => __( 'Show children terms', 'tainacan' ),
'label_begin_slide_transition' => __( 'Begin slide transition', 'tainacan' ),
'label_pause_slide_transition' => __( 'Pause slide transition', 'tainacan' ),
'label_next_group_slides' => __( 'Next group of slides', 'tainacan' ),
'label_previous_group_slides' => __( 'Previous group of slides', 'tainacan' ),
'label_plugin_home_page' => __( 'Plugin home page', 'tainacan' ),
'label_wordpress_admin_page' => __( 'WordPress Admin Page', 'tainacan' ),
'label_shrink_menu' => __( 'Shrink menu', 'tainacan' ),
'label_document_uploaded' => __( 'Document uploaded', 'tainacan' ),
'label_repository_filter' => __( 'Repository filter', 'tainacan' ),
'label_repository_metadatum' => __( 'Repository metadatum', 'tainacan' ),
'label_collection_filter' => __( 'Collection filter', 'tainacan' ),
'label_collection_metadatum' => __( 'Collection metadatum', 'tainacan' ),
'label_collection_capabilities' => __( 'Collection capabilities', 'tainacan' ),
'label_recover_from_trash' => __( 'Recover from trash', 'tainacan' ),
'label_show_children_terms' => __( 'Show children terms', 'tainacan' ),
'label_begin_slide_transition' => __( 'Begin slide transition', 'tainacan' ),
'label_pause_slide_transition' => __( 'Pause slide transition', 'tainacan' ),
'label_next_group_slides' => __( 'Next group of slides', 'tainacan' ),
'label_previous_group_slides' => __( 'Previous group of slides', 'tainacan' ),
'label_plugin_home_page' => __( 'Plugin home page', 'tainacan' ),
'label_wordpress_admin_page' => __( 'WordPress Admin Page', 'tainacan' ),
'label_view_all_%s_collections' => __( 'View all %s collections', 'tainacan' ),
'label_view_collections_list' => __( 'View collections list', 'tainacan' ),
'label_comparator' => __( 'Comparator', 'tainacan' ),
@ -442,7 +445,10 @@ return apply_filters( 'tainacan-admin-i18n', [
'label_month' => __( 'Month', 'tainacan' ),
'label_year' => __( 'Year', 'tainacan' ),
'label_related_to' => __( 'Related to', 'tainacan' ),
'label_user_roles' => __( 'User roles', 'tainacan' ),
'label_associated_roles' => __( 'Associated roles', 'tainacan' ),
'label_inherited_roles' => __( 'Inherited roles', 'tainacan' ),
// Instructions. More complex sentences to guide user and placeholders
'instruction_delete_selected_collections' => __( 'Delete selected collections', 'tainacan' ),
'instruction_delete_selected_items' => __( 'Delete selected items', 'tainacan' ),
@ -456,6 +462,7 @@ return apply_filters( 'tainacan-admin-i18n', [
'instruction_cover_page' => __( 'Type to search a Page to choose.', 'tainacan' ),
'instruction_moderators' => __( 'Type to search a User to add.', 'tainacan' ),
'instruction_type_search_users_filter' => __( 'Type to search users to filter...', 'tainacan' ),
'instruction_type_search_roles_filter' => __( 'Type to search roles to filter...', 'tainacan' ),
'instruction_select_a_parent_collection' => __( 'Select a parent collection.', 'tainacan' ),
'instruction_select_collection_thumbnail' => __( 'Select a thumbnail image for collection', 'tainacan' ),
'instruction_select_item_thumbnail' => __( 'Select a thumbnail image for item', 'tainacan' ),
@ -467,7 +474,7 @@ return apply_filters( 'tainacan-admin-i18n', [
'instruction_write_text' => __( 'Write Text', 'tainacan' ),
'instruction_search' => __( 'Search', 'tainacan' ),
'instruction_search_in_repository' => __( 'Search in repository', 'tainacan' ),
'instruction_select_a_target_collection' => __( 'Select a target collection', 'tainacan' ),
'instruction_select_a_target_collection' => __( 'Select a target collection.', 'tainacan' ),
'instruction_select_a_mapper' => __( 'Select a mapper', 'tainacan' ),
'instruction_select_an_importer_type' => __( 'Select an importer from the options below:', 'tainacan' ),
'instruction_drop_file_or_click_to_upload' => __( 'Drop your source file or click here to upload.', 'tainacan' ),
@ -519,22 +526,22 @@ return apply_filters( 'tainacan-admin-i18n', [
'info_search_criteria' => __( 'Advanced Search Criteria', 'tainacan' ),
'info_name_is_required' => __( 'Name is required.', 'tainacan' ),
'info_no_collection_created' => __( 'No collection was created in this repository.', 'tainacan' ),
'info_no_items_publish' => __( 'No public items found.', 'tainacan' ),
'info_no_items_private' => __( 'No private items found.', 'tainacan' ),
'info_no_items_draft' => __( 'No draft items found.', 'tainacan' ),
'info_no_items_trash' => __( 'No items found on trash.', 'tainacan' ),
'info_no_collections_publish' => __( 'No public collections found.', 'tainacan' ),
'info_no_collections_private' => __( 'No private collections found.', 'tainacan' ),
'info_no_collections_draft' => __( 'No draft collections found.', 'tainacan' ),
'info_no_collections_trash' => __( 'No collections found on trash.', 'tainacan' ),
'info_no_taxonomies_publish' => __( 'No public taxonomies found.', 'tainacan' ),
'info_no_taxonomies_private' => __( 'No private taxonomies found.', 'tainacan' ),
'info_no_taxonomies_draft' => __( 'No draft taxonomies found.', 'tainacan' ),
'info_no_taxonomies_trash' => __( 'No taxonomies found on trash.', 'tainacan' ),
'info_no_taxonomy_created' => __( 'No taxonomy was created in this repository.', 'tainacan' ),
'info_no_terms_created_on_taxonomy' => __( 'No term was created for this taxonomy.', 'tainacan' ),
'info_no_terms_found' => __( 'No term was found here', 'tainacan' ),
@ -545,7 +552,7 @@ return apply_filters( 'tainacan-admin-i18n', [
'info_no_item_found_filter' => __( 'No item was found here with these filters.', 'tainacan' ),
'info_no_item_found' => __( 'No item was found.', 'tainacan' ),
'info_item_not_saved' => __( 'Warning: Item not saved.', 'tainacan' ),
'info_no_moderator_on_collection' => __( "This collection doesn't have any moderator yet.", 'tainacan' ),
'info_no_associated_role' => __( 'No associated role.', 'tainacan' ),
'info_error_deleting_collection' => __( 'Error on deleting collection.', 'tainacan' ),
'info_error_deleting_taxonomy' => __( 'Error on deleting taxonomy', 'tainacan' ),
'info_error_first_value_greater' => __( 'First value should be lower than second value', 'tainacan' ),
@ -575,6 +582,11 @@ return apply_filters( 'tainacan-admin-i18n', [
'info_showing_taxonomies' => __( 'Showing taxonomies ', 'tainacan' ),
'info_showing_activities' => __( 'Showing activities ', 'tainacan' ),
'info_showing_processes' => __( 'Showing processes ', 'tainacan' ),
'info_showing_capabilities' => __( 'Showing capabilities ', 'tainacan' ),
'info_no_capabilities_found' => __( 'No capabilities found.', 'tainacan' ),
'info_no_role_associated_capability' => __( 'No role associated to this capability', 'tainacan' ),
'info_associated_roles' => __( 'These are the roles that have this capability set. You may add or remove the capability to customize the role.', 'tainacan' ),
'info_inherited_roles' => __( 'These are the roles that have greater capabilities, which inherit this one. You can not edit this as it will not have precendece over the greater capability.', 'tainacan' ),
'info_showing_terms' => __( 'Showing terms ', 'tainacan' ),
'info_warning_remove_from_trash_first' => __( 'Remove this item from trash first' ),
'info_to' => __( ' to ', 'tainacan' ),
@ -606,7 +618,7 @@ return apply_filters( 'tainacan-admin-i18n', [
'info_create_filters' => __( 'Click or Drag and Drop Metadata here for creating a new Filter.', 'tainacan' ),
'info_create_metadata' => __( 'Click or Drag and Drop Metadata Types here for creating a new Metadata.', 'tainacan' ),
'info_choose_your_metadata' => __( 'Choose your metadata.', 'tainacan' ),
'info_target_collection_helper' => __( 'The collection where imported item will be added.', 'tainacan' ),
'info_target_collection_helper' => __( 'The collection where imported item will be added. Only those that you have permission are listed.', 'tainacan' ),
'info_source_file_upload' => __( 'The file containing the data to be imported.', 'tainacan' ),
'info_no_metadata_source_file' => __( 'No metadata was found from the source file.', 'tainacan' ),
'info_no_special_fields_available' => __( 'No special field was found.', 'tainacan' ),
@ -674,6 +686,16 @@ return apply_filters( 'tainacan-admin-i18n', [
'info_final_value' => __( 'Final value', 'tainacan' ),
'info_show_interval_on_tag' => __( 'Show applied interval on tags', 'tainacan' ),
'info_title_mapping' => __( 'The title is the most relevant metadata, that shall identify your item on lists for different view modes. Select the title source metadata first, or skip to run importer as it is.', 'taincan'),
'info_can_not_edit_collection' => __( 'You are not allowed to edit this collection.', 'tainacan' ),
'info_can_not_edit_taxonomy' => __( 'You are not allowed to edit this taxonomy.', 'tainacan' ),
'info_can_not_edit_filters' => __( 'You are not allowed to edit filters.', 'tainacan' ),
'info_can_not_edit_metadata' => __( 'You are not allowed to edit metadata.', 'tainacan' ),
'info_can_not_edit_capabilities' => __( 'You are not allowed to edit capabilities.', 'tainacan' ),
'info_can_not_read_activities' => __( 'You are not allowed to read activities.', 'tainacan' ),
'info_can_not_edit_item' => __( 'You are not allowed to edit this item.', 'tainacan' ),
'info_can_not_bulk_edit_items_collection' => __( 'You are not allowed to bulk edit items from this collection.', 'tainacan' ),
'info_not_allowed_change_order_metadata' => __( 'Can not change metadata order now.', 'tainacan' ),
'info_not_allowed_change_order_filters' => __( 'Can not change filters order now.', 'tainacan' ),
// Datepicker months
'datepicker_month_january' => __( 'January', 'tainacan' ),
@ -704,6 +726,6 @@ return apply_filters( 'tainacan-admin-i18n', [
'datepicker_short_friday' => __( 'F', 'tainacan' ),
/* translators: This refers to the short label that will appear on datepickers for Saturday */
'datepicker_short_saturday' => __( 'Sa', 'tainacan' )
] );
?>

View File

@ -5,7 +5,8 @@
class="theme-items-list"
:enabled-view-modes="$root.enabledViewModes"
:default-view-mode="$root.defaultViewMode"
:collection-id="$root.collectionId" />
:collection-id="$root.collectionId"
:is-on-theme="true" />
<term-items-page
v-if="$root.termId != undefined && $root.termId != ''"
class="theme-items-list"
@ -14,7 +15,8 @@
:collection-id="$root.collectionId"
:enabled-view-modes="$root.enabledViewModes"
:default-view-mode="$root.defaultViewMode"
:term-id="$root.termId" />
:term-id="$root.termId"
:is-on-theme="true" />
</div>
</template>

View File

@ -183,9 +183,10 @@ class REST_Bulkedit_Controller extends REST_Controller {
public function bulk_edit_permissions_check($request) {
$collection = $this->collections_repository->fetch($request['collection_id']);
$bulk_cap = 'tnc_col_' . $request['collection_id'] . '_bulk_edit';
if ($collection instanceof Entities\Collection) {
return current_user_can($collection->get_items_capabilities()->edit_others_posts);
return current_user_can($bulk_cap) && current_user_can($collection->get_items_capabilities()->edit_others_posts);
}
return false;

View File

@ -25,7 +25,7 @@ class REST_Collections_Controller extends REST_Controller {
parent::__construct();
add_action('init', array(&$this, 'init_objects'), 11);
}
/**
* Initialize objects after post_type register
*/
@ -59,7 +59,7 @@ class REST_Collections_Controller extends REST_Controller {
'callback' => array($this, 'get_item'),
'permission_callback' => array($this, 'get_item_permissions_check'),
'args' => $this->get_wp_query_params(),
),
array(
'methods' => \WP_REST_Server::EDITABLE,
@ -79,6 +79,36 @@ class REST_Collections_Controller extends REST_Controller {
)
),
'schema' => [$this, 'get_schema'],
));
register_rest_route($this->namespace, '/' . $this->rest_base . '/(?P<collection_id>[\d]+)/metadata_order', array(
array(
'methods' => \WP_REST_Server::EDITABLE,
'callback' => array($this, 'update_metadata_order'),
'permission_callback' => array($this, 'update_metadata_order_permissions_check'),
'args' => [
'metadata_order' => [
'description' => __( 'The order of the metadata in the collection, an array of objects with integer id and bool enabled.', 'tainacan' ),
'required' => true,
'validate_callback' => [$this, 'validate_filters_metadata_order']
]
],
),
'schema' => [$this, 'get_schema'],
));
register_rest_route($this->namespace, '/' . $this->rest_base . '/(?P<collection_id>[\d]+)/filters_order', array(
array(
'methods' => \WP_REST_Server::EDITABLE,
'callback' => array($this, 'update_filters_order'),
'permission_callback' => array($this, 'update_filters_order_permissions_check'),
'args' => [
'filters_order' => [
'description' => __( 'The order of the filters in the collection, an array of objects with integer id and bool enabled.', 'tainacan' ),
'required' => true,
'validate_callback' => [$this, 'validate_filters_metadata_order']
]
],
),
'schema' => [$this, 'get_schema'],
));
}
@ -165,29 +195,21 @@ class REST_Collections_Controller extends REST_Controller {
$item_arr = $item->_toArray();
if ( $request['context'] === 'edit' ) {
$moderators_ids = $item_arr['moderators_ids'];
$moderators = [];
foreach ($moderators_ids as $id){
$user_data = get_userdata($id);
if($user_data){
$user['name'] = $user_data->display_name;
//$user['roles'] = $user_data->roles;
$user['id'] = $user_data->ID;
$moderators[] = $user;
}
}
$item_arr['moderators'] = $moderators;
$item_arr['current_user_can_edit'] = $item->can_edit();
$item_arr['current_user_can_delete'] = $item->can_delete();
$item_arr['current_user_can_delete'] = $item->can_delete();
$collection_caps = \tainacan_roles()->get_collection_caps_slugs();
foreach ($collection_caps as $ccap) {
if ( strpos($ccap, 'tnc_col_') !== 0 ) {
continue;
}
$cap_key = str_replace( 'tnc_col_%d_', 'current_user_can_', $ccap );
$cap_check = str_replace( '%d', $item->get_id(), $ccap );
$item_arr[$cap_key] = current_user_can( $cap_check );
}
}
unset($item_arr['moderators_ids']);
} else {
$attributes_to_filter = $request['fetch_only'];
@ -204,14 +226,24 @@ class REST_Collections_Controller extends REST_Controller {
if ( $request['context'] === 'edit' ) {
$item_arr['current_user_can_edit'] = $item->can_edit();
$item_arr['current_user_can_delete'] = $item->can_delete();
$item_arr['current_user_can_delete'] = $item->can_delete();
$collection_caps = \tainacan_roles()->get_collection_caps_slugs();
foreach ($collection_caps as $ccap) {
if ( strpos($ccap, 'tnc_col_') !== 0 ) {
continue;
}
$cap_key = str_replace( 'tnc_col_%d_', 'current_user_can_', $ccap );
$cap_check = str_replace( '%d', $item->get_id(), $ccap );
$item_arr[$cap_key] = current_user_can( $cap_check );
}
}
$item_arr['url'] = get_permalink( $item_arr['id'] );
}
$total_items = wp_count_posts( $item->get_db_identifier(), 'readable' );
if (isset($total_items->publish) ||
isset($total_items->private) ||
isset($total_items->trash) ||
@ -227,7 +259,7 @@ class REST_Collections_Controller extends REST_Controller {
* Use this filter to add additional post_meta to the api response
* Use the $request object to get the context of the request and other variables
* For example, id context is edit, you may want to add your meta or not.
*
*
* Also take care to do any permissions verification before exposing the data
*/
$extra_metadata = apply_filters('tainacan-api-response-collection-meta', [], $request);
@ -235,7 +267,7 @@ class REST_Collections_Controller extends REST_Controller {
foreach ($extra_metadata as $extra_meta) {
$item_arr[$extra_meta] = get_post_meta($item_arr['id'], $extra_meta, true);
}
return $item_arr;
}
@ -250,11 +282,6 @@ class REST_Collections_Controller extends REST_Controller {
* @throws \Exception
*/
public function get_items_permissions_check($request){
$dummy = new Entities\Collection();
if ( 'edit' === $request['context'] && ! current_user_can($dummy->get_capabilities()->edit_posts) ) {
return false;
}
return true;
}
@ -269,12 +296,8 @@ class REST_Collections_Controller extends REST_Controller {
$collection = $this->collections_repository->fetch($request['collection_id']);
if(($collection instanceof Entities\Collection)) {
if('edit' === $request['context'] && !$collection->can_read()) {
return false;
}
return true;
}
return $collection->can_read();
}
return false;
}
@ -296,7 +319,7 @@ class REST_Collections_Controller extends REST_Controller {
'collection' => $body
], 400);
}
$this->collection = new Collection();
try {
@ -309,7 +332,7 @@ class REST_Collections_Controller extends REST_Controller {
$collection = $this->collections_repository->insert( $prepared_post );
$response = $this->prepare_item_for_response($collection, $request);
do_action('tainacan-api-collection-created', $response, $request);
return new \WP_REST_Response($response, 201);
@ -331,8 +354,7 @@ class REST_Collections_Controller extends REST_Controller {
* @throws \Exception
*/
public function create_item_permissions_check( $request ) {
$dummy = new Entities\Collection();
return current_user_can($dummy->get_capabilities()->edit_posts);
return current_user_can($this->collections_repository->get_capabilities()->edit_posts);
}
/**
@ -466,6 +488,157 @@ class REST_Collections_Controller extends REST_Controller {
return $collection->can_edit();
}
return false;
}
public function validate_filters_metadata_order($value, $request, $param) {
if ( is_array($value) ) {
foreach ($value as $val) {
if ( !is_array($val) ) {
return false;
}
if ( !isset($val['id']) || !is_numeric($val['id']) ) {
return false;
}
if ( !isset($val['enabled']) || !is_bool($val['enabled']) ) {
return false;
}
}
return true;
}
return false;
}
/**
* Update a collection metadata order
*
* @param \WP_REST_Request $request
*
* @return string|\WP_Error|\WP_REST_Response
*/
public function update_metadata_order( $request ) {
$collection_id = $request['collection_id'];
$body = json_decode($request->get_body(), true);
if( !empty($body) && isset($body['metadata_order']) ) {
$collection = $this->collections_repository->fetch($collection_id);
if( $collection instanceof Entities\Collection) {
$collection->set_metadata_order( $body['metadata_order'] );
if ( $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 this ID was not found', 'tainacan' ),
'collection_id' => $collection_id
], 400);
}
return new \WP_REST_Response([
'error_message' => __('The body could not be empty', 'tainacan'),
'body' => $body
], 400);
}
/**
* Verify if current user has permission to update metadata order
*
* @param \WP_REST_Request $request
*
* @return bool|\WP_Error
* @throws \Exception
*/
public function update_metadata_order_permissions_check( $request ) {
$collection = $this->collections_repository->fetch($request['collection_id']);
if($collection instanceof Entities\Collection) {
return $collection->user_can( 'edit_metadata' );
}
return false;
}
/**
* Update a collection metadata order
*
* @param \WP_REST_Request $request
*
* @return string|\WP_Error|\WP_REST_Response
*/
public function update_filters_order( $request ) {
$collection_id = $request['collection_id'];
$body = json_decode($request->get_body(), true);
if( !empty($body) && isset($body['filters_order']) ) {
$collection = $this->collections_repository->fetch($collection_id);
if( $collection instanceof Entities\Collection) {
$collection->set_filters_order( $body['filters_order'] );
if ( $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 this ID was not found', 'tainacan' ),
'collection_id' => $collection_id
], 400);
}
return new \WP_REST_Response([
'error_message' => __('The body could not be empty', 'tainacan'),
'body' => $body
], 400);
}
/**
* Verify if current user has permission to update metadata order
*
* @param \WP_REST_Request $request
*
* @return bool|\WP_Error
* @throws \Exception
*/
public function update_filters_order_permissions_check( $request ) {
$collection = $this->collections_repository->fetch($request['collection_id']);
if($collection instanceof Entities\Collection) {
return $collection->user_can( 'edit_filters' );
}
return false;
}
@ -477,14 +650,14 @@ class REST_Collections_Controller extends REST_Controller {
public function get_endpoint_args_for_item_schema( $method = null ) {
$endpoint_args = [];
if($method === \WP_REST_Server::READABLE) {
$endpoint_args['name'] = array(
'description' => __('Limits the result set to collections with a specific name'),
'type' => 'string',
);
$endpoint_args = array_merge(
$endpoint_args,
$endpoint_args,
parent::get_wp_query_params(),
parent::get_fetch_only_param(),
parent::get_meta_queries_params()
@ -507,29 +680,25 @@ class REST_Collections_Controller extends REST_Controller {
return $endpoint_args;
}
function get_schema() {
$schema = [
'$schema' => 'http://json-schema.org/draft-04/schema#',
'title' => 'collection',
'type' => 'object'
];
$main_schema = parent::get_repository_schema( $this->collections_repository );
$permissions_schema = parent::get_permissions_schema();
// transformation done in $this->prepare_item_for_response()
$main_schema['moderators'] = $main_schema['moderators_ids'];
$main_schema['moderators']['contex'] = 'edit';
$schema['properties'] = array_merge(
parent::get_base_properties_schema(),
$main_schema,
$permissions_schema
);
return $schema;
}
}

View File

@ -21,7 +21,7 @@ class REST_Facets_Controller extends REST_Controller {
parent::__construct();
add_action('init', array(&$this, 'init_objects'), 11);
}
/**
* Initialize objects after post_type register
*/
@ -37,7 +37,7 @@ class REST_Facets_Controller extends REST_Controller {
'permission_callback' => array($this, 'get_items_permissions_check')
)
));
register_rest_route($this->namespace, '/' . $this->rest_base . '/(?P<metadatum_id>[\d]+)', array(
array(
'methods' => \WP_REST_Server::READABLE,
@ -53,40 +53,40 @@ class REST_Facets_Controller extends REST_Controller {
* @return \WP_Error|\WP_REST_Response
*/
public function get_items( $request ) {
// Free php session early so simultaneous requests dont get queued
session_write_close();
$metadatum_id = $request['metadatum_id'];
if( !empty($metadatum_id) ) {
$metadatum = $this->metadatum_repository->fetch($metadatum_id);
$metadatum_type = $metadatum->get_metadata_type();
$metadatum_type_object = $metadatum->get_metadata_type_object();
$offset = null;
$number = null;
$_search = null;
$collection_id = ( isset($request['collection_id']) ) ? $request['collection_id'] : null;
$last_term = ( isset($request['last_term']) ) ? $request['last_term'] : '';
$query_args = defined('TAINACAN_FACETS_DISABLE_FILTER_ITEMS') && true === TAINACAN_FACETS_DISABLE_FILTER_ITEMS ? [] : $request['current_query'];
$query_args = $this->prepare_filters($query_args);
if ( isset($request['hideempty']) && $request['hideempty'] == 0 ) {
$query_args = false;
}
if($request['offset'] >= 0 && $request['number'] >= 1){
$offset = $request['offset'];
$number = $request['number'];
}
if($request['search']) {
$_search = $request['search'];
}
$include = [];
if ( isset($request['getSelected']) && $request['getSelected'] == 1 ) {
if ( $metadatum_type === 'Tainacan\Metadata_Types\Taxonomy' ) {
@ -96,7 +96,7 @@ class REST_Facets_Controller extends REST_Controller {
if( isset($request['current_query']['taxquery']) ){
foreach( $request['current_query']['taxquery'] as $taxquery ){
if( $taxquery['taxonomy'] === $taxonomy_slug ){
$include = $taxquery['terms'];
$include = $taxquery['terms'];
}
}
}
@ -110,13 +110,13 @@ class REST_Facets_Controller extends REST_Controller {
}
}
}
$parent_id = 0;
if ( isset($request['parent']) ) {
$parent_id = (int) $request['parent'];
}
$args = [
'collection_id' => $collection_id,
'search' => $_search,
@ -128,14 +128,14 @@ class REST_Facets_Controller extends REST_Controller {
'count_items' => defined('TAINACAN_FACETS_DISABLE_COUNT_ITEMS') && true === TAINACAN_FACETS_DISABLE_COUNT_ITEMS ? false : true,
'last_term' => $last_term
];
$all_values = $this->metadatum_repository->fetch_all_metadatum_values( $metadatum_id, $args );
if (isset($request['context']) && $request['context'] == 'extended') {
if ($metadatum_type_object->get_repository() instanceof \Tainacan\Repositories\Repository) {
$all_values['values'] = array_map(function($val) use($metadatum_type_object) {
$second_arg = [];
if (isset($val['taxonomy'])) {
$second_arg = $val['taxonomy'];
@ -147,23 +147,23 @@ class REST_Facets_Controller extends REST_Controller {
return $val;
}, $all_values['values']);
}
}
$response = [
'values' => $all_values['values'],
'last_term' => $all_values['last_term']
];
$rest_response = new \WP_REST_Response($response, 200);
$rest_response->header('X-WP-Total', isset($all_values['total']) ? $all_values['total'] : 0 );
$rest_response->header('X-WP-TotalPages', isset($all_values['pages']) ? $all_values['pages'] : 0 );
return $rest_response;
}
}
/**
@ -172,9 +172,17 @@ class REST_Facets_Controller extends REST_Controller {
* @return bool|\WP_Error
*/
public function get_items_permissions_check( $request ) {
return true;
$metadatum_id = $request['metadatum_id'];
$metadatum = $this->metadatum_repository->fetch($metadatum_id);
if ($metadatum instanceof Entities\Metadatum) {
return $metadatum->can_read();
}
return false;
}
}
?>
?>

View File

@ -23,16 +23,16 @@ class REST_Filters_Controller extends REST_Controller {
parent::__construct();
add_action('init', array(&$this, 'init_objects'), 11);
}
/**
* Initialize objects after post_type register
*/
public function init_objects() {
$this->collection = new Entities\Collection();
$this->collection_repository = Repositories\Collections::get_instance();
$this->metadatum_repository = Repositories\Metadata::get_instance();
$this->filter_repository = Repositories\Filters::get_instance();
}
@ -199,24 +199,20 @@ class REST_Filters_Controller extends REST_Controller {
* @throws \Exception
*/
public function create_item_permissions_check( $request ) {
if(isset($request['collection_id']) && isset($request['metadatum_id'])) {
$metadata = $this->metadatum_repository->fetch( $request['metadatum_id'] );
$collection = $this->collection_repository->fetch( $request['collection_id'] );
if ( ( $metadata instanceof Entities\Metadatum ) && ( $collection instanceof Entities\Collection ) ) {
return $this->filter_repository->can_edit( new Entities\Filter() ) && $metadata->can_edit() && $collection->can_edit();
}
} elseif (isset($request['collection_id'])){
if( isset($request['collection_id']) ) {
$collection = $this->collection_repository->fetch( $request['collection_id'] );
if ( $collection instanceof Entities\Collection ) {
return $collection->can_edit();
return current_user_can( 'tnc_col_' . $collection->get_id() . '_manage_filters' );
}
} else {
return current_user_can( 'tnc_rep_manage_filters' );
}
return $this->filter_repository->can_edit(new Entities\Metadatum());
return false;
}
/**
@ -226,7 +222,7 @@ class REST_Filters_Controller extends REST_Controller {
*/
public function delete_item( $request ) {
$permanently = $request['permanently'];
$filter = $this->filter_repository->fetch($request['filter_id']);
if (! $filter instanceof Entities\Filter) {
@ -346,7 +342,7 @@ class REST_Filters_Controller extends REST_Controller {
* Use this filter to add additional post_meta to the api response
* Use the $request object to get the context of the request and other variables
* For example, id context is edit, you may want to add your meta or not.
*
*
* Also take care to do any permissions verification before exposing the data
*/
$extra_metadata = apply_filters('tainacan-api-response-filter-meta', [], $request);
@ -354,7 +350,7 @@ class REST_Filters_Controller extends REST_Controller {
foreach ($extra_metadata as $extra_meta) {
$item_arr[$extra_meta] = get_post_meta($item_arr['id'], $extra_meta, true);
}
return $item_arr;
}
@ -384,7 +380,7 @@ class REST_Filters_Controller extends REST_Controller {
$filters = $this->filter_repository->fetch( $args, 'OBJECT' );
} else {
$collection = $this->collection_repository->fetch($request['collection_id']);
$filters = $this->filter_repository->fetch_by_collection($collection, $args, 'OBJECT');
$filters = $this->filter_repository->fetch_by_collection($collection, $args);
}
$response = [];
@ -402,9 +398,6 @@ class REST_Filters_Controller extends REST_Controller {
*/
public function get_items_permissions_check( $request ) {
if(!isset($request['collection_id'])) {
if ( 'edit' === $request['context'] && ! $this->filter_repository->can_edit( new Entities\Filter() ) ) {
return false;
}
return true;
}
@ -412,7 +405,7 @@ class REST_Filters_Controller extends REST_Controller {
$collection = $this->collection_repository->fetch($request['collection_id']);
if($collection instanceof Entities\Collection){
if ( 'edit' === $request['context'] && ! $collection->can_read() ) {
if ( ! $collection->can_read() ) {
return false;
}
@ -431,14 +424,14 @@ class REST_Filters_Controller extends REST_Controller {
$filter_id = $request['filter_id'];
$filter = $this->filter_repository->fetch($filter_id);
if(! $filter instanceof Entities\Filter) {
return new \WP_REST_Response([
'error_message' => __('A filter with this ID was not found', 'tainacan' ),
'filter_id' => $filter_id
], 400);
}
return new \WP_REST_Response($this->prepare_item_for_response($filter, $request), 200);
}
@ -451,11 +444,7 @@ class REST_Filters_Controller extends REST_Controller {
$filter = $this->filter_repository->fetch($request['filter_id']);
if(($filter instanceof Entities\Filter)) {
if('edit' === $request['context'] && !$filter->can_read()) {
return false;
}
return true;
return $filter->can_read();
}
return false;
@ -470,7 +459,7 @@ class REST_Filters_Controller extends REST_Controller {
$endpoint_args = [];
if($method === \WP_REST_Server::READABLE) {
$endpoint_args = array_merge(
$endpoint_args,
$endpoint_args,
parent::get_wp_query_params()
);
} elseif ($method === \WP_REST_Server::CREATABLE || $method === \WP_REST_Server::EDITABLE) {
@ -504,7 +493,7 @@ class REST_Filters_Controller extends REST_Controller {
);
$query_params = array_merge(
$query_params,
$query_params,
parent::get_wp_query_params(),
parent::get_meta_queries_params()
);
@ -518,10 +507,10 @@ class REST_Filters_Controller extends REST_Controller {
'title' => 'filter',
'type' => 'object'
];
$main_schema = parent::get_repository_schema( $this->filter_repository );
$permissions_schema = parent::get_permissions_schema();
// $collection_scheme = parent::get_repository_schema( $this->collection_repository );
// $metadatum_scheme = parent::get_repository_schema( $this->metadatum_repository );
@ -532,9 +521,9 @@ class REST_Filters_Controller extends REST_Controller {
// $collection_scheme,
// $metadatum_scheme
);
return $schema;
}
}
?>
?>

View File

@ -127,7 +127,7 @@ class REST_Item_Metadata_Controller extends REST_Controller {
return new \WP_REST_Response(apply_filters('tainacan-rest-response', $prepared_item, $request), 200);
}
/**
* @param \WP_REST_Request $request
*
@ -136,13 +136,13 @@ class REST_Item_Metadata_Controller extends REST_Controller {
public function get_item_metadatum_value( $request ) {
$item_id = $request['item_id'];
$metadatum_id = $request['metadatum_id'];
$item = $this->item_repository->fetch($item_id);
$items_metadata = $item->get_metadata();
$prepared_item = '';
foreach ($items_metadata as $item_metadata){
$metadatum = $item_metadata->get_metadatum();
if($metadatum->get_id() == $metadatum_id) {
@ -150,7 +150,7 @@ class REST_Item_Metadata_Controller extends REST_Controller {
$prepared_item['metadatum']['metadata_type_object'] = $metadatum->get_metadata_type_object()->_toArray();
}
}
return new \WP_REST_Response(apply_filters('tainacan-rest-response', $prepared_item, $request), 200);
}
@ -164,11 +164,7 @@ class REST_Item_Metadata_Controller extends REST_Controller {
$item = $this->item_repository->fetch($request['item_id']);
if(($item instanceof Entities\Item)) {
if('edit' === $request['context'] && !$item->can_read()) {
return false;
}
return true;
return $item->can_read();
}
return false;
@ -243,15 +239,15 @@ class REST_Item_Metadata_Controller extends REST_Controller {
public function update_item_permissions_check( $request ) {
if (isset($request['item_id'])) {
$item = $this->item_repository->fetch($request['item_id']);
$metadatum = $this->metadatum_repository->fetch( $request['metadatum_id'] );
if ($item instanceof Entities\Item) {
if($item->can_edit()) {
return $item->can_edit();
if ( $item instanceof Entities\Item && $metadatum instanceof Entities\Metadatum ) {
if( $item->can_edit() && $metadatum->can_read() ) {
return true;
}
else {
$metadatum_id = $request['metadatum_id'];
$metadatum = $this->metadatum_repository->fetch( $metadatum_id );
return 'publish' === $metadatum->get_status() && $metadatum->get_accept_suggestion();
// not yet implemented
// return 'publish' === $metadatum->get_status() && $metadatum->get_accept_suggestion();
}
}

View File

@ -27,7 +27,7 @@ class REST_Items_Controller extends REST_Controller {
parent::__construct();
add_action('init', array(&$this, 'init_objects'), 11);
}
/**
* Initialize objects after post_type register
*/
@ -146,7 +146,7 @@ class REST_Items_Controller extends REST_Controller {
$item_array['metadata'][ $slug ]['value_as_html'] = $item_metadata_array['value_as_html'];
$item_array['metadata'][ $slug ]['value_as_string'] = $item_metadata_array['value_as_string'];
$item_array['metadata'][ $slug ]['semantic_uri'] = $metadatum->get_semantic_uri();
$item_array['metadata'][ $slug ]['multiple'] = $metadatum->get_multiple();
$item_array['metadata'][ $slug ]['mapping'] = $metadatum->get_exposer_mapping();
}
@ -167,7 +167,7 @@ class REST_Items_Controller extends REST_Controller {
* Use this filter to add additional post_meta to the api response
* Use the $request object to get the context of the request and other variables
* For example, id context is edit, you may want to add your meta or not.
*
*
* Also take care to do any permissions verification before exposing the data
*/
$extra_metadata = apply_filters('tainacan-api-response-item-meta', [], $request);
@ -176,10 +176,10 @@ class REST_Items_Controller extends REST_Controller {
foreach ($extra_metadata as $extra_meta) {
$extra_metadata_values[$extra_meta] = get_post_meta($item->get_id(), $extra_meta, true);
}
if(!isset($request['fetch_only'])) {
$item_arr = $item->_toArray();
$item_arr = array_merge($extra_metadata_values, $item_arr);
if ( $request['context'] === 'edit' ) {
@ -196,13 +196,13 @@ class REST_Items_Controller extends REST_Controller {
$item_arr['document_as_html'] = $item->get_document_as_html($img_size);
$item_arr['exposer_urls'] = \Tainacan\Exposers_Handler::get_exposer_urls(rest_url("{$this->namespace}/{$this->rest_base}/{$item->get_id()}/"));
$item_arr = $this->add_metadata_to_item( $item, $item_arr );
if ( $request->get_method() != 'GET') {
$item_arr['thumbnail'] = $item->get_thumbnail();
}
} else {
$attributes_to_filter = $request['fetch_only'];
$meta_to_filter = $request['fetch_only_meta'];
@ -240,7 +240,7 @@ class REST_Items_Controller extends REST_Controller {
$item_arr['url'] = get_permalink( $item_arr['id'] );
$item_arr['exposer_urls'] = \Tainacan\Exposers_Handler::get_exposer_urls(get_rest_url(null, "{$this->namespace}/{$this->rest_base}/{$item->get_id()}/"));
}
// \error_log("DEBUG-1:");
@ -248,7 +248,7 @@ class REST_Items_Controller extends REST_Controller {
$item_arr = apply_filters('tainacan-api-items-prepare-for-response', $item_arr, $item, $request);
// \error_log("DEBUG-2:");
// \error_log(\json_encode($item_arr));
return $item_arr;
}
@ -264,14 +264,14 @@ class REST_Items_Controller extends REST_Controller {
$item_id = $request['item_id'];
$item = $this->items_repository->fetch($item_id);
if (! $item instanceof Entities\Item) {
return new \WP_REST_Response([
'error_message' => __('An item with this ID was not found', 'tainacan' ),
'item_id' => $item_id
], 400);
}
$response = $this->prepare_item_for_response($item, $request);
return new \WP_REST_Response(apply_filters('tainacan-rest-response', $response, $request), 200);
@ -284,65 +284,65 @@ class REST_Items_Controller extends REST_Controller {
* @throws \Exception
*/
public function get_items( $request ) {
global $TAINACAN_API_MAX_ITEMS_PER_PAGE;
// Free php session early so simultaneous requests dont get queued
session_write_close();
$args = $this->prepare_filters($request);
/**
* allow plugins to hijack the process.
*
*
* If it returns a \WP_REST_Response, the method will return it and ignore the rest of the script
*/
$alternate_response = apply_filters('tainacan-api-get-items-alternate', false, $request);
if ( $alternate_response instanceof \WP_REST_Response ) {
return $alternate_response;
}
$collection_id = [];
if($request['collection_id']) {
$collection_id = $request['collection_id'];
}
$max_items_per_page = $TAINACAN_API_MAX_ITEMS_PER_PAGE;
if ( $max_items_per_page > -1 ) {
if ( isset($args['posts_per_page']) && (int) $args['posts_per_page'] > $max_items_per_page ) {
$args['posts_per_page'] = $max_items_per_page;
}
}
$response = [];
$response['items'] = [];
$response['template'] = '';
$query_start = microtime(true);
$items = $this->items_repository->fetch($args, $collection_id, 'WP_Query');
// Filter right after the ->fetch() method. Elastic Search integration relies on this on its 'last_aggregations' hook
$response['filters'] = apply_filters('tainacan-api-items-filters-response', [], $request);
$query_end = microtime(true);
$return_template = false;
if ( isset($request['view_mode']) ) {
// TODO: Check if requested view mode is really enabled for current collection
$view_mode = \Tainacan\Theme_Helper::get_instance();
$view_mode = $view_mode->get_view_mode($request['view_mode']);
if ($view_mode && $view_mode['type'] == 'template' && isset($view_mode['template']) && file_exists($view_mode['template'])) {
$return_template = true;
}
}
if ( $return_template ) {
if ( $return_template ) {
ob_start();
global $wp_query, $view_mode_displayed_metadata;
@ -365,9 +365,9 @@ class REST_Items_Controller extends REST_Controller {
$view_mode_displayed_metadata['meta'] = array_map( function ( $el ) {
return (int) $el;
}, $meta);
include $view_mode['template'];
$response['template'] = ob_get_clean();
} else {
@ -375,21 +375,21 @@ class REST_Items_Controller extends REST_Controller {
if ($items->have_posts()) {
while ( $items->have_posts() ) {
$items->the_post();
$item = new Entities\Item($items->post);
$prepared_item = $this->prepare_item_for_response($item, $request);
array_push($response['items'], $prepared_item);
}
wp_reset_postdata();
}
}
$response = apply_filters('tainacan-api-items-response', $response, $request);
$total_items = $items->found_posts;
$max_pages = ceil($total_items / (int) $items->query_vars['posts_per_page']);
@ -413,11 +413,7 @@ class REST_Items_Controller extends REST_Controller {
$item = $this->items_repository->fetch($request['item_id']);
if(($item instanceof Entities\Item)) {
if('edit' === $request['context'] && !$item->can_read()) {
return false;
}
return true;
return $item->can_read();
}
return false;
@ -431,15 +427,11 @@ class REST_Items_Controller extends REST_Controller {
public function get_items_permissions_check( $request ) {
$collection = $this->collections_repository->fetch($request['collection_id']);
if('edit' === $request['context'] && !is_user_logged_in()) {
return false;
}
if ( isset($request['taxquery']) && !$this->get_items_permissions_check_for_taxonomy($request['taxquery']) ) {
return false;
}
if(($collection instanceof Entities\Collection)) {
if( $collection instanceof Entities\Collection ) {
if(!$collection->can_read()) {
return false;
}
@ -450,11 +442,11 @@ class REST_Items_Controller extends REST_Controller {
}
private function get_items_permissions_check_for_taxonomy($taxonomies) {
$taxonomy_repository = Repositories\Taxonomies::get_instance();
foreach ($taxonomies as $tax) {
$tax_id = $taxonomy_repository->get_id_by_db_identifier($tax['taxonomy']);
$taxonomy = $taxonomy_repository->fetch($tax_id);
if(($taxonomy instanceof Entities\Taxonomy)) {
$taxonomy = \tainacan_taxonomies()->fetch_by_db_identifier( $tax['taxonomy'] );
if( $taxonomy instanceof Entities\Taxonomy ) {
if(!$taxonomy->can_read()) {
return false;
}
@ -470,9 +462,9 @@ class REST_Items_Controller extends REST_Controller {
* @throws \Exception
*/
public function prepare_item_for_database( $request ) {
$item = new Entities\Item();
$item_as_array = $request[0];
foreach ($item_as_array as $key => $value){
@ -547,7 +539,7 @@ class REST_Items_Controller extends REST_Controller {
public function delete_item( $request ) {
$item_id = $request['item_id'];
$permanently = $request['permanently'];
$item = $this->items_repository->fetch($request['item_id']);
if (! $item instanceof Entities\Item) {
@ -556,7 +548,7 @@ class REST_Items_Controller extends REST_Controller {
'item_id' => $item_id
], 400);
}
if($permanently == true) {
$item = $this->items_repository->delete($item);
} else {
@ -608,9 +600,9 @@ class REST_Items_Controller extends REST_Controller {
if($prepared_item->validate()){
$updated_item = $this->items_repository->update($prepared_item);
do_action('tainacan-api-item-updated', $updated_item, $attributes);
return new \WP_REST_Response($this->prepare_item_for_response($updated_item, $request), 200);
}
@ -648,7 +640,7 @@ class REST_Items_Controller extends REST_Controller {
return false;
}
/**
* @param \WP_REST_Request $request
*
@ -658,74 +650,74 @@ class REST_Items_Controller extends REST_Controller {
$item_id = $request['item_id'];
$item = $this->items_repository->fetch($item_id);
$defaults = [
'copies' => 1,
'status' => 'draft'
];
$body = json_decode($request->get_body(), true);
if (!is_array($body)) {
$body = [];
}
$args = array_merge($defaults, $body);
if ($item) {
$response = [
'items' => []
];
for ($i=1; $i<=$args['copies']; $i++) {
$new_item = new Entities\Item();
$items_repo = Repositories\Items::get_instance();
$new_item->set_status( 'draft' );
$new_item->set_title( $item->get_title() );
$new_item->set_description( $item->get_description() );
$new_item->set_collection_id( $item->get_collection_id() );
if ( $new_item->validate() ) {
$new_item = $items_repo->insert($new_item);
$metadata = $item->get_metadata();
$errors = [];
foreach ($metadata as $item_metadatum) {
$new_item_metadatum = new Entities\Item_Metadata_Entity( $new_item, $item_metadatum->get_metadatum() );
$new_item_metadatum->set_value( $item_metadatum->get_value() );
if ( $new_item_metadatum->validate() ) {
Repositories\Item_Metadata::get_instance()->insert( $new_item_metadatum );
} else {
$errors[] = $new_item_metadatum->get_errors();
}
}
if ($args['status'] != 'draft') {
$new_item->set_status( $args['status'] );
if ( $new_item->validate() ) {
if ( $new_item->validate() ) {
$new_item = $items_repo->insert($new_item);
} else {
$new_item->set_status( 'draft' );
}
}
$response['items'][] = $this->prepare_item_for_response($new_item, $request);
do_action('tainacan-api-item-duplicated', $item, $new_item);
} else {
return new \WP_REST_Response([
'error_message' => __('Error duplicating item', 'tainacan'),
@ -733,11 +725,11 @@ class REST_Items_Controller extends REST_Controller {
'item' => $this->prepare_item_for_response($item, $request)
], 400);
}
}
return new \WP_REST_Response($response, 201);
}
return new \WP_REST_Response([
@ -767,7 +759,7 @@ class REST_Items_Controller extends REST_Controller {
),
);
$endpoint_args = array_merge(
$endpoint_args,
$endpoint_args,
parent::get_fetch_only_param()
);
} elseif ($method === \WP_REST_Server::CREATABLE || $method === \WP_REST_Server::EDITABLE) {
@ -800,7 +792,7 @@ class REST_Items_Controller extends REST_Controller {
);
$query_params = array_merge(
$query_params,
$query_params,
parent::get_wp_query_params(),
parent::get_meta_queries_params()
);
@ -809,4 +801,4 @@ class REST_Items_Controller extends REST_Controller {
}
}
?>
?>

View File

@ -38,17 +38,6 @@ class REST_Logs_Controller extends REST_Controller {
'methods' => \WP_REST_Server::READABLE,
'callback' => array($this, 'get_item'),
'permission_callback' => array($this, 'get_item_permissions_check'),
'args' => array(
'context' => array(
'type' => 'string',
'default' => 'view',
'description' => 'The context in which the request is made.',
'enum' => array(
'view',
'edit'
)
),
),
),
'schema' => [$this, 'get_schema']
)
@ -134,14 +123,14 @@ class REST_Logs_Controller extends REST_Controller {
if ( !empty( get_post_meta($item->get_id(), 'value', true) ) && !empty( get_post_meta($item->get_id(), 'log_diffs', true) ) ) {
return $this->prepare_legacy_item_for_response($item, $request);
}
if ($request['log_id']) {
$item_array = $item->_toArray();
$related_object = true;
if ($item_array['item_id']) {
$item = Repositories\Items::get_instance()->fetch( (int) $item_array['item_id'] );
if ($item instanceof Entities\Item ) {
@ -154,55 +143,55 @@ class REST_Logs_Controller extends REST_Controller {
$item_array['collection'] = $collection->_toArray();
}
}
if ( $item_array['object_id'] ) {
if ( $item_array['object_type'] == 'Tainacan\Entities\Term' ) {
$related_entity = Repositories\Terms::get_instance()->fetch( (int) $item_array['object_id'] );
} else {
$related_post = get_post($item_array['object_id']);
$related_entity = Repository::get_entity_by_post( $related_post );
}
if ($related_entity instanceof Entities\Entity ) {
$item_array[ 'object' ] = $related_entity->_toArray();
}
}
if ( $item_array['action'] == 'new-attachment' ) {
if ( isset($item_array['new_value']['id']) ) {
$item_array['new_value']['url'] = wp_get_attachment_url($item_array['new_value']['id']);
$item_array['new_value']['thumb'] = wp_get_attachment_image_src($item_array['new_value']['id'], 'thumbnail');
}
} elseif ( $item_array['action'] == 'update-document' ) {
if ( isset( $item_array['new_value']['document'] ) && is_numeric( $item_array['new_value']['document'] ) ) {
if ( isset( $item_array['new_value']['document'] ) && is_numeric( $item_array['new_value']['document'] ) ) {
$item_array['new_value']['url'] = wp_get_attachment_url($item_array['new_value']['document']);
$item_array['new_value']['thumb'] = wp_get_attachment_image_src($item_array['new_value']['document'], 'thumbnail');
}
if ( isset( $item_array['old_value']['document'] ) && is_numeric( $item_array['old_value']['document'] ) ) {
if ( isset( $item_array['old_value']['document'] ) && is_numeric( $item_array['old_value']['document'] ) ) {
$item_array['old_value']['url'] = wp_get_attachment_url($item_array['old_value']['document']);
$item_array['old_value']['thumb'] = wp_get_attachment_image_src($item_array['old_value']['document'], 'thumbnail');
}
} elseif ( $item_array['action'] == 'update-thumbnail' ) {
if ( isset( $item_array['new_value']['_thumbnail_id'] ) ) {
if ( isset( $item_array['new_value']['_thumbnail_id'] ) ) {
$item_array['new_value']['url'] = wp_get_attachment_url($item_array['new_value']['_thumbnail_id']);
$item_array['new_value']['thumb'] = wp_get_attachment_image_src($item_array['new_value']['_thumbnail_id'], 'thumbnail');
}
if ( isset( $item_array['old_value']['_thumbnail_id'] ) ) {
if ( isset( $item_array['old_value']['_thumbnail_id'] ) ) {
$item_array['old_value']['url'] = wp_get_attachment_url($item_array['old_value']['_thumbnail_id']);
$item_array['old_value']['thumb'] = wp_get_attachment_image_src($item_array['old_value']['_thumbnail_id'], 'thumbnail');
}
}
// translate
// translate
if (isset($related_entity) && $related_entity instanceof Entities\Entity ) {
$map = $related_entity->get_repository()->get_map();
foreach ( $map as $slug => $m ) {
if ( isset($item_array['new_value'][$slug]) ) {
$item_array['new_value'][$m['title']] = $item_array['new_value'][$slug];
unset($item_array['new_value'][$slug]);
@ -211,22 +200,22 @@ class REST_Logs_Controller extends REST_Controller {
$item_array['old_value'][$m['title']] = $item_array['old_value'][$slug];
unset($item_array['old_value'][$slug]);
}
}
}
return $item_array;
} else {
if(!isset($request['fetch_only'])) {
$item_array = $item->_toArray();
return $item_array;
}
$attributes_to_filter = $request['fetch_only'];
}
return $this->filter_object_by_attributes($item, $attributes_to_filter);
@ -234,14 +223,14 @@ class REST_Logs_Controller extends REST_Controller {
return $item;
}
private function prepare_legacy_item_for_response($item, $request) {
if(!isset($request['fetch_only'])) {
$item_array = $item->_toArray();
unset($item_array['value']);
unset($item_array['old_value']);
$item_array['legacy'] = true;
return $item_array;
@ -282,9 +271,9 @@ class REST_Logs_Controller extends REST_Controller {
}
$logs = Repositories\Logs::get_instance()->fetch($args);
$response = [];
if($logs->have_posts()){
while ($logs->have_posts()){
$logs->the_post();
@ -314,8 +303,7 @@ class REST_Logs_Controller extends REST_Controller {
* @return bool|\WP_Error
*/
public function get_items_permissions_check( $request ) {
return true;
return current_user_can('read');
return current_user_can( 'tnc_rep_read_logs' );
}
/**
@ -339,18 +327,7 @@ class REST_Logs_Controller extends REST_Controller {
* @return bool|\WP_Error
*/
public function get_item_permissions_check( $request ) {
return true;
$log = Repositories\Logs::get_instance()->fetch($request['log_id']);
if(($log instanceof Entities\Log)) {
if('edit' === $request['context'] && !$log->can_read()) {
return false;
}
return true;
}
return false;
return current_user_can( 'tnc_rep_read_logs' );
}
/**
@ -362,7 +339,7 @@ class REST_Logs_Controller extends REST_Controller {
$endpoint_args = [];
if($method === \WP_REST_Server::READABLE) {
$endpoint_args = array_merge(
$endpoint_args,
$endpoint_args,
parent::get_wp_query_params()
);
}
@ -376,18 +353,18 @@ class REST_Logs_Controller extends REST_Controller {
'title' => 'log',
'type' => 'object'
];
$main_schema = parent::get_repository_schema( $this->logs_repository );
$permissions_schema = parent::get_permissions_schema();
$schema['properties'] = array_merge(
parent::get_base_properties_schema(),
$main_schema,
$permissions_schema
);
return $schema;
}
}
?>
?>

View File

@ -159,7 +159,7 @@ class REST_Metadata_Controller extends REST_Controller {
}
$result = $this->metadatum_repository->fetch($metadatum_id, 'OBJECT');
if (! $result instanceof Entities\Metadatum) {
return new \WP_REST_Response([
'error_message' => __('Metadata with this ID was not found', 'tainacan'),
@ -177,28 +177,10 @@ class REST_Metadata_Controller extends REST_Controller {
* @throws \Exception
*/
public function get_item_permissions_check( $request ) {
$collection_id = isset($request['collection_id']) ? $request['collection_id'] : false;
$metadatum = $this->metadatum_repository->fetch($request['metadatum_id']);
if($collection_id) {
$collection = $this->collection_repository->fetch( $collection_id );
if ( $collection instanceof Entities\Collection ) {
if ( $request['context'] === 'edit' && ! $collection->can_read() ) {
return false;
}
return true;
}
} elseif($request['metadatum_id']) {
$metadatum = $this->metadatum_repository->fetch($request['metadatum_id']);
if ( $metadatum instanceof Entities\Metadatum ) {
if ( $request['context'] === 'edit' && ! $metadatum->can_read() ) {
return false;
}
return true;
}
if ( $metadatum instanceof Entities\Metadatum ) {
return $metadatum->can_read();
}
return false;
@ -297,11 +279,22 @@ class REST_Metadata_Controller extends REST_Controller {
* @throws \Exception
*/
public function create_item_permissions_check( $request ) {
if(isset($request['collection_id'])) {
return $this->collection_repository->can_edit( new Entities\Collection() );
if( isset($request['collection_id']) ) {
$collection = $this->collection_repository->fetch( $request['collection_id'] );
if ( $collection instanceof Entities\Collection ) {
return $collection->user_can( 'edit_metadata' );
}
} else {
return current_user_can( 'tnc_rep_edit_metadata' );
}
return $this->metadatum_repository->can_edit(new Entities\Metadatum());
return false;
}
/**
@ -313,7 +306,7 @@ class REST_Metadata_Controller extends REST_Controller {
public function prepare_item_for_response( $item, $request ) {
if(!empty($item)){
$item_arr = $item->_toArray();
$item_arr['metadata_type_object'] = $item->get_metadata_type_object()->_toArray();
if(isset($item_arr['metadata_type_options']) && isset($item_arr['metadata_type_options']['taxonomy_id'])){
@ -322,7 +315,7 @@ class REST_Metadata_Controller extends REST_Controller {
//$item_arr['metadata_type_options']['taxonomy'] = $taxonomy->get_db_identifier();
$item_arr['metadata_type_options']['taxonomy'] = $taxonomy;
}
if($request['context'] === 'edit'){
$item_arr['current_user_can_edit'] = $item->can_edit();
$item_arr['current_user_can_delete'] = $item->can_delete();
@ -337,7 +330,7 @@ class REST_Metadata_Controller extends REST_Controller {
* Use this filter to add additional post_meta to the api response
* Use the $request object to get the context of the request and other variables
* For example, id context is edit, you may want to add your meta or not.
*
*
* Also take care to do any permissions verification before exposing the data
*/
$extra_metadata = apply_filters('tainacan-api-response-metadatum-meta', [], $request);
@ -345,7 +338,7 @@ class REST_Metadata_Controller extends REST_Controller {
foreach ($extra_metadata as $extra_meta) {
$item_arr[$extra_meta] = get_post_meta($item_arr['id'], $extra_meta, true);
}
return $item_arr;
}
@ -363,14 +356,14 @@ class REST_Metadata_Controller extends REST_Controller {
$collection_id = $request['collection_id'];
$args = $this->prepare_filters( $request );
if ($request['include_disabled'] === 'true') {
$args['include_disabled'] = true;
}
$collection = new Entities\Collection( $collection_id );
$result = $this->metadatum_repository->fetch_by_collection( $collection, $args, 'OBJECT' );
$result = $this->metadatum_repository->fetch_by_collection( $collection, $args );
} else {
$args = [
'meta_query' => [
@ -400,19 +393,16 @@ class REST_Metadata_Controller extends REST_Controller {
* @throws \Exception
*/
public function get_items_permissions_check( $request ) {
if(!isset($request['collection_id'])) {
if ( 'edit' === $request['context'] && ! $this->metadatum_repository->can_edit( new Entities\Metadatum() ) ) {
return false;
}
if(!isset($request['collection_id'])) {
return true;
}
$collection = $this->collection_repository->fetch($request['collection_id']);
if($collection instanceof Entities\Collection){
if ( 'edit' === $request['context'] && ! $collection->can_read() ) {
if ( ! $collection->can_read() ) {
return false;
}
@ -420,6 +410,7 @@ class REST_Metadata_Controller extends REST_Controller {
}
return false;
}
/**
@ -429,16 +420,16 @@ class REST_Metadata_Controller extends REST_Controller {
*/
public function delete_item( $request ) {
$metadatum_id = $request['metadatum_id'];
$metadatum = $this->metadatum_repository->fetch($metadatum_id);
if (! $metadatum instanceof Entities\Metadatum) {
return new \WP_REST_Response([
'error_message' => __('Metadata with this ID was not found', 'tainacan'),
'item_id' => $item_id
], 400);
}
$metadatum_trashed = $this->metadatum_repository->trash($metadatum);
$prepared = $this->prepare_item_for_response($metadatum_trashed, $request);
@ -453,15 +444,14 @@ class REST_Metadata_Controller extends REST_Controller {
* @throws \Exception
*/
public function delete_item_permissions_check( $request ) {
if (isset($request['collection_id'])) {
$collection = $this->collection_repository->fetch($request['collection_id']);
$metadatum = $this->metadatum_repository->fetch($request['metadatum_id']);
if ($collection instanceof Entities\Collection) {
return $collection->can_delete();
}
if ($metadatum instanceof Entities\Metadatum) {
return $metadatum->can_delete();
}
return $this->metadatum_repository->can_edit(new Entities\Metadatum());
return false;
}
/**
@ -541,16 +531,13 @@ class REST_Metadata_Controller extends REST_Controller {
* @throws \Exception
*/
public function update_item_permissions_check( $request ) {
if(isset($request['collection_id'])) {
$collection = $this->collection_repository->fetch($request['collection_id']);
$metadatum = $this->metadatum_repository->fetch($request['metadatum_id']);
if ($collection instanceof Entities\Collection) {
return $collection->can_edit();
}
if ($metadatum instanceof Entities\Metadatum) {
return $metadatum->can_edit();
}
}
return $this->metadatum_repository->can_edit(new Entities\Metadatum());
return false;
}
/**
@ -583,7 +570,7 @@ class REST_Metadata_Controller extends REST_Controller {
$endpoint_args = [];
if($method === \WP_REST_Server::READABLE) {
$endpoint_args = array_merge(
$endpoint_args,
$endpoint_args,
parent::get_wp_query_params()
);
} elseif ($method === \WP_REST_Server::CREATABLE || $method === \WP_REST_Server::EDITABLE) {
@ -610,14 +597,14 @@ class REST_Metadata_Controller extends REST_Controller {
'title' => 'metadatum',
'type' => 'object'
];
$main_schema = parent::get_repository_schema( $this->metadatum_repository );
$permissions_schema = parent::get_permissions_schema();
// $item_metadata_scheme = parent::get_repository_schema( $this->item_metadata_repository );
// $item_scheme = parent::get_repository_schema( $this->item_repository );
// $collection_scheme = parent::get_repository_schema( $this->collection_repository );
$schema['properties'] = array_merge(
parent::get_base_properties_schema(),
$main_schema,
@ -626,9 +613,9 @@ class REST_Metadata_Controller extends REST_Controller {
// $item_scheme,
// $collection_scheme
);
return $schema;
}
}
?>
?>

View File

@ -0,0 +1,525 @@
<?php
namespace Tainacan\API\EndPoints;
use \Tainacan\API\REST_Controller;
use Tainacan\Entities;
use Tainacan\Repositories;
class REST_Roles_Controller extends REST_Controller {
/**
* REST_Roles_Controller constructor.
*/
public function __construct() {
$this->rest_base = 'roles';
parent::__construct();
$this->core_roles = [
'administrator',
'editor',
'author',
'contributor',
'subscriber'
];
}
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'),
//'args' => $this->get_endpoint_args_for_item_schema(\WP_REST_Server::READABLE)
),
array(
'methods' => \WP_REST_Server::CREATABLE,
'callback' => array($this, 'create_item'),
'permission_callback' => array($this, 'create_item_permissions_check'),
'args' => array(
'name' => array(
'description' => __('New role name', 'tainacan'),
'type' => 'string',
'required' => true
),
'capabilities' => array(
'description' => __('Array of capabilities, where the keys are capability slugs and values are booleans', 'tainacan'),
'required' => false,
'validate_callback' => [$this, 'validate_roles_capabilities_arg']
),
)
),
'schema' => [$this, 'get_schema']
));
register_rest_route($this->namespace, '/' . $this->rest_base . '/(?P<role>[a-z-]+)', 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'),
'args' => array(
'name' => array(
'description' => __('New role name', 'tainacan'),
'type' => 'string',
'required' => false
),
'capabilities' => array(
'description' => __('Array of capabilities, where the keys are capability slugs and values are booleans', 'tainacan'),
'required' => false,
'validate_callback' => [$this, 'validate_roles_capabilities_arg']
),
)
),
array(
'methods' => \WP_REST_Server::READABLE,
'callback' => array($this, 'get_item'),
'permission_callback' => array($this, 'get_item_permissions_check'),
),
'schema' => [$this, 'get_schema']
));
register_rest_route(
$this->namespace, '/collection/(?P<collection_id>[\d]+)/capabilities',
array(
array(
'methods' => \WP_REST_Server::READABLE,
'callback' => array($this, 'get_capabilities'),
'permission_callback' => array($this, 'get_capabilities_permissions_check'),
)
));
register_rest_route(
$this->namespace, '/capabilities',
array(
array(
'methods' => \WP_REST_Server::READABLE,
'callback' => array($this, 'get_capabilities'),
'permission_callback' => array($this, 'get_capabilities_permissions_check'),
)
));
}
/**
* @param \WP_REST_Request $request
*
* @return \WP_Error|\WP_REST_Response
*/
public function create_item( $request ) {
//global $wpdb;
//$name = $wpdb->prepare( $request['name'], '' );
$name = esc_html( esc_sql( $request['name'] ) );
$role_slug = sanitize_title($name);
// avoid confusion ...
if ( in_array($role_slug, $this->core_roles) ) {
return new \WP_REST_Response([
'error_message' => __('This role name is protected.', 'tainacan'),
'error' => $name
], 400);
}
// ... even though it could work
$role_slug = 'tainacan-' . $role_slug;
// check if role exists
$role = get_role($role_slug);
if ( $role ) {
return new \WP_REST_Response([
'error_message' => __('Role already exists.', 'tainacan'),
'error' => $name
], 400);
}
$new_role = add_role($role_slug, $name);
if ( isset($request['capabilities']) && is_array($request['capabilities']) ) {
$this->handle_capabilities_for_role($role_slug, $request['capabilities']);
}
if ($new_role instanceof \WP_Role) {
return new \WP_REST_Response($this->_prepare_item_for_response($role_slug, $name, $new_role->capabilities, $request), 201);
}
return new \WP_REST_Response([
'error_message' => __('Error creating role', 'tainacan'),
'error' => $name
], 400);
}
/**
* @param \WP_REST_Request $request
*
* @return bool|\WP_Error
* @throws \Exception
*/
public function create_item_permissions_check( $request ) {
return current_user_can('tnc_rep_edit_users');
}
/**
* @param \WP_REST_Request $request
*
* @return \WP_Error|\WP_REST_Response
*/
public function delete_item( $request ) {
$role_slug = $request['role'];
// avoid confusion ...
if ( in_array($role_slug, $this->core_roles) ) {
return new \WP_REST_Response([
'error_message' => __('This role name is protected.', 'tainacan'),
'error' => $name
], 400);
}
// ... even though it could work
$role_slug = 0 === \strpos($role_slug, 'tainacan-') ? $role_slug : 'tainacan-' . $role_slug;
// check if role exists
$role = get_role($role_slug);
if ( ! $role ) {
return new \WP_REST_Response([
'error_message' => __('Role not found.', 'tainacan'),
'error' => $role_slug
], 400);
}
\remove_role($role_slug);
return new \WP_REST_Response($role_slug, 200);
}
/**
* @param \WP_REST_Request $request
*
* @return bool|\WP_Error
*/
public function delete_item_permissions_check( $request ) {
return current_user_can('tnc_rep_edit_users');
}
/**
* @param $request
*
* @return \WP_Error|\WP_REST_Response
*/
public function update_item( $request ) {
$role_slug = $request['role'];
// check if role exists
// get the role from roles array that contains the display_name
$roles = \wp_roles()->roles;
if ( ! isset($roles[$role_slug]) ) {
return new \WP_REST_Response([
'error_message' => __('Role not found.', 'tainacan'),
'error' => $role_slug
], 400);
}
$role = $roles[$role_slug];
if ( isset($request['name']) ) {
$name = esc_html( esc_sql( $request['name'] ) );
// the slug remains the same
\wp_roles()->roles[$role_slug]['name'] = $name;
update_option( \wp_roles()->role_key, \wp_roles()->roles );
\wp_roles()->role_names[$role_slug] = $name;
}
if ( is_array($request['capabilities']) ) {
$this->handle_capabilities_for_role($role_slug, $request['capabilities']);
} elseif ( isset($request['add_cap']) ) {
// validate that we only deal with tainacan capabilities
if ( ! in_array( \tainacan_roles()->get_cap_generic_name($request['add_cap']) , \tainacan_roles()->get_all_caps_slugs() ) ) {
return new \WP_REST_Response([
'error_message' => __('Not allowed to edit non Tainacan capabilities.', 'tainacan'),
'error' => $request['add_cap']
], 400);
}
\wp_roles()->add_cap($role_slug, $request['add_cap']);
\tainacan_roles()->add_dependencies($role_slug, $request['add_cap']);
} elseif ( isset($request['remove_cap']) ) {
// validate that we only deal with tainacan capabilities
if ( ! in_array( \tainacan_roles()->get_cap_generic_name($request['remove_cap']) , \tainacan_roles()->get_all_caps_slugs() ) ) {
return new \WP_REST_Response([
'error_message' => __('Not allowed to edit non Tainacan capabilities.', 'tainacan'),
'error' => $request['remove_cap']
], 400);
}
\wp_roles()->remove_cap($role_slug, $request['remove_cap']);
}
return new \WP_REST_Response($this->_prepare_item_for_response($role_slug, \wp_roles()->roles[$role_slug]['name'], \wp_roles()->roles[$role_slug]['capabilities'], $request), 200);
}
private function handle_capabilities_for_role($role_slug, $newcaps) {
if ( !isset( \wp_roles()->roles[$role_slug] ) ) {
return false;
}
$role = \wp_roles()->roles[$role_slug];
foreach ($role['capabilities'] as $cap => $val) {
if ( ! in_array( \tainacan_roles()->get_cap_generic_name($cap) , \tainacan_roles()->get_all_caps_slugs() ) ) {
continue;
}
if ( !array_key_exists($cap, $newcaps) ) {
\wp_roles()->remove_cap($role_slug, $cap);
}
}
foreach ( $newcaps as $cap => $val ) {
\wp_roles()->add_cap($role_slug, $cap, $val);
}
}
/**
* @param \WP_REST_Request $request
*
* @return bool|\WP_Error
*/
public function update_item_permissions_check( $request ) {
if ( current_user_can('tnc_rep_edit_users') ) {
return true;
}
if ( !isset($request['name']) ) {
$return = true;
$cap = '';
if ( isset($request['add_cap']) ) {
$return = in_array( \tainacan_roles()->get_cap_generic_name($request['add_cap']), \tainacan_roles()->get_collection_caps_slugs());
$cap = $request['add_cap'];
} elseif ( isset($request['remove_cap']) ) {
$return = in_array( \tainacan_roles()->get_cap_generic_name($request['remove_cap']), \tainacan_roles()->get_collection_caps_slugs());
$cap = $request['remove_cap'];
}
if ($return) {
$collection_id = preg_replace('/[a-z_]/', '', $cap);
if ( is_numeric($collection_id) ) {
return current_user_can('tnc_col_' . $collection_id . '_edit_users');
}
}
}
return false;
}
public function validate_roles_capabilities_arg($value, $request, $param) {
if ( is_array($value) ) {
foreach ($value as $cap => $val) {
if ( ! in_array( \tainacan_roles()->get_cap_generic_name($cap), \tainacan_roles()->get_all_caps_slugs() ) ) {
return false;
}
}
return true;
}
return false;
}
/**
* @param $item
* @param \WP_REST_Request $request
*
* @return array|mixed|\WP_Error|\WP_REST_Response
*/
public function _prepare_item_for_response( $slug, $name, $caps, $request ) {
$caps = array_filter($caps, function($el) {
return in_array( \tainacan_roles()->get_cap_generic_name($el), \tainacan_roles()->get_all_caps_slugs() );
}, ARRAY_FILTER_USE_KEY);
$return = [
'slug' => $slug,
'name' => translate_user_role($name),
'capabilities' => $caps
];
return $return;
}
/**
* @param \WP_REST_Request $request
*
* @return \WP_Error|\WP_REST_Response
* @throws \Exception
*/
public function get_items( $request ) {
if (!function_exists('get_editable_roles')) {
require_once(ABSPATH . '/wp-admin/includes/user.php');
}
$roles = \get_editable_roles();
$response = [];
foreach ( $roles as $slug => $role ) {
$response[$slug] = $this->_prepare_item_for_response( $slug, $role['name'], $role['capabilities'], $request );
}
return new \WP_REST_Response($response, 200);
}
/**
* @param \WP_REST_Request $request
*
* @return bool|\WP_Error
*/
public function get_items_permissions_check( $request ) {
return current_user_can('read');
}
/**
* @param \WP_REST_Request $request
*
* @return \WP_Error|\WP_REST_Response
*/
public function get_item( $request ) {
$role_slug = $request['role'];
// check if role exists
// get the role from roles array that contains the display_name
$roles = \wp_roles()->roles;
if ( ! isset($roles[$role_slug]) ) {
return new \WP_REST_Response([
'error_message' => __('Role not found.', 'tainacan'),
'error' => $role_slug
], 400);
}
return new \WP_REST_Response($this->_prepare_item_for_response($role_slug, $roles[$role_slug]['name'], $roles[$role_slug]['capabilities'], $request), 200);
}
/**
* @param \WP_REST_Request $request
*
* @return bool|\WP_Error
*/
public function get_item_permissions_check( $request ) {
return current_user_can('tnc_rep_edit_users');
}
/**
* @param \WP_REST_Request $request
*
* @return bool|\WP_Error
*/
public function get_capabilities_permissions_check( $request ) {
if ( current_user_can('tnc_rep_edit_users') ) {
return true;
}
if ( isset($request['collection_id']) ) {
return current_user_can( 'tnc_col_' . $request['collection_id'] . '_edit_users' );
}
return false;
}
/**
* @param \WP_REST_Request $request
*
* @return \WP_Error|\WP_REST_Response
*/
public function get_capabilities( $request ) {
$collection_id = isset($request['collection_id']) ? $request['collection_id'] : false;
$roles = \wp_roles()->roles;
$caps_return = [];
if ($collection_id) {
$caps = \tainacan_roles()->get_collection_caps();
} else {
$caps = \tainacan_roles()->get_all_caps();
}
foreach ($caps as $cap => $c) {
$realcap = $cap;
if ($collection_id) {
$realcap = str_replace('%d', $collection_id, $cap);
}
$caps_return[$realcap] = $caps[$cap];
$caps_return[$realcap]['roles'] = [];
$caps_return[$realcap]['roles_inherited'] = [];
foreach ($roles as $slug => $role) {
if ( array_key_exists($realcap, $role['capabilities']) ) {
$caps_return[$realcap]['roles'][$slug] = [
'slug' => $slug,
'name' => translate_user_role($role['name']),
];
continue;
}
// inherited roles
$supercaps = $c['supercaps'];
foreach ($supercaps as $supercap) {
if ($collection_id) {
$supercap = str_replace('%d', $collection_id, $supercap);
}
if ( array_key_exists($supercap, $role['capabilities']) ) {
$caps_return[$realcap]['roles_inherited'][$slug] = [
'slug' => $slug,
'name' => translate_user_role($role['name']),
];
break;
}
} // for each supercaps
} // for each role
} // for each cap
return new \WP_REST_Response(['capabilities' => $caps_return], 200);
}
function get_schema() {
$schema = [
'$schema' => 'http://json-schema.org/draft-04/schema#',
'title' => 'filter',
'type' => 'object'
];
return $schema;
}
}
?>

View File

@ -18,7 +18,7 @@ class REST_Taxonomies_Controller extends REST_Controller {
parent::__construct();
add_action('init', array(&$this, 'init_objects'), 11);
}
/**
* Initialize objects after post_type register
*/
@ -125,34 +125,34 @@ class REST_Taxonomies_Controller extends REST_Controller {
// $item_arr['collections'][] = $tax_collection->_toArray();
// }
// }
//
//
// }
}
/**
*
*
*
*
* See issue #229
*
*
*/
$item_arr['collections'] = [];
$item_arr['collections_ids'] = [];
$taxonomy = get_taxonomy( $item->get_db_identifier() );
if (is_object($taxonomy) && isset($taxonomy->object_type) && is_array($taxonomy->object_type)) {
foreach ($taxonomy->object_type as $slug) {
$tax_collection = Repositories\Collections::get_instance()->fetch_by_db_identifier($slug);
if ( $tax_collection instanceof \Tainacan\Entities\Collection ) {
$item_arr['collections'][] = $tax_collection->_toArray();
$item_arr['collections_ids'][] = $tax_collection->get_id();
}
}
}
} else {
$attributes_to_filter = $request['fetch_only'];
@ -163,7 +163,7 @@ class REST_Taxonomies_Controller extends REST_Controller {
* Use this filter to add additional post_meta to the api response
* Use the $request object to get the context of the request and other variables
* For example, id context is edit, you may want to add your meta or not.
*
*
* Also take care to do any permissions verification before exposing the data
*/
$extra_metadata = apply_filters('tainacan-api-response-taxonomy-meta', [], $request);
@ -171,7 +171,7 @@ class REST_Taxonomies_Controller extends REST_Controller {
foreach ($extra_metadata as $extra_meta) {
$item_arr[$extra_meta] = get_post_meta($item_arr['id'], $extra_meta, true);
}
return $item_arr;
}
@ -198,14 +198,14 @@ class REST_Taxonomies_Controller extends REST_Controller {
$taxonomy_id = $request['taxonomy_id'];
$taxonomy = $this->taxonomy_repository->fetch($taxonomy_id);
if (! $taxonomy instanceof Entities\Taxonomy) {
return new \WP_REST_Response([
'error_message' => __('A taxonomy with this ID was not found', 'tainacan' ),
'taxonomy_id' => $taxonomy_id
], 400);
}
$taxonomy_prepared = $this->prepare_item_for_response($taxonomy, $request);
return new \WP_REST_Response($taxonomy_prepared, 200);
@ -220,14 +220,7 @@ class REST_Taxonomies_Controller extends REST_Controller {
$taxonomy = $this->taxonomy_repository->fetch($request['taxonomy_id']);
if(($taxonomy instanceof Entities\Taxonomy)) {
if('edit' === $request['context'] && !is_user_logged_in()) {
return false;
}
if(!$taxonomy->can_read()) {
return false;
}
return true;
return $taxonomy->can_read();
}
return false;
@ -241,16 +234,16 @@ class REST_Taxonomies_Controller extends REST_Controller {
public function delete_item( $request ) {
$taxonomy_id = $request['taxonomy_id'];
$permanently = $request['permanently'];
$taxonomy = $this->taxonomy_repository->fetch($taxonomy_id);
if (! $taxonomy instanceof Entities\Taxonomy) {
return new \WP_REST_Response([
'error_message' => __('A taxonomy with this ID was not found', 'tainacan' ),
'taxonomy_id' => $taxonomy_id
], 400);
}
if($permanently == true){
$deleted = $this->taxonomy_repository->delete($taxonomy);
} else {
@ -341,9 +334,7 @@ class REST_Taxonomies_Controller extends REST_Controller {
* @return bool|\WP_Error
*/
public function get_items_permissions_check( $request ) {
if('edit' === $request['context'] && !is_user_logged_in()) {
return false;
}
// if(!$this->taxonomy_repository->can_read($this->taxonomy)) {
// return false;
// }
@ -387,7 +378,7 @@ class REST_Taxonomies_Controller extends REST_Controller {
* @return bool|\WP_Error
*/
public function create_item_permissions_check( $request ) {
return $this->taxonomy_repository->can_edit($this->taxonomy);
return current_user_can( $this->taxonomy_repository->get_capabilities()->edit_posts );
}
/**
@ -467,7 +458,7 @@ class REST_Taxonomies_Controller extends REST_Controller {
$endpoint_args = [];
if($method === \WP_REST_Server::READABLE) {
$endpoint_args = array_merge(
$endpoint_args,
$endpoint_args,
parent::get_wp_query_params(),
parent::get_fetch_only_param()
);
@ -518,19 +509,19 @@ class REST_Taxonomies_Controller extends REST_Controller {
'title' => 'taxonomy',
'type' => 'object'
];
$main_schema = parent::get_repository_schema( $this->taxonomy_repository );
$permissions_schema = parent::get_permissions_schema();
$schema['properties'] = array_merge(
parent::get_base_properties_schema(),
$main_schema,
$permissions_schema
);
return $schema;
}
}
?>
?>

View File

@ -20,7 +20,7 @@ class REST_Terms_Controller extends REST_Controller {
parent::__construct();
add_action('init', array(&$this, 'init_objects'), 11);
}
/**
* Initialize objects after post_type register
*/
@ -138,7 +138,22 @@ class REST_Terms_Controller extends REST_Controller {
$taxonomy = $this->taxonomy_repository->fetch($request['taxonomy_id']);
if ($taxonomy instanceof Entities\Taxonomy) {
return $taxonomy->can_edit();
if ( $taxonomy->can_edit() ) {
return true;
}
$body = json_decode($request->get_body(), true);
if ( isset( $body['metadatum_id'] ) && isset( $body['item_id'] ) ) {
$metadatum = \tainacan_metadata()->fetch( $body['metadatum_id'] );
$item = \tainacan_items()->fetch( $body['item_id'] );
if ( $metadatum instanceof Entities\Metadatum ) {
$options = $metadatum->get_metadata_type_options();
if ( isset( $options['allow_new_terms'] ) && $options['allow_new_terms'] == 'yes' ) {
return $item->can_edit() && $taxonomy->get_allow_insert() == 'yes';
}
}
}
}
return false;
@ -152,11 +167,11 @@ class REST_Terms_Controller extends REST_Controller {
public function delete_item( $request ) {
$term_id = $request['term_id'];
$taxonomy_id = $request['taxonomy_id'];
$taxonomy = $this->taxonomy_repository->fetch($taxonomy_id);
$term = $this->terms_repository->fetch($term_id, $taxonomy);
if ( ! $term instanceof Entities\Term ) {
return new \WP_REST_Response([
'error_message' => __('A term with this ID was not found', 'tainacan' ),
@ -176,10 +191,12 @@ class REST_Terms_Controller extends REST_Controller {
* @return bool|\WP_Error
*/
public function delete_item_permissions_check( $request ) {
$taxonomy = $this->taxonomy_repository->fetch($request['taxonomy_id']);
$term_id = $request['term_id'];
$taxonomy = $this->taxonomy_repository->fetch($request['taxonomy_id']);
$term = $this->terms_repository->fetch($term_id, $taxonomy);
if ($taxonomy instanceof Entities\Taxonomy) {
return $taxonomy->can_edit();
if ($term instanceof Entities\Term) {
return $term->can_delete();
}
return false;
@ -243,10 +260,12 @@ class REST_Terms_Controller extends REST_Controller {
* @return bool|\WP_Error
*/
public function update_item_permissions_check( $request ) {
$taxonomy = $this->taxonomy_repository->fetch($request['taxonomy_id']);
$term_id = $request['term_id'];
$taxonomy = $this->taxonomy_repository->fetch($request['taxonomy_id']);
$term = $this->terms_repository->fetch($term_id, $taxonomy);
if ($taxonomy instanceof Entities\Taxonomy) {
return $taxonomy->can_edit();
if ($term instanceof Entities\Term) {
return $term->can_edit();
}
return false;
@ -286,7 +305,7 @@ class REST_Terms_Controller extends REST_Controller {
* Use this filter to add additional term_meta to the api response
* Use the $request object to get the context of the request and other variables
* For example, id context is edit, you may want to add your meta or not.
*
*
* Also take care to do any permissions verification before exposing the data
*/
$extra_metadata = apply_filters('tainacan-api-response-term-meta', [], $request);
@ -294,7 +313,7 @@ class REST_Terms_Controller extends REST_Controller {
foreach ($extra_metadata as $extra_meta) {
$item_arr[$extra_meta] = get_term_meta($item_arr['id'], $extra_meta, true);
}
return $item_arr;
}
@ -334,14 +353,14 @@ class REST_Terms_Controller extends REST_Controller {
$per_page = (int) $prepared_args['number'];
$page = ceil( ( ( (int) $prepared_args['offset'] ) / $per_page ) + 1 );
$response->header( 'X-WP-Total', (int) $total_terms );
$max_pages = ceil( $total_terms / $per_page );
$response->header( 'X-WP-TotalPages', (int) $max_pages );
}
return $response;
}
@ -354,14 +373,7 @@ class REST_Terms_Controller extends REST_Controller {
$taxonomy = $this->taxonomy_repository->fetch($request['taxonomy_id']);
if(($taxonomy instanceof Entities\Taxonomy)) {
if('edit' === $request['context'] && !is_user_logged_in()) {
return false;
}
if(!$taxonomy->can_read()) {
return false;
}
return true;
return $taxonomy->can_read();
}
return false;
@ -379,7 +391,7 @@ class REST_Terms_Controller extends REST_Controller {
$taxonomy = $this->taxonomy_repository->fetch($tax_id);
$term = $this->terms_repository->fetch($term_id, $taxonomy);
if ( ! $term instanceof Entities\Term ) {
return new \WP_REST_Response([
'error_message' => __('A term with this ID was not found', 'tainacan' ),
@ -397,20 +409,15 @@ class REST_Terms_Controller extends REST_Controller {
* @return bool|\WP_Error
*/
public function get_item_permissions_check( $request ) {
$term_id = $request['term_id'];
$taxonomy = $this->taxonomy_repository->fetch($request['taxonomy_id']);
$term = $this->terms_repository->fetch($term_id, $taxonomy);
if(($taxonomy instanceof Entities\Taxonomy)) {
if('edit' === $request['context'] && !is_user_logged_in()) {
return false;
}
if(!$taxonomy->can_read()) {
return false;
}
if ($term instanceof Entities\Term) {
return $term->can_read();
}
return true;
}
return false;
return false;
}
/**
@ -422,7 +429,7 @@ class REST_Terms_Controller extends REST_Controller {
$endpoint_args = [];
if($method === \WP_REST_Server::READABLE) {
$endpoint_args = array_merge(
$endpoint_args,
$endpoint_args,
parent::get_wp_query_params()
);
} elseif ($method === \WP_REST_Server::CREATABLE || $method === \WP_REST_Server::EDITABLE) {
@ -438,6 +445,20 @@ class REST_Terms_Controller extends REST_Controller {
}
$endpoint_args = $map;
if ($method === \WP_REST_Server::CREATABLE) {
$endpoint_args['metadatum_id'] = [
'required' => false,
'type' => 'integer',
'description' => __('If term is being created in the context of a Taxonomy metadatum, inform its ID')
];
$endpoint_args['item_id'] = [
'required' => false,
'type' => 'integer',
'description' => __('If term is being created in the context of a Taxonomy metadatum, inform the ID of the item being edited')
];
}
}
return $endpoint_args;
@ -472,21 +493,21 @@ class REST_Terms_Controller extends REST_Controller {
'title' => 'term',
'type' => 'object'
];
$main_schema = parent::get_repository_schema( $this->terms_repository );
$permissions_schema = parent::get_permissions_schema();
// $taxonomy_scheme = parent::get_repository_schema( $this->taxonomy_repository );
$schema['properties'] = array_merge(
parent::get_base_properties_schema(),
$main_schema,
$permissions_schema
// $taxonomy_scheme
);
return $schema;
}
}
?>
?>

View File

@ -18,9 +18,10 @@ $rest_exporters_controller = new \Tainacan\API\EndPoints\REST_Exporters_Contr
$rest_background_processes_controller = new \Tainacan\API\EndPoints\REST_Background_Processes_Controller();
$rest_bulkedit_controller = new \Tainacan\API\EndPoints\REST_Bulkedit_Controller();
$rest_exposers_controller = new \Tainacan\API\EndPoints\REST_Exposers_Controller();
$rest_roles_controller = new \Tainacan\API\EndPoints\REST_Roles_Controller();
new \Tainacan\API\EndPoints\REST_Metadatum_Mappers_Controller();
$rest_facets_controller = new \Tainacan\API\EndPoints\REST_Facets_Controller();
$rest_oaipmh_expose_controller = new \Tainacan\API\EndPoints\REST_Oaipmh_Expose_Controller();
// Add here other endpoints imports
?>
?>

View File

@ -0,0 +1,3 @@
/*# sourceMappingURL=tainacan-roles.css.map */

View File

@ -0,0 +1,7 @@
{
"version": 3,
"mappings": "",
"sources": [],
"names": [],
"file": "tainacan-roles.css"
}

View File

@ -1,570 +0,0 @@
<?php
namespace Tainacan;
use Tainacan\Repositories\Repository;
class Capabilities {
public $defaults = [
"tainacan-collection"=> [
"administrator"=> [
"delete_posts",
"delete_private_posts",
"edit_posts",
"edit_private_posts",
"publish_posts",
"read_private_posts",
"delete_published_posts",
"edit_published_posts",
"edit_others_posts",
"delete_others_posts",
"read"
],
"editor"=> [
"delete_posts",
"delete_private_posts",
"edit_posts",
"edit_private_posts",
"publish_posts",
"read_private_posts",
"delete_published_posts",
"edit_published_posts",
"edit_others_posts",
"delete_others_posts",
"read"
],
"author"=> [
"delete_posts",
"edit_posts",
"publish_posts",
"delete_published_posts",
"edit_published_posts",
"read"
],
"contributor"=> [
"read"
],
"subscriber"=> [
"read"
]
],
"tainacan-metadatum"=> [
"administrator"=> [
"delete_posts",
"delete_private_posts",
"edit_posts",
"edit_private_posts",
"publish_posts",
"read_private_posts",
"delete_published_posts",
"edit_published_posts",
"edit_others_posts",
"delete_others_posts",
"read"
],
"editor"=> [
"delete_posts",
"delete_private_posts",
"edit_posts",
"edit_private_posts",
"publish_posts",
"read_private_posts",
"delete_published_posts",
"edit_published_posts",
"edit_others_posts",
"delete_others_posts",
"read"
],
"author"=> [
"delete_posts",
"edit_posts",
"publish_posts",
"delete_published_posts",
"edit_published_posts",
"read"
],
"contributor"=> [
"delete_posts",
"edit_posts",
"read"
],
"subscriber"=> [
"read"
]
],
"tainacan-filter"=> [
"administrator"=> [
"delete_posts",
"delete_private_posts",
"edit_posts",
"edit_private_posts",
"publish_posts",
"read_private_posts",
"delete_published_posts",
"edit_published_posts",
"edit_others_posts",
"delete_others_posts",
"read"
],
"editor"=> [
"delete_posts",
"delete_private_posts",
"edit_posts",
"edit_private_posts",
"publish_posts",
"read_private_posts",
"delete_published_posts",
"edit_published_posts",
"edit_others_posts",
"delete_others_posts",
"read"
],
"author"=> [
"delete_posts",
"edit_posts",
"publish_posts",
"delete_published_posts",
"edit_published_posts",
"read"
],
"contributor"=> [
"delete_posts",
"edit_posts",
"read"
],
"subscriber"=> [
"read"
]
],
"tainacan-taxonomy"=> [
"administrator"=> [
"delete_posts",
"delete_private_posts",
"edit_posts",
"edit_private_posts",
"publish_posts",
"read_private_posts",
"delete_published_posts",
"edit_published_posts",
"edit_others_posts",
"delete_others_posts",
"read"
],
"editor"=> [
"delete_posts",
"delete_private_posts",
"edit_posts",
"edit_private_posts",
"publish_posts",
"read_private_posts",
"delete_published_posts",
"edit_published_posts",
"edit_others_posts",
"delete_others_posts",
"read"
],
"author"=> [
"delete_posts",
"edit_posts",
"publish_posts",
"delete_published_posts",
"edit_published_posts",
"read"
],
"contributor"=> [
"delete_posts",
"edit_posts",
"read"
],
"subscriber"=> [
"read"
]
],
"tainacan-log"=> [
"administrator"=> [
"delete_posts",
"delete_private_posts",
"edit_posts",
"edit_private_posts",
"publish_posts",
"read_private_posts",
"delete_published_posts",
"edit_published_posts",
"edit_others_posts",
"delete_others_posts",
"read"
],
"editor"=> [
"delete_posts",
"delete_private_posts",
"edit_posts",
"edit_private_posts",
"publish_posts",
"read_private_posts",
"delete_published_posts",
"edit_published_posts",
"edit_others_posts",
"delete_others_posts",
"read"
],
"author"=> [
"delete_posts",
"edit_posts",
"publish_posts",
"delete_published_posts",
"edit_published_posts",
"read"
],
"contributor"=> [
"delete_posts",
"edit_posts",
"read"
],
"subscriber"=> [
"read"
]
],
"tainacan-items"=> [ // abstract, will apply to collection Items
"administrator"=> [
"delete_posts",
"delete_private_posts",
"edit_posts",
"edit_private_posts",
"publish_posts",
"read_private_posts",
"delete_published_posts",
"edit_published_posts",
"edit_others_posts",
"delete_others_posts",
"read"
],
"editor"=> [
"delete_posts",
"delete_private_posts",
"edit_posts",
"edit_private_posts",
"publish_posts",
"read_private_posts",
"delete_published_posts",
"edit_published_posts",
"edit_others_posts",
"delete_others_posts",
"read"
],
"author"=> [
"delete_posts",
"edit_posts",
"publish_posts",
"delete_published_posts",
"edit_published_posts",
"read"
],
"contributor"=> [
"delete_posts",
"edit_posts",
"read"
],
"subscriber"=> [
"read"
]
],
];
public static $dependencies = [
"tainacan-items" => [
'edit_posts' => 'upload_files',
"edit_private_posts" => 'upload_files',
"edit_published_posts" => 'upload_files',
"edit_others_posts" => 'upload_files'
]
];
private static $instance = null;
public static function get_instance()
{
if(!isset(self::$instance))
{
self::$instance = new self();
}
return self::$instance;
}
/**
* Register hooks
*/
private function __construct() {
add_action('tainacan-insert-tainacan-collection', array(&$this, 'new_collection'));
add_action('tainacan-add-collection-moderators', array(&$this, 'add_moderators'), 10, 2);
add_action('tainacan-remove-collection-moderators', array(&$this, 'remove_moderators'), 10, 2);
add_filter( 'gettext_with_context', array(&$this, 'translate_user_roles'), 10, 4 );
// Dummy calls for translation.
_x('Tainacan Author', 'User role', 'tainacan');
_x('Tainacan Contributor', 'User role', 'tainacan');
_x('Tainacan Editor', 'User role', 'tainacan');
}
/**
* Tainacan default roles
*
* These are roles relative to the native WordPress roles. They will have
* the same capabilities of their relatives native roles in what is concerned to
* tainacan activities, but will have no other WordPress capabilities.
*
* @return array Tainacan roles
*/
public function get_tainacan_roles() {
$tainacan_roles = [
'editor' => [
'slug' => 'tainacan-editor',
'display_name' => 'Tainacan Editor'
],
'contributor' => [
'slug' => 'tainacan-contributor',
'display_name' => 'Tainacan Contributor'
],
'author' => [
'slug' => 'tainacan-author',
'display_name' => 'Tainacan Author'
],
];
return $tainacan_roles;
}
/**
* Callback to gettext_with_context hook to translate custom ueser roles.
*
* Since user roles are stored in the database, we have to translate them on the fly
* using translate_user_role() function.
*
* @see https://wordpress.stackexchange.com/questions/141551/how-to-auto-translate-custom-user-roles
*/
public function translate_user_roles( $translations, $text, $context, $domain ) {
$plugin_domain = 'tainacan';
$roles_names = array_map(function($role) {
return $role['display_name'];
}, $this->get_tainacan_roles());
if ( $context === 'User role' && in_array( $text, $roles_names ) && $domain !== $plugin_domain ) {
return translate_with_gettext_context( $text, $context, $plugin_domain );
}
return $translations;
}
protected function check_dependencies($role, $post_type, $cap, $add = true) {
if(
array_key_exists($post_type, self::$dependencies) &&
array_key_exists($cap, self::$dependencies[$post_type])
) {
$added = false;
if(! $role->has_cap(self::$dependencies[$post_type][$cap]) && $add) {
$role->add_cap(self::$dependencies[$post_type][$cap]);
$added = true;
}
if($role instanceof \WP_User && $add) { //moderator
$append_caps = get_user_meta($role->ID, '.tainacan-dependecies-caps', true);
if(! is_array($append_caps)) $append_caps = [];
if(
(! array_key_exists(self::$dependencies[$post_type][$cap], $append_caps) && $added ) || // we never added and need to add
(
array_key_exists(self::$dependencies[$post_type][$cap], $append_caps) &&
$append_caps[self::$dependencies[$post_type][$cap]] === false &&
$added
) // we added but before is not need to add
) {
$append_caps[self::$dependencies[$post_type][$cap]] = 0;
}
else { // we to not added this cap
$append_caps[self::$dependencies[$post_type][$cap]] = false;
}
if($append_caps[self::$dependencies[$post_type][$cap]] !== false) {
$append_caps[self::$dependencies[$post_type][$cap]]++; // add 1 to each collection he is a moderator
update_user_meta($role->ID, '.tainacan-dependecies-caps', $append_caps);
}
}
return self::$dependencies[$post_type][$cap];
}
return false;
}
/**
* Update post_type caps using WordPress basic roles and register tainacan roles
*/
public function init() {
$defaults_caps = apply_filters('tainacan-defaults-capabilities', $this->defaults);
foreach ($defaults_caps as $post_type => $wp_append_roles) {
if($post_type == 'tainacan-items') continue;
$entity = Repository::get_entity_by_post_type($post_type);
$entity_cap = $entity->get_capabilities();
// append new capabilities to WordPress default roles
foreach ($wp_append_roles as $role_name => $caps) {
$role = get_role($role_name);
if(!is_object($role)) {
throw new \Exception(sprintf('Role "%s" not found', $role_name));
}
foreach ($caps as $cap) {
$role->add_cap($entity_cap->$cap);
$this->check_dependencies($role, $post_type, $cap);
}
$tainacan_roles = $this->get_tainacan_roles();
if (array_key_exists($role_name, $tainacan_roles)) {
$tainacan_role = add_role( $tainacan_roles[$role_name]['slug'], $tainacan_roles[$role_name]['display_name'] );
if(!is_object($tainacan_role)) {
$tainacan_role = get_role($tainacan_roles[$role_name]['slug']);
}
if(!is_object($tainacan_role)) {
throw new \Exception(sprintf('Role "%s" not found', $tainacan_roles[$role_name]['slug']));
}
foreach ($caps as $cap) {
$tainacan_role->add_cap($entity_cap->$cap);
$this->check_dependencies($tainacan_role, $post_type, $cap);
}
}
}
}
$Tainacan_Collections = \Tainacan\Repositories\Collections::get_instance();
$collections = $Tainacan_Collections->fetch([], 'OBJECT');
foreach ($collections as $collection) {
$this->set_items_capabilities($collection, $defaults_caps);
}
}
/**
* Set config roles for items
* @param \Tainacan\Entities\Collection $collection
*/
public function set_items_capabilities($collection, $defaults_caps = null) {
if(is_null($defaults_caps)) $defaults_caps = apply_filters('tainacan-defaults-capabilities', $this->defaults); // External Call
$collection_items_caps = $collection->get_items_capabilities();
foreach ($defaults_caps['tainacan-items'] as $role_name => $caps) {
$role = get_role($role_name);
if(!is_object($role)) {
throw new \Exception(sprintf('Role "%s" not found', $role_name));
}
foreach ($caps as $cap) {
$role->add_cap($collection_items_caps->$cap);
$this->check_dependencies($role, 'tainacan-items', $cap);
}
// Tainacan relative role
$role = get_role('tainacan-' . $role_name);
if (\is_object($role)) {
foreach ($caps as $cap) {
$role->add_cap($collection_items_caps->$cap);
$this->check_dependencies($role, 'tainacan-items', $cap);
}
}
}
// Refresh roles capabilities for current user to have instant effect
global $current_user;
$current_user->get_role_caps();
}
/**
* Return list of editable roles
* @return array List of roles
*/
public static function get_editable_roles() {
// TODO $this->security_check();
global $wp_roles;
if(! isset($wp_roles)) $wp_roles = new \WP_Roles();
$all_roles = $wp_roles->get_names();
$editable_roles = apply_filters('tainacan-editable-roles', $all_roles);
return $all_roles;
}
/**
* Hook to set capabilities to the new created collection
* @param \Tainacan\Entities\Collection $collection
*/
public function new_collection($collection)
{
$this->set_items_capabilities($collection);
}
/**
* Hooke to revoke the capabilities for the items post type of the collection
* @param \Tainacan\Entities\Collection $collection The collection object
* @param array $moderators List of IDs of user IDs removed from the moderators list of the collection
* @return void
*/
public function remove_moderators($collection, $moderators) {
$defaults_caps = apply_filters('tainacan-defaults-capabilities', $this->defaults);
if (is_array($moderators)) {
$collection_items_caps = $collection->get_items_capabilities();
foreach ($moderators as $moderator) {
$user = get_userdata($moderator);
if ($user instanceof \WP_User && is_object($collection_items_caps)) {
$caps = $defaults_caps['tainacan-items']['editor'];
foreach ($caps as $cap) {
$user->remove_cap($collection_items_caps->$cap);
$dep_cap = $this->check_dependencies($user, 'tainacan-items', $cap, false);
if($dep_cap !== false) {
$appended_caps = get_user_meta($user->ID, '.tainacan-dependecies-caps', true);
if(array_key_exists($dep_cap, $appended_caps) && $appended_caps[$dep_cap] !== false && $appended_caps[$dep_cap] > 0) {
$appended_caps[$dep_cap]--;
update_user_meta($user->ID, '.tainacan-dependecies-caps', $appended_caps);
if($appended_caps == 0) { // they are not a moderator of a collection, remove cap at all
$user->remove_cap($cap);
}
}
}
}
}
}
}
}
/**
* Hooke to grant the capabilities for the items post type of the collection
* @param \Tainacan\Entities\Collection $collection The collection object
* @param array $moderators List of IDs of user IDs added to the moderators list of the collection
* @return void
*/
public function add_moderators($collection, $moderators) {
$defaults_caps = apply_filters('tainacan-defaults-capabilities', $this->defaults);
if (is_array($moderators)) {
$collection_items_caps = $collection->get_items_capabilities();
foreach ($moderators as $moderator) {
$user = get_userdata($moderator);
if ($user instanceof \WP_User && is_object($collection_items_caps)) {
$caps = $defaults_caps['tainacan-items']['editor'];
foreach ($caps as $cap) {
$user->add_cap($collection_items_caps->$cap);
$this->check_dependencies($user, 'tainacan-items', $cap);
}
}
}
}
}
}

View File

@ -140,7 +140,7 @@ class Elastic_Press {
if ($col) {
$metadata = $Tainacan_Metadata->fetch_by_collection($col, ['posts_per_page' => -1], 'OBJECT');
$metadata = $Tainacan_Metadata->fetch_by_collection($col, ['posts_per_page' => -1]);
foreach ($metadata as $meta) {
$meta_ids[] = $meta->get_id();
@ -200,7 +200,7 @@ class Elastic_Press {
foreach ( $args['post_type'] as $cpt ) {
$col = $Tainacan_Collections->fetch_by_db_identifier($cpt);
$_filters = $Tainacan_Filters->fetch_by_collection($col, ['posts_per_page' => -1], 'OBJECT');
$_filters = $Tainacan_Filters->fetch_by_collection($col, ['posts_per_page' => -1]);
foreach ($_filters as $filter) {
$include = [];
$filter_id = $filter->get_id();

View File

@ -0,0 +1,747 @@
<?php
namespace Tainacan;
use Tainacan\Repositories\Repository;
class Roles {
public static $dependencies = [
"tainacan-items" => [
'edit_posts' => 'upload_files',
"edit_private_posts" => 'upload_files',
"edit_published_posts" => 'upload_files',
"edit_others_posts" => 'upload_files'
]
];
private static $instance = null;
private $capabilities;
public static function get_instance()
{
if(!isset(self::$instance))
{
self::$instance = new self();
}
return self::$instance;
}
/**
*
*/
private function __construct() {
$this->capabilities = [
'manage_tainacan' => [
'display_name' => __('Manage Tainacan', 'tainacan'),
'description' => __('Manage all Tainacan features and all Collections', 'tainacan'),
'scope' => 'repository',
'supercaps' => []
],
'tnc_rep_edit_users' => [
'display_name' => __('Manage Users', 'tainacan'),
'description' => __('Manage users roles and permissions', 'tainacan'),
'scope' => 'repository',
'supercaps' => [
'manage_tainacan'
]
],
'tnc_rep_edit_collections' => [
'display_name' => __('Create Collections', 'tainacan'),
'description' => __('Create new collections to the repository and edit its details', 'tainacan'),
'dependencies' => [
'upload_files'
],
'scope' => 'repository',
'supercaps' => [
'manage_tainacan'
]
],
'tnc_rep_delete_collections' => [
'display_name' => __('Delete Collections', 'tainacan'),
'description' => __('Delete their own collections from the repository', 'tainacan'),
'scope' => 'repository',
'supercaps' => [
'manage_tainacan'
]
],
'tnc_rep_edit_taxonomies' => [
'display_name' => __('Create and edit taxonomies', 'tainacan'),
'description' => __('Create new taxonomies and edit its terms', 'tainacan'),
'scope' => 'repository',
'supercaps' => [
'manage_tainacan'
]
],
'tnc_rep_edit_others_taxonomies' => [
'display_name' => __('Edit all Taxonomies', 'tainacan'),
'description' => __('Edit all taxonomies and terms, including taxonomies created by other users', 'tainacan'),
'scope' => 'repository',
'supercaps' => [
'manage_tainacan'
]
],
'tnc_rep_delete_taxonomies' => [
'display_name' => __('Delete Taxonomies', 'tainacan'),
'description' => __('Delete taxonomies', 'tainacan'),
'scope' => 'repository',
'supercaps' => [
'manage_tainacan'
]
],
'tnc_rep_delete_others_taxonomies' => [
'display_name' => __('Delete all Taxonomies', 'tainacan'),
'description' => __('Delete all taxonomies and terms, including taxonomies created by other users', 'tainacan'),
'scope' => 'repository',
'supercaps' => [
'manage_tainacan'
]
],
'tnc_rep_edit_metadata' => [
'display_name' => __('Manage Repository Metadata', 'tainacan'),
'description' => __('Create/edit metadata in repository level', 'tainacan'),
'scope' => 'repository',
'supercaps' => [
'manage_tainacan'
]
],
'tnc_rep_edit_filters' => [
'display_name' => __('Manage Repository Filters', 'tainacan'),
'description' => __('Create/edit filters in repository level', 'tainacan'),
'scope' => 'repository',
'supercaps' => [
'manage_tainacan'
]
],
'tnc_rep_delete_metadata' => [
'display_name' => __('Delete Repository Metadata', 'tainacan'),
'description' => __('Delete metadata in repository level', 'tainacan'),
'scope' => 'repository',
'supercaps' => [
'manage_tainacan'
]
],
'tnc_rep_delete_filters' => [
'display_name' => __('Delete Repository Filters', 'tainacan'),
'description' => __('Delete filters in repository level', 'tainacan'),
'scope' => 'repository',
'supercaps' => [
'manage_tainacan'
]
],
'tnc_rep_read_private_collections' => [
'display_name' => __('View private collections', 'tainacan'),
'description' => __('Access to view and browse private collections', 'tainacan'),
'scope' => 'repository',
'supercaps' => [
'manage_tainacan'
]
],
'tnc_rep_read_private_taxonomies' => [
'display_name' => __('View private taxonomies', 'tainacan'),
'description' => __('Access to private taxonomies information', 'tainacan'),
'scope' => 'repository',
'supercaps' => [
'manage_tainacan'
]
],
'tnc_rep_read_private_metadata' => [
'display_name' => __('View private repository metadata', 'tainacan'),
'description' => __('Access to private metadata in repository level', 'tainacan'),
'scope' => 'repository',
'supercaps' => [
'manage_tainacan'
]
],
'tnc_rep_read_private_filters' => [
'display_name' => __('View private repository filters', 'tainacan'),
'description' => __('Access to private filters in repository level', 'tainacan'),
'scope' => 'repository',
'supercaps' => [
'manage_tainacan'
]
],
'tnc_rep_read_logs' => [
'display_name' => __('View Logs', 'tainacan'),
'description' => __('Access to activities logs. Note that activity logs might contain information on private collections, items and metadata.', 'tainacan'),
'scope' => 'repository',
'supercaps' => [
'manage_tainacan'
]
],
/**
* Collections capabilities
* There is a set of this capabilities for each collection, where %d is collection ID
* If %d is "all" then the user will have this capability to all collections
*/
'manage_tainacan_collection_%d' => [
'display_name' => __('Manage Collection', 'tainacan'),
'description' => __('Manage all collection settings, items, metadata, filters, etc.', 'tainacan'),
'scope' => 'collection',
'supercaps' => [
'manage_tainacan',
'manage_tainacan_collection_all'
]
],
'tnc_col_%d_edit_users' => [
'display_name' => __('Edit users permissions', 'tainacan'),
'description' => __('Configure which roles and users have permission to perform actions in this collection', 'tainacan'),
'scope' => 'collection',
'supercaps' => [
'manage_tainacan',
'manage_tainacan_collection_all',
'manage_tainacan_collection_%d',
'tnc_col_all_edit_users',
]
],
'tnc_col_%d_bulk_edit' => [
'display_name' => __('Bulk edit items', 'tainacan'),
'description' => __('Access to the Bulk edit items feature.', 'tainacan'),
'scope' => 'collection',
'supercaps' => [
'manage_tainacan',
'manage_tainacan_collection_all',
'manage_tainacan_collection_%d',
'tnc_col_all_bulk_edit',
]
],
'tnc_col_%d_edit_metadata' => [
'display_name' => __('Manage metadata', 'tainacan'),
'description' => __('Create/edit metadata in this collection', 'tainacan'),
'scope' => 'collection',
'supercaps' => [
'manage_tainacan',
'manage_tainacan_collection_all',
'manage_tainacan_collection_%d',
'tnc_col_all_edit_metadata',
]
],
'tnc_col_%d_edit_filters' => [
'display_name' => __('Manage filters', 'tainacan'),
'description' => __('Create/edit filters in this collection', 'tainacan'),
'scope' => 'collection',
'supercaps' => [
'manage_tainacan',
'manage_tainacan_collection_all',
'manage_tainacan_collection_%d',
'tnc_col_all_edit_filters',
]
],
'tnc_col_%d_delete_metadata' => [
'display_name' => __('Delete metadata', 'tainacan'),
'description' => __('Delete metadata in this collection', 'tainacan'),
'scope' => 'collection',
'supercaps' => [
'manage_tainacan',
'manage_tainacan_collection_all',
'manage_tainacan_collection_%d',
'tnc_col_all_delete_metadata',
]
],
'tnc_col_%d_delete_filters' => [
'display_name' => __('Delete filters', 'tainacan'),
'description' => __('Delete filters in this collection', 'tainacan'),
'scope' => 'collection',
'supercaps' => [
'manage_tainacan',
'manage_tainacan_collection_all',
'manage_tainacan_collection_%d',
'tnc_col_all_delete_filters',
]
],
'tnc_col_%d_read_private_metadata' => [
'display_name' => __('View private metadata', 'tainacan'),
'description' => __('Access private metadata in this collection', 'tainacan'),
'scope' => 'collection',
'supercaps' => [
'manage_tainacan',
'manage_tainacan_collection_all',
'manage_tainacan_collection_%d',
'tnc_col_all_read_private_metadata',
]
],
'tnc_col_%d_read_private_filters' => [
'display_name' => __('View private filters', 'tainacan'),
'description' => __('Access private filters in this collection', 'tainacan'),
'scope' => 'collection',
'supercaps' => [
'manage_tainacan',
'manage_tainacan_collection_all',
'manage_tainacan_collection_%d',
'tnc_col_all_read_private_filters',
]
],
'tnc_col_%d_read_private_items' => [
'display_name' => __('View private items', 'tainacan'),
'description' => __('Access to view private items in this collection', 'tainacan'),
'scope' => 'collection',
'supercaps' => [
'manage_tainacan',
'manage_tainacan_collection_all',
'manage_tainacan_collection_%d',
'tnc_col_all_read_private_items',
]
],
'tnc_col_%d_edit_items' => [
'display_name' => __('Edit items', 'tainacan'),
'description' => __('Create and edit items in this collection', 'tainacan'),
'dependencies' => [
'upload_files'
],
'scope' => 'collection',
'supercaps' => [
'manage_tainacan',
'manage_tainacan_collection_all',
'manage_tainacan_collection_%d',
'tnc_col_all_edit_items',
]
],
'tnc_col_%d_publish_items' => [
'display_name' => __('Publish items', 'tainacan'),
'description' => __('Publish items in this collection', 'tainacan'),
'dependencies' => [
'upload_files'
],
'scope' => 'collection',
'supercaps' => [
'manage_tainacan',
'manage_tainacan_collection_all',
'manage_tainacan_collection_%d',
'tnc_col_all_publish_items',
]
],
'tnc_col_%d_edit_others_items' => [
'display_name' => __('Edit others items', 'tainacan'),
'description' => __('Edit items created by other users in this collection', 'tainacan'),
'dependencies' => [
'upload_files'
],
'scope' => 'collection',
'supercaps' => [
'manage_tainacan',
'manage_tainacan_collection_all',
'manage_tainacan_collection_%d',
'tnc_col_all_edit_others_items',
]
],
'tnc_col_%d_edit_published_items' => [
'display_name' => __('Edit published items', 'tainacan'),
'description' => __('Edit items in this collection after they are published', 'tainacan'),
'dependencies' => [
'upload_files'
],
'scope' => 'collection',
'supercaps' => [
'manage_tainacan',
'manage_tainacan_collection_all',
'manage_tainacan_collection_%d',
'tnc_col_all_edit_published_items',
]
],
'tnc_col_%d_delete_items' => [
'display_name' => __('Delete items', 'tainacan'),
'description' => __('Delete items in this collection', 'tainacan'),
'scope' => 'collection',
'supercaps' => [
'manage_tainacan',
'manage_tainacan_collection_all',
'manage_tainacan_collection_%d',
'tnc_col_all_delete_items',
]
],
'tnc_col_%d_delete_others_items' => [
'display_name' => __('Delete others items', 'tainacan'),
'description' => __('Delete items created by other users in this collection', 'tainacan'),
'scope' => 'collection',
'supercaps' => [
'manage_tainacan',
'manage_tainacan_collection_all',
'manage_tainacan_collection_%d',
'tnc_col_all_delete_others_items',
]
],
'tnc_col_%d_delete_published_items' => [
'display_name' => __('Delete published items', 'tainacan'),
'description' => __('Delete items in this collection after they are published', 'tainacan'),
'scope' => 'collection',
'supercaps' => [
'manage_tainacan',
'manage_tainacan_collection_all',
'manage_tainacan_collection_%d',
'tnc_col_all_delete_published_items',
]
],
];
add_filter( 'user_has_cap', [$this, 'user_has_cap_filter'], 10, 4 );
add_filter( 'map_meta_cap', [$this, 'map_meta_cap'], 10, 4 );
add_filter( 'gettext_with_context', array(&$this, 'translate_user_roles'), 10, 4 );
// Dummy calls for translation.
_x('Tainacan Administrator', 'User role', 'tainacan');
_x('Tainacan Editor', 'User role', 'tainacan');
_x('Tainacan Author', 'User role', 'tainacan');
}
/**
* Tainacan default roles
*
* @return array Tainacan roles
*/
public function get_tainacan_roles() {
$tainacan_roles = [
'tainacan-administrator' => [
'slug' => 'tainacan-administrator',
'display_name' => 'Tainacan Administrator',
'caps' => [
'manage_tainacan' => true
]
],
'tainacan-editor' => [
'slug' => 'tainacan-editor',
'display_name' => 'Tainacan Editor',
'caps' => [
'tnc_rep_edit_collections' => true,
'tnc_rep_delete_collections' => true,
'tnc_rep_edit_taxonomies' => true,
'tnc_rep_edit_others_taxonomies' => true,
'tnc_rep_delete_taxonomies' => true,
'tnc_rep_delete_others_taxonomies' => true,
'tnc_rep_edit_metadata' => true,
'tnc_rep_edit_filters' => true,
'tnc_rep_delete_metadata' => true,
'tnc_rep_delete_filters' => true,
'tnc_rep_read_private_collections' => true,
'tnc_rep_read_private_taxonomies' => true,
'tnc_rep_read_private_metadata' => true,
'tnc_rep_read_private_filters' => true,
'tnc_rep_read_logs' => true,
'manage_tainacan_collection_all' => true
]
],
'tainacan-author' => [
'slug' => 'tainacan-author',
'display_name' => 'Tainacan Author',
'caps' => [
'tnc_rep_edit_collections' => true,
'tnc_rep_edit_taxonomies' => true,
'tnc_rep_read_private_collections' => true,
'tnc_rep_read_private_taxonomies' => true,
'tnc_rep_read_private_metadata' => true,
'tnc_rep_read_private_filters' => true,
]
],
];
return $tainacan_roles;
}
/**
* Callback to gettext_with_context hook to translate custom ueser roles.
*
* Since user roles are stored in the database, we have to translate them on the fly
* using translate_user_role() function.
*
* @see https://wordpress.stackexchange.com/questions/141551/how-to-auto-translate-custom-user-roles
*/
public function translate_user_roles( $translations, $text, $context, $domain ) {
$plugin_domain = 'tainacan';
$roles_names = array_map(function($role) {
return $role['display_name'];
}, $this->get_tainacan_roles());
if ( $context === 'User role' && in_array( $text, $roles_names ) && $domain !== $plugin_domain ) {
return translate_with_gettext_context( $text, $context, $plugin_domain );
}
return $translations;
}
public function get_all_caps() {
return $this->capabilities;
}
public function get_collection_caps() {
return array_filter( $this->get_all_caps(), function($el) { return $el['scope'] == 'collection'; } );
}
public function get_repository_caps() {
return array_filter( $this->get_all_caps(), function($el) { return $el['scope'] == 'repository'; } );
}
public function get_all_caps_slugs() {
return array_keys($this->get_all_caps());
}
public function get_collection_caps_slugs() {
return array_keys($this->get_collection_caps());
}
public function get_repository_caps_slugs() {
return array_keys($this->get_repository_caps());
}
public function init_default_roles() {
foreach ($this->get_tainacan_roles() as $role) {
$new_role = add_role(
$role['slug'],
$role['display_name'],
$role['caps']
);
}
$admin = get_role('administrator');
$admin->add_cap('manage_tainacan');
$editor = get_role('editor');
$editor->add_cap('manage_tainacan');
}
/**
* Gets the capabilty generic name as present in
* Tainacan\Roles::capabilities
*
* For example: tnc_col_12_edit or tnc_col_all_edit will return tnc_col_%d_edit
*
* @param string $cap
* @return string Capability slug as in the keys of $this->capabilities
*/
public function get_cap_generic_name($cap) {
$cap = preg_replace('/^(.+_)[0-9]+(_.+)?$/', '${1}%d${2}', $cap);
$cap = preg_replace('/^(.+_)all(_.+)?$/', '${1}%d${2}', $cap);
return $cap;
}
public function user_has_cap_filter( $allcaps, $caps, $args, $user ) {
$requested_cap = $args[0];
// Administrators will always be able to edit users
if ( $requested_cap == 'tnc_rep_edit_users' ) {
if ( array_key_exists('edit_users', $allcaps) && $allcaps['edit_users'] === true ) {
$allcaps['tnc_rep_edit_users'] = true;
return $allcaps;
}
}
foreach ( $caps as $cap ) {
if ( array_key_exists($cap, $allcaps) && $allcaps[$cap] === true ) {
continue;
}
if ( \strpos($cap, 'tnc_') === 0 ) {
if ( $user->has_cap('manage_tainacan') ) {
$allcaps = array_merge($allcaps, [ $cap => true ]);
} elseif ( \strpos($cap, 'tnc_col_') === 0 ) {
$col_id = preg_replace('/[a-z_]+(\d+)[a-z_]+?$/', '$1', $cap );
if ( ! is_numeric($col_id) ) {
continue;
}
// check for tnc_col_all_* capabilities
$all_collections_cap = preg_replace('/([a-z_]+)(\d+)([a-z_]+?)$/', '${1}all${3}', $cap );
if (
$user->has_cap('manage_tainacan_collection_' . $col_id) ||
$user->has_cap('manage_tainacan_collection_all') ||
$user->has_cap($all_collections_cap) ) {
$allcaps = array_merge($allcaps, [ $cap => true ]);
} else {
// check if the user is the owner
$collection = tainacan_collections()->fetch( (int) $col_id );
if ( $collection instanceof \Tainacan\Entities\Collection ) {
if ( (int) $collection->get_author_id() == (int) $user->ID ) {
$allcaps = array_merge($allcaps, [ $cap => true ]);
}
}
}
}
}
}
return $allcaps;
}
public function add_dependencies($role, $cap) {
// convert cap name to the name declared in the roles of this class. tnc_col_12_edit or tnc_col_all_edit should become tnc_col_%d_edit
$cap = $this->get_cap_generic_name($cap);
if ( isset( $this->capabilities[$cap] ) && isset( $this->capabilities[$cap]['dependencies'] ) ) {
$role = \get_role($role);
if ( ! $role instanceof \WP_Role ) {
return;
}
foreach ( $this->capabilities[$cap]['dependencies'] as $dep ) {
$role->add_cap($dep);
}
}
}
public function map_meta_cap( $caps, $cap, $user_id, $args ) {
$meta_caps = new \Tainacan\Entities\Metadatum();
$meta_caps = $meta_caps->get_capabilities();
$filters_caps = new \Tainacan\Entities\Filter();
$filters_caps = $filters_caps->get_capabilities();
$edit_meta = [
$meta_caps->edit_posts,
$meta_caps->edit_others_posts,
$meta_caps->publish_posts,
$meta_caps->delete_posts,
$meta_caps->delete_private_posts,
$meta_caps->delete_published_posts,
$meta_caps->delete_others_posts,
$meta_caps->edit_private_posts,
$meta_caps->edit_published_posts,
$meta_caps->create_posts,
];
$read_private_meta = [
$meta_caps->read_private_posts
];
$edit_filters = [
$filters_caps->edit_posts,
$filters_caps->edit_others_posts,
$filters_caps->publish_posts,
$filters_caps->delete_posts,
$filters_caps->delete_private_posts,
$filters_caps->delete_published_posts,
$filters_caps->delete_others_posts,
$filters_caps->edit_private_posts,
$filters_caps->edit_published_posts,
$filters_caps->create_posts,
];
$read_private_filters = [
$filters_caps->read_private_posts
];
if ( !is_array( $args ) || !array_key_exists( 0, $args ) ) {
return $caps;
}
$object = $args[0];
switch ($cap) {
case 'edit_post':
case 'delete_post':
$action = \str_replace('_post', '', $cap);
foreach ($caps as $i => $c) {
// Handle edit metadata
if ( in_array($c, $edit_meta) ) {
if (is_numeric($object)) {
$object = tainacan_metadata()->fetch ( (int) $object );
}
if ( $object instanceof \Tainacan\Entities\Metadatum ) {
if ( $object->get_collection_id() == 'default' ) {
unset($caps[$i]);
$caps[] = 'tnc_rep_' . $action. '_metadata';
} elseif ( is_numeric($object->get_collection_id()) ) {
unset($caps[$i]);
$caps[] = 'tnc_col_' . $object->get_collection_id() . '_' . $action. '_metadata';
}
}
}
// Handle edit filters
if ( in_array($c, $edit_filters) ) {
if (is_numeric($object)) {
$object = tainacan_filters()->fetch ( (int) $object );
}
if ( $object instanceof \Tainacan\Entities\Filter ) {
if ( $object->get_collection_id() == 'default' ) {
unset($caps[$i]);
$caps[] = 'tnc_rep_' . $action. '_filters';
} elseif ( is_numeric($object->get_collection_id()) ) {
unset($caps[$i]);
$caps[] = 'tnc_col_' . $object->get_collection_id() . '_' . $action. '_filters';
}
}
}
}
break;
case 'read_post':
foreach ($caps as $i => $c) {
// Handle read private metadata
if ( in_array($c, $read_private_meta) ) {
if (is_numeric($object)) {
$object = tainacan_metadata()->fetch ( (int) $object );
}
if ( $object instanceof \Tainacan\Entities\Metadatum ) {
if ( $object->get_collection_id() == 'default' ) {
unset($caps[$i]);
$caps[] = 'tnc_rep_read_private_metadata';
} elseif ( is_numeric($object->get_collection_id()) ) {
unset($caps[$i]);
$caps[] = 'tnc_col_' . $object->get_collection_id() . '_read_private_metadata';
}
}
}
// Handle read private filters
if ( in_array($c, $read_private_filters) ) {
if (is_numeric($object)) {
$object = tainacan_filters()->fetch ( (int) $object );
}
if ( $object instanceof \Tainacan\Entities\Filter ) {
if ( $object->get_collection_id() == 'default' ) {
unset($caps[$i]);
$caps[] = 'tnc_rep_read_private_filters';
} elseif ( is_numeric($object->get_collection_id()) ) {
unset($caps[$i]);
$caps[] = 'tnc_col_' . $object->get_collection_id() . '_read_private_filters';
}
}
}
}
break;
default:
# code...
break;
}
return $caps;
}
}

View File

@ -33,7 +33,6 @@ class Collection extends Entity {
$cover_page_id,
$header_image_id,
$header_image,
$moderators_ids,
$comment_status,
$allow_comments;
@ -145,21 +144,60 @@ class Collection extends Entity {
/**
* Get the capabilities list for the post type of the items of this collection
*
* @uses get_post_type_capabilities to get the list.
*
* This method is usefull for getting the capabilities of the collection's items post type
* regardless if it has been already registered or not.
*
* @return object Object with all the capabilities as member variables.
*/
function get_items_capabilities() {
$args = [
'map_meta_cap' => true,
'capability_type' => $this->get_db_identifier(),
'capabilities' => array()
$id = $this->get_id();
return (object) [
// meta
'edit_post' => "tnc_col_{$id}_edit_item",
'read_post' => "tnc_col_{$id}_read_item",
'delete_post' => "tnc_col_{$id}_delete_item",
// primitive
'edit_posts' => "tnc_col_{$id}_edit_items",
'edit_others_posts' => "tnc_col_{$id}_edit_others_items",
'publish_posts' => "tnc_col_{$id}_publish_items",
'read_private_posts' => "tnc_col_{$id}_read_private_items",
'read' => "read",
'delete_posts' => "tnc_col_{$id}_delete_items",
'delete_private_posts' => "tnc_col_{$id}_delete_items",
'delete_published_posts' => "tnc_col_{$id}_delete_published_items",
'delete_others_posts' => "tnc_col_{$id}_delete_others_items",
'edit_private_posts' => "tnc_col_{$id}_edit_others_items",
'edit_published_posts' => "tnc_col_{$id}_edit_published_items",
'create_posts' => "tnc_col_{$id}_edit_items"
];
}
public function get_capabilities() {
return (object) [
// meta
'edit_post' => "tnc_rep_edit_collection",
'read_post' => "tnc_rep_read_collection",
'delete_post' => "tnc_rep_delete_collection",
// primitive
'edit_posts' => "tnc_rep_edit_collections",
'edit_others_posts' => "manage_tainacan",
'publish_posts' => "tnc_rep_edit_collections",
'read_private_posts' => "tnc_rep_read_private_collections",
'read' => "read",
'delete_posts' => "tnc_rep_delete_collections",
'delete_private_posts' => "tnc_rep_delete_collections",
'delete_published_posts' => "tnc_rep_delete_collections",
'delete_others_posts' => "manage_tainacan",
'edit_private_posts' => "tnc_rep_edit_collections",
'edit_published_posts' => "tnc_rep_edit_collections",
'create_posts' => "tnc_rep_edit_collections"
];
return get_post_type_capabilities( (object) $args );
}
/**
@ -204,7 +242,7 @@ class Collection extends Entity {
$sizes = get_intermediate_image_sizes();
array_unshift($sizes, 'full');
foreach ( $sizes as $size ) {
$thumbs[$size] = wp_get_attachment_image_src( $this->get__thumbnail_id(), $size );
}
@ -366,7 +404,7 @@ class Collection extends Entity {
function get_metadata_order() {
return $this->get_mapped_property( 'metadata_order' );
}
/**
* Get enable cover page attribute
*
@ -375,7 +413,7 @@ class Collection extends Entity {
function get_enable_cover_page() {
return $this->get_mapped_property( 'enable_cover_page' );
}
/**
* Get Header Image ID attribute
*
@ -384,7 +422,7 @@ class Collection extends Entity {
function get_header_image_id() {
return $this->get_mapped_property( 'header_image_id' );
}
/**
* Return true if enabled cover page is set to yes
*
@ -393,7 +431,7 @@ class Collection extends Entity {
function is_cover_page_enabled() {
return $this->get_enable_cover_page() === 'yes';
}
/**
* Get enable cover page attribute
*
@ -412,15 +450,6 @@ class Collection extends Entity {
return $this->get_mapped_property( 'filters_order' );
}
/**
* Get collection moderators ids
*
* @return array
*/
function get_moderators_ids() {
return $this->get_mapped_property( 'moderators_ids' );
}
/**
* Get collection DB identifier
*
@ -445,12 +474,12 @@ class Collection extends Entity {
function get_metadata() {
$Tainacan_Metadata = \Tainacan\Repositories\Metadata::get_instance();
return $Tainacan_Metadata->fetch_by_collection( $this, [], 'OBJECT' );
return $Tainacan_Metadata->fetch_by_collection( $this );
}
/**
* Get the two core metadata of the collection (title and description)
*
*
* @return array[\Tainacan\Entities\Metadatum]
*/
function get_core_metadata() {
@ -462,7 +491,7 @@ class Collection extends Entity {
/**
* Get the Core Title Metadatum for this collection
*
*
* @return \Tainacan\Entities\Metadatum The Core Title Metadatum
*/
function get_core_title_metadatum() {
@ -473,7 +502,7 @@ class Collection extends Entity {
/**
* Get the Core Description Metadatum for this collection
*
*
* @return \Tainacan\Entities\Metadatum The Core Description Metadatum
*/
function get_core_description_metadatum() {
@ -481,7 +510,7 @@ class Collection extends Entity {
return $repo->get_core_description_metadatum($this);
}
/**
* Checks if comments are allowed for the current Collection.
* @return string "open"|"closed"
@ -489,7 +518,7 @@ class Collection extends Entity {
public function get_comment_status() {
return $this->get_mapped_property('comment_status');
}
/**
* Checks if comments are allowed for the current Collection Items.
* @return bool
@ -635,7 +664,7 @@ class Collection extends Entity {
function set_filters_order( $value ) {
$this->set_mapped_property( 'filters_order', $value );
}
/**
* Set enable cover page attribute
*
@ -646,7 +675,7 @@ class Collection extends Entity {
function set_enable_cover_page( $value ) {
$this->set_mapped_property( 'enable_cover_page', $value );
}
/**
* Set cover page ID
*
@ -657,7 +686,7 @@ class Collection extends Entity {
function set_cover_page_id( $value ) {
$this->set_mapped_property( 'cover_page_id', $value );
}
/**
* Set Header Image ID
*
@ -669,36 +698,15 @@ class Collection extends Entity {
$this->set_mapped_property( 'header_image_id', $value );
}
/**
* Set collection moderators ids
*
* @param [string] $value
*
* @return void
*/
function set_moderators_ids( $value ) {
if(!is_array($value)) {
if(empty($value)) {
$value = [];
} else {
throw new \Exception('moderators_ids must be a array of users ids');
}
}
// make sure you never have duplicated moderators
$value = array_unique($value);
$this->set_mapped_property( 'moderators_ids', $value );
}
/**
* Sets if comments are allowed for the current Collection.
*
*
* @param $value string "open"|"closed"
*/
public function set_comment_status( $value ) {
$this->set_mapped_property('comment_status', $value);
}
/**
* Sets if comments are allowed for the current Collection Items.
*
@ -708,59 +716,6 @@ class Collection extends Entity {
$this->set_mapped_property('allow_comments', $value );
}
// Moderators methods
/**
* Add a moderator ID to the moderators_ids list
*
* @param int $user_id The user ID to be added
*
* @return boolean Wether the ID was added or not. (if it already existed in the list it returns false)
*/
function add_moderator_id( $user_id ) {
if ( is_integer( $user_id ) ) {
$current_moderators = $this->get_moderators_ids();
if ( ! in_array( $user_id, $current_moderators ) ) {
$current_moderators[] = $user_id;
$this->set_moderators_ids( $current_moderators );
return true;
}
}
return false;
}
/**
* Remove a moderator ID to the moderators_ids list
*
* @param int $user_id The user ID to be removed
*
* @return boolean Wether the ID was added or not. (if it did not exist in the list it returns false)
*/
function remove_moderator_id( $user_id ) {
if ( is_integer( $user_id ) ) {
$current_moderators = $this->get_moderators_ids();
if ( ( $key = array_search( $user_id, $current_moderators ) ) !== false ) {
unset( $current_moderators[ $key ] );
$this->set_moderators_ids( $current_moderators );
return true;
}
}
return false;
}
/**
* TODO implement the following methods to handle moderators_ids
*
* set_moderators
* get_moderators
* (the same as moderators_ids but gets and sets WP_User objects)
*
*/
/**
* Validate Collection
*
@ -779,4 +734,30 @@ class Collection extends Entity {
}
/**
* Checks if an user have permission on any of the collections capabilities
* defined in Tainacan\Roles class.
* It applies to all capabilities with 'collection' scope starting with 'tnc_col_'
*
* Usage: use only the suffix of the capability (after tnc_col_%d_). For example, to check if user can
* tnc_col_%d_read_private_items for this collection, simply call:
* $collection->user_can('read_private_items');
*
* @param string $capability The capability to be checked.
* @param int|\WP_User|null $user the user for capability check, null for the current user
* @return void
*/
public function user_can($capability, $user = null) {
if ( \is_null($user) ) {
$user = wp_get_current_user();
}
if ( is_int($user) || $user instanceof \WP_User ) {
return user_can( $user, 'tnc_col_' . $this->get_id() . '_' . $capability );
}
return false;
}
}

View File

@ -706,5 +706,4 @@ class Item extends Entity {
$id = $this->get_id();
return admin_url("?page=tainacan_admin#/collections/$collection_id/items/$id/edit");
}
}

View File

@ -55,6 +55,31 @@ class Log extends Entity {
return apply_filters('tainacan-log-to-array', $array_log, $this);
}
public function get_capabilities() {
return (object) [
// meta
'edit_post' => "tnc_rep_edit_logs",
'read_post' => "tnc_rep_read_log",
'delete_post' => "tnc_rep_edit_logs",
// primitive
'edit_posts' => "do_not_allow",
'edit_others_posts' => "do_not_allow",
'publish_posts' => "do_not_allow",
'read_private_posts' => "do_not_allow",
'read' => "tnc_rep_read_logs",
'delete_posts' => "do_not_allow",
'delete_private_posts' => "do_not_allow",
'delete_published_posts' => "do_not_allow",
'delete_others_posts' => "do_not_allow",
'edit_private_posts' => "do_not_allow",
'edit_published_posts' => "do_not_allow",
'create_posts' => "do_not_allow"
];
}
/**
* @param $collection_id

View File

@ -99,6 +99,31 @@ class Taxonomy extends Entity {
return true;
}
public function get_capabilities() {
return (object) [
// meta
'edit_post' => "tnc_rep_edit_taxonomy",
'read_post' => "tnc_rep_read_taxonomy",
'delete_post' => "tnc_rep_delete_taxonomy",
// primitive
'edit_posts' => "tnc_rep_edit_taxonomies",
'edit_others_posts' => "tnc_rep_edit_others_taxonomies",
'publish_posts' => "tnc_rep_edit_taxonomies",
'read_private_posts' => "tnc_rep_read_private_taxonomies",
'read' => "read",
'delete_posts' => "tnc_rep_delete_taxonomies",
'delete_private_posts' => "tnc_rep_delete_taxonomies",
'delete_published_posts' => "tnc_rep_delete_taxonomies",
'delete_others_posts' => "tnc_rep_delete_others_taxonomies",
'edit_private_posts' => "tnc_rep_edit_taxonomies",
'edit_published_posts' => "tnc_rep_edit_taxonomies",
'create_posts' => "tnc_rep_edit_taxonomies"
];
}
// Getters
/**

View File

@ -181,7 +181,9 @@
axios.post(`/taxonomy/${this.taxonomyId}/terms?hideempty=0&order=asc`, {
name: this.name,
parent: this.parent
parent: this.parent,
metadatum_id: this.metadatumId,
item_id: this.itemId
})
.then(res => {

View File

@ -65,8 +65,8 @@
</b-select>
</b-field>
<b-field
<b-field
v-if="taxonomy_id && taxonomies.length && taxonomies.find((taxonomy) => taxonomy.id == taxonomy_id).allow_insert == 'yes'"
:addons="false"
:label="$i18n.get('label_taxonomy_allow_new_terms')">
<b-switch

View File

@ -18,7 +18,7 @@ class Taxonomy extends Metadata_Type {
parent::__construct();
$this->set_primitive_type('term');
$this->set_repository( \Tainacan\Repositories\Terms::get_instance() );
$this->set_default_options([
'allow_new_terms' => 'no'
]);
@ -30,44 +30,44 @@ class Taxonomy extends Metadata_Type {
$this->set_preview_template('
<div>
<div>
<p class="has-text-gray">'. __('Selected terms') . ': </p>
<p class="has-text-gray">'. __('Selected terms') . ': </p>
<div class="field selected-tags is-grouped-multiline is-grouped">
<div>
<div class="tags has-addons">
<span class="tag"><span>'. __('Term') . ' 2</span></span>
<span class="tag"><span>'. __('Term') . ' 2</span></span>
<a class="tag is-delete"></a>
</div>
</div>
<div>
<div class="tags has-addons">
<span class="tag"><span>'. __('Term') . ' 3</span></span>
<span class="tag"><span>'. __('Term') . ' 3</span></span>
<a class="tag is-delete"></a>
</div>
</div>
</div>
</div>
<div>
<label class="b-checkbox checkbox" border="" style="padding-left: 8px;">
<input type="checkbox" value="option1">
<span class="check"></span>
<span class="control-label">'. __('Term') . ' 1</span>
</label>
</label>
<br>
</div>
<div>
<label class="b-checkbox checkbox" border="" style="padding-left: 8px;">
<input type="checkbox" checked value="option2">
<span class="check"></span>
<span class="check"></span>
<span class="control-label">'. __('Term') . ' 2</span>
</label>
</label>
</div>
<div>
<label class="b-checkbox checkbox" border="" style="padding-left: 8px;">
<input type="checkbox" checked value="option3">
<span class="check"></span>
<span class="check"></span>
<span class="control-label">'. __('Term') . ' 3</span>
</label>
</label>
</div>
</div>
</div>
<a class="add-new-term">'. __('View all') . '</a>
</div>
');
@ -100,25 +100,25 @@ class Taxonomy extends Metadata_Type {
public function render( $itemMetadata ){
$options = ( isset( $this->get_options()['options'] ) ) ? $this->get_options()['options'] : '';
return '<tainacan-selectbox
return '<tainacan-selectbox
options="' . $options . '"
metadatum_id ="'.$itemMetadata->get_metadatum()->get_id().'"
item_id="'.$itemMetadata->get_item()->get_id().'"
metadatum_id ="'.$itemMetadata->get_metadatum()->get_id().'"
item_id="'.$itemMetadata->get_item()->get_id().'"
value=\''.json_encode( $itemMetadata->get_value() ).'\'
name="'.$itemMetadata->get_metadatum()->get_name().'"></tainacan-selectbox>';
}
public function validate_options( Metadatum $metadatum) {
if ( !in_array($metadatum->get_status(), apply_filters('tainacan-status-require-validation', ['publish','future','private'])) )
return true;
if (empty($this->get_option('taxonomy_id')))
return ['taxonomy_id' => __('Please select a taxonomy', 'tainacan')];
$Tainacan_Metadata = Metadata::get_instance();
// Check taxonomy visibility
// Check taxonomy visibility
$status = get_post_status( $this->get_option('taxonomy_id') );
$post_status_obj = get_post_status_object($status);
if ( ! $post_status_obj->public ) {
@ -127,12 +127,21 @@ class Taxonomy extends Metadata_Type {
return ['taxonomy_id' => __('This metadatum can not be public because chosen taxonomy is not.', 'tainacan')];
}
}
if ( $this->get_option('allow_new_terms') == 'yes' ) {
$taxonomy = \tainacan_taxonomies()->fetch( (int) $this->get_option('taxonomy_id') );
if ( $taxonomy instanceof \Tainacan\Entities\Taxonomy ) {
if ( $taxonomy->get_allow_insert() != 'yes' ) {
return ['allow_new_terms' => __('This metadatum can not allow new terms to be created because the chosen taxonomy does not allow it.', 'tainacan')];
}
}
}
$taxonomy_metadata = $Tainacan_Metadata->fetch([
'collection_id' => $metadatum->get_collection_id(),
'metadata_type' => 'Tainacan\\Metadata_Types\\Taxonomy'
], 'OBJECT');
$taxonomy_metadata = array_map(function ($metadatum_map) {
$fto = $metadatum_map->get_metadata_type_object();
return [ $metadatum_map->get_id() => $fto->get_option('taxonomy_id') ];
@ -146,9 +155,9 @@ class Taxonomy extends Metadata_Type {
}
}
}
return true;
}
/**
@ -159,28 +168,28 @@ class Taxonomy extends Metadata_Type {
* @return bool Valid or not
*/
public function validate( 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;
$valid = true;
if ('no' === $this->get_option('allow_new_terms') || false === $this->get_option('allow_new_terms')) { //support legacy bug when it was saved as false
$terms = $item_metadata->get_value();
if (false === $terms)
return true;
if (!is_array($terms))
$terms = array($terms);
foreach ($terms as $term) {
if (is_object($term) && $term instanceof \Tainacan\Entities\Term) {
$term = $term->get_id();
}
// TODO term_exists is not fully reliable. Use $terms_repository->term_exists. see issue #159
if (!term_exists($term)) {
$valid = false;
@ -188,115 +197,115 @@ class Taxonomy extends Metadata_Type {
break;
}
}
}
return $valid;
}
/**
* Return the value of an Item_Metadata_Entity using a metadatum of this metadatum type as an html string
* @param Item_Metadata_Entity $item_metadata
* @param Item_Metadata_Entity $item_metadata
* @return string The HTML representation of the value, containing one or multiple terms, separated by comma, linked to term page
*/
public function get_value_as_html(Item_Metadata_Entity $item_metadata) {
$value = $item_metadata->get_value();
$return = '';
if ( $item_metadata->is_multiple() ) {
$count = 1;
$total = sizeof($value);
$prefix = $item_metadata->get_multivalue_prefix();
$suffix = $item_metadata->get_multivalue_suffix();
$separator = $item_metadata->get_multivalue_separator();
foreach ( $value as $term ) {
$count ++;
if ( is_integer($term) ) {
$term = \Tainacan\Repositories\Terms::get_instance()->fetch($term, $this->get_option('taxonomy_id'));
}
if ( $term instanceof \Tainacan\Entities\Term ) {
$return .= $prefix;
$return .= $this->get_term_hierarchy_html($term);
$return .= $suffix;
if ( $count <= $total ) {
$return .= $separator;
}
}
}
} else {
if ( $value instanceof \Tainacan\Entities\Term ) {
$return .= $this->get_term_hierarchy_html($value);
}
}
return $return;
}
private function get_term_hierarchy_html( \Tainacan\Entities\Term $term ) {
$terms = [];
$terms[] = $term->_toHtml();
while ($term->get_parent() > 0) {
$term = \Tainacan\Repositories\Terms::get_instance()->fetch( (int) $term->get_parent(), $term->get_taxonomy() );
$terms[] = $term->_toHtml();
}
$terms = \array_reverse($terms);
$glue = apply_filters('tainacan-terms-hierarchy-html-separator', '<span class="hierarchy-separator"> > </span>');
return \implode($glue, $terms);
}
public function _toArray() {
$array = parent::_toArray();
if ( isset($array['options']['taxonomy_id']) ) {
$array['options']['taxonomy'] = \Tainacan\Repositories\Taxonomies::get_instance()->get_db_identifier_by_id( $array['options']['taxonomy_id'] );
}
return $array;
}
/**
* Get related taxonomy object
* Get related taxonomy object
* @return \Tainacan\Entities\Taxonomy|false The Taxonomy object or false
*/
public function get_taxonomy() {
$taxonomy_id = $this->get_option('taxonomy_id');
if ( is_numeric($taxonomy_id) ) {
$taxonomy = \Tainacan\Repositories\Taxonomies::get_instance()->fetch( (int) $taxonomy_id );
if ( $taxonomy instanceof \Tainacan\Entities\Taxonomy ) {
return $taxonomy;
}
}
return false;
}
}
}

View File

@ -22,14 +22,6 @@ class Collections extends Repository {
return self::$instance;
}
/**
* Collections constructor.
*/
protected function __construct() {
parent::__construct();
add_filter( 'map_meta_cap', array( $this, 'map_meta_cap' ), 10, 4 );
}
/**
* {@inheritDoc}
* @see \Tainacan\Repositories\Repository::get_map()
@ -182,14 +174,6 @@ class Collections extends Repository {
//'validation' => v::numeric(),
'default' => ''
],
'moderators_ids' => [
'map' => 'meta_multi',
'title' => __( 'Moderators', 'tainacan' ),
'type' => 'array/object/string',
'items' => [ 'type' => 'array/string/integer/object' ],
'description' => __( 'Moderators of this collection', 'tainacan' ),
'validation' => ''
],
'_thumbnail_id' => [
'map' => 'meta',
'title' => __( 'Thumbnail', 'tainacan' ),
@ -255,7 +239,7 @@ class Collections extends Repository {
'can_export' => true,
/* Translators: The Collections slug - will be the URL for the collections archive */
'rewrite' => ['slug' => sanitize_title(_x('collections', 'Slug: the string that will be used to build the URL', 'tainacan'))],
'capability_type' => Entities\Collection::get_capability_type(),
'capabilities' => (array) $this->get_capabilities(),
'map_meta_cap' => true,
'supports' => [
'title',
@ -285,7 +269,6 @@ class Collections extends Repository {
$collection->register_collection_item_post_type();
flush_rewrite_rules( false ); // needed to activate items post type archive url
$this->update_moderators( $new_collection );
return $new_collection;
}
@ -301,7 +284,7 @@ class Collections extends Repository {
* 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
*
*
* If a number is passed to $args, it will return a \Tainacan\Entities\Collection object. But if the post is not found or
* does not match the entity post type, it will return an empty array
*
@ -361,9 +344,6 @@ class Collections extends Repository {
}
function pre_process( $collection ) {
// make sure we get the current value from database
$current_moderators = $this->get_mapped_property( $collection, 'moderators_ids' );
$this->current_moderators = is_array( $current_moderators ) ? $current_moderators : [];
$this->old_collection = $this->fetch( $collection->get_id() );
$this->old_core_title = $collection->get_core_title_metadatum();
@ -372,68 +352,20 @@ class Collections extends Repository {
}
function update_moderators( $collection ) {
$moderators = $collection->get_moderators_ids();
$deleted = array_diff( $this->current_moderators, $moderators );
$added = array_diff( $moderators, $this->current_moderators );
do_action( 'tainacan-add-collection-moderators', $collection, $added );
do_action( 'tainacan-remove-collection-moderators', $collection, $deleted );
}
function handle_core_metadata( $collection ) {
$Tainacan_Metadata = \Tainacan\Repositories\Metadata::get_instance();
$Tainacan_Metadata->register_core_metadata( $collection );
if ( $this->old_collection instanceof Entities\Collection &&
if ( $this->old_collection instanceof Entities\Collection &&
$this->old_collection->get_parent() != $collection->get_parent() &&
$this->old_core_title instanceof Entities\Metadatum &&
$this->old_core_description instanceof Entities\Metadatum
$this->old_core_description instanceof Entities\Metadatum
) {
$Tainacan_Metadata->maybe_update_core_metadata_meta_keys( $collection, $this->old_collection, $this->old_core_title, $this->old_core_description );
}
}
/**
* Filter to handle special permissions
*
* @see https://developer.wordpress.org/reference/hooks/map_meta_cap/
*
*/
public function map_meta_cap( $caps, $cap, $user_id, $args ) {
// Filters meta caps edit_tainacan-collection and check if user is moderator
if ( $cap == 'edit_post' && is_array( $args ) && array_key_exists( 0, $args ) ) { // edit_tainacan-colletion is mapped to edit_post
$entity = $args[0];
if ( is_numeric( $entity ) || $entity instanceof Entities\Collection ) {
if ( is_numeric( $entity ) ) {
$post = get_post( $entity );
if ( $post instanceof \WP_Post && $post->post_type == Entities\Collection::get_post_type() ) {
$entity = new Entities\Collection( $post );
}
}
if ( $entity instanceof Entities\Collection ) {
$moderators = $entity->get_moderators_ids();
if ( is_array( $moderators ) && in_array( $user_id, $moderators ) ) {
// if user is moderator, we clear the current caps
// (that might fave edit_others_posts) and leave only read, that everybody has
$collection_cpt = get_post_type_object( Entities\Collection::get_post_type() );
$caps = [ 'read' ];
}
}
}
}
return $caps;
}
}
}

View File

@ -138,7 +138,7 @@ class Filters extends Repository {
'rewrite' => true,
'map_meta_cap' => true,
'show_in_nav_menus' => false,
'capability_type' => Entities\Metadatum::get_capability_type(),
'capabilities' => (array) $this->get_capabilities(),
'supports' => [
'title',
'editor',
@ -351,41 +351,87 @@ class Filters extends Repository {
*
* @param Entities\Collection $collection
* @param array $args WP_Query args plus disabled_metadata
* @param string $output The desired output format (@see \Tainacan\Repositories\Repository::fetch_output() for possible values)
*
* @return array Entities\Metadatum
* @throws \Exception
*/
public function fetch_by_collection( Entities\Collection $collection, $args = [], $output = null ) {
public function fetch_by_collection( Entities\Collection $collection, $args = [] ) {
$collection_id = $collection->get_id();
//get parent collections
$parents = get_post_ancestors( $collection_id );
//insert the actual collection
$parents[] = $collection_id;
if ( is_numeric($collection_id) ) {
$parents[] = $collection_id;
}
//search for default metadatum
$parents[] = 'default';
$meta_query = array(
'key' => 'collection_id',
'value' => $parents,
'compare' => 'IN',
);
$results = [];
$args = array_merge( [
'parent' => 0
], $args );
$original_meta_q = isset( $args['meta_query'] ) ? $args['meta_query'] : [];
/**
* Since we introduced roles & capabalities management, we can not rely
* on WordPress behavior when handling default post status values.
* WordPress checks if the current user can read_priva_posts, but this is
* not enough for us. We have to handle this ourselves to mimic WordPress behavior
* considering how tainacan manages metadata capabilities
*/
if ( ! isset($args['post_status']) ) {
foreach ( $parents as $parent_id ) {
// Add public states.
$statuses = get_post_stati( array( 'public' => true ) );
$read_private_cap = 'default' == $parent_id ? 'tnc_rep_read_private_filters' : 'tnc_col_' . $parent_id . '_read_private_filters';
if ( current_user_can($read_private_cap) ) {
$statuses = array_merge( $statuses, get_post_stati( array( 'private' => true ) ) );
}
$args['post_status'] = $statuses;
if ( isset( $args['meta_query'] ) ) {
$args['meta_query'][] = $meta_query;
$meta_query = array(
'key' => 'collection_id',
'value' => $parent_id,
);
$args['meta_query'] = $original_meta_q;
$args['meta_query'][] = $meta_query;
//var_dump($args);
$results = array_merge($results, $this->fetch( $args, 'OBJECT' ));
}
} else {
$args['meta_query'] = array( $meta_query );
}
$meta_query = array(
'key' => 'collection_id',
'value' => $parents,
'compare' => 'IN',
);
$args = array_merge( [
'parent' => 0
], $args );
$args['meta_query'] = $original_meta_q;
$args['meta_query'][] = $meta_query;
$results = $this->fetch( $args, 'OBJECT' );
}
return $this->order_result(
$this->fetch( $args, $output ),
$results,
$collection,
isset( $args['include_disabled'] ) ? $args['include_disabled'] : false
);
@ -414,28 +460,72 @@ class Filters extends Repository {
$parents = get_post_ancestors( $collection_id );
//insert the actual collection
$parents[] = $collection_id;
if ( is_numeric($collection_id) ) {
$parents[] = $collection_id;
}
//search for default metadatum
$parents[] = 'default';
$meta_query = array(
'key' => 'collection_id',
'value' => $parents,
'compare' => 'IN',
);
$results = [];
$args = array_merge( [
'parent' => 0
], $args );
$original_meta_q = isset( $args['meta_query'] ) ? $args['meta_query'] : [];
/**
* Since we introduced roles & capabalities management, we can not rely
* on WordPress behavior when handling default post status values.
* WordPress checks if the current user can read_priva_posts, but this is
* not enough for us. We have to handle this ourselves to mimic WordPress behavior
* considering how tainacan manages metadata capabilities
*/
if ( ! isset($args['post_status']) ) {
foreach ( $parents as $parent_id ) {
// Add public states.
$statuses = get_post_stati( array( 'public' => true ) );
$read_private_cap = 'default' == $parent_id ? 'tnc_rep_read_private_filters' : 'tnc_col_' . $parent_id . '_read_private_filters';
if ( current_user_can($read_private_cap) ) {
$statuses = array_merge( $statuses, get_post_stati( array( 'private' => true ) ) );
}
$args['post_status'] = $statuses;
if ( isset( $args['meta_query'] ) ) {
$meta_query = array(
'key' => 'collection_id',
'value' => $parent_id,
);
$args['meta_query'] = $original_meta_q;
$args['meta_query'][] = $meta_query;
$results = array_merge($results, $this->fetch_ids( $args ));
}
} else {
$meta_query = array(
'key' => 'collection_id',
'value' => $parents,
'compare' => 'IN',
);
$args = array_merge( [
'parent' => 0
], $args );
$args['meta_query'] = $original_meta_q;
$args['meta_query'][] = $meta_query;
} elseif ( is_array( $args ) ) {
$args['meta_query'] = array( $meta_query );
$results = $this->fetch_ids( $args );
}
return $this->fetch_ids( $args );
return $results;
}
/**

View File

@ -229,7 +229,7 @@ class Item_Metadata extends Repository {
return [];
}
$meta_list = $Tainacan_Metadata->fetch_by_collection( $collection, $args, 'OBJECT' );
$meta_list = $Tainacan_Metadata->fetch_by_collection( $collection, $args );
$return = [];

View File

@ -13,6 +13,9 @@ class Items extends Repository {
private static $instance = null;
// temporary variable used to filter items query
private $fetching_from_collections = [];
public static function get_instance() {
if ( ! isset( self::$instance ) ) {
self::$instance = new self();
@ -23,8 +26,6 @@ class Items extends Repository {
protected function __construct() {
parent::__construct();
add_filter( 'posts_where', array( &$this, 'title_in_posts_where' ), 10, 2 );
add_filter( 'posts_where', array( &$this, 'content_in_posts_where' ), 10, 2 );
add_filter( 'comments_open', [$this, 'hook_comments_open'], 10, 2);
add_action( 'tainacan-api-item-updated', array( &$this, 'hook_api_updated_item' ), 10, 2 );
add_filter( 'map_meta_cap', array( $this, 'map_meta_cap' ), 10, 4 );
@ -227,8 +228,20 @@ class Items extends Repository {
}
$no_collection_set = false;
/**
* We can not user $collections->fetch() here because facets
* filter wp_query to just return the query and not the results
* See Repositories\Metadata::fetch_all_metadatum_values()
*
* Conceptually, it's a good idea that a fetch() method like this only
* produces one WP_Query request
*/
if ( empty( $collections ) ) {
$no_collection_set = true;
$post_types = get_post_types();
$collections = array_map( function($el) use ($Tainacan_Collections) {
if ( $id = $Tainacan_Collections->get_id_by_db_identifier($el) ) {
return $id;
@ -258,15 +271,13 @@ class Items extends Repository {
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 no specific collection was queried, we will fetch
* mimick WordPress behavior and return only collections current user can read
*/
if ( ! isset( $args['post_status'] ) ) {
if ( $no_collection_set) {
$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 {
@ -275,13 +286,12 @@ class Items extends Repository {
}
$this->fetching_from_collections = $collections_objects;
if ( empty( $cpt ) ) {
$cpt[] = 'please-return-nothing';
}
//TODO: get collection order and order by options
$args = $this->parse_fetch_args( $args );
$args['post_type'] = $cpt;
@ -305,8 +315,18 @@ class Items extends Repository {
$args = apply_filters( 'tainacan_fetch_args', $args, 'items' );
$should_filter = is_user_logged_in() && ! isset($args['post_status']) && sizeof($cpt) > 1;
if ( $should_filter ) {
add_filter('posts_where', [$this, '_filter_where'], 10, 2);
}
$wp_query = new \WP_Query( $args );
if ( $should_filter ) {
remove_filter('posts_where', [$this, '_filter_where']);
}
return $this->fetch_output( $wp_query, $output );
}
@ -332,67 +352,68 @@ class Items extends Repository {
return $this->fetch( $args, $collections )->get_posts();
}
/**
* When querying posts without explictly asking for a post_status, WordPress will
* check current user capabilities and return posts user can read based on read_private_posts capabilities.
*
* However, when querying for multiple post types, WordPress does not handle a per post type permission check. It either
* return only public posts or all private posts if read_private_multiple_post_types cap is present.
*
* This hook fixes this, modifying the where clause.
*
* @param string $where the wehere clause
* @param \WP_Query $wp_query
* @return string The modified where clause
*/
public function _filter_where($where, $wp_query) {
global $wpdb;
$clauses = [];
$user_id = get_current_user_id();
foreach ($this->fetching_from_collections as $collection) {
$read_private_cap = $collection->get_items_capabilities()->read_private_posts;
$clause = '(';
$clause .= "{$wpdb->posts}.post_type = '{$collection->get_db_identifier()}' AND (";
// public status
$public_states = get_post_stati( array( 'public' => true ) );
$status_clause = [];
foreach ( (array) $public_states as $state ) {
$status_clause[] = "{$wpdb->posts}.post_status = '$state'";
}
$clause .= implode(' OR ', $status_clause);
// private statuses
$private_states = get_post_stati( array( 'private' => true ) );
foreach ( (array) $private_states as $state ) {
$clause .= current_user_can( $read_private_cap ) ? " OR {$wpdb->posts}.post_status = '$state'" : " OR {$wpdb->posts}.post_author = $user_id AND {$wpdb->posts}.post_status = '$state'";
}
$clause .= ')';
$clause .= ')';
$clauses[] = $clause;
}
$final = '(' . implode(' OR ', $clauses) . ')';
// find post_type and post_status queries. They always come one right after another
$regexp = '/(' . $wpdb->posts . '\.post_type.+AND \(' . $wpdb->posts . '\.post_status.+\))/';
return \preg_replace($regexp, $final, $where);
}
public function update( $object, $new_values = null ) {
return $this->insert( $object );
}
/**
* allow wp query filter post by array of titles
*
* @param $where
* @param $wp_query
*
* @return string
*/
public function title_in_posts_where( $where, $wp_query ) {
global $wpdb;
if ( $post_title_in = $wp_query->get( 'post_title_in' ) ) {
if ( is_array( $post_title_in ) && isset( $post_title_in['value'] ) ) {
$quotes = [];
foreach ( $post_title_in['value'] as $title ) {
$quotes[] = " $wpdb->posts.post_title LIKE '%" . esc_sql( $wpdb->esc_like( $title ) ) . "%'";
}
}
// retrieve only posts for the specified collection and status
$type = " $wpdb->posts.post_type = '" . $wp_query->get( 'post_type' )[0] . "' ";
$status = " ( $wpdb->posts.post_status = 'publish' OR $wpdb->posts.post_status = 'private') ";
$where .= ' ' . $post_title_in['relation'] . '( ( ' . implode( ' OR ', $quotes ) . ' ) AND ' .
$status . ' AND ' . $type . ' )';
}
return $where;
}
/**
* allow wp query filter post by array of content
*
* @param $where
* @param $wp_query
*
* @return string
*/
public function content_in_posts_where( $where, $wp_query ) {
global $wpdb;
if ( $post_content_in = $wp_query->get( 'post_content_in' ) ) {
if ( is_array( $post_content_in ) && isset( $post_content_in['value'] ) ) {
$quotes = [];
foreach ( $post_content_in['value'] as $title ) {
$quotes[] = " $wpdb->posts.post_content LIKE '%" . esc_sql( $wpdb->esc_like( $title ) ) . "%'";
}
}
// retrieve only posts for the specified collection and status
$type = " $wpdb->posts.post_type = '" . $wp_query->get( 'post_type' )[0] . "' ";
$status = " ( $wpdb->posts.post_status = 'publish' OR $wpdb->posts.post_status = 'private') ";
$where .= ' ' . $post_content_in['relation'] . '( ( ' . implode( ' OR ', $quotes ) . ' ) AND ' .
$status . ' AND ' . $type . ' )';
}
return $where;
}
/**
* generate a content of document to index.
*
@ -522,8 +543,7 @@ class Items extends Repository {
*/
public function map_meta_cap( $caps, $cap, $user_id, $args ) {
// Filters meta caps edit_tainacan-collection and check if user is moderator
// Even if the item is public, user must have read_private_posts if the collection is private
if ( $cap == 'read_post' && is_array( $args ) && array_key_exists( 0, $args ) ) {
$entity = $args[0];
@ -541,7 +561,7 @@ class Items extends Repository {
if ( $collection instanceof Entities\Collection ) {
$status_obj = get_post_status_object( $collection->get_status() );
if ( ! $status_obj->public ) {
$caps[] = $entity->get_capabilities()->read_private_posts;
$caps[] = $collection->get_capabilities()->read_private_posts;
}
}
@ -562,38 +582,38 @@ class Items extends Repository {
* @return boolean
* @throws \Exception
*/
public function can_read( Entities\Entity $entity, $user = null ) {
// public function can_read( Entities\Entity $entity, $user = null ) {
if ( ! $entity instanceof Entities\Item) {
throw new InvalidArgumentException('Items::can_read() expects an Item entity as the first parameter');
}
// if ( ! $entity instanceof Entities\Item) {
// throw new InvalidArgumentException('Items::can_read() expects an Item entity as the first parameter');
// }
// can read the item looking only to the item
$can_read = parent::can_read($entity, $user);
// // can read the item looking only to the item
// $can_read = parent::can_read($entity, $user);
if ( $can_read ) {
$collection = $entity->get_collection();
$status_obj = get_post_status_object( $collection->get_status() );
// if ( $can_read ) {
// $collection = $entity->get_collection();
// $status_obj = get_post_status_object( $collection->get_status() );
if ( $status_obj->public ) {
return $can_read;
}
}
// if ( $status_obj->public ) {
// return $can_read;
// }
// }
if ( is_null($user) ) {
$user = get_current_user_id();
}
// if ( is_null($user) ) {
// $user = get_current_user_id();
// }
if ( ! $user ) {
return false;
} elseif ( is_object( $user ) ) {
$user = $user->ID;
}
// if ( ! $user ) {
// return false;
// } elseif ( is_object( $user ) ) {
// $user = $user->ID;
// }
$entity_cap = $entity->get_capabilities();
// $entity_cap = $entity->get_capabilities();
return user_can( $user, $entity_cap->read_private_posts, $entity->get_id() );
// return user_can( $user, $entity_cap->read_private_posts, $entity->get_id() );
}
// }
}

View File

@ -164,7 +164,7 @@ class Logs extends Repository {
'can_export' => true,
'rewrite' => true,
'map_meta_cap' => true,
'capability_type' => Entities\Log::get_capability_type(),
'capabilities' => (array) $this->get_capabilities(),
'supports' => [
'title',
'editor',

View File

@ -40,6 +40,8 @@ class Metadata extends Repository {
add_filter( 'pre_trash_post', array( &$this, 'disable_delete_core_metadata' ), 10, 2 );
add_filter( 'pre_delete_post', array( &$this, 'force_delete_core_metadata' ), 10, 3 );
add_action('tainacan-insert-tainacan-taxonomy', [$this, 'hook_taxonomies_saved_as_private']);
}
/**
@ -238,7 +240,7 @@ class Metadata extends Repository {
'rewrite' => true,
'map_meta_cap' => true,
'show_in_nav_menus' => false,
'capability_type' => Entities\Metadatum::get_capability_type(),
'capabilities' => (array) $this->get_capabilities(),
'supports' => [
'title',
'editor',
@ -364,41 +366,87 @@ class Metadata extends Repository {
*
* @param Entities\Collection $collection
* @param array $args WP_Query args plus disabled_metadata
* @param string $output The desired output format (@see \Tainacan\Repositories\Repository::fetch_output() for possible values)
*
* @return array Entities\Metadatum
* @throws \Exception
*/
public function fetch_by_collection( Entities\Collection $collection, $args = [], $output = null ) {
public function fetch_by_collection( Entities\Collection $collection, $args = [] ) {
$collection_id = $collection->get_id();
//get parent collections
$parents = get_post_ancestors( $collection_id );
//insert the actual collection
$parents[] = $collection_id;
if ( is_numeric($collection_id) ) {
$parents[] = $collection_id;
}
//search for default metadatum
$parents[] = $this->get_default_metadata_attribute();
$meta_query = array(
'key' => 'collection_id',
'value' => $parents,
'compare' => 'IN',
);
$results = [];
$args = array_merge( [
'parent' => 0
], $args );
if ( isset( $args['meta_query'] ) ) {
$original_meta_q = isset( $args['meta_query'] ) ? $args['meta_query'] : [];
/**
* Since we introduced roles & capabalities management, we can not rely
* on WordPress behavior when handling default post status values.
* WordPress checks if the current user can read_priva_posts, but this is
* not enough for us. We have to handle this ourselves to mimic WordPress behavior
* considering how tainacan manages metadata capabilities
*/
if ( ! isset($args['post_status']) ) {
foreach ( $parents as $parent_id ) {
// Add public states.
$statuses = get_post_stati( array( 'public' => true ) );
$read_private_cap = $this->get_default_metadata_attribute() == $parent_id ? 'tnc_rep_read_private_metadata' : 'tnc_col_' . $parent_id . '_read_private_metadata';
if ( current_user_can($read_private_cap) ) {
$statuses = array_merge( $statuses, get_post_stati( array( 'private' => true ) ) );
}
$args['post_status'] = $statuses;
$meta_query = array(
'key' => 'collection_id',
'value' => $parent_id,
);
$args['meta_query'] = $original_meta_q;
$args['meta_query'][] = $meta_query;
//var_dump($args);
$results = array_merge($results, $this->fetch( $args, 'OBJECT' ));
}
} else {
$meta_query = array(
'key' => 'collection_id',
'value' => $parents,
'compare' => 'IN',
);
$args = array_merge( [
'parent' => 0
], $args );
$args['meta_query'] = $original_meta_q;
$args['meta_query'][] = $meta_query;
} elseif ( is_array( $args ) ) {
$args['meta_query'] = array( $meta_query );
$results = $this->fetch( $args, 'OBJECT' );
}
return $this->order_result(
$this->fetch( $args, $output ),
$results,
$collection,
isset( $args['include_disabled'] ) ? $args['include_disabled'] : false
);
@ -427,28 +475,72 @@ class Metadata extends Repository {
$parents = get_post_ancestors( $collection_id );
//insert the actual collection
$parents[] = $collection_id;
if ( is_numeric($collection_id) ) {
$parents[] = $collection_id;
}
//search for default metadatum
$parents[] = $this->get_default_metadata_attribute();
$meta_query = array(
'key' => 'collection_id',
'value' => $parents,
'compare' => 'IN',
);
$results = [];
$args = array_merge( [
'parent' => 0
], $args );
if ( isset( $args['meta_query'] ) ) {
$original_meta_q = isset( $args['meta_query'] ) ? $args['meta_query'] : [];
/**
* Since we introduced roles & capabalities management, we can not rely
* on WordPress behavior when handling default post status values.
* WordPress checks if the current user can read_priva_posts, but this is
* not enough for us. We have to handle this ourselves to mimic WordPress behavior
* considering how tainacan manages metadata capabilities
*/
if ( ! isset($args['post_status']) ) {
foreach ( $parents as $parent_id ) {
// Add public states.
$statuses = get_post_stati( array( 'public' => true ) );
$read_private_cap = $this->get_default_metadata_attribute() == $parent_id ? 'tnc_rep_read_private_metadata' : 'tnc_col_' . $parent_id . '_read_private_metadata';
if ( current_user_can($read_private_cap) ) {
$statuses = array_merge( $statuses, get_post_stati( array( 'private' => true ) ) );
}
$args['post_status'] = $statuses;
$meta_query = array(
'key' => 'collection_id',
'value' => $parent_id,
);
$args['meta_query'] = $original_meta_q;
$args['meta_query'][] = $meta_query;
$results = array_merge($results, $this->fetch_ids( $args ));
}
} else {
$meta_query = array(
'key' => 'collection_id',
'value' => $parents,
'compare' => 'IN',
);
$args = array_merge( [
'parent' => 0
], $args );
$args['meta_query'] = $original_meta_q;
$args['meta_query'][] = $meta_query;
} elseif ( is_array( $args ) ) {
$args['meta_query'] = array( $meta_query );
$results = $this->fetch_ids( $args );
}
return $this->fetch_ids( $args );
return $results;
}
/**
@ -456,11 +548,11 @@ class Metadata extends Repository {
* metadata not ordinated appear on the end of the list
*
*
* @param \WP_Query|array $result Response from method fetch
* @param array $result Response from method fetch_by_collection
* @param Entities\Collection $collection
* @param bool $include_disabled Wether to include disabled metadata in the results or not
*
* @return array or WP_Query ordinate
* @return array
*/
public function order_result( $result, Entities\Collection $collection, $include_disabled = false ) {
$order = $collection->get_metadata_order();
@ -495,29 +587,8 @@ class Metadata extends Repository {
$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;
@ -609,8 +680,8 @@ class Metadata extends Repository {
"UPDATE $wpdb->postmeta
SET meta_key = %s
WHERE meta_key = %s AND post_id IN (
SELECT ID
FROM $wpdb->posts
SELECT ID
FROM $wpdb->posts
WHERE post_type = %s
)", $new_title_metadatum->get_id(), $old_title_metadatum->get_id(), $item_post_type
);
@ -621,8 +692,8 @@ class Metadata extends Repository {
"UPDATE $wpdb->postmeta
SET meta_key = %s
WHERE meta_key = %s AND post_id IN (
SELECT ID
FROM $wpdb->posts
SELECT ID
FROM $wpdb->posts
WHERE post_type = %s
)", $new_description_metadatum->get_id(), $old_description_metadatum->get_id(), $item_post_type
);
@ -639,7 +710,7 @@ class Metadata extends Repository {
* @return array
*/
private function get_data_core_metadata( Entities\Collection $collection ) {
return $data_core_metadata = [
'core_description' => [
'name' => 'Description',
@ -706,11 +777,11 @@ class Metadata extends Repository {
* @throws \Exception
*/
public function disable_delete_core_metadata( $before, $post ) {
if ( Entities\Metadatum::get_post_type() != $post->post_type ) {
return null;
}
$metadatum = $this->fetch( $post->ID );
if ( $metadatum && in_array( $metadatum->get_metadata_type(), $this->core_metadata ) && is_numeric( $metadatum->get_collection_id() ) ) {
@ -730,11 +801,11 @@ class Metadata extends Repository {
* @internal param The $post_id post ID which is deleting
*/
public function force_delete_core_metadata( $before, $post, $force_delete ) {
if ( Entities\Metadatum::get_post_type() != $post->post_type ) {
return null;
}
$metadatum = $this->fetch( $post->ID );
if ( $metadatum && in_array( $metadatum->get_metadata_type(), $this->core_metadata ) && is_numeric( $metadatum->get_collection_id() ) ) {
@ -761,7 +832,7 @@ class Metadata extends Repository {
]
],
'include_disabled' => true
], 'OBJECT' );
] );
}
@ -783,7 +854,7 @@ class Metadata extends Repository {
]
],
'posts_per_page' => 1
], 'OBJECT' );
] );
if ( is_array( $results ) && sizeof( $results ) == 1 && $results[0] instanceof \Tainacan\Entities\Metadatum ) {
return $results[0];
@ -810,7 +881,7 @@ class Metadata extends Repository {
]
],
'posts_per_page' => 1
], 'OBJECT' );
] );
if ( is_array( $results ) && sizeof( $results ) == 1 && $results[0] instanceof \Tainacan\Entities\Metadatum ) {
return $results[0];
@ -846,7 +917,7 @@ class Metadata extends Repository {
/**
* Return all possible values for a metadatum
* Return all possible values for a metadatum
*
* Each metadata is a label with the metadatum name and the value.
*
@ -856,32 +927,32 @@ class Metadata extends Repository {
* @param int $metadatum_id The ID of the metadata to fetch values from
* @param array|string $args {
* Optional. Array or string of arguments.
*
*
* @type mixed $collection_id The collection ID you want to consider or null for all collections. If a collectoin is set
* then only values applied to items in this collection will be returned
*
*
* @type int $number The number of values to return (for pagination). Default empty (unlimited)
*
*
* @type int $offset The offset (for pagination). Default 0
*
*
* @type array|bool $items_filter Array in the same format used in @see \Tainacan\Repositories\Items::fetch(). It will filter the results to only return values used in the items inside this criteria. If false, it will return all values, even unused ones. Defatul [] (all items)
*
*
* @type array $include Array if ids to be included in the result. Default [] (nothing)
*
* @type array $search String to search. It will only return values that has this string. Default '' (nothing)
*
* @type array $parent_id Used by taxonomy metadata. The ID of the parent term to retrieve terms from. Default 0
* @type array $parent_id Used by taxonomy metadata. The ID of the parent term to retrieve terms from. Default 0
*
* @type bool $count_items Include the count of items that can be found in each value (uses $items_filter as well). Default false
*
* @type string $last_term The last term returned when using a elasticsearch for calculates the facet.
*
*
* }
*
*
* @return array Array with the total number of values found. The total number of pages with the current number and the results with id and label for each value. Terms also include parent, taxonomy and number of children.
*/
public function fetch_all_metadatum_values( $metadatum_id, $args = [] ) {
$defaults = array(
'collection_id' => null,
'search' => '',
@ -894,34 +965,34 @@ class Metadata extends Repository {
'last_term' => ''
);
$args = wp_parse_args($args, $defaults);
global $wpdb;
$itemsRepo = Items::get_instance();
$metadataRepo = Metadata::get_instance();
$metadatum = $metadataRepo->fetch($metadatum_id);
$metadatum_type = $metadatum->get_metadata_type();
$metadatum_options = $metadatum->get_metadata_type_options();
if ( $metadatum_type === 'Tainacan\Metadata_Types\Taxonomy' ) {
$taxonomy_id = $metadatum_options['taxonomy_id'];
$taxonomy_slug = Taxonomies::get_instance()->get_db_identifier_by_id($taxonomy_id);
}
$items_query = false;
if ( false !== $args['items_filter'] && is_array($args['items_filter']) ) {
$args['items_filter']['fields'] = 'ids';
unset($args['items_filter']['paged']);
unset($args['items_filter']['offset']);
unset($args['items_filter']['perpage']);
$args['items_filter']['nopaging'] = 1;
// When filtering the items, we should consider only other metadata, and ignore current metadatum
// This is because the relation between values from the same metadatum when filtering item is OR,
// When filtering the items, we should consider only other metadata, and ignore current metadatum
// This is because the relation between values from the same metadatum when filtering item is OR,
// so when you filter items by one value of a metadatum you dont want to exclude all the other possibilities for that meta.
// Only values of all other filters (facets) are reduced.
if ( $metadatum_type == 'Tainacan\Metadata_Types\Taxonomy' && isset($args['items_filter']['tax_query']) && is_array($args['items_filter']['tax_query']) ) {
@ -935,12 +1006,12 @@ class Metadata extends Repository {
}
}
$filter = apply_filters('tainacan-fetch-all-metadatum-values', null, $metadatum, $args);
if ($filter !== null) {
return $filter;
}
//////////////////////////////////////////
// Get the query for current items
// this avoids wp_query to run the query. We just want to build the query
@ -954,12 +1025,12 @@ class Metadata extends Repository {
////////////////////////////////////////////
////////////////////////////////////////////
$pagination = '';
if ( $args['offset'] >= 0 && $args['number'] >= 1 ) {
$pagination = $wpdb->prepare( "LIMIT %d,%d", (int) $args['offset'], (int) $args['number'] );
}
$search_q = '';
$search = trim($args['search']);
if (!empty($search)) {
@ -970,24 +1041,24 @@ class Metadata extends Repository {
} else {
$search_q = $wpdb->prepare("AND meta_value LIKE %s", '%' . $search . '%');
}
}
if ( $metadatum_type === 'Tainacan\Metadata_Types\Taxonomy' ) {
if ($items_query) {
$check_hierarchy_q = $wpdb->prepare("SELECT term_id FROM $wpdb->term_taxonomy WHERE taxonomy = %s AND parent > 0 LIMIT 1", $taxonomy_slug);
$has_hierarchy = ! is_null($wpdb->get_var($check_hierarchy_q));
if ( ! $has_hierarchy ) {
$base_query = $wpdb->prepare("FROM $wpdb->term_relationships tr
$base_query = $wpdb->prepare("FROM $wpdb->term_relationships tr
INNER JOIN $wpdb->term_taxonomy tt ON tr.term_taxonomy_id = tt.term_taxonomy_id
INNER JOIN $wpdb->terms t ON tt.term_id = t.term_id
WHERE
INNER JOIN $wpdb->terms t ON tt.term_id = t.term_id
WHERE
tt.parent = %d AND
tr.object_id IN ($items_query) AND
tr.object_id IN ($items_query) AND
tt.taxonomy = %s
$search_q
ORDER BY t.name ASC
@ -995,46 +1066,46 @@ class Metadata extends Repository {
$args['parent_id'],
$taxonomy_slug
);
$query = "SELECT DISTINCT t.name, t.term_id, tt.term_taxonomy_id, tt.parent $base_query $pagination";
$total_query = "SELECT COUNT(DISTINCT tt.term_taxonomy_id) $base_query";
$total = $wpdb->get_var($total_query);
$results = $wpdb->get_results($query);
} else {
$base_query = $wpdb->prepare("
SELECT DISTINCT t.term_id, t.name, tt.parent, coalesce(tr.term_taxonomy_id, 0) as have_items
FROM
$wpdb->terms t INNER JOIN $wpdb->term_taxonomy tt ON t.term_id = tt.term_id
FROM
$wpdb->terms t INNER JOIN $wpdb->term_taxonomy tt ON t.term_id = tt.term_id
LEFT JOIN $wpdb->term_relationships tr ON tt.term_taxonomy_id = tr.term_taxonomy_id AND tr.object_id IN ($items_query)
WHERE tt.taxonomy = %s ORDER BY t.name ASC", $taxonomy_slug
);
$all_hierarchy = $wpdb->get_results($base_query);
if (empty($search)) {
$results = $this->_process_terms_tree($all_hierarchy, $args['parent_id'], 'parent');
} else {
$results = $this->_process_terms_tree($all_hierarchy, $search, 'name');
}
$total = count($results);
if ( $args['offset'] >= 0 && $args['number'] >= 1 ) {
$results = array_slice($results, (int) $args['offset'], (int) $args['number']);
}
}
} else {
$parent_q = $wpdb->prepare("AND tt.parent = %d", $args['parent_id']);
if ($search_q) {
$parent_q = '';
}
$base_query = $wpdb->prepare("FROM $wpdb->term_taxonomy tt
INNER JOIN $wpdb->terms t ON tt.term_id = t.term_id
$base_query = $wpdb->prepare("FROM $wpdb->term_taxonomy tt
INNER JOIN $wpdb->terms t ON tt.term_id = t.term_id
WHERE 1=1
$parent_q
AND tt.taxonomy = %s
@ -1043,59 +1114,59 @@ class Metadata extends Repository {
",
$taxonomy_slug
);
$query = "SELECT DISTINCT t.name, t.term_id, tt.term_taxonomy_id, tt.parent $base_query $pagination";
$total_query = "SELECT COUNT(DISTINCT tt.term_taxonomy_id) $base_query";
$total = $wpdb->get_var($total_query);
$results = $wpdb->get_results($query);
}
// add selected to the result
if ( !empty($args['include']) ) {
if ( is_array($args['include']) && !empty($args['include']) ) {
// protect sql
$args['include'] = array_map(function($t) { return (int) $t; }, $args['include']);
$include_ids = implode(',', $args['include']);
$query_to_include = "SELECT DISTINCT t.name, t.term_id, tt.term_taxonomy_id, tt.parent FROM $wpdb->term_taxonomy tt
INNER JOIN $wpdb->terms t ON tt.term_id = t.term_id
WHERE
$query_to_include = "SELECT DISTINCT t.name, t.term_id, tt.term_taxonomy_id, tt.parent FROM $wpdb->term_taxonomy tt
INNER JOIN $wpdb->terms t ON tt.term_id = t.term_id
WHERE
t.term_id IN ($include_ids)";
$to_include = $wpdb->get_results($query_to_include);
// remove terms that will be included at the begining
$results = array_filter($results, function($t) use($args) { return !in_array($t->term_id, $args['include']); });
$results = array_merge($to_include, $results);
}
}
$number = ctype_digit($args['number']) && $args['number'] >=1 ? $args['number'] : $total;
if( $number < 1){
$pages = 1;
} else {
$pages = ceil( $total / $number );
}
$values = [];
foreach ($results as $r) {
$count_query = $wpdb->prepare("SELECT COUNT(term_id) FROM $wpdb->term_taxonomy WHERE parent = %d", $r->term_id);
$total_children = $wpdb->get_var($count_query);
$label = wp_specialchars_decode($r->name);
$total_items = null;
if ( $args['count_items'] ) {
$count_items_query = $args['items_filter'];
$count_items_query['posts_per_page'] = 1;
@ -1108,11 +1179,11 @@ class Metadata extends Repository {
];
$count_items_results = $itemsRepo->fetch($count_items_query, $args['collection_id']);
$total_items = $count_items_results->found_posts;
//$label .= " ($total_items)";
}
$values[] = [
'value' => $r->term_id,
'label' => $label,
@ -1123,22 +1194,22 @@ class Metadata extends Repository {
'total_items' => $total_items,
'type' => 'Taxonomy'
];
}
} else {
$items_query_clause = '';
if ($items_query) {
$items_query_clause = "AND post_id IN($items_query)";
}
$base_query = $wpdb->prepare( "FROM $wpdb->postmeta WHERE meta_key = %s $search_q $items_query_clause ORDER BY meta_value", $metadatum_id );
$total_query = "SELECT COUNT(DISTINCT meta_value) $base_query";
$query = "SELECT DISTINCT meta_value $base_query $pagination";
$results = $wpdb->get_col($query);
$total = $wpdb->get_var($total_query);
$number = ctype_digit($args['number']) && $args['number'] >=1 ? $args['number'] : $total;
@ -1147,17 +1218,17 @@ class Metadata extends Repository {
} else {
$pages = ceil( $total / $number );
}
// add selected to the result
if ( !empty($args['include']) ) {
if ( is_array($args['include']) ) {
$results = array_unique( array_merge($args['include'], $results) );
}
}
$values = [];
foreach ($results as $r) {
$label = $r;
if ( $metadatum_type === 'Tainacan\Metadata_Types\Relationship' ) {
@ -1167,9 +1238,9 @@ class Metadata extends Repository {
}
$label = $_post->post_title;
}
$total_items = null;
if ( $args['count_items'] ) {
$count_items_query = $args['items_filter'];
$count_items_query['posts_per_page'] = 1;
@ -1182,50 +1253,50 @@ class Metadata extends Repository {
];
$count_items_results = $itemsRepo->fetch($count_items_query, $args['collection_id']);
$total_items = $count_items_results->found_posts;
//$label .= " ($total_items)";
}
$values[] = [
'label' => $label,
'value' => $r,
'total_items' => $total_items,
'type' => 'Text'
];
}
}
return [
'total' => $total,
'pages' => $pages,
'values' => $values,
'last_term' => $args['last_term']
];
}
/**
* This method processes the result of the query for all terms in a taxonomy done in get_all_metadatum_values()
* It efficiently runs through all the terms and checks what terms with a given $parent have items in itself or any of
* It efficiently runs through all the terms and checks what terms with a given $parent have items in itself or any of
* its descendants, keeping the order they originally came.
*
* It returns an array with the term objects with the given $parent that have items considering items in its descendants. The objects are
* in the same format they came, as expected by the rest of the method.
*
* It returns an array with the term objects with the given $parent that have items considering items in its descendants. The objects are
* in the same format they came, as expected by the rest of the method.
*
* This method is public only for tests purposes, it should not be used anywhere else
*/
public function _process_terms_tree($tree, $search_value, $search_type='parent') {
$h_map = []; // all terms will mapped to this array
$h_map = []; // all terms will mapped to this array
$results = []; // terms that match search criteria will be copied to this array
foreach ( $tree as $h ) {
// if current term comes with have_items = 1 from the database
// if current term comes with have_items = 1 from the database
// or, if it was temporarily added by its child that had have_items = 1:
if ( $h->have_items > 0 || ( isset($h_map[$h->term_id]) && $h_map[$h->term_id]->have_items > 0 ) ) {
// in the case of a parent that was temporarily added by a child, mark it as having items as well
$h->have_items = 1;
$h_map[$h->term_id] = $h; // send it to the map array, overriding temporary item if existed
@ -1235,25 +1306,25 @@ class Metadata extends Repository {
($search_type == 'name' && strpos(strtolower($h->name), strtolower($search_value)) !== false)) {
$results[$h->term_id] = $h;
}
// Now that we know this ter have_items. Lets climb the tree all the way up
// Now that we know this ter have_items. Lets climb the tree all the way up
// marking all parents with have_items = 1
$_parent = $h->parent;
// If parent was not added to the map array yet
// Lets add a temporary entry
if ( $h->parent > 0 && !isset($h_map[$_parent]) ) {
$h_map[$_parent] = (object)['have_items' => 1];
}
// Now lets loop thorough the map array until I check all my parents
while( isset($h_map[$_parent]) && $h_map[$_parent]->have_items != 1 ) {
// If my parent was added before, but marked with have_items = 0
// Lets set it to 1
$h_map[$_parent]->have_items = 1;
// If my parent is a whole object, and not a temporary one
if ( isset($h_map[$_parent]->parent) ) {
// if parent matches search criteira, add to results
@ -1267,21 +1338,21 @@ class Metadata extends Repository {
// Quit loop. We have reached as high as we could in the tree
$_parent = 0;
}
}
} else {
// if current term have_items = 0
// add it to the map
$h_map[$h->term_id] = $h;
// if parent was not mapped yet, create a temporary entry for him
if ( $h->parent > 0 && !isset($h_map[$h->parent]) ) {
$h_map[$h->parent] = (object)['have_items' => $h->have_items];
}
// if item matches search criteria, add it to the results
if(($search_type == 'parent' && $h->parent == $search_value) ||
($search_type == 'name' && $h->have_items > 0 && strpos(strtolower($h->name), strtolower($search_value)) !== false)) {
@ -1290,7 +1361,7 @@ class Metadata extends Repository {
}
}
// Results have all terms that matches search criteria. Now we unset those who dont have items
// and set it back to incremental keys]
// we could have sent to $results only those with items, but doing that we would not preserve their order
@ -1300,9 +1371,9 @@ class Metadata extends Repository {
}
return $return;
}, []);
return $results;
}
/**
@ -1352,10 +1423,10 @@ class Metadata extends Repository {
}
}
/**
* Creates an index with the exploded values of metadata_type_options array. Each option is prefixed with '_option_'
* This is useful to allow metadata to be queried based on a specific value of a metadata type option.
* This is useful to allow metadata to be queried based on a specific value of a metadata type option.
* For example, fetch all taxonomy metadata which the taxonomy_id metadata type option is equal to 4
*
* $metadata_repository->fetch([
@ -1366,11 +1437,11 @@ class Metadata extends Repository {
* ]
* ]
* ])
*
*
* @var Entities\Metadatum $metadatum
*/
private function update_metadata_type_index( Entities\Metadatum $metadatum ) {
$options = $this->get_mapped_property($metadatum, 'metadata_type_options');
if (!is_array($options)) {
return;
@ -1378,9 +1449,9 @@ class Metadata extends Repository {
foreach ($options as $option => $value) {
update_post_meta($metadatum->get_id(), '_option_' . $option, $value);
}
}
/**
* @inheritDoc
*/
@ -1402,4 +1473,39 @@ class Metadata extends Repository {
}
}
}
/**
* When a private taxonomy is saved, check if there are any public metadata and set them to private
*
* @param \Tainacan\Entities\Taxonomy $taxonomy
* @return void
*/
public function hook_taxonomies_saved_as_private($taxonomy) {
if ( $taxonomy instanceof Entities\Taxonomy ) {
$status_obj = get_post_status_object( $taxonomy->get_status() );
if ( is_object($status_obj) && ! $status_obj->public ) {
$stati = get_post_stati(['public' => true]);
$taxonomy_id = $taxonomy->get_id();
$metadata = $this->fetch(['metadata_type' => 'Tainacan\Metadata_Types\Taxonomy', '_option_taxonomy_id' => $taxonomy_id, 'post_status' => $stati], 'OBJECT');
foreach ($metadata as $meta) {
$meta->set_status( $taxonomy->get_status() );
if ( $meta->validate() ) {
$this->insert($meta);
}
}
}
}
}
}

View File

@ -28,7 +28,7 @@ abstract class Repository {
* @var Repositories\Logs
*/
protected $logs_repository;
private $map = [];
/**
@ -52,7 +52,7 @@ abstract class Repository {
protected function __construct() {
add_action( 'init', array( &$this, 'register_post_type' ) );
add_action( 'init', array( &$this, 'init_objects' ) );
add_filter( 'tainacan-get-map-' . $this->get_name(), array( $this, 'get_default_properties' ) );
}
@ -128,7 +128,7 @@ abstract class Repository {
$old = '';
$diffs = [];
do_action( 'tainacan-pre-insert', $obj );
do_action( 'tainacan-pre-insert-' . $obj->get_post_type(), $obj );
@ -273,7 +273,7 @@ abstract class Repository {
*
* @see https://core.trac.wordpress.org/ticket/18408
*/
foreach ( $WP_Query->get_posts() as $p ) {
foreach ( $WP_Query->posts as $p ) {
$result[] = new $this->entities_type( $p->ID );
}
}
@ -588,10 +588,10 @@ abstract class Repository {
return false;
}
/**
* Shortcut to delete($entity, false)
*
*
* @param Entities\Entity $entity
*
* @return mixed|Entity @see https://developer.wordpress.org/reference/functions/wp_delete_post/
@ -607,24 +607,24 @@ abstract class Repository {
* @return mixed|Entity @see https://developer.wordpress.org/reference/functions/wp_delete_post/
*/
public function delete( Entities\Entity $entity, $permanent = true ) {
do_action( 'tainacan-pre-delete', $entity, $permanent );
do_action( 'tainacan-pre-delete-' . $entity->get_post_type(), $entity, $permanent );
if ($permanent === true) {
$return = wp_delete_post( $entity->get_id(), $permanent );
} elseif ($permanent === false) {
$return = wp_trash_post( $entity->get_id() );
}
if ( $return instanceof \WP_Post && $this->use_logs ) {
do_action( 'tainacan-deleted', $entity, $permanent );
do_action( 'tainacan-deleted-' . $entity->get_post_type(), $entity, $permanent );
$return = $this->get_entity_by_post($return);
}
return $return;
@ -664,7 +664,6 @@ abstract class Repository {
} elseif ( is_object( $user ) ) {
$user = $user->ID;
}
$entity = self::get_entity_by_post( $entity );
$entity_cap = $entity->get_capabilities();
if ( ! isset( $entity_cap->edit_post ) ) {
@ -701,9 +700,8 @@ abstract class Repository {
} elseif ( is_object( $user ) ) {
$user = $user->ID;
}
$entity = self::get_entity_by_post( $entity );
$entity_cap = $entity->get_capabilities();
if ( ! isset( $entity_cap->read ) ) {
if ( $entity->get_post_type() === false ) { // Allow read of not post entities
return true;
@ -730,7 +728,6 @@ abstract class Repository {
} elseif ( is_object( $user ) ) {
$user = $user->ID;
}
$entity = self::get_entity_by_post( $entity );
$entity_cap = $entity->get_capabilities();
if ( ! isset( $entity_cap->delete_post ) ) {
@ -741,7 +738,7 @@ abstract class Repository {
}
/**
* Check if $user can publish the entity
* Check if $user can publish entity
*
* @param Entities\Entity $entity
* @param int|\WP_User|null $user default is null for the current user
@ -749,20 +746,20 @@ abstract class Repository {
* @return boolean
* @throws \Exception
*/
public function can_publish( $entity, $user = null ) {
public function can_publish(Entities\Entity $entity, $user = null) {
if ( is_null( $user ) ) {
$user = get_current_user_id();
} elseif ( is_object( $user ) ) {
$user = $user->ID;
}
$entity = self::get_entity_by_post( $entity );
$entity_cap = $entity->get_capabilities();
if ( ! isset( $entity_cap->publish_posts ) ) {
if ( ! $user || ! isset( $entity_cap->publish_posts ) ) {
return false;
}
return user_can( $user, $entity_cap->publish_posts, $entity->get_id() );
return user_can( $user, $entity_cap->publish_posts );
}
/**
@ -845,7 +842,7 @@ abstract class Repository {
return $diffs;
}
/**
* Get IDs for all children, grand children till the depth parameter is reached
* @param int|\Tainacan\Entities\Entity $id The Entity ID or object
@ -857,17 +854,17 @@ abstract class Repository {
if (is_integer($id)) {
$object = $this->fetch($id);
}
if ( ! $object instanceof \Tainacan\Entities\Entity) {
return [];
}
global $wpdb;
$go_deeper = false === $depth || (is_integer($depth) && $depth > 1);
$new_depth = is_integer($depth) ? $depth - 1 : $depth;
$children = $wpdb->get_col( $wpdb->prepare("SELECT ID FROM $wpdb->posts WHERE post_parent = %d AND post_type = %s", $object->get_id(), $object->get_post_type() ) );
if ($go_deeper && sizeof($children) > 0) {
$gchildren = [];
foreach ($children as $child) {
@ -877,13 +874,30 @@ abstract class Repository {
}
}
$children = array_merge($children, $gchildren);
}
return $children;
}
/**
* Get the capabilities list for the post type of the entity
*
* @uses get_post_type_capabilities to get the list.
*
* This method is usefull for getting the capabilities of the entity post type
* regardless if it has been already registered or not.
*
* @return object Object with all the capabilities as member variables.
*/
public function get_capabilities() {
$entity = new $this->entities_type();
return $entity->get_capabilities();
}
}
?>

View File

@ -125,7 +125,7 @@ class Taxonomies extends Repository {
'rewrite' => true,
'map_meta_cap' => true,
'show_in_nav_menus' => false,
'capability_type' => Entities\Taxonomy::get_capability_type(),
'capabilities' => (array) $this->get_capabilities(),
'supports' => [
'title',
'editor',
@ -206,18 +206,17 @@ class Taxonomies extends Repository {
*
* @param Entities\Collection $collection
* @param array $args WP_Query args plus disabled_metadata
* @param string $output The desired output format (@see \Tainacan\Repositories\Repository::fetch_output() for possible values)
*
* @return array Entities\Taxonomy
* @throws \Exception
*/
public function fetch_by_collection( Entities\Collection $collection, $args = [], $output = null ) {
public function fetch_by_collection( Entities\Collection $collection, $args = [] ) {
$collection_id = $collection->get_id();
$Tainacan_Metadata = Metadata::get_instance();
// get all taxonomy metadata in this collection
$taxonomy_metas = $Tainacan_Metadata->fetch_by_collection($collection, ['metadata_type' => 'Tainacan\Metadata_Types\Taxonomy'], 'OBJECT');
$taxonomy_metas = $Tainacan_Metadata->fetch_by_collection($collection, ['metadata_type' => 'Tainacan\Metadata_Types\Taxonomy']);
$tax_ids = [];
@ -237,7 +236,7 @@ class Taxonomies extends Repository {
];
$args = array_merge($args, $newargs);
return $this->fetch($args, $output);
return $this->fetch($args, 'OBJECT');
}
@ -339,7 +338,7 @@ class Taxonomies extends Repository {
// register taxonomies to other collections considering metadata inheritance
foreach ( $collections as $collection ) {
$taxonomies = $this->fetch_by_collection($collection, ['nopaging' => true], 'OBJECT');
$taxonomies = $this->fetch_by_collection($collection, ['nopaging' => true]);
foreach ( $taxonomies as $taxonomy ) {
register_taxonomy_for_object_type( $taxonomy->get_db_identifier(), $collection->get_db_identifier() );
}

View File

@ -116,10 +116,10 @@ class Terms extends Repository {
if ( ! $term->get_validated() ) {
throw new \Exception( 'Entities must be validated before you can save them' );
}
do_action( 'tainacan-pre-insert', $term );
do_action( 'tainacan-pre-insert-term', $term );
// First iterate through the native post properties
$map = $this->get_map();
foreach ( $map as $prop => $mapped ) {
@ -151,7 +151,7 @@ class Terms extends Repository {
'description' => $term->get_description(),
] );
}
if ( is_wp_error($term_saved) ) {
throw new \Exception( 'Error adding term ' . $term->get_name() . ' - ' . $term_saved->get_error_message() );
}
@ -162,7 +162,7 @@ class Terms extends Repository {
update_term_meta( $term_saved['term_id'], $prop, wp_slash( $term->get_mapped_property( $prop ) ) );
}
}
$new_entity = new Entities\Term( $term_saved['term_id'], $term->get_taxonomy() );
do_action( 'tainacan-insert', $new_entity );
@ -170,7 +170,7 @@ class Terms extends Repository {
return $new_entity;
}
// TODO: Is this workaround ok to avoid getting htmlentities ?
function get_mapped_property($entity, $prop) {
$property = parent::get_mapped_property($entity, $prop);
@ -235,7 +235,7 @@ class Terms extends Repository {
}
return $return;
} elseif ( is_numeric( $args ) ) {
} elseif ( is_numeric( $args ) ) {
$wp_term = get_term( (int) $args, $cpt );
$tainacan_term = new Entities\Term( $wp_term );
return $tainacan_term;
@ -256,12 +256,12 @@ class Terms extends Repository {
*/
public function delete( Entities\Entity $term, $permanent = true ) {
$deleted = $term;
$permanent = true; // there is no such option for terms
do_action( 'tainacan-pre-delete', $deleted, $permanent );
do_action( 'tainacan-pre-delete-term', $deleted, $permanent );
$return = wp_delete_term( $term->get_id(), $term->get_taxonomy() );
if ( $deleted ) {
@ -271,16 +271,16 @@ class Terms extends Repository {
return $return;
}
/**
* Check if a term already exists
* Check if a term already exists
*
* @param string $searched_term The term name (string) or term_id (integer). If term id is passed, parent is not considered.
* @param string $searched_term The term name (string) or term_id (integer). If term id is passed, parent is not considered.
* @param mixed $taxonomy The taxonomy ID, slug or Entity.
* @param int $parent The ID of the parent term to look for children or null to look for terms in any hierarchical position. Default is null
* @param bool $return_term wether to return the term object if it exists. default is to false
*
* @return bool|WP_Term return boolean indicating if term exists. If $return_term is true and term exists, return WP_Term object
* @param int $parent The ID of the parent term to look for children or null to look for terms in any hierarchical position. Default is null
* @param bool $return_term wether to return the term object if it exists. default is to false
*
* @return bool|WP_Term return boolean indicating if term exists. If $return_term is true and term exists, return WP_Term object
*/
public function term_exists($searched_term, $taxonomy, $parent = null, $return_term = false) {
if ($searched_term == "") {
@ -298,13 +298,13 @@ class Terms extends Repository {
}
if(is_int($searched_term)){
$term = get_term_by( 'id', $searched_term, $taxonomy_slug );
if ( ! $term ) {
return false;
}
} else {
$args = [
'name' => $searched_term,
@ -313,30 +313,121 @@ class Terms extends Repository {
'hide_empty' => 0,
'suppress_filter' => true
];
if (is_null($parent)) {
unset($args['parent']);
}
$terms = get_terms($args);
if (empty($terms)) {
return false;
}
$term = $terms[0];
}
if ($return_term) {
return $term;
}
return true;
}
public function register_post_type() {
}
}
/**
* Check if $user can edit/create a entity
*
* @param Entities\Entity $entity
* @param int|\WP_User|null $user default is null for the current user
*
* @return boolean
* @throws \Exception
*/
public function can_edit( Entities\Entity $term, $user = null ) {
$taxonomy = null;
if ( $term instanceof Entities\Term ) {
$taxonomy = \tainacan_taxonomies()->fetch_by_db_identifier( $term->get_taxonomy() );
}
if ( ! $taxonomy instanceof Entities\Taxonomy ) {
return false;
}
return $taxonomy->can_edit();
}
/**
* Check if $user can read the entity
*
* @param Entities\Entity $term
* @param int|\WP_User|null $user default is null for the current user
*
* @return boolean
* @throws \Exception
*/
public function can_read( Entities\Entity $term, $user = null ) {
$taxonomy = null;
if ( $term instanceof Entities\Term ) {
$taxonomy = \tainacan_taxonomies()->fetch_by_db_identifier( $term->get_taxonomy() );
}
if ( ! $taxonomy instanceof Entities\Taxonomy ) {
return false;
}
return $taxonomy->can_read();
}
/**
* Check if $user can delete the entity
*
* @param Entities\Entity $entity
* @param int|\WP_User|null $user default is null for the current user
*
* @return boolean
* @throws \Exception
*/
public function can_delete( $term, $user = null ) {
$taxonomy = null;
if ( $term instanceof Entities\Term ) {
$taxonomy = \tainacan_taxonomies()->fetch_by_db_identifier( $term->get_taxonomy() );
}
if ( ! $taxonomy instanceof Entities\Taxonomy ) {
return false;
}
return $taxonomy->can_edit(); // if user can EDIT taxonomy, it means he can delete terms
}
/**
* Check if $user can publish entity
*
* @param Entities\Entity $entity
* @param int|\WP_User|null $user default is null for the current user
*
* @return boolean
* @throws \Exception
*/
public function can_publish(Entities\Entity $term, $user = null) {
$taxonomy = null;
if ( $term instanceof Entities\Term ) {
$taxonomy = \tainacan_taxonomies()->fetch_by_db_identifier( $term->get_taxonomy() );
}
if ( ! $taxonomy instanceof Entities\Taxonomy ) {
return false;
}
return $taxonomy->can_edit(); // if user can EDIT taxonomy, it means he can publish terms
}
}

View File

@ -177,7 +177,7 @@ $Tainacan_Elastic_press = \Tainacan\Elastic_Press::get_instance();
require_once(__DIR__ . '/class-tainacan-background-process-heartbeat.php');
$Tainacan_Importer_Heartbeat = new \Tainacan\Background_Importer_Heartbeat();
$Tainacan_Capabilities = \Tainacan\Capabilities::get_instance();
$Tainacan_Roles = \Tainacan\Roles::get_instance();
$TainacanPrivateFiles = \Tainacan\Private_Files::get_instance();
@ -188,5 +188,6 @@ if (class_exists('WP_CLI')) {
$Tainacan_Cli = \Tainacan\Cli::get_instance();
}
include_once('tainacan-loaders.php');
?>

View File

@ -0,0 +1,73 @@
<?php
/**
* Retrieve the singleton Collections Repository instance
* @return \Tainacan\Repositories\Collections The Tainacan Collections Repository
*/
function tainacan_collections() {
return \Tainacan\Repositories\Collections::get_instance();
}
/**
* Retrieve the singleton Filters Repository instance
* @return \Tainacan\Repositories\Filters The Tainacan Filters Repository
*/
function tainacan_filters() {
return \Tainacan\Repositories\Filters::get_instance();
}
/**
* Retrieve the singleton Item_Metadata Repository instance
* @return \Tainacan\Repositories\Item_Metadata The Tainacan Item_Metadata Repository
*/
function tainacan_item_metadata() {
return \Tainacan\Repositories\Item_Metadata::get_instance();
}
/**
* Retrieve the singleton Items Repository instance
* @return \Tainacan\Repositories\Items The Tainacan Items Repository
*/
function tainacan_items() {
return \Tainacan\Repositories\Items::get_instance();
}
/**
* Retrieve the singleton Logs Repository instance
* @return \Tainacan\Repositories\Logs The Tainacan Logs Repository
*/
function tainacan_logs() {
return \Tainacan\Repositories\Logs::get_instance();
}
/**
* Retrieve the singleton Metadata Repository instance
* @return \Tainacan\Repositories\Metadata The Tainacan Metadata Repository
*/
function tainacan_metadata() {
return \Tainacan\Repositories\Metadata::get_instance();
}
/**
* Retrieve the singleton Taxonomies Repository instance
* @return \Tainacan\Repositories\Taxonomies The Tainacan Taxonomies Repository
*/
function tainacan_taxonomies() {
return \Tainacan\Repositories\Taxonomies::get_instance();
}
/**
* Retrieve the singleton Terms Repository instance
* @return \Tainacan\Repositories\Terms The Tainacan Terms Repository
*/
function tainacan_terms() {
return \Tainacan\Repositories\Terms::get_instance();
}
/**
* Retrieve the singleton Tainacan Roles instance
* @return \Tainacan\Roles The Tainacan Roles class
*/
function tainacan_roles() {
return \Tainacan\Roles::get_instance();
}

View File

@ -505,7 +505,7 @@ class DevInterface {
}
$metalist = $Tainacan_Metadata->fetch_by_collection($cpts[$post_type], [], 'OBJECT');
$metalist = $Tainacan_Metadata->fetch_by_collection( $cpts[$post_type] );
foreach ($metalist as $meta) {
$item_meta = new \Tainacan\Entities\Item_Metadata_Entity($entity, $meta);

View File

@ -53,7 +53,7 @@ use Tainacan\Entities;
public static function metadata_dropdown( $collection , $selected, $name_metadatum = 'tnc_prop_metadatum_id', $args = []){
$Tainacan_Metadata = \Tainacan\Repositories\Metadata::get_instance();
$collection = ( is_numeric( $collection ) ) ? new Entities\Collection( $collection ) : $collection;
$metadatum = $Tainacan_Metadata->fetch_by_collection( $collection, $args, 'OBJECT');
$metadatum = $Tainacan_Metadata->fetch_by_collection( $collection, $args);
?>
<select name="<?php echo $name_metadatum ?>">
<option value=""><?php echo __('Select an option','tainacan') ?>...</option>
@ -76,7 +76,7 @@ use Tainacan\Entities;
public static function metadata_checkbox_list( $collection , $selected,$name_metadatum = 'tnc_prop_tnc_metadatum_ids[]', $args = []) {
$Tainacan_Metadata = \Tainacan\Repositories\Metadata::get_instance();
$collection = ( is_numeric( $collection ) ) ? new Entities\Collection( $collection ) : $collection;
$metadatum = $Tainacan_Metadata->fetch_by_collection( $collection, $args, 'OBJECT');
$metadatum = $Tainacan_Metadata->fetch_by_collection( $collection, $args);
$selected = ( is_array( $selected) ) ? $selected : json_decode($selected);
$selected = ( $selected ) ? $selected : [];
?>

View File

@ -599,7 +599,12 @@ class CSV extends Importer {
return false;
}
$collection = \Tainacan\Repositories\Collections::get_instance()->fetch($collection_definition['id']);
$collection = \Tainacan\Repositories\Collections::get_instance()->fetch($collection_definition['id']);
if ( $collection instanceof Entities\Collection && ! $collection->user_can('edit_items') ) {
$this->add_error_log( __("You don't have permission to create items in this collection.", 'tainacan') );
return false;
}
$Tainacan_Metadata = \Tainacan\Repositories\Metadata::get_instance();
$Tainacan_Item_Metadata = \Tainacan\Repositories\Item_Metadata::get_instance();
@ -621,6 +626,10 @@ class CSV extends Importer {
if( is_numeric($this->get_transient('item_id')) ) {
if ( $item instanceof Entities\Item && $item->get_id() == $this->get_transient('item_id') ) {
if ( ! $item->can_edit() ) {
$this->add_error_log("You don't have permission to edit item:" . $item->get_id() );
return $item;
}
$this->add_log('item will be updated ID:' . $item->get_id() );
$updating_item = true;
// When creating a new item, disable log for each metadata to speed things up

View File

@ -786,6 +786,11 @@ abstract class Importer {
$collection = \Tainacan\Repositories\Collections::get_instance()->fetch($collection_definition['id']);
if ( $collection instanceof Entities\Collection && ! $collection->user_can('edit_items') ) {
$this->add_error_log( __("You don't have permission to create items in this collection.", 'tainacan') );
return false;
}
$Tainacan_Metadata = \Tainacan\Repositories\Metadata::get_instance();
$Tainacan_Item_Metadata = \Tainacan\Repositories\Item_Metadata::get_instance();
$Tainacan_Items = \Tainacan\Repositories\Items::get_instance();

View File

@ -1,6 +1,6 @@
<?php
/**
/**
* @author: MediaLab-UFG(Vinicius Nunes).
* Term Importer
*
@ -36,7 +36,7 @@ class Term_Importer extends Importer {
'new_taxonomy' => ''
]);
}
public function options_form() {
ob_start();
?>
@ -55,7 +55,7 @@ class Term_Importer extends Importer {
<div class="help-tooltip-body">
<p><?php _e('The character used to separate each column in your CSV (e.g. , or ;)', 'tainacan'); ?></p>
</div>
</div>
</div>
</span>
<div class="control is-clearfix">
<input class="input" type="text" name="delimiter" value="<?php echo $this->get_option('delimiter'); ?>">
@ -78,7 +78,7 @@ class Term_Importer extends Importer {
<p><?php _e('Inform the taxonomy you want to import the terms to.', 'tainacan'); ?></p>
<p><?php _e('Select an existing taxonomy or create a new one on the fly.', 'tainacan'); ?></p>
</div>
</div>
</div>
</span>
<div class="control is-clearfix">
<div class="select">
@ -94,25 +94,25 @@ class Term_Importer extends Importer {
}
?>
</select>
</div>
<input class="input new_taxonomy" type="text" name="new_taxonomy" value="<?php echo $this->get_option('new_taxonomy'); ?>" placeholder="<?php _e('New taxonomy name', 'tainacan'); ?>" >
</div>
</div>
<?php
return ob_get_clean();
}
public function process_item($index, $collection_definition) {
return true;
}
public function create_terms( ) {
if (($handle = fopen($this->tmp_file, "r")) !== false) {
$file = $handle;
$this->set_current_step_total( filesize($this->tmp_file) );
@ -126,7 +126,14 @@ class Term_Importer extends Importer {
$position = $this->get_transient('position') == null ? 0: $this->get_transient('position');
$last_term = $this->get_transient('last_term') == null ? 0: $this->get_transient('last_term');
$id_taxonomy= $this->get_transient('new_taxonomy');
$taxonomy = \tainacan_taxonomies()->fetch( (int) $id_taxonomy );
if ( $taxonomy instanceof Entities\Taxonomy && ! $taxonomy->can_edit() ) {
$this->add_error_log("You don't have permission to add terms to this taxonomy");
$this->abort();
return false;
}
$position_file = $this->get_in_step_count();
fseek($file, $position_file);
if (($values = fgetcsv($file, 0, $this->get_option('delimiter'), '"')) !== FALSE) {
@ -144,16 +151,16 @@ class Term_Importer extends Importer {
$this->abort();
return false;
}
$term = new \Tainacan\Entities\Term();
$term->set_name($values[$position]);
$term->set_description($values[$position+1]);
$term->set_taxonomy($id_taxonomy);
$term_repo = \Tainacan\Repositories\Terms::get_instance();
if(end($parent))
$term->set_parent(end($parent));
if ($term->validate()) {
$term_insert = $term_repo->insert($term);
$last_term = $term_insert->get_id();
@ -179,19 +186,22 @@ class Term_Importer extends Importer {
$this->add_transient('new_taxonomy', $this->get_option('select_taxonomy'));
return false;
}
if ( $this->get_option('select_taxonomy') == '' && $this->get_option('new_taxonomy') == '' ) {
$this->abort();
$this->add_error_log('No taxonomy selected');
return false;
}
$tax1 = new Entities\Taxonomy();
$tax1->set_name($this->get_option('new_taxonomy'));
$tax1->set_allow_insert('yes');
$tax1->set_status('publish');
if ($tax1->validate()) {
if ( ! $tax1->get_capabilities()->edit_posts ) {
$this->add_error_log('Error creating taxonomy. Permission denied');
$this->abort();
} elseif ($tax1->validate()) {
$tax_repo = \Tainacan\Repositories\Taxonomies::get_instance();
$tax1 = $tax_repo->insert($tax1);
$name = $tax1->get_name();
@ -206,4 +216,4 @@ class Term_Importer extends Importer {
return false;
}
}
}

View File

@ -0,0 +1,142 @@
import axios from '../../../axios/axios'
// ROLES
export const addCapabilityToRole = ({ commit }, { capabilityKey, role }) => {
return new Promise(( resolve, reject ) => {
axios.tainacan.patch('/roles/' + role + '?add_cap=' + capabilityKey)
.then( res => {
let role = res.data;
commit('addCapabilityToRole', {capabilityKey, role });
resolve(role);
})
.catch(error => {
reject(error);
});
});
};
export const removeCapabilityFromRole = ({ commit }, { capabilityKey, role }) => {
return new Promise(( resolve, reject ) => {
axios.tainacan.patch('/roles/' + role + '?remove_cap=' + capabilityKey)
.then( res => {
let role = res.data;
commit('removeCapabilityFromRole', {capabilityKey, role });
resolve(role);
})
.catch(error => {
reject(error);
});
});
};
export const fetchRoles = ({ commit }) => {
return new Promise((resolve, reject) => {
axios.tainacan.get('/roles')
.then(res => {
const roles = res.data
commit('setRoles', roles);
resolve(roles);
})
.catch(error => {
reject(error);
});
});
};
export const fetchRole = ({ commit }, roleSlug) => {
return new Promise((resolve, reject) => {
axios.tainacan.get('/roles/' + roleSlug)
.then(res => {
const role = res.data
commit('setRole', role);
resolve(role);
})
.catch(error => {
reject(error);
});
});
};
export const createRole = ({ commit }, role) => {
return new Promise((resolve, reject) => {
axios.tainacan.post('/roles/', role)
.then(res => {
const role = res.data
commit('setRole', role);
resolve(role);
})
.catch(error => {
reject(error);
});
});
};
export const updateRole = ({ commit }, role) => {
return new Promise((resolve, reject) => {
axios.tainacan.patch('/roles/' + role.slug, role)
.then(res => {
const updatedRole = res.data
commit('setRole', updatedRole);
resolve(updatedRole);
})
.catch(error => {
reject(error);
});
});
};
export const deleteRole = ({ commit }, roleSlug) => {
return new Promise((resolve, reject) => {
axios.tainacan.delete('/roles/' + roleSlug)
.then(res => {
const roleSlug = res.data
commit('deleteRole', roleSlug);
resolve(roleSlug);
})
.catch(error => {
reject(error);
});
});
};
// CAPABILITIES
export const fetchCapabilities = ({ commit }, { collectionId } ) => {
return new Promise((resolve, reject) => {
const endpoint = collectionId != undefined ? `/collection/${collectionId}/capabilities` : `/capabilities`;
axios.tainacan.get(endpoint)
.then(res => {
let capabilities = res.data.capabilities;
commit('setCapabilities', capabilities);
resolve(capabilities);
})
.catch(error => {
reject(error);
});
});
};
export const fetchCapability = ({ commit }, capabilityId) => {
return new Promise((resolve, reject) => {
axios.tainacan.get(`/capabilities/${capabilityId}`)
.then(res => {
let capability = res.data;
commit('setCapability', capability);
resolve({
'capability': capability
})
})
.catch(error => {
reject(error);
})
});
};

View File

@ -0,0 +1,15 @@
export const getRoles = state => {
return state.roles;
};
export const getRole = state => {
return state.role;
};
export const getCapability = state => {
return state.capability;
};
export const getCapabilities = state => {
return state.capabilities;
};

View File

@ -0,0 +1,18 @@
import * as actions from './actions';
import * as getters from './getters';
import * as mutations from './mutations';
const state = {
capabilities: [],
capability: {},
roles: [],
role: {}
};
export default {
namespaced: true,
state,
mutations,
actions,
getters
}

View File

@ -0,0 +1,43 @@
import Vue from 'vue';
// Roles
export const addCapabilityToRole = (state, {capabilityKey, role}) => {
if (state.capabilities[capabilityKey] && state.capabilities[capabilityKey].roles[role.slug] == undefined) {
let updateRoles = state.capabilities[capabilityKey].roles.length ? state.capabilities[capabilityKey].roles : {};
updateRoles[role.slug] = role;
Vue.set(state.capabilities[capabilityKey], 'roles', updateRoles)
}
if (state.role && state.role.slug && state.role.slug == role.slug)
state.role = role;
};
export const removeCapabilityFromRole = (state, {capabilityKey, role}) => {
if (state.capabilities[capabilityKey]) {
let updateRoles = state.capabilities[capabilityKey].roles;
delete updateRoles[role.slug];
Vue.set(state.capabilities[capabilityKey], 'roles', updateRoles)
}
if (state.role && state.role.slug && state.role.slug == role.slug)
state.role = role;
};
export const setRoles = (state, roles) => {
state.roles = roles;
};
export const setRole = (state, role) => {
state.role = role;
};
export const deleteRole = (state, roleSlug) => {
delete state.roles[roleSlug]
};
// CAPABILITIES
export const setCapabilities = (state, capabilities) => {
state.capabilities = capabilities;
};
export const setCapability = (state, capability) => {
state.capability = capability;
};

View File

@ -183,9 +183,12 @@ export const cleanItems = ({ commit }) => {
commit('cleanItems');
};
export const fetchCollection = ({ commit }, id) => {
export const cleanCollection = ({ commit }) => {
commit('cleanCollection');
return new Promise((resolve, reject) =>{
};
export const fetchCollection = ({ commit, }, id) => {
return new Promise((resolve, reject) => {
axios.tainacan.get('/collections/' + id + '?context=edit')
.then(res => {
let collection = res.data;
@ -198,37 +201,17 @@ export const fetchCollection = ({ commit }, id) => {
});
};
export const fetchCollectionName = ({ commit }, id) => {
const source = axios.CancelToken.source();
return new Object({
request: new Promise((resolve, reject) => {
axios.tainacan.get('/collections/' + id + '?fetch_only=name', { cancelToken: source.token })
.then(res => {
let collectionName = res.data;
commit('setCollectionName', collectionName.name);
resolve( collectionName.name );
})
.catch(error => {
if (axios.isCancel(error)) {
console.log('Request canceled: ', error.message);
} else {
reject(error);
}
})
}),
source: source
});
};
export const fetchCollectionUserCanEdit = ({ commit }, id) => {
return new Promise ((resolve, reject) => {
axios.tainacan.get('/collections/' + id + '?context=edit&fetch_only')
export const fetchCollectionBasics = ({ commit }, {collectionId, isContextEdit }) => {
return new Promise((resolve, reject) => {
let endpoint = '/collections/' + collectionId + '?fetch_only=name,url,allow_comments';
if (isContextEdit)
endpoint += '&context=edit';
axios.tainacan.get(endpoint)
.then(res => {
let caps = res.data.current_user_can_edit;
resolve( caps );
let collection = res.data;
commit('setCollection', collection);
resolve( res.data );
})
.catch(error => {
reject(error);
@ -236,27 +219,12 @@ export const fetchCollectionUserCanEdit = ({ commit }, id) => {
});
};
export const fetchCollectionTotalItems = ({ commit }, id) => {
return new Promise ((resolve, reject) => {
axios.tainacan.get('/collections/' + id + '?fetch_only=name')
.then(res => {
commit('setCollectionTotalItems', res.data);
resolve( res.data );
})
.catch(error => {
reject(error);
})
});
};
export const fetchCollectionCommentStatus = ({ commit }, id) => {
return new Promise((resolve, reject) =>{
axios.tainacan.get('/collections/' + id + '?fetch_only=comment_status')
export const fetchCollectionForExposer = ({ commit }, collectionId) => {
return new Promise((resolve, reject) => {
let endpoint = '/collections/' + collectionId + '?fetch_only=name,url';
axios.tainacan.get(endpoint)
.then(res => {
let collectionCommentStatus = res.data;
commit('setCollectionCommentStatus', collectionCommentStatus.comment_status);
resolve( collectionCommentStatus.comment_status );
resolve( res.data );
})
.catch(error => {
reject(error);
@ -264,47 +232,6 @@ export const fetchCollectionCommentStatus = ({ commit }, id) => {
});
};
export const fetchCollectionAllowComments = ({ commit }, id) => {
return new Promise((resolve, reject) =>{
axios.tainacan.get('/collections/' + id + '?fetch_only=allow_comments')
.then(res => {
let collectionAllowComments = res.data;
commit('setCollectionAllowComments', collectionAllowComments.allow_comments);
resolve( collectionAllowComments.allow_comments );
})
.catch(error => {
reject(error);
})
});
};
export const fetchCollectionNameAndURL = ({ commit }, id) => {
const source = axios.CancelToken.source();
return new Object({
request: new Promise ((resolve, reject) => {
axios.tainacan.get(
'/collections/' + id + '?fetch_only=name,url',
{ cancelToken: source.token })
.then(res => {
let collection = res.data;
commit('setCollectionName', collection.name);
commit('setCollectionURL', collection.url);
resolve( collection );
})
.catch((thrown) => {
if (axios.isCancel(thrown)) {
console.log('Request canceled: ', thrown.message);
} else {
reject(thrown);
}
});
}),
source: source
});
};
export const deleteCollection = ({ commit }, { collectionId, isPermanently }) => {
return new Promise((resolve, reject) => {
let endpoint = '/collections/' + collectionId;
@ -331,10 +258,8 @@ export const updateCollection = ({ commit }, {
collection
}) => {
return new Promise((resolve, reject) => {
axios.tainacan.patch('/collections/' + collection_id, collection).then( res => {
axios.tainacan.patch('/collections/' + collection_id + '?context=edit', collection).then( res => {
commit('setCollection', collection);
commit('setCollectionName', res.data.name);
commit('setCollectionURL', res.data.url);
resolve( res.data );
}).catch( error => {
reject({ error_message: error['response']['data'].error_message, errors: error['response']['data'].errors });
@ -347,7 +272,7 @@ export const sendCollection = ( { commit }, collection) => {
return new Promise(( resolve, reject ) => {
let param = collection;
param[tainacan_plugin.exposer_mapper_param] = collection.mapper;
axios.tainacan.post('/collections/', param)
axios.tainacan.post('/collections/?context=edit', param)
.then( res => {
let collection = res.data;
commit('setCollection', collection);
@ -401,7 +326,7 @@ export const fetchAttachments = ({ commit }, collection_id) => {
export const updateThumbnail = ({ commit }, { collectionId, thumbnailId }) => {
return new Promise((resolve, reject) => {
axios.tainacan.patch('/collections/' + collectionId, {
axios.tainacan.patch('/collections/' + collectionId + '?context=edit', {
_thumbnail_id: thumbnailId
}).then( res => {
let collection = res.data
@ -416,7 +341,7 @@ export const updateThumbnail = ({ commit }, { collectionId, thumbnailId }) => {
export const updateHeaderImage = ({ commit }, { collectionId, headerImageId }) => {
return new Promise((resolve, reject) => {
axios.tainacan.patch('/collections/' + collectionId, {
axios.tainacan.patch('/collections/' + collectionId + '?context=edit', {
header_image_id: headerImageId + ''
}).then( res => {
let collection = res.data
@ -456,35 +381,24 @@ export const fetchPage = ({ commit }, pageId ) => {
});
};
// Users for moderators configuration
export const fetchUsers = ({ commit }, { search, exceptions }) => {
// Fetch Collections for listing repository filters, parent collection selection, importer destiny...
export const fetchAllCollectionNames = ({ commit }, collectionsIds) => {
let endpoint = '/users?search=' + search;
let endpoint = '/collections/?context=edit&nopaging=1&fetch_only=name,id';
if (exceptions.length > 0)
endpoint += '&exclude=' + exceptions.toString();
if (collectionsIds != undefined && collectionsIds.length > 0) {
const postin = { 'postin': collectionsIds };
endpoint += '&' + qs.stringify(postin);
}
return new Promise((resolve, reject) => {
axios.wp.get(endpoint)
.then(res => {
let users = res.data;
resolve( users );
})
.catch(error => {
reject( error );
});
});
};
// Fetch Collections for choosing Parent Collection
export const fetchCollectionsForParent = ({ commit }) => {
const source = axios.CancelToken.source();
return new Object({
request: new Promise((resolve, reject) => {
axios.tainacan.get('/collections/?nopaging=1fetch_only=name,id', { cancelToken: source.token })
axios.tainacan.get(endpoint, { cancelToken: source.token })
.then(res => {
let collections = res.data;
const collections = res.data;
commit('setCollections', collections);
resolve( collections );
})
.catch((error) => {

View File

@ -14,14 +14,6 @@ export const getCollection = state => {
return state.collection;
}
export const getCollectionName = state => {
return state.collectionName;
}
export const getCollectionURL = state => {
return state.collectionURL;
}
export const getAttachments = state => {
return state.attachments;
}
@ -30,18 +22,6 @@ export const getFiles = state => {
return state.files;
}
export const getCollectionCommentStatus = state => {
return state.collectionCommentStatus;
}
export const getCollectionAllowComments = state => {
return state.collectionAllowComments;
}
export const getCollectionTotalItems = state => {
return state.collectionTotalItems;
}
export const getRepositoryTotalCollections = (state) => {
return state.repositoryTotalCollections;
}

View File

@ -8,13 +8,8 @@ const state = {
itemsListTemplate: '',
collections: [],
collection: null,
collectionName: '',
collectionURL: '',
attachments: [],
collectionCommentStatus: '',
collectionAllowComments: '',
files: [],
collectionTotalItems: {},
repositoryTotalCollections: '',
};

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