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-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-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!" echo "Compilação do Sass Concluído!"
exit 0 exit 0

View File

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

View File

@ -29,14 +29,11 @@ class Admin {
} }
function add_admin_menu() { 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( $page_suffix = add_menu_page(
__( 'Tainacan', 'tainacan' ), __( 'Tainacan', 'tainacan' ),
__( 'Tainacan', 'tainacan' ), __( 'Tainacan', 'tainacan' ),
$entity_cap, 'read',
$this->menu_slug, $this->menu_slug,
array( &$this, 'admin_page' ), array( &$this, 'admin_page' ),
plugin_dir_url( __FILE__ ) . 'images/tainacan_logo_symbol.svg' plugin_dir_url( __FILE__ ) . 'images/tainacan_logo_symbol.svg'
@ -51,7 +48,17 @@ class Admin {
array( &$this, 'systemcheck_page' ) 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-' . $page_suffix, array( &$this, 'load_admin_page' ) );
add_action( 'load-' . $roles_page_suffix, array( &$this, 'load_roles_page' ) );
} }
function load_admin_page() { function load_admin_page() {
@ -60,6 +67,10 @@ class Admin {
add_action( 'admin_enqueue_scripts', array(&$this, 'add_theme_files') ); 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 ) { function login_styles_reset( $style ) {
if ( strpos( $style, 'wp-admin-css' ) !== false ) { if ( strpos( $style, 'wp-admin-css' ) !== false ) {
@ -82,6 +93,32 @@ class Admin {
wp_enqueue_script('underscore'); 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() { function add_admin_css() {
global $TAINACAN_BASE_URL; global $TAINACAN_BASE_URL;
@ -170,12 +207,9 @@ class Admin {
$user_caps = array(); $user_caps = array();
$prefs = array(); $prefs = array();
if ( $cur_user instanceof \WP_User ) { if ( $cur_user instanceof \WP_User ) {
if ( is_array( $cur_user->allcaps ) ) { $tainacan_caps = \tainacan_roles()->get_repository_caps_slugs();
foreach ( $cur_user->allcaps as $cap => $bool ) { foreach ($tainacan_caps as $tcap) {
if ( $bool === true ) { $user_caps[$tcap] = current_user_can( $tcap );
$user_caps[] = $cap;
}
}
} }
$prefs = get_user_meta( $cur_user->ID, 'tainacan_prefs', true ); $prefs = get_user_meta( $cur_user->ID, 'tainacan_prefs', true );
} }
@ -341,6 +375,5 @@ class Admin {
$check = new System_Check(); $check = new System_Check();
$check->admin_page(); $check->admin_page();
} }
} }

View File

@ -5,7 +5,7 @@
<tainacan-title <tainacan-title
:bread-crumb-items="[{ path: '', label: $i18n.get('collection') }]"/> :bread-crumb-items="[{ path: '', label: $i18n.get('collection') }]"/>
<form <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" class="tainacan-form"
label-width="120px"> label-width="120px">
@ -256,6 +256,7 @@
</option> </option>
</b-select> </b-select>
</b-field> </b-field>
<!-- Comment Status ------------------------ --> <!-- Comment Status ------------------------ -->
<b-field <b-field
:addons="false" :addons="false"
@ -411,49 +412,6 @@
</b-select> </b-select>
</b-field> </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 -------------------------------- --> <!-- Slug -------------------------------- -->
<b-field <b-field
:addons="false" :addons="false"
@ -498,14 +456,14 @@
style="margin-left: auto;" style="margin-left: auto;"
class="control"> class="control">
<button <button
v-if="isNewCollection" v-if="isNewCollection && $userCaps.hasCapability('tnc_rep_edit_metadata')"
id="button-submit-goto-metadata" id="button-submit-goto-metadata"
@click.prevent="onSubmit('metadata')" @click.prevent="onSubmit('metadata')"
class="button is-secondary">{{ $i18n.get('label_save_goto_metadata') }}</button> class="button is-secondary">{{ $i18n.get('label_save_goto_metadata') }}</button>
</div> </div>
<div class="control"> <div class="control">
<button <button
v-if="isNewCollection" v-if="isNewCollection && $userCaps.hasCapability('tnc_rep_edit_metadata')"
id="button-submit-goto-filter" id="button-submit-goto-filter"
@click.prevent="onSubmit('filters')" @click.prevent="onSubmit('filters')"
class="button is-secondary">{{ $i18n.get('label_save_goto_filter') }}</button> class="button is-secondary">{{ $i18n.get('label_save_goto_filter') }}</button>
@ -520,6 +478,19 @@
<p class="help is-danger">{{ formErrorMessage }}</p> <p class="help is-danger">{{ formErrorMessage }}</p>
</form> </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 <b-loading
:active.sync="isLoading" :active.sync="isLoading"
:can-cancel="false"/> :can-cancel="false"/>
@ -549,7 +520,6 @@ export default {
thumbnail: '', thumbnail: '',
header_image: '', header_image: '',
files:[], files:[],
moderators_ids: [],
enabled_view_modes: [], enabled_view_modes: [],
default_view_mode: [], default_view_mode: [],
allow_comments: '' allow_comments: ''
@ -568,9 +538,6 @@ export default {
mapper: false, mapper: false,
thumbPlaceholderPath: tainacan_plugin.base_url + '/admin/images/placeholder_square.png', thumbPlaceholderPath: tainacan_plugin.base_url + '/admin/images/placeholder_square.png',
headerPlaceholderPath: tainacan_plugin.base_url + '/admin/images/placeholder_rectangle.png', headerPlaceholderPath: tainacan_plugin.base_url + '/admin/images/placeholder_rectangle.png',
isFetchingModerators: false,
users: [],
moderators: [],
collections: [], collections: [],
isFetchingCollections: true, isFetchingCollections: true,
thumbnailMediaFrame: undefined, thumbnailMediaFrame: undefined,
@ -596,8 +563,7 @@ export default {
'updateHeaderImage', 'updateHeaderImage',
'fetchPages', 'fetchPages',
'fetchPage', 'fetchPage',
'fetchUsers', 'fetchAllCollectionNames'
'fetchCollectionsForParent'
]), ]),
updateSlug: _.debounce(function() { updateSlug: _.debounce(function() {
if(!this.form.name || this.form.name.length <= 0){ if(!this.form.name || this.form.name.length <= 0){
@ -623,9 +589,6 @@ export default {
onSubmit(goTo) { onSubmit(goTo) {
this.isLoading = true; this.isLoading = true;
this.form.moderators_ids = [];
for (let moderator of this.moderators)
this.form.moderators_ids.push(moderator.id);
let data = { let data = {
collection_id: this.collectionId, collection_id: this.collectionId,
@ -635,7 +598,6 @@ export default {
cover_page_id: this.form.cover_page_id, cover_page_id: this.form.cover_page_id,
slug: this.form.slug, slug: this.form.slug,
status: this.form.status, status: this.form.status,
moderators_ids: this.form.moderators_ids,
parent: this.form.parent, parent: this.form.parent,
enabled_view_modes: this.form.enabled_view_modes, enabled_view_modes: this.form.enabled_view_modes,
default_view_mode: this.form.default_view_mode, default_view_mode: this.form.default_view_mode,
@ -712,7 +674,6 @@ export default {
this.form.parent = this.collection.parent; this.form.parent = this.collection.parent;
this.form.default_view_mode = this.collection.default_view_mode; this.form.default_view_mode = this.collection.default_view_mode;
this.form.enabled_view_modes = []; this.form.enabled_view_modes = [];
this.moderators = [];
this.form.allow_comments = this.collection.allow_comments; this.form.allow_comments = this.collection.allow_comments;
// Pre-fill status with publish to incentivate it // Pre-fill status with publish to incentivate it
@ -720,7 +681,7 @@ export default {
// Generates options for parent collection // Generates options for parent collection
this.isFetchingCollections = true; this.isFetchingCollections = true;
this.fetchCollectionsForParent() this.fetchAllCollectionNames()
.then((collections) => { .then((collections) => {
this.collections = collections; this.collections = collections;
this.isFetchingCollections = false; this.isFetchingCollections = false;
@ -733,7 +694,10 @@ export default {
this.isLoading = false; this.isLoading = false;
}) })
.catch(error => this.$console.error(error)); .catch((error) => {
this.$console.error(error);
this.isLoading = false;
});
}, },
clearErrors(attribute) { clearErrors(attribute) {
this.editFormErrors[attribute] = undefined; this.editFormErrors[attribute] = undefined;
@ -774,30 +738,6 @@ export default {
this.coverPageTitle = this.coverPage.title.rendered; this.coverPageTitle = this.coverPage.title.rendered;
this.coverPageEditPath = tainacan_plugin.admin_url + 'post.php?post=' + selectedPage.id + '&action=edit'; 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() { removeCoverPage() {
this.coverPage = {}; this.coverPage = {};
this.coverPageTitle = ''; this.coverPageTitle = '';
@ -878,6 +818,7 @@ export default {
// Obtains current Collection ID from URL // Obtains current Collection ID from URL
this.pathArray = this.$route.path.split("/").reverse(); this.pathArray = this.$route.path.split("/").reverse();
this.collectionId = this.pathArray[1]; this.collectionId = this.pathArray[1];
this.fetchCollection(this.collectionId).then(res => { this.fetchCollection(this.collectionId).then(res => {
@ -902,7 +843,6 @@ export default {
this.form.parent = this.collection.parent; this.form.parent = this.collection.parent;
this.form.default_view_mode = this.collection.default_view_mode; 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.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; this.form.allow_comments = this.collection.allow_comments;
// Generates CoverPage from current cover_page_id info // Generates CoverPage from current cover_page_id info
@ -925,7 +865,7 @@ export default {
// Generates options for parent collection // Generates options for parent collection
this.isFetchingCollections = true; this.isFetchingCollections = true;
this.fetchCollectionsForParent() this.fetchAllCollectionNames()
.then((resp) => { .then((resp) => {
resp.request.then((collections) => { resp.request.then((collections) => {
this.collections = collections; this.collections = collections;
@ -1087,10 +1027,6 @@ export default {
align-items: center; align-items: center;
} }
} }
.moderators-empty-list {
color: $gray4;
font-size: 0.85rem;
}
</style> </style>

View File

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

View File

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

View File

@ -14,7 +14,7 @@
<hr> <hr>
</div> </div>
<form <form
v-if="!isLoading" v-if="!isLoading && collection && collection.current_user_can_bulk_edit"
class="tainacan-form" class="tainacan-form"
label-width="120px"> label-width="120px">
@ -170,6 +170,18 @@
</div> </div>
</footer> </footer>
</form> </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> </div>
</template> </template>
@ -194,6 +206,9 @@ export default {
computed: { computed: {
uploadedFileList() { uploadedFileList() {
return this.getFiles(); return this.getFiles();
},
collection() {
return this.getCollection()
} }
}, },
methods: { methods: {
@ -204,6 +219,7 @@ export default {
]), ]),
...mapGetters('collection', [ ...mapGetters('collection', [
'getFiles', 'getFiles',
'getCollection'
]), ]),
...mapActions('item', [ ...mapActions('item', [
'sendItem', 'sendItem',

View File

@ -11,7 +11,7 @@
v-if="(item != null && item != undefined && item.status != undefined && !isLoading)" v-if="(item != null && item != undefined && item.status != undefined && !isLoading)"
class="status-tag">{{ $i18n.get('status_' + item.status) }}</span> class="status-tag">{{ $i18n.get('status_' + item.status) }}</span>
{{ $i18n.get('title_create_item_collection') + ' ' }} {{ $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>
<h1 v-else> <h1 v-else>
<span <span
@ -31,7 +31,7 @@
mode="out-in" mode="out-in"
:name="(isOnSequenceEdit && sequenceRightDirection != undefined) ? (sequenceRightDirection ? 'page-right' : 'page-left') : ''"> :name="(isOnSequenceEdit && sequenceRightDirection != undefined) ? (sequenceRightDirection ? 'page-right' : 'page-left') : ''">
<form <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" class="tainacan-form"
label-width="120px"> label-width="120px">
<div class="columns"> <div class="columns">
@ -386,7 +386,7 @@
<span class="icon"> <span class="icon">
<i class="tainacan-icon tainacan-icon-collection"/> <i class="tainacan-icon tainacan-icon-collection"/>
</span> </span>
{{ collectionName }} {{ collection && collection.name ? collection.name : '' }}
</span> </span>
</div> </div>
</div> </div>
@ -432,7 +432,7 @@
<!-- Comment Status ------------------------ --> <!-- Comment Status ------------------------ -->
<div <div
class="column is-narrow" class="column is-narrow"
v-if="collectionAllowComments == 'open'"> v-if="collection && collection.allow_comments && collection.allow_comments == 'open'">
<div class="section-label"> <div class="section-label">
<label>{{ $i18n.get('label_comments') }}</label> <label>{{ $i18n.get('label_comments') }}</label>
<help-button <help-button
@ -536,6 +536,21 @@
</div> </div>
</form> </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> </transition>
<footer class="footer"> <footer class="footer">
<!-- Sequence Progress --> <!-- Sequence Progress -->
@ -572,6 +587,7 @@
class="form-submission-footer" class="form-submission-footer"
v-if="form.status == 'trash'"> v-if="form.status == 'trash'">
<button <button
v-if="item && item.current_user_can_delete"
@click="onDeletePermanently()" @click="onDeletePermanently()"
type="button" type="button"
class="button is-outlined">{{ $i18n.get('label_delete_permanently') }}</button> class="button is-outlined">{{ $i18n.get('label_delete_permanently') }}</button>
@ -580,6 +596,7 @@
type="button" type="button"
class="button is-secondary">{{ $i18n.get('label_save_as_draft') }}</button> class="button is-secondary">{{ $i18n.get('label_save_as_draft') }}</button>
<button <button
v-if="collection && collection.current_user_can_publish_items"
@click="onSubmit(visibility)" @click="onSubmit(visibility)"
type="button" type="button"
class="button is-success">{{ $i18n.get('label_publish') }}</button> class="button is-success">{{ $i18n.get('label_publish') }}</button>
@ -598,7 +615,7 @@
<span>{{ $i18n.get('previous') }}</span> <span>{{ $i18n.get('previous') }}</span>
</button> </button>
<button <button
v-if="form.status == 'draft' && !isOnSequenceEdit" v-if="form.status == 'draft' && !isOnSequenceEdit && item && item.current_user_can_delete"
@click="onSubmit('trash')" @click="onSubmit('trash')"
type="button" type="button"
class="button is-outlined">{{ $i18n.get('label_send_to_trash') }}</button> class="button is-outlined">{{ $i18n.get('label_send_to_trash') }}</button>
@ -622,6 +639,7 @@
<i class="tainacan-icon tainacan-icon-20px tainacan-icon-next"/> <i class="tainacan-icon tainacan-icon-20px tainacan-icon-next"/>
</span> </span>
</button> </button>
<template v-if="collection && collection.current_user_can_publish_items">
<button <button
v-if="!isOnSequenceEdit || (group != null && group.items_count != undefined && group.items_count == itemPosition)" v-if="!isOnSequenceEdit || (group != null && group.items_count != undefined && group.items_count == itemPosition)"
@click="onSubmit(visibility)" @click="onSubmit(visibility)"
@ -637,6 +655,19 @@
<i class="tainacan-icon tainacan-icon-20px tainacan-icon-next"/> <i class="tainacan-icon tainacan-icon-20px tainacan-icon-next"/>
</span> </span>
</button> </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 <button
v-if="isOnSequenceEdit && (group != null && group.items_count != undefined && group.items_count == itemPosition)" v-if="isOnSequenceEdit && (group != null && group.items_count != undefined && group.items_count == itemPosition)"
@click="$router.push($routerHelper.getCollectionPath(form.collectionId))" @click="$router.push($routerHelper.getCollectionPath(form.collectionId))"
@ -662,7 +693,7 @@
<span>{{ $i18n.get('previous') }}</span> <span>{{ $i18n.get('previous') }}</span>
</button> </button>
<button <button
v-if="!isOnSequenceEdit" v-if="!isOnSequenceEdit && item && item.current_user_can_delete"
@click="onSubmit('trash')" @click="onSubmit('trash')"
type="button" type="button"
class="button is-outlined">{{ $i18n.get('label_send_to_trash') }}</button> class="button is-outlined">{{ $i18n.get('label_send_to_trash') }}</button>
@ -681,6 +712,7 @@
<i class="tainacan-icon tainacan-icon-20px tainacan-icon-next"/> <i class="tainacan-icon tainacan-icon-20px tainacan-icon-next"/>
</span> </span>
</button> </button>
<template v-if="collection && collection.current_user_can_publish_items">
<button <button
v-if="!isOnSequenceEdit || (group != null && group.items_count != undefined && group.items_count == itemPosition)" v-if="!isOnSequenceEdit || (group != null && group.items_count != undefined && group.items_count == itemPosition)"
:disabled="formErrorMessage != undefined && formErrorMessage != ''" :disabled="formErrorMessage != undefined && formErrorMessage != ''"
@ -698,6 +730,20 @@
<i class="tainacan-icon tainacan-icon-20px tainacan-icon-next"/> <i class="tainacan-icon tainacan-icon-20px tainacan-icon-next"/>
</span> </span>
</button> </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 <button
v-if="isOnSequenceEdit && (group != null && group.items_count != undefined && group.items_count == itemPosition)" v-if="isOnSequenceEdit && (group != null && group.items_count != undefined && group.items_count == itemPosition)"
@click="$router.push($routerHelper.getCollectionPath(form.collectionId))" @click="$router.push($routerHelper.getCollectionPath(form.collectionId))"
@ -759,15 +805,15 @@ export default {
isTextModalActive: false, isTextModalActive: false,
textLink: '', textLink: '',
isUpdatingValues: false, isUpdatingValues: false,
collectionName: '',
collectionAllowComments: '',
entityName: 'item', entityName: 'item',
activeTab: 0, activeTab: 0,
isLoadingAttachments: false, isLoadingAttachments: false,
collectionNameSearchCancel: undefined
} }
}, },
computed: { computed: {
collection() {
return this.getCollection()
},
metadatumList() { metadatumList() {
return JSON.parse(JSON.stringify(this.getMetadata())); return JSON.parse(JSON.stringify(this.getMetadata()));
}, },
@ -843,10 +889,11 @@ export default {
'getLastUpdated' 'getLastUpdated'
]), ]),
...mapActions('collection', [ ...mapActions('collection', [
'fetchCollectionName',
'fetchCollectionAllowComments',
'deleteItem', 'deleteItem',
]), ]),
...mapGetters('collection', [
'getCollection',
]),
...mapActions('bulkedition', [ ...mapActions('bulkedition', [
'fetchItemIdInSequence', 'fetchItemIdInSequence',
'fetchGroup' 'fetchGroup'
@ -910,7 +957,6 @@ export default {
for (let metadatum of Object.keys(error)){ 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; this.formErrorMessage = errors.error_message;
} }
@ -960,7 +1006,10 @@ export default {
.catch(() => this.isLoadingAttachments = false); .catch(() => this.isLoadingAttachments = false);
}) })
.catch(error => this.$console.error(error)); .catch((error) => {
this.$console.error(error);
this.isLoading = false;
});
}, },
loadMetadata() { loadMetadata() {
// Obtains Item Metadatum // Obtains Item Metadatum
@ -1326,30 +1375,6 @@ export default {
this.fetchGroup({ collectionId: this.collectionId, groupId: this.sequenceId }); 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 // Sets feedback variables
eventBus.$on('isUpdatingValue', (status) => { eventBus.$on('isUpdatingValue', (status) => {
this.isUpdatingValues = status; this.isUpdatingValues = status;
@ -1365,10 +1390,6 @@ export default {
beforeDestroy () { beforeDestroy () {
eventBus.$off('isUpdatingValue'); eventBus.$off('isUpdatingValue');
eventBus.$off('hasErrorsOnForm'); eventBus.$off('hasErrorsOnForm');
// Cancels previous collection name Request
if (this.collectionNameSearchCancel != undefined)
this.collectionNameSearchCancel.cancel('Collection name search Canceled.');
}, },
beforeRouteLeave ( to, from, next ) { beforeRouteLeave ( to, from, next ) {
if (this.item.status == 'auto-draft') { if (this.item.status == 'auto-draft') {

View File

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

View File

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

View File

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

View File

@ -21,6 +21,7 @@
</span> </span>
</span> </span>
<span <span
v-if="currentUserCanEditTaxonomy"
class="controls" class="controls"
:class="{'is-disabled': isEditingTerm}"> :class="{'is-disabled': isEditingTerm}">
<a <a
@ -60,7 +61,7 @@ export default {
data(){ data(){
return { return {
isLoadingTerms: false, isLoadingTerms: false,
isEditingTerm: false, isEditingTerm: false
} }
}, },
props: { props: {
@ -68,6 +69,7 @@ export default {
index: Number, index: Number,
taxonomyId: Number, taxonomyId: Number,
order: String, order: String,
currentUserCanEditTaxonomy: Boolean
}, },
methods: { methods: {
...mapActions('taxonomy', [ ...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 <div
style="min-height: initial; position: relative" style="min-height: initial; position: relative"
class="tainacan-cards-container"> 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"> <ul class="new-collection-menu">
<li> <li>
<router-link <router-link
@ -76,6 +76,7 @@
:gutter="25" :gutter="25"
style="width:100%;"> style="width:100%;">
<router-link <router-link
v-if="$userCaps.hasCapability('tnc_rep_edit_collections')"
tag="a" tag="a"
:to="$routerHelper.getNewCollectionPath()" :to="$routerHelper.getNewCollectionPath()"
class="tainacan-card new-card"> class="tainacan-card new-card">
@ -170,7 +171,7 @@
<!-- <span class="menu-text">{{ $i18n.get('label_settings') }}</span> --> <!-- <span class="menu-text">{{ $i18n.get('label_settings') }}</span> -->
</router-link> </router-link>
</li> </li>
<li> <li v-if="collection.current_user_can_edit_metadata">
<router-link <router-link
tag="a" tag="a"
:to="{ path: $routerHelper.getCollectionMetadataPath(collection.id) }" :to="{ path: $routerHelper.getCollectionMetadataPath(collection.id) }"
@ -185,7 +186,7 @@
<!-- <span class="menu-text">{{ $i18n.getFrom('metadata', 'name') }}</span> --> <!-- <span class="menu-text">{{ $i18n.getFrom('metadata', 'name') }}</span> -->
</router-link> </router-link>
</li> </li>
<li> <li v-if="collection.current_user_can_edit_filters">
<router-link <router-link
tag="a" tag="a"
:to="{ path: $routerHelper.getCollectionFiltersPath(collection.id) }" :to="{ path: $routerHelper.getCollectionFiltersPath(collection.id) }"
@ -201,7 +202,7 @@
<!-- <span class="menu-text">{{ $i18n.getFrom('filters', 'name') }}</span> --> <!-- <span class="menu-text">{{ $i18n.getFrom('filters', 'name') }}</span> -->
</router-link> </router-link>
</li> </li>
<li> <li v-if="$userCaps.hasCapability('tnc_rep_read_logs')">
<router-link <router-link
tag="a" tag="a"
:to="{ path: $routerHelper.getCollectionActivitiesPath(collection.id) }" :to="{ path: $routerHelper.getCollectionActivitiesPath(collection.id) }"

View File

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

View File

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

View File

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

View File

@ -7,7 +7,7 @@
class="tainacan-page-title"> class="tainacan-page-title">
<h1> <h1>
{{ $i18n.get('title_collection_metadata_edition') + ' ' }} {{ $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> </h1>
<a <a
@click="$router.go(-1)" @click="$router.go(-1)"
@ -18,7 +18,9 @@
</div> </div>
<p v-if="isRepositoryLevel">{{ $i18n.get('info_repository_metadata_inheritance') }}</p> <p v-if="isRepositoryLevel">{{ $i18n.get('info_repository_metadata_inheritance') }}</p>
<br> <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')"> <b-tab-item :label="$i18n.get('metadata')">
<div <div
:style="{ height: activeMetadatumList.length <= 0 && !isLoadingMetadata ? 'auto' : 'calc(100vh - 6px - ' + columnsTopY + 'px)'}" :style="{ height: activeMetadatumList.length <= 0 && !isLoadingMetadata ? 'auto' : 'calc(100vh - 6px - ' + columnsTopY + 'px)'}"
@ -64,7 +66,7 @@
<span <span
v-if="!(isRepositoryLevel || metadatum.id == undefined || openedMetadatumId != '' || isUpdatingMetadataOrder)" v-if="!(isRepositoryLevel || metadatum.id == undefined || openedMetadatumId != '' || isUpdatingMetadataOrder)"
v-tooltip="{ 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, autoHide: true,
classes: ['tooltip', isRepositoryLevel ? 'repository-tooltip' : ''], classes: ['tooltip', isRepositoryLevel ? 'repository-tooltip' : ''],
placement: 'auto-start' placement: 'auto-start'
@ -129,6 +131,7 @@
:value="metadatum.enabled" :value="metadatum.enabled"
@input="onChangeEnable($event, index)"/> @input="onChangeEnable($event, index)"/>
<a <a
v-if="metadatum.current_user_can_edit"
:style="{ visibility: :style="{ visibility:
metadatum.collection_id != collectionId metadatum.collection_id != collectionId
? 'hidden' : 'visible' ? 'hidden' : 'visible'
@ -146,6 +149,7 @@
</span> </span>
</a> </a>
<a <a
v-if="metadatum.current_user_can_delete"
:style="{ visibility: :style="{ visibility:
metadatum.collection_id != collectionId || metadatum.collection_id != collectionId ||
metadatum.metadata_type_object.related_mapped_prop == 'title' || metadatum.metadata_type_object.related_mapped_prop == 'title' ||
@ -183,7 +187,9 @@
</draggable> </draggable>
</div> </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"> <div class="field">
<h3 class="label has-text-secondary">{{ $i18n.get('label_available_metadata_types') }}</h3> <h3 class="label has-text-secondary">{{ $i18n.get('label_available_metadata_types') }}</h3>
<draggable <draggable
@ -230,7 +236,9 @@
</b-tab-item> </b-tab-item>
<!-- Mapping --------------- --> <!-- 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> <div>
<section <section
v-if="activeMetadatumList.length <= 0 && !isLoadingMetadata" v-if="activeMetadatumList.length <= 0 && !isLoadingMetadata"
@ -428,6 +436,18 @@
</div> </div>
</b-tab-item> </b-tab-item>
</b-tabs> </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> </div>
</template> </template>
@ -462,14 +482,16 @@ export default {
new_metadata_uri: '', new_metadata_uri: '',
new_metadata_slug: '', new_metadata_slug: '',
columnsTopY: 0, columnsTopY: 0,
metadataSearchCancel: undefined, metadataSearchCancel: undefined
collectionNameSearchCancel: undefined
} }
}, },
components: { components: {
MetadatumEditionForm MetadatumEditionForm
}, },
computed: { computed: {
collection() {
return this.getCollection();
},
availableMetadatumList: { availableMetadatumList: {
get() { get() {
return this.getMetadatumTypes(); return this.getMetadatumTypes();
@ -552,8 +574,8 @@ export default {
'getMetadata', 'getMetadata',
'getMetadatumMappers' 'getMetadatumMappers'
]), ]),
...mapActions('collection', [ ...mapGetters('collection', [
'fetchCollectionName' 'getCollection',
]), ]),
handleChange(event) { handleChange(event) {
if (event.added) { if (event.added) {
@ -921,7 +943,7 @@ export default {
this.$root.$emit('onCollectionBreadCrumbUpdate', [{ path: '', label: this.$i18n.get('metadata') }]); this.$root.$emit('onCollectionBreadCrumbUpdate', [{ path: '', label: this.$i18n.get('metadata') }]);
this.$nextTick(() => { this.$nextTick(() => {
this.columnsTopY = this.$refs.metadataEditionPageColumns.getBoundingClientRect().top; this.columnsTopY = this.$refs.metadataEditionPageColumns ? this.$refs.metadataEditionPageColumns.getBoundingClientRect().top : 0;
}); });
this.cleanMetadata(); this.cleanMetadata();
@ -943,25 +965,6 @@ export default {
.catch(() => { .catch(() => {
this.isLoadingMetadatumMappers = false; 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() { beforeDestroy() {
@ -969,10 +972,6 @@ export default {
if (this.metadataSearchCancel != undefined) if (this.metadataSearchCancel != undefined)
this.metadataSearchCancel.cancel('Metadata search Canceled.'); this.metadataSearchCancel.cancel('Metadata search Canceled.');
// Cancels previous collection name Request
if (this.collectionNameSearchCancel != undefined)
this.collectionNameSearchCancel.cancel('Collection name search Canceled.');
} }
} }
</script> </script>

View File

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

View File

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

View File

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

View File

@ -48,7 +48,7 @@
</router-link> </router-link>
</li> </li>
<li class="separator"/> <li class="separator"/>
<li> <li v-if="$userCaps.hasCapability('tnc_rep_edit_metadata')">
<router-link <router-link
tag="a" tag="a"
to="/metadata" to="/metadata"
@ -67,7 +67,7 @@
<span class="menu-text">{{ $i18n.get('metadata') }}</span> <span class="menu-text">{{ $i18n.get('metadata') }}</span>
</router-link> </router-link>
</li> </li>
<li> <li v-if="$userCaps.hasCapability('tnc_rep_edit_filters')">
<router-link <router-link
tag="a" tag="a"
to="/filters" to="/filters"
@ -124,6 +124,25 @@
<span class="menu-text">{{ $i18n.get('activities') }}</span> <span class="menu-text">{{ $i18n.get('activities') }}</span>
</router-link> </router-link>
</li> </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> <li>
<router-link <router-link
tag="a" tag="a"

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -222,27 +222,20 @@
this.$set(this.taxonomyFiltersCollectionNames, 'repository-filters', this.$i18n.get('repository')); 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 // Cancels previous collection name Request
if (this.collectionNameSearchCancel != undefined) if (this.collectionNameSearchCancel != undefined)
this.collectionNameSearchCancel.cancel('Collection name search Canceled.'); this.collectionNameSearchCancel.cancel('Collection name search Canceled.');
this.fetchCollectionName(taxonomyFilter) this.fetchAllCollectionNames(Object.keys(this.taxonomyFilters))
.then((resp) => { .then((resp) => {
resp.request resp.request
.then((collectionName) => { .then((collections) => {
this.$nextTick(() => { for (let collection of collections)
this.$set(this.taxonomyFiltersCollectionNames, taxonomyFilter, collectionName); this.$set(this.taxonomyFiltersCollectionNames, '' + collection.id, collection.name);
}); });
});
// Search Request Token for cancelling // Search Request Token for cancelling
this.collectionNameSearchCancel = resp.source; this.collectionNameSearchCancel = resp.source;
}) });
}
}
} }
}, },
repositoryCollectionFilters() { repositoryCollectionFilters() {
@ -250,27 +243,20 @@
this.$set(this.repositoryCollectionNames, 'repository-filters', this.$i18n.get('repository')); 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 // Cancels previous collection name Request
if (this.collectionNameSearchCancel != undefined) if (this.collectionNameSearchCancel != undefined)
this.collectionNameSearchCancel.cancel('Collection name search Canceled.'); this.collectionNameSearchCancel.cancel('Collection name search Canceled.');
this.fetchCollectionName(repositoryCollectionFilter) this.fetchAllCollectionNames()
.then((resp) => { .then((resp) => {
resp.request resp.request
.then((collectionName) => { .then((collections) => {
this.$nextTick(() => { for (let collection of collections)
this.$set(this.repositoryCollectionNames, '' + repositoryCollectionFilter, collectionName); this.$set(this.repositoryCollectionNames, '' + collection.id, collection.name);
}); });
});
// Search Request Token for cancelling // Search Request Token for cancelling
this.collectionNameSearchCancel = resp.source; this.collectionNameSearchCancel = resp.source;
}) });
}
}
} }
} }
}, },
@ -279,7 +265,7 @@
'getPostQuery' 'getPostQuery'
]), ]),
...mapActions('collection',[ ...mapActions('collection',[
'fetchCollectionName' 'fetchAllCollectionNames'
]), ]),
}, },
computed: { computed: {
@ -287,7 +273,7 @@
return this.getPostQuery(); return this.getPostQuery();
}, },
taxonomyId () { taxonomyId () {
let taxonomyArray = this.taxonomy.split("_"); const taxonomyArray = this.taxonomy.split("_");
return taxonomyArray[taxonomyArray.length - 1]; 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 ItemPage from '../pages/singles/item-page.vue'
import MetadataPage from '../pages/lists/metadata-page.vue' import MetadataPage from '../pages/lists/metadata-page.vue'
import FiltersPage from '../pages/lists/filters-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 ActivitiesPage from '../pages/lists/activities-page.vue'
import AvailableExportersPage from '../pages/lists/available-exporters-page.vue' import AvailableExportersPage from '../pages/lists/available-exporters-page.vue'
import AvailableImportersPage from '../pages/lists/available-importers-page.vue' import AvailableImportersPage from '../pages/lists/available-importers-page.vue'
import CapabilitiesPage from '../pages/lists/capabilities-page.vue'
// Edition Form Components // Edition Form Components
import CollectionEditionForm from '../components/edition/collection-edition-form.vue' import CollectionEditionForm from '../components/edition/collection-edition-form.vue'
@ -40,49 +41,52 @@ const routes = [
{ path: '/', redirect:'/home' }, { path: '/', redirect:'/home' },
{ path: '/home', name: 'HomePage', component: HomePage, meta: {title: 'Tainacan'} }, { 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', name: 'CollectionsPage', component: CollectionsPage, meta: { title: i18nGet('title_repository_collections_page') } },
{ path: '/collections/new', name: 'CollectionCreationForm', component: CollectionEditionForm, meta: {title: i18nGet('title_create_collection'), icon: 'folder-multiple'} }, { 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'), icon: 'folder-multiple'} }, { 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: [ children: [
{ path: '', redirect: 'items'}, { path: '', redirect: 'items'},
{ path: 'items', component: ItemsPage, name: 'CollectionItemsPage', meta: {title: i18nGet('title_collection_page'), 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'), icon: 'folder-multiple'} }, { 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'), icon: 'folder-multiple'} }, { 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'), icon: 'folder-multiple'} }, { 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'), icon: 'folder-multiple'} }, { 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'), icon: 'folder-multiple'} }, { 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'), icon: 'folder-multiple'} }, { 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'), icon: 'folder-multiple'} }, { 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'), icon: 'folder-multiple'} }, { 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'), icon: 'flash'} }, { path: 'activities', component: ActivitiesPage, name: 'CollectionActivitiesPage', meta: {title: i18nGet('title_collection_activities') } },
{ path: 'sequence/:sequenceId', name: 'SavedSequenceEditionForm', component: ItemEditionForm, meta: {title: i18nGet('title_edit_item'), icon: 'folder-multiple'} }, { path: 'capabilities', component: CapabilitiesPage, name: 'CollectionCapabilitiesPage', meta: {title: i18nGet('title_collection_capabilities') } },
{ path: 'sequence/:sequenceId/:itemPosition', name: 'SequenceEditionForm', component: ItemEditionForm, meta: {title: i18nGet('title_edit_item'), icon: 'folder-multiple'} }, { 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', name: 'ItemsPage', component: ItemsPage, meta: {title: i18nGet('title_items_page') } },
{ path: '/items/new', name: 'ItemCreationForm', component: ItemEditionForm, meta: {title: i18nGet('title_create_item'), icon: 'file-multiple'} }, { 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', name: 'TaxonomyPage', component: TaxonomyPage, meta: {title: i18nGet('title_taxonomies_page') } },
{ path: '/taxonomies/new', name: 'TaxonomyCreationForm', component: TaxonomyEditionForm, meta: {title: i18nGet('title_create_taxonomy_page'), icon: 'shape'} }, { 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'), icon: 'shape'} }, { path: '/taxonomies/:taxonomyId/edit', name: 'TaxonomyEditionForm', component: TaxonomyEditionForm, meta: {title: i18nGet('title_taxonomy_edition_page') } },
{ path: '/taxonomies/:taxonomyId', redirect: '/taxonomies/:taxonomyId/edit' }, { 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: '/capabilities', component: CapabilitiesPage, name: 'CapabilitiesPage', meta: {title: i18nGet('title_repository_capabilities') } },
{ 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: '/exporters/', name: 'ExportersPage', component: AvailableExportersPage, meta: {title: i18nGet('title_exporters_page'), icon: 'export'} }, { path: '/importers/', name: 'AvailableImportersPage', component: AvailableImportersPage, meta: {title: i18nGet('title_importers_page') } },
{ path: '/exporters/:exporterSlug', name: 'ExporterEditionForm', component: ExporterEditionForm, meta: {title: i18nGet('title_exporter_page'), icon: 'export'}}, { 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: '/'} { path: '*', redirect: '/'}
]; ];

View File

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

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

@ -394,6 +394,62 @@ $modal-z: 9999999;
animation-timing-function: ease; 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 // Filters menu
@keyframes filters-menu-in { @keyframes filters-menu-in {
from { 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' ), 'processes' => __( 'Processes', 'tainacan' ),
'sequence' => __( 'Sequence', 'tainacan' ), 'sequence' => __( 'Sequence', 'tainacan' ),
'exporters' => __( 'Exporters', 'tainacan' ), 'exporters' => __( 'Exporters', 'tainacan' ),
'capabilities' => __( 'Capabilities', 'tainacan' ),
// Actions // Actions
'close' => __( 'Close', 'tainacan' ), 'close' => __( 'Close', 'tainacan' ),
@ -132,6 +133,8 @@ return apply_filters( 'tainacan-admin-i18n', [
'title_processes_page' => __( 'Processes', 'tainacan' ), 'title_processes_page' => __( 'Processes', 'tainacan' ),
'title_item_bulk_add' => __( 'Bulk Add Items', 'tainacan' ), 'title_item_bulk_add' => __( 'Bulk Add Items', 'tainacan' ),
'title_exporter_page' => __( 'Exporter', '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) // Labels (used mainly on Aria Labels and Inputs)
'label' => __( 'Label', 'tainacan' ), 'label' => __( 'Label', 'tainacan' ),
@ -156,7 +159,6 @@ return apply_filters( 'tainacan-admin-i18n', [
'label_thumbnail' => __( 'Thumbnail', 'tainacan' ), 'label_thumbnail' => __( 'Thumbnail', 'tainacan' ),
'label_empty_thumbnail' => __( 'Empty Thumbnail', 'tainacan' ), 'label_empty_thumbnail' => __( 'Empty Thumbnail', 'tainacan' ),
'label_empty_term_image' => __( 'Empty Term Image', 'tainacan' ), 'label_empty_term_image' => __( 'Empty Term Image', 'tainacan' ),
'label_moderators' => __( 'Moderators', 'tainacan' ),
'label_parent_collection' => __( 'Parent collection', 'tainacan' ), 'label_parent_collection' => __( 'Parent collection', 'tainacan' ),
'label_no_parent_collection' => __( 'No parent collection', 'tainacan' ), 'label_no_parent_collection' => __( 'No parent collection', 'tainacan' ),
'label_button_view' => __( 'Button View', 'tainacan' ), 'label_button_view' => __( 'Button View', 'tainacan' ),
@ -413,6 +415,7 @@ return apply_filters( 'tainacan-admin-i18n', [
'label_repository_metadatum' => __( 'Repository metadatum', 'tainacan' ), 'label_repository_metadatum' => __( 'Repository metadatum', 'tainacan' ),
'label_collection_filter' => __( 'Collection filter', 'tainacan' ), 'label_collection_filter' => __( 'Collection filter', 'tainacan' ),
'label_collection_metadatum' => __( 'Collection metadatum', 'tainacan' ), 'label_collection_metadatum' => __( 'Collection metadatum', 'tainacan' ),
'label_collection_capabilities' => __( 'Collection capabilities', 'tainacan' ),
'label_recover_from_trash' => __( 'Recover from trash', 'tainacan' ), 'label_recover_from_trash' => __( 'Recover from trash', 'tainacan' ),
'label_show_children_terms' => __( 'Show children terms', 'tainacan' ), 'label_show_children_terms' => __( 'Show children terms', 'tainacan' ),
'label_begin_slide_transition' => __( 'Begin slide transition', 'tainacan' ), 'label_begin_slide_transition' => __( 'Begin slide transition', 'tainacan' ),
@ -442,6 +445,9 @@ return apply_filters( 'tainacan-admin-i18n', [
'label_month' => __( 'Month', 'tainacan' ), 'label_month' => __( 'Month', 'tainacan' ),
'label_year' => __( 'Year', 'tainacan' ), 'label_year' => __( 'Year', 'tainacan' ),
'label_related_to' => __( 'Related to', '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 // Instructions. More complex sentences to guide user and placeholders
'instruction_delete_selected_collections' => __( 'Delete selected collections', 'tainacan' ), 'instruction_delete_selected_collections' => __( 'Delete selected collections', 'tainacan' ),
@ -456,6 +462,7 @@ return apply_filters( 'tainacan-admin-i18n', [
'instruction_cover_page' => __( 'Type to search a Page to choose.', 'tainacan' ), 'instruction_cover_page' => __( 'Type to search a Page to choose.', 'tainacan' ),
'instruction_moderators' => __( 'Type to search a User to add.', '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_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_a_parent_collection' => __( 'Select a parent collection.', 'tainacan' ),
'instruction_select_collection_thumbnail' => __( 'Select a thumbnail image for collection', 'tainacan' ), 'instruction_select_collection_thumbnail' => __( 'Select a thumbnail image for collection', 'tainacan' ),
'instruction_select_item_thumbnail' => __( 'Select a thumbnail image for item', 'tainacan' ), 'instruction_select_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_write_text' => __( 'Write Text', 'tainacan' ),
'instruction_search' => __( 'Search', 'tainacan' ), 'instruction_search' => __( 'Search', 'tainacan' ),
'instruction_search_in_repository' => __( 'Search in repository', '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_a_mapper' => __( 'Select a mapper', 'tainacan' ),
'instruction_select_an_importer_type' => __( 'Select an importer from the options below:', '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' ), 'instruction_drop_file_or_click_to_upload' => __( 'Drop your source file or click here to upload.', '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_filter' => __( 'No item was found here with these filters.', 'tainacan' ),
'info_no_item_found' => __( 'No item was found.', 'tainacan' ), 'info_no_item_found' => __( 'No item was found.', 'tainacan' ),
'info_item_not_saved' => __( 'Warning: Item not saved.', '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_collection' => __( 'Error on deleting collection.', 'tainacan' ),
'info_error_deleting_taxonomy' => __( 'Error on deleting taxonomy', 'tainacan' ), 'info_error_deleting_taxonomy' => __( 'Error on deleting taxonomy', 'tainacan' ),
'info_error_first_value_greater' => __( 'First value should be lower than second value', '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_taxonomies' => __( 'Showing taxonomies ', 'tainacan' ),
'info_showing_activities' => __( 'Showing activities ', 'tainacan' ), 'info_showing_activities' => __( 'Showing activities ', 'tainacan' ),
'info_showing_processes' => __( 'Showing processes ', '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_showing_terms' => __( 'Showing terms ', 'tainacan' ),
'info_warning_remove_from_trash_first' => __( 'Remove this item from trash first' ), 'info_warning_remove_from_trash_first' => __( 'Remove this item from trash first' ),
'info_to' => __( ' to ', 'tainacan' ), '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_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_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_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_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_metadata_source_file' => __( 'No metadata was found from the source file.', 'tainacan' ),
'info_no_special_fields_available' => __( 'No special field was found.', '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_final_value' => __( 'Final value', 'tainacan' ),
'info_show_interval_on_tag' => __( 'Show applied interval on tags', '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_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 months
'datepicker_month_january' => __( 'January', 'tainacan' ), 'datepicker_month_january' => __( 'January', 'tainacan' ),

View File

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

View File

@ -183,9 +183,10 @@ class REST_Bulkedit_Controller extends REST_Controller {
public function bulk_edit_permissions_check($request) { public function bulk_edit_permissions_check($request) {
$collection = $this->collections_repository->fetch($request['collection_id']); $collection = $this->collections_repository->fetch($request['collection_id']);
$bulk_cap = 'tnc_col_' . $request['collection_id'] . '_bulk_edit';
if ($collection instanceof Entities\Collection) { 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; return false;

View File

@ -80,6 +80,36 @@ class REST_Collections_Controller extends REST_Controller {
), ),
'schema' => [$this, 'get_schema'], '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(); $item_arr = $item->_toArray();
if ( $request['context'] === 'edit' ) { 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_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 { } else {
$attributes_to_filter = $request['fetch_only']; $attributes_to_filter = $request['fetch_only'];
@ -205,6 +227,16 @@ class REST_Collections_Controller extends REST_Controller {
if ( $request['context'] === 'edit' ) { if ( $request['context'] === 'edit' ) {
$item_arr['current_user_can_edit'] = $item->can_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'] ); $item_arr['url'] = get_permalink( $item_arr['id'] );
@ -250,11 +282,6 @@ class REST_Collections_Controller extends REST_Controller {
* @throws \Exception * @throws \Exception
*/ */
public function get_items_permissions_check($request){ 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; return true;
} }
@ -269,11 +296,7 @@ class REST_Collections_Controller extends REST_Controller {
$collection = $this->collections_repository->fetch($request['collection_id']); $collection = $this->collections_repository->fetch($request['collection_id']);
if(($collection instanceof Entities\Collection)) { if(($collection instanceof Entities\Collection)) {
if('edit' === $request['context'] && !$collection->can_read()) { return $collection->can_read();
return false;
}
return true;
} }
return false; return false;
@ -331,8 +354,7 @@ class REST_Collections_Controller extends REST_Controller {
* @throws \Exception * @throws \Exception
*/ */
public function create_item_permissions_check( $request ) { public function create_item_permissions_check( $request ) {
$dummy = new Entities\Collection(); return current_user_can($this->collections_repository->get_capabilities()->edit_posts);
return current_user_can($dummy->get_capabilities()->edit_posts);
} }
/** /**
@ -469,6 +491,157 @@ class REST_Collections_Controller extends REST_Controller {
return false; 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;
}
/** /**
* @param string $method * @param string $method
* *
@ -518,10 +691,6 @@ class REST_Collections_Controller extends REST_Controller {
$main_schema = parent::get_repository_schema( $this->collections_repository ); $main_schema = parent::get_repository_schema( $this->collections_repository );
$permissions_schema = parent::get_permissions_schema(); $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( $schema['properties'] = array_merge(
parent::get_base_properties_schema(), parent::get_base_properties_schema(),
$main_schema, $main_schema,

View File

@ -172,7 +172,15 @@ class REST_Facets_Controller extends REST_Controller {
* @return bool|\WP_Error * @return bool|\WP_Error
*/ */
public function get_items_permissions_check( $request ) { 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

@ -199,24 +199,20 @@ class REST_Filters_Controller extends REST_Controller {
* @throws \Exception * @throws \Exception
*/ */
public function create_item_permissions_check( $request ) { public function create_item_permissions_check( $request ) {
if(isset($request['collection_id']) && isset($request['metadatum_id'])) { if( isset($request['collection_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'])){
$collection = $this->collection_repository->fetch( $request['collection_id'] ); $collection = $this->collection_repository->fetch( $request['collection_id'] );
if ( $collection instanceof Entities\Collection ) { 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;
} }
/** /**
@ -384,7 +380,7 @@ class REST_Filters_Controller extends REST_Controller {
$filters = $this->filter_repository->fetch( $args, 'OBJECT' ); $filters = $this->filter_repository->fetch( $args, 'OBJECT' );
} else { } else {
$collection = $this->collection_repository->fetch($request['collection_id']); $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 = []; $response = [];
@ -402,9 +398,6 @@ class REST_Filters_Controller extends REST_Controller {
*/ */
public function get_items_permissions_check( $request ) { public function get_items_permissions_check( $request ) {
if(!isset($request['collection_id'])) { if(!isset($request['collection_id'])) {
if ( 'edit' === $request['context'] && ! $this->filter_repository->can_edit( new Entities\Filter() ) ) {
return false;
}
return true; return true;
} }
@ -412,7 +405,7 @@ class REST_Filters_Controller extends REST_Controller {
$collection = $this->collection_repository->fetch($request['collection_id']); $collection = $this->collection_repository->fetch($request['collection_id']);
if($collection instanceof Entities\Collection){ if($collection instanceof Entities\Collection){
if ( 'edit' === $request['context'] && ! $collection->can_read() ) { if ( ! $collection->can_read() ) {
return false; return false;
} }
@ -451,11 +444,7 @@ class REST_Filters_Controller extends REST_Controller {
$filter = $this->filter_repository->fetch($request['filter_id']); $filter = $this->filter_repository->fetch($request['filter_id']);
if(($filter instanceof Entities\Filter)) { if(($filter instanceof Entities\Filter)) {
if('edit' === $request['context'] && !$filter->can_read()) { return $filter->can_read();
return false;
}
return true;
} }
return false; return false;

View File

@ -164,11 +164,7 @@ class REST_Item_Metadata_Controller extends REST_Controller {
$item = $this->item_repository->fetch($request['item_id']); $item = $this->item_repository->fetch($request['item_id']);
if(($item instanceof Entities\Item)) { if(($item instanceof Entities\Item)) {
if('edit' === $request['context'] && !$item->can_read()) { return $item->can_read();
return false;
}
return true;
} }
return false; return false;
@ -243,15 +239,15 @@ class REST_Item_Metadata_Controller extends REST_Controller {
public function update_item_permissions_check( $request ) { public function update_item_permissions_check( $request ) {
if (isset($request['item_id'])) { if (isset($request['item_id'])) {
$item = $this->item_repository->fetch($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 instanceof Entities\Item && $metadatum instanceof Entities\Metadatum ) {
if($item->can_edit()) { if( $item->can_edit() && $metadatum->can_read() ) {
return $item->can_edit(); return true;
} }
else { else {
$metadatum_id = $request['metadatum_id']; // not yet implemented
$metadatum = $this->metadatum_repository->fetch( $metadatum_id ); // return 'publish' === $metadatum->get_status() && $metadatum->get_accept_suggestion();
return 'publish' === $metadatum->get_status() && $metadatum->get_accept_suggestion();
} }
} }

View File

@ -413,11 +413,7 @@ class REST_Items_Controller extends REST_Controller {
$item = $this->items_repository->fetch($request['item_id']); $item = $this->items_repository->fetch($request['item_id']);
if(($item instanceof Entities\Item)) { if(($item instanceof Entities\Item)) {
if('edit' === $request['context'] && !$item->can_read()) { return $item->can_read();
return false;
}
return true;
} }
return false; return false;
@ -431,15 +427,11 @@ class REST_Items_Controller extends REST_Controller {
public function get_items_permissions_check( $request ) { public function get_items_permissions_check( $request ) {
$collection = $this->collections_repository->fetch($request['collection_id']); $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']) ) { if ( isset($request['taxquery']) && !$this->get_items_permissions_check_for_taxonomy($request['taxquery']) ) {
return false; return false;
} }
if(($collection instanceof Entities\Collection)) { if( $collection instanceof Entities\Collection ) {
if(!$collection->can_read()) { if(!$collection->can_read()) {
return false; return false;
} }
@ -450,11 +442,11 @@ class REST_Items_Controller extends REST_Controller {
} }
private function get_items_permissions_check_for_taxonomy($taxonomies) { private function get_items_permissions_check_for_taxonomy($taxonomies) {
$taxonomy_repository = Repositories\Taxonomies::get_instance();
foreach ($taxonomies as $tax) { foreach ($taxonomies as $tax) {
$tax_id = $taxonomy_repository->get_id_by_db_identifier($tax['taxonomy']); $taxonomy = \tainacan_taxonomies()->fetch_by_db_identifier( $tax['taxonomy'] );
$taxonomy = $taxonomy_repository->fetch($tax_id);
if(($taxonomy instanceof Entities\Taxonomy)) { if( $taxonomy instanceof Entities\Taxonomy ) {
if(!$taxonomy->can_read()) { if(!$taxonomy->can_read()) {
return false; return false;
} }

View File

@ -38,17 +38,6 @@ class REST_Logs_Controller extends REST_Controller {
'methods' => \WP_REST_Server::READABLE, 'methods' => \WP_REST_Server::READABLE,
'callback' => array($this, 'get_item'), 'callback' => array($this, 'get_item'),
'permission_callback' => array($this, 'get_item_permissions_check'), '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'] 'schema' => [$this, 'get_schema']
) )
@ -314,8 +303,7 @@ class REST_Logs_Controller extends REST_Controller {
* @return bool|\WP_Error * @return bool|\WP_Error
*/ */
public function get_items_permissions_check( $request ) { public function get_items_permissions_check( $request ) {
return true; return current_user_can( 'tnc_rep_read_logs' );
return current_user_can('read');
} }
/** /**
@ -339,18 +327,7 @@ class REST_Logs_Controller extends REST_Controller {
* @return bool|\WP_Error * @return bool|\WP_Error
*/ */
public function get_item_permissions_check( $request ) { public function get_item_permissions_check( $request ) {
return true; return current_user_can( 'tnc_rep_read_logs' );
$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;
} }
/** /**

View File

@ -177,28 +177,10 @@ class REST_Metadata_Controller extends REST_Controller {
* @throws \Exception * @throws \Exception
*/ */
public function get_item_permissions_check( $request ) { public function get_item_permissions_check( $request ) {
$collection_id = isset($request['collection_id']) ? $request['collection_id'] : false;
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']); $metadatum = $this->metadatum_repository->fetch($request['metadatum_id']);
if ( $metadatum instanceof Entities\Metadatum ) { if ( $metadatum instanceof Entities\Metadatum ) {
if ( $request['context'] === 'edit' && ! $metadatum->can_read() ) { return $metadatum->can_read();
return false;
}
return true;
}
} }
return false; return false;
@ -297,11 +279,22 @@ class REST_Metadata_Controller extends REST_Controller {
* @throws \Exception * @throws \Exception
*/ */
public function create_item_permissions_check( $request ) { public function create_item_permissions_check( $request ) {
if( isset($request['collection_id']) ) { if( isset($request['collection_id']) ) {
return $this->collection_repository->can_edit( new Entities\Collection() ); $collection = $this->collection_repository->fetch( $request['collection_id'] );
if ( $collection instanceof Entities\Collection ) {
return $collection->user_can( 'edit_metadata' );
} }
return $this->metadatum_repository->can_edit(new Entities\Metadatum()); } else {
return current_user_can( 'tnc_rep_edit_metadata' );
}
return false;
} }
/** /**
@ -370,7 +363,7 @@ class REST_Metadata_Controller extends REST_Controller {
$collection = new Entities\Collection( $collection_id ); $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 { } else {
$args = [ $args = [
'meta_query' => [ 'meta_query' => [
@ -401,18 +394,15 @@ class REST_Metadata_Controller extends REST_Controller {
*/ */
public function get_items_permissions_check( $request ) { 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; return true;
} }
$collection = $this->collection_repository->fetch($request['collection_id']); $collection = $this->collection_repository->fetch($request['collection_id']);
if($collection instanceof Entities\Collection){ if($collection instanceof Entities\Collection){
if ( 'edit' === $request['context'] && ! $collection->can_read() ) { if ( ! $collection->can_read() ) {
return false; return false;
} }
@ -420,6 +410,7 @@ class REST_Metadata_Controller extends REST_Controller {
} }
return false; return false;
} }
/** /**
@ -453,15 +444,14 @@ class REST_Metadata_Controller extends REST_Controller {
* @throws \Exception * @throws \Exception
*/ */
public function delete_item_permissions_check( $request ) { public function delete_item_permissions_check( $request ) {
if (isset($request['collection_id'])) { $metadatum = $this->metadatum_repository->fetch($request['metadatum_id']);
$collection = $this->collection_repository->fetch($request['collection_id']);
if ($collection instanceof Entities\Collection) { if ($metadatum instanceof Entities\Metadatum) {
return $collection->can_delete(); 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 * @throws \Exception
*/ */
public function update_item_permissions_check( $request ) { public function update_item_permissions_check( $request ) {
if(isset($request['collection_id'])) { $metadatum = $this->metadatum_repository->fetch($request['metadatum_id']);
$collection = $this->collection_repository->fetch($request['collection_id']);
if ($collection instanceof Entities\Collection) { if ($metadatum instanceof Entities\Metadatum) {
return $collection->can_edit(); return $metadatum->can_edit();
} }
} return false;
return $this->metadatum_repository->can_edit(new Entities\Metadatum());
} }
/** /**

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

@ -220,14 +220,7 @@ class REST_Taxonomies_Controller extends REST_Controller {
$taxonomy = $this->taxonomy_repository->fetch($request['taxonomy_id']); $taxonomy = $this->taxonomy_repository->fetch($request['taxonomy_id']);
if(($taxonomy instanceof Entities\Taxonomy)) { if(($taxonomy instanceof Entities\Taxonomy)) {
if('edit' === $request['context'] && !is_user_logged_in()) { return $taxonomy->can_read();
return false;
}
if(!$taxonomy->can_read()) {
return false;
}
return true;
} }
return false; return false;
@ -341,9 +334,7 @@ class REST_Taxonomies_Controller extends REST_Controller {
* @return bool|\WP_Error * @return bool|\WP_Error
*/ */
public function get_items_permissions_check( $request ) { 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)) { // if(!$this->taxonomy_repository->can_read($this->taxonomy)) {
// return false; // return false;
// } // }
@ -387,7 +378,7 @@ class REST_Taxonomies_Controller extends REST_Controller {
* @return bool|\WP_Error * @return bool|\WP_Error
*/ */
public function create_item_permissions_check( $request ) { 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 );
} }
/** /**

View File

@ -138,7 +138,22 @@ class REST_Terms_Controller extends REST_Controller {
$taxonomy = $this->taxonomy_repository->fetch($request['taxonomy_id']); $taxonomy = $this->taxonomy_repository->fetch($request['taxonomy_id']);
if ($taxonomy instanceof Entities\Taxonomy) { 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; return false;
@ -176,10 +191,12 @@ class REST_Terms_Controller extends REST_Controller {
* @return bool|\WP_Error * @return bool|\WP_Error
*/ */
public function delete_item_permissions_check( $request ) { public function delete_item_permissions_check( $request ) {
$term_id = $request['term_id'];
$taxonomy = $this->taxonomy_repository->fetch($request['taxonomy_id']); $taxonomy = $this->taxonomy_repository->fetch($request['taxonomy_id']);
$term = $this->terms_repository->fetch($term_id, $taxonomy);
if ($taxonomy instanceof Entities\Taxonomy) { if ($term instanceof Entities\Term) {
return $taxonomy->can_edit(); return $term->can_delete();
} }
return false; return false;
@ -243,10 +260,12 @@ class REST_Terms_Controller extends REST_Controller {
* @return bool|\WP_Error * @return bool|\WP_Error
*/ */
public function update_item_permissions_check( $request ) { public function update_item_permissions_check( $request ) {
$term_id = $request['term_id'];
$taxonomy = $this->taxonomy_repository->fetch($request['taxonomy_id']); $taxonomy = $this->taxonomy_repository->fetch($request['taxonomy_id']);
$term = $this->terms_repository->fetch($term_id, $taxonomy);
if ($taxonomy instanceof Entities\Taxonomy) { if ($term instanceof Entities\Term) {
return $taxonomy->can_edit(); return $term->can_edit();
} }
return false; return false;
@ -354,14 +373,7 @@ class REST_Terms_Controller extends REST_Controller {
$taxonomy = $this->taxonomy_repository->fetch($request['taxonomy_id']); $taxonomy = $this->taxonomy_repository->fetch($request['taxonomy_id']);
if(($taxonomy instanceof Entities\Taxonomy)) { if(($taxonomy instanceof Entities\Taxonomy)) {
if('edit' === $request['context'] && !is_user_logged_in()) { return $taxonomy->can_read();
return false;
}
if(!$taxonomy->can_read()) {
return false;
}
return true;
} }
return false; return false;
@ -397,17 +409,12 @@ class REST_Terms_Controller extends REST_Controller {
* @return bool|\WP_Error * @return bool|\WP_Error
*/ */
public function get_item_permissions_check( $request ) { public function get_item_permissions_check( $request ) {
$term_id = $request['term_id'];
$taxonomy = $this->taxonomy_repository->fetch($request['taxonomy_id']); $taxonomy = $this->taxonomy_repository->fetch($request['taxonomy_id']);
$term = $this->terms_repository->fetch($term_id, $taxonomy);
if(($taxonomy instanceof Entities\Taxonomy)) { if ($term instanceof Entities\Term) {
if('edit' === $request['context'] && !is_user_logged_in()) { return $term->can_read();
return false;
}
if(!$taxonomy->can_read()) {
return false;
}
return true;
} }
return false; return false;
@ -438,6 +445,20 @@ class REST_Terms_Controller extends REST_Controller {
} }
$endpoint_args = $map; $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; return $endpoint_args;

View File

@ -18,6 +18,7 @@ $rest_exporters_controller = new \Tainacan\API\EndPoints\REST_Exporters_Contr
$rest_background_processes_controller = new \Tainacan\API\EndPoints\REST_Background_Processes_Controller(); $rest_background_processes_controller = new \Tainacan\API\EndPoints\REST_Background_Processes_Controller();
$rest_bulkedit_controller = new \Tainacan\API\EndPoints\REST_Bulkedit_Controller(); $rest_bulkedit_controller = new \Tainacan\API\EndPoints\REST_Bulkedit_Controller();
$rest_exposers_controller = new \Tainacan\API\EndPoints\REST_Exposers_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(); new \Tainacan\API\EndPoints\REST_Metadatum_Mappers_Controller();
$rest_facets_controller = new \Tainacan\API\EndPoints\REST_Facets_Controller(); $rest_facets_controller = new \Tainacan\API\EndPoints\REST_Facets_Controller();
$rest_oaipmh_expose_controller = new \Tainacan\API\EndPoints\REST_Oaipmh_Expose_Controller(); $rest_oaipmh_expose_controller = new \Tainacan\API\EndPoints\REST_Oaipmh_Expose_Controller();

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) { 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) { foreach ($metadata as $meta) {
$meta_ids[] = $meta->get_id(); $meta_ids[] = $meta->get_id();
@ -200,7 +200,7 @@ class Elastic_Press {
foreach ( $args['post_type'] as $cpt ) { foreach ( $args['post_type'] as $cpt ) {
$col = $Tainacan_Collections->fetch_by_db_identifier($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) { foreach ($_filters as $filter) {
$include = []; $include = [];
$filter_id = $filter->get_id(); $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, $cover_page_id,
$header_image_id, $header_image_id,
$header_image, $header_image,
$moderators_ids,
$comment_status, $comment_status,
$allow_comments; $allow_comments;
@ -145,21 +144,60 @@ class Collection extends Entity {
/** /**
* Get the capabilities list for the post type of the items of this collection * 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 * This method is usefull for getting the capabilities of the collection's items post type
* regardless if it has been already registered or not. * regardless if it has been already registered or not.
* *
* @return object Object with all the capabilities as member variables. * @return object Object with all the capabilities as member variables.
*/ */
function get_items_capabilities() { function get_items_capabilities() {
$args = [
'map_meta_cap' => true, $id = $this->get_id();
'capability_type' => $this->get_db_identifier(),
'capabilities' => array() 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 );
} }
/** /**
@ -412,15 +450,6 @@ class Collection extends Entity {
return $this->get_mapped_property( 'filters_order' ); 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 * Get collection DB identifier
* *
@ -445,7 +474,7 @@ class Collection extends Entity {
function get_metadata() { function get_metadata() {
$Tainacan_Metadata = \Tainacan\Repositories\Metadata::get_instance(); $Tainacan_Metadata = \Tainacan\Repositories\Metadata::get_instance();
return $Tainacan_Metadata->fetch_by_collection( $this, [], 'OBJECT' ); return $Tainacan_Metadata->fetch_by_collection( $this );
} }
/** /**
@ -669,27 +698,6 @@ class Collection extends Entity {
$this->set_mapped_property( 'header_image_id', $value ); $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. * Sets if comments are allowed for the current Collection.
* *
@ -708,59 +716,6 @@ class Collection extends Entity {
$this->set_mapped_property('allow_comments', $value ); $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 * 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(); $id = $this->get_id();
return admin_url("?page=tainacan_admin#/collections/$collection_id/items/$id/edit"); 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); 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 * @param $collection_id

View File

@ -99,6 +99,31 @@ class Taxonomy extends Entity {
return true; 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 // Getters
/** /**

View File

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

View File

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

View File

@ -128,6 +128,15 @@ class Taxonomy extends Metadata_Type {
} }
} }
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([ $taxonomy_metadata = $Tainacan_Metadata->fetch([
'collection_id' => $metadatum->get_collection_id(), 'collection_id' => $metadatum->get_collection_id(),
'metadata_type' => 'Tainacan\\Metadata_Types\\Taxonomy' 'metadata_type' => 'Tainacan\\Metadata_Types\\Taxonomy'

View File

@ -22,14 +22,6 @@ class Collections extends Repository {
return self::$instance; return self::$instance;
} }
/**
* Collections constructor.
*/
protected function __construct() {
parent::__construct();
add_filter( 'map_meta_cap', array( $this, 'map_meta_cap' ), 10, 4 );
}
/** /**
* {@inheritDoc} * {@inheritDoc}
* @see \Tainacan\Repositories\Repository::get_map() * @see \Tainacan\Repositories\Repository::get_map()
@ -182,14 +174,6 @@ class Collections extends Repository {
//'validation' => v::numeric(), //'validation' => v::numeric(),
'default' => '' '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' => [ '_thumbnail_id' => [
'map' => 'meta', 'map' => 'meta',
'title' => __( 'Thumbnail', 'tainacan' ), 'title' => __( 'Thumbnail', 'tainacan' ),
@ -255,7 +239,7 @@ class Collections extends Repository {
'can_export' => true, 'can_export' => true,
/* Translators: The Collections slug - will be the URL for the collections archive */ /* 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'))], '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, 'map_meta_cap' => true,
'supports' => [ 'supports' => [
'title', 'title',
@ -285,7 +269,6 @@ class Collections extends Repository {
$collection->register_collection_item_post_type(); $collection->register_collection_item_post_type();
flush_rewrite_rules( false ); // needed to activate items post type archive url flush_rewrite_rules( false ); // needed to activate items post type archive url
$this->update_moderators( $new_collection );
return $new_collection; return $new_collection;
} }
@ -361,9 +344,6 @@ class Collections extends Repository {
} }
function pre_process( $collection ) { 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_collection = $this->fetch( $collection->get_id() );
$this->old_core_title = $collection->get_core_title_metadatum(); $this->old_core_title = $collection->get_core_title_metadatum();
@ -372,16 +352,6 @@ 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 ) { function handle_core_metadata( $collection ) {
$Tainacan_Metadata = \Tainacan\Repositories\Metadata::get_instance(); $Tainacan_Metadata = \Tainacan\Repositories\Metadata::get_instance();
@ -396,44 +366,6 @@ class Collections extends Repository {
} }
} }
/**
* 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, 'rewrite' => true,
'map_meta_cap' => true, 'map_meta_cap' => true,
'show_in_nav_menus' => false, 'show_in_nav_menus' => false,
'capability_type' => Entities\Metadatum::get_capability_type(), 'capabilities' => (array) $this->get_capabilities(),
'supports' => [ 'supports' => [
'title', 'title',
'editor', 'editor',
@ -351,23 +351,67 @@ class Filters extends Repository {
* *
* @param Entities\Collection $collection * @param Entities\Collection $collection
* @param array $args WP_Query args plus disabled_metadata * @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 * @return array Entities\Metadatum
* @throws \Exception * @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(); $collection_id = $collection->get_id();
//get parent collections //get parent collections
$parents = get_post_ancestors( $collection_id ); $parents = get_post_ancestors( $collection_id );
//insert the actual collection //insert the actual collection
if ( is_numeric($collection_id) ) {
$parents[] = $collection_id; $parents[] = $collection_id;
}
//search for default metadatum //search for default metadatum
$parents[] = 'default'; $parents[] = 'default';
$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;
$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( $meta_query = array(
'key' => 'collection_id', 'key' => 'collection_id',
'value' => $parents, 'value' => $parents,
@ -378,14 +422,16 @@ class Filters extends Repository {
'parent' => 0 'parent' => 0
], $args ); ], $args );
if ( isset( $args['meta_query'] ) ) { $args['meta_query'] = $original_meta_q;
$args['meta_query'][] = $meta_query; $args['meta_query'][] = $meta_query;
} else {
$args['meta_query'] = array( $meta_query ); $results = $this->fetch( $args, 'OBJECT' );
} }
return $this->order_result( return $this->order_result(
$this->fetch( $args, $output ), $results,
$collection, $collection,
isset( $args['include_disabled'] ) ? $args['include_disabled'] : false isset( $args['include_disabled'] ) ? $args['include_disabled'] : false
); );
@ -414,11 +460,55 @@ class Filters extends Repository {
$parents = get_post_ancestors( $collection_id ); $parents = get_post_ancestors( $collection_id );
//insert the actual collection //insert the actual collection
if ( is_numeric($collection_id) ) {
$parents[] = $collection_id; $parents[] = $collection_id;
}
//search for default metadatum //search for default metadatum
$parents[] = 'default'; $parents[] = 'default';
$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;
$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( $meta_query = array(
'key' => 'collection_id', 'key' => 'collection_id',
'value' => $parents, 'value' => $parents,
@ -429,13 +519,13 @@ class Filters extends Repository {
'parent' => 0 'parent' => 0
], $args ); ], $args );
if ( isset( $args['meta_query'] ) ) { $args['meta_query'] = $original_meta_q;
$args['meta_query'][] = $meta_query; $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 []; return [];
} }
$meta_list = $Tainacan_Metadata->fetch_by_collection( $collection, $args, 'OBJECT' ); $meta_list = $Tainacan_Metadata->fetch_by_collection( $collection, $args );
$return = []; $return = [];

View File

@ -13,6 +13,9 @@ class Items extends Repository {
private static $instance = null; private static $instance = null;
// temporary variable used to filter items query
private $fetching_from_collections = [];
public static function get_instance() { public static function get_instance() {
if ( ! isset( self::$instance ) ) { if ( ! isset( self::$instance ) ) {
self::$instance = new self(); self::$instance = new self();
@ -23,8 +26,6 @@ class Items extends Repository {
protected function __construct() { protected function __construct() {
parent::__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_filter( 'comments_open', [$this, 'hook_comments_open'], 10, 2);
add_action( 'tainacan-api-item-updated', array( &$this, 'hook_api_updated_item' ), 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 ); 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 ) ) { if ( empty( $collections ) ) {
$no_collection_set = true;
$post_types = get_post_types(); $post_types = get_post_types();
$collections = array_map( function($el) use ($Tainacan_Collections) { $collections = array_map( function($el) use ($Tainacan_Collections) {
if ( $id = $Tainacan_Collections->get_id_by_db_identifier($el) ) { if ( $id = $Tainacan_Collections->get_id_by_db_identifier($el) ) {
return $id; return $id;
@ -258,15 +271,13 @@ class Items extends Repository {
foreach ( $collections_objects as $collection ) { foreach ( $collections_objects as $collection ) {
/** /**
* If no specific status is defined in the query, WordPress will fetch * If no specific collection was queried, we will fetch
* public items and private items for users withe the correct permission. * mimick WordPress behavior and return only collections current user can read
*
* If a collection is private, it must have the same behavior, despite its
* items are public or not.
*/ */
if ( ! isset( $args['post_status'] ) ) { if ( $no_collection_set) {
$status_obj = get_post_status_object( $collection->get_status() ); $status_obj = get_post_status_object( $collection->get_status() );
if ( $status_obj->public || current_user_can( $collection->cap->read_private_posts ) ) { if ( $status_obj->public || current_user_can( $collection->cap->read_private_posts ) ) {
$cpt[] = $collection->get_db_identifier(); $cpt[] = $collection->get_db_identifier();
} }
} else { } else {
@ -275,13 +286,12 @@ class Items extends Repository {
} }
$this->fetching_from_collections = $collections_objects;
if ( empty( $cpt ) ) { if ( empty( $cpt ) ) {
$cpt[] = 'please-return-nothing'; $cpt[] = 'please-return-nothing';
} }
//TODO: get collection order and order by options
$args = $this->parse_fetch_args( $args ); $args = $this->parse_fetch_args( $args );
$args['post_type'] = $cpt; $args['post_type'] = $cpt;
@ -305,8 +315,18 @@ class Items extends Repository {
$args = apply_filters( 'tainacan_fetch_args', $args, 'items' ); $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 ); $wp_query = new \WP_Query( $args );
if ( $should_filter ) {
remove_filter('posts_where', [$this, '_filter_where']);
}
return $this->fetch_output( $wp_query, $output ); return $this->fetch_output( $wp_query, $output );
} }
@ -332,67 +352,68 @@ class Items extends Repository {
return $this->fetch( $args, $collections )->get_posts(); 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 ) { public function update( $object, $new_values = null ) {
return $this->insert( $object ); 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. * 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 ) { 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 ) ) { if ( $cap == 'read_post' && is_array( $args ) && array_key_exists( 0, $args ) ) {
$entity = $args[0]; $entity = $args[0];
@ -541,7 +561,7 @@ class Items extends Repository {
if ( $collection instanceof Entities\Collection ) { if ( $collection instanceof Entities\Collection ) {
$status_obj = get_post_status_object( $collection->get_status() ); $status_obj = get_post_status_object( $collection->get_status() );
if ( ! $status_obj->public ) { 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 * @return boolean
* @throws \Exception * @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) { // if ( ! $entity instanceof Entities\Item) {
throw new InvalidArgumentException('Items::can_read() expects an Item entity as the first parameter'); // 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 the item looking only to the item
$can_read = parent::can_read($entity, $user); // $can_read = parent::can_read($entity, $user);
if ( $can_read ) { // if ( $can_read ) {
$collection = $entity->get_collection(); // $collection = $entity->get_collection();
$status_obj = get_post_status_object( $collection->get_status() ); // $status_obj = get_post_status_object( $collection->get_status() );
if ( $status_obj->public ) { // if ( $status_obj->public ) {
return $can_read; // return $can_read;
} // }
} // }
if ( is_null($user) ) { // if ( is_null($user) ) {
$user = get_current_user_id(); // $user = get_current_user_id();
} // }
if ( ! $user ) { // if ( ! $user ) {
return false; // return false;
} elseif ( is_object( $user ) ) { // } elseif ( is_object( $user ) ) {
$user = $user->ID; // $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, 'can_export' => true,
'rewrite' => true, 'rewrite' => true,
'map_meta_cap' => true, 'map_meta_cap' => true,
'capability_type' => Entities\Log::get_capability_type(), 'capabilities' => (array) $this->get_capabilities(),
'supports' => [ 'supports' => [
'title', 'title',
'editor', '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_trash_post', array( &$this, 'disable_delete_core_metadata' ), 10, 2 );
add_filter( 'pre_delete_post', array( &$this, 'force_delete_core_metadata' ), 10, 3 ); 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, 'rewrite' => true,
'map_meta_cap' => true, 'map_meta_cap' => true,
'show_in_nav_menus' => false, 'show_in_nav_menus' => false,
'capability_type' => Entities\Metadatum::get_capability_type(), 'capabilities' => (array) $this->get_capabilities(),
'supports' => [ 'supports' => [
'title', 'title',
'editor', 'editor',
@ -364,23 +366,67 @@ class Metadata extends Repository {
* *
* @param Entities\Collection $collection * @param Entities\Collection $collection
* @param array $args WP_Query args plus disabled_metadata * @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 * @return array Entities\Metadatum
* @throws \Exception * @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(); $collection_id = $collection->get_id();
//get parent collections //get parent collections
$parents = get_post_ancestors( $collection_id ); $parents = get_post_ancestors( $collection_id );
//insert the actual collection //insert the actual collection
if ( is_numeric($collection_id) ) {
$parents[] = $collection_id; $parents[] = $collection_id;
}
//search for default metadatum //search for default metadatum
$parents[] = $this->get_default_metadata_attribute(); $parents[] = $this->get_default_metadata_attribute();
$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 = $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( $meta_query = array(
'key' => 'collection_id', 'key' => 'collection_id',
'value' => $parents, 'value' => $parents,
@ -391,14 +437,16 @@ class Metadata extends Repository {
'parent' => 0 'parent' => 0
], $args ); ], $args );
if ( isset( $args['meta_query'] ) ) { $args['meta_query'] = $original_meta_q;
$args['meta_query'][] = $meta_query; $args['meta_query'][] = $meta_query;
} elseif ( is_array( $args ) ) {
$args['meta_query'] = array( $meta_query ); $results = $this->fetch( $args, 'OBJECT' );
} }
return $this->order_result( return $this->order_result(
$this->fetch( $args, $output ), $results,
$collection, $collection,
isset( $args['include_disabled'] ) ? $args['include_disabled'] : false isset( $args['include_disabled'] ) ? $args['include_disabled'] : false
); );
@ -427,11 +475,55 @@ class Metadata extends Repository {
$parents = get_post_ancestors( $collection_id ); $parents = get_post_ancestors( $collection_id );
//insert the actual collection //insert the actual collection
if ( is_numeric($collection_id) ) {
$parents[] = $collection_id; $parents[] = $collection_id;
}
//search for default metadatum //search for default metadatum
$parents[] = $this->get_default_metadata_attribute(); $parents[] = $this->get_default_metadata_attribute();
$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 = $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( $meta_query = array(
'key' => 'collection_id', 'key' => 'collection_id',
'value' => $parents, 'value' => $parents,
@ -442,13 +534,13 @@ class Metadata extends Repository {
'parent' => 0 'parent' => 0
], $args ); ], $args );
if ( isset( $args['meta_query'] ) ) { $args['meta_query'] = $original_meta_q;
$args['meta_query'][] = $meta_query; $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 * 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 Entities\Collection $collection
* @param bool $include_disabled Wether to include disabled metadata in the results or not * @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 ) { public function order_result( $result, Entities\Collection $collection, $include_disabled = false ) {
$order = $collection->get_metadata_order(); $order = $collection->get_metadata_order();
@ -495,29 +587,8 @@ class Metadata extends Repository {
$result_ordinate = array_merge( $result_ordinate, $not_ordinate ); $result_ordinate = array_merge( $result_ordinate, $not_ordinate );
return $result_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; return $result;
@ -761,7 +832,7 @@ class Metadata extends Repository {
] ]
], ],
'include_disabled' => true 'include_disabled' => true
], 'OBJECT' ); ] );
} }
@ -783,7 +854,7 @@ class Metadata extends Repository {
] ]
], ],
'posts_per_page' => 1 'posts_per_page' => 1
], 'OBJECT' ); ] );
if ( is_array( $results ) && sizeof( $results ) == 1 && $results[0] instanceof \Tainacan\Entities\Metadatum ) { if ( is_array( $results ) && sizeof( $results ) == 1 && $results[0] instanceof \Tainacan\Entities\Metadatum ) {
return $results[0]; return $results[0];
@ -810,7 +881,7 @@ class Metadata extends Repository {
] ]
], ],
'posts_per_page' => 1 'posts_per_page' => 1
], 'OBJECT' ); ] );
if ( is_array( $results ) && sizeof( $results ) == 1 && $results[0] instanceof \Tainacan\Entities\Metadatum ) { if ( is_array( $results ) && sizeof( $results ) == 1 && $results[0] instanceof \Tainacan\Entities\Metadatum ) {
return $results[0]; return $results[0];
@ -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

@ -273,7 +273,7 @@ abstract class Repository {
* *
* @see https://core.trac.wordpress.org/ticket/18408 * @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 ); $result[] = new $this->entities_type( $p->ID );
} }
} }
@ -664,7 +664,6 @@ abstract class Repository {
} elseif ( is_object( $user ) ) { } elseif ( is_object( $user ) ) {
$user = $user->ID; $user = $user->ID;
} }
$entity = self::get_entity_by_post( $entity );
$entity_cap = $entity->get_capabilities(); $entity_cap = $entity->get_capabilities();
if ( ! isset( $entity_cap->edit_post ) ) { if ( ! isset( $entity_cap->edit_post ) ) {
@ -701,7 +700,6 @@ abstract class Repository {
} elseif ( is_object( $user ) ) { } elseif ( is_object( $user ) ) {
$user = $user->ID; $user = $user->ID;
} }
$entity = self::get_entity_by_post( $entity );
$entity_cap = $entity->get_capabilities(); $entity_cap = $entity->get_capabilities();
if ( ! isset( $entity_cap->read ) ) { if ( ! isset( $entity_cap->read ) ) {
@ -730,7 +728,6 @@ abstract class Repository {
} elseif ( is_object( $user ) ) { } elseif ( is_object( $user ) ) {
$user = $user->ID; $user = $user->ID;
} }
$entity = self::get_entity_by_post( $entity );
$entity_cap = $entity->get_capabilities(); $entity_cap = $entity->get_capabilities();
if ( ! isset( $entity_cap->delete_post ) ) { 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 Entities\Entity $entity
* @param int|\WP_User|null $user default is null for the current user * @param int|\WP_User|null $user default is null for the current user
@ -749,20 +746,20 @@ abstract class Repository {
* @return boolean * @return boolean
* @throws \Exception * @throws \Exception
*/ */
public function can_publish( $entity, $user = null ) { public function can_publish(Entities\Entity $entity, $user = null) {
if ( is_null( $user ) ) { if ( is_null( $user ) ) {
$user = get_current_user_id(); $user = get_current_user_id();
} elseif ( is_object( $user ) ) { } elseif ( is_object( $user ) ) {
$user = $user->ID; $user = $user->ID;
} }
$entity = self::get_entity_by_post( $entity );
$entity_cap = $entity->get_capabilities(); $entity_cap = $entity->get_capabilities();
if ( ! isset( $entity_cap->publish_posts ) ) { if ( ! $user || ! isset( $entity_cap->publish_posts ) ) {
return false; return false;
} }
return user_can( $user, $entity_cap->publish_posts, $entity->get_id() ); return user_can( $user, $entity_cap->publish_posts );
} }
/** /**
@ -884,6 +881,23 @@ abstract class Repository {
} }
/**
* 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, 'rewrite' => true,
'map_meta_cap' => true, 'map_meta_cap' => true,
'show_in_nav_menus' => false, 'show_in_nav_menus' => false,
'capability_type' => Entities\Taxonomy::get_capability_type(), 'capabilities' => (array) $this->get_capabilities(),
'supports' => [ 'supports' => [
'title', 'title',
'editor', 'editor',
@ -206,18 +206,17 @@ class Taxonomies extends Repository {
* *
* @param Entities\Collection $collection * @param Entities\Collection $collection
* @param array $args WP_Query args plus disabled_metadata * @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 * @return array Entities\Taxonomy
* @throws \Exception * @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(); $collection_id = $collection->get_id();
$Tainacan_Metadata = Metadata::get_instance(); $Tainacan_Metadata = Metadata::get_instance();
// get all taxonomy metadata in this collection // 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 = []; $tax_ids = [];
@ -237,7 +236,7 @@ class Taxonomies extends Repository {
]; ];
$args = array_merge($args, $newargs); $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 // register taxonomies to other collections considering metadata inheritance
foreach ( $collections as $collection ) { 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 ) { foreach ( $taxonomies as $taxonomy ) {
register_taxonomy_for_object_type( $taxonomy->get_db_identifier(), $collection->get_db_identifier() ); register_taxonomy_for_object_type( $taxonomy->get_db_identifier(), $collection->get_db_identifier() );
} }

View File

@ -339,4 +339,95 @@ class Terms extends Repository {
public function register_post_type() { 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'); require_once(__DIR__ . '/class-tainacan-background-process-heartbeat.php');
$Tainacan_Importer_Heartbeat = new \Tainacan\Background_Importer_Heartbeat(); $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(); $TainacanPrivateFiles = \Tainacan\Private_Files::get_instance();
@ -188,5 +188,6 @@ if (class_exists('WP_CLI')) {
$Tainacan_Cli = \Tainacan\Cli::get_instance(); $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) { foreach ($metalist as $meta) {
$item_meta = new \Tainacan\Entities\Item_Metadata_Entity($entity, $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 = []){ public static function metadata_dropdown( $collection , $selected, $name_metadatum = 'tnc_prop_metadatum_id', $args = []){
$Tainacan_Metadata = \Tainacan\Repositories\Metadata::get_instance(); $Tainacan_Metadata = \Tainacan\Repositories\Metadata::get_instance();
$collection = ( is_numeric( $collection ) ) ? new Entities\Collection( $collection ) : $collection; $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 ?>"> <select name="<?php echo $name_metadatum ?>">
<option value=""><?php echo __('Select an option','tainacan') ?>...</option> <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 = []) { public static function metadata_checkbox_list( $collection , $selected,$name_metadatum = 'tnc_prop_tnc_metadatum_ids[]', $args = []) {
$Tainacan_Metadata = \Tainacan\Repositories\Metadata::get_instance(); $Tainacan_Metadata = \Tainacan\Repositories\Metadata::get_instance();
$collection = ( is_numeric( $collection ) ) ? new Entities\Collection( $collection ) : $collection; $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 = ( is_array( $selected) ) ? $selected : json_decode($selected);
$selected = ( $selected ) ? $selected : []; $selected = ( $selected ) ? $selected : [];
?> ?>

View File

@ -601,6 +601,11 @@ class CSV extends Importer {
$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_Metadata = \Tainacan\Repositories\Metadata::get_instance();
$Tainacan_Item_Metadata = \Tainacan\Repositories\Item_Metadata::get_instance(); $Tainacan_Item_Metadata = \Tainacan\Repositories\Item_Metadata::get_instance();
$Tainacan_Items = \Tainacan\Repositories\Items::get_instance(); $Tainacan_Items = \Tainacan\Repositories\Items::get_instance();
@ -621,6 +626,10 @@ class CSV extends Importer {
if( is_numeric($this->get_transient('item_id')) ) { if( is_numeric($this->get_transient('item_id')) ) {
if ( $item instanceof Entities\Item && $item->get_id() == $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() ); $this->add_log('item will be updated ID:' . $item->get_id() );
$updating_item = true; $updating_item = true;
// When creating a new item, disable log for each metadata to speed things up // 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']); $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_Metadata = \Tainacan\Repositories\Metadata::get_instance();
$Tainacan_Item_Metadata = \Tainacan\Repositories\Item_Metadata::get_instance(); $Tainacan_Item_Metadata = \Tainacan\Repositories\Item_Metadata::get_instance();
$Tainacan_Items = \Tainacan\Repositories\Items::get_instance(); $Tainacan_Items = \Tainacan\Repositories\Items::get_instance();

View File

@ -127,6 +127,13 @@ class Term_Importer extends Importer {
$last_term = $this->get_transient('last_term') == null ? 0: $this->get_transient('last_term'); $last_term = $this->get_transient('last_term') == null ? 0: $this->get_transient('last_term');
$id_taxonomy= $this->get_transient('new_taxonomy'); $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(); $position_file = $this->get_in_step_count();
fseek($file, $position_file); fseek($file, $position_file);
if (($values = fgetcsv($file, 0, $this->get_option('delimiter'), '"')) !== FALSE) { if (($values = fgetcsv($file, 0, $this->get_option('delimiter'), '"')) !== FALSE) {
@ -191,7 +198,10 @@ class Term_Importer extends Importer {
$tax1->set_allow_insert('yes'); $tax1->set_allow_insert('yes');
$tax1->set_status('publish'); $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(); $tax_repo = \Tainacan\Repositories\Taxonomies::get_instance();
$tax1 = $tax_repo->insert($tax1); $tax1 = $tax_repo->insert($tax1);
$name = $tax1->get_name(); $name = $tax1->get_name();

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,8 +183,11 @@ export const cleanItems = ({ commit }) => {
commit('cleanItems'); commit('cleanItems');
}; };
export const fetchCollection = ({ commit }, id) => { export const cleanCollection = ({ commit }) => {
commit('cleanCollection'); commit('cleanCollection');
};
export const fetchCollection = ({ commit, }, id) => {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
axios.tainacan.get('/collections/' + id + '?context=edit') axios.tainacan.get('/collections/' + id + '?context=edit')
.then(res => { .then(res => {
@ -198,50 +201,16 @@ export const fetchCollection = ({ commit }, id) => {
}); });
}; };
export const fetchCollectionName = ({ commit }, id) => { export const fetchCollectionBasics = ({ commit }, {collectionId, isContextEdit }) => {
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) => { return new Promise((resolve, reject) => {
axios.tainacan.get('/collections/' + id + '?context=edit&fetch_only') let endpoint = '/collections/' + collectionId + '?fetch_only=name,url,allow_comments';
.then(res => { if (isContextEdit)
let caps = res.data.current_user_can_edit; endpoint += '&context=edit';
resolve( caps );
})
.catch(error => {
reject(error);
})
});
};
export const fetchCollectionTotalItems = ({ commit }, id) => { axios.tainacan.get(endpoint)
return new Promise ((resolve, reject) => {
axios.tainacan.get('/collections/' + id + '?fetch_only=name')
.then(res => { .then(res => {
commit('setCollectionTotalItems', res.data); let collection = res.data;
commit('setCollection', collection);
resolve( res.data ); resolve( res.data );
}) })
.catch(error => { .catch(error => {
@ -250,13 +219,12 @@ export const fetchCollectionTotalItems = ({ commit }, id) => {
}); });
}; };
export const fetchCollectionCommentStatus = ({ commit }, id) => { export const fetchCollectionForExposer = ({ commit }, collectionId) => {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
axios.tainacan.get('/collections/' + id + '?fetch_only=comment_status') let endpoint = '/collections/' + collectionId + '?fetch_only=name,url';
axios.tainacan.get(endpoint)
.then(res => { .then(res => {
let collectionCommentStatus = res.data; resolve( res.data );
commit('setCollectionCommentStatus', collectionCommentStatus.comment_status);
resolve( collectionCommentStatus.comment_status );
}) })
.catch(error => { .catch(error => {
reject(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 }) => { export const deleteCollection = ({ commit }, { collectionId, isPermanently }) => {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
let endpoint = '/collections/' + collectionId; let endpoint = '/collections/' + collectionId;
@ -331,10 +258,8 @@ export const updateCollection = ({ commit }, {
collection collection
}) => { }) => {
return new Promise((resolve, reject) => { 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('setCollection', collection);
commit('setCollectionName', res.data.name);
commit('setCollectionURL', res.data.url);
resolve( res.data ); resolve( res.data );
}).catch( error => { }).catch( error => {
reject({ error_message: error['response']['data'].error_message, errors: error['response']['data'].errors }); reject({ error_message: error['response']['data'].error_message, errors: error['response']['data'].errors });
@ -347,7 +272,7 @@ export const sendCollection = ( { commit }, collection) => {
return new Promise(( resolve, reject ) => { return new Promise(( resolve, reject ) => {
let param = collection; let param = collection;
param[tainacan_plugin.exposer_mapper_param] = collection.mapper; param[tainacan_plugin.exposer_mapper_param] = collection.mapper;
axios.tainacan.post('/collections/', param) axios.tainacan.post('/collections/?context=edit', param)
.then( res => { .then( res => {
let collection = res.data; let collection = res.data;
commit('setCollection', collection); commit('setCollection', collection);
@ -401,7 +326,7 @@ export const fetchAttachments = ({ commit }, collection_id) => {
export const updateThumbnail = ({ commit }, { collectionId, thumbnailId }) => { export const updateThumbnail = ({ commit }, { collectionId, thumbnailId }) => {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
axios.tainacan.patch('/collections/' + collectionId, { axios.tainacan.patch('/collections/' + collectionId + '?context=edit', {
_thumbnail_id: thumbnailId _thumbnail_id: thumbnailId
}).then( res => { }).then( res => {
let collection = res.data let collection = res.data
@ -416,7 +341,7 @@ export const updateThumbnail = ({ commit }, { collectionId, thumbnailId }) => {
export const updateHeaderImage = ({ commit }, { collectionId, headerImageId }) => { export const updateHeaderImage = ({ commit }, { collectionId, headerImageId }) => {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
axios.tainacan.patch('/collections/' + collectionId, { axios.tainacan.patch('/collections/' + collectionId + '?context=edit', {
header_image_id: headerImageId + '' header_image_id: headerImageId + ''
}).then( res => { }).then( res => {
let collection = res.data let collection = res.data
@ -456,35 +381,24 @@ export const fetchPage = ({ commit }, pageId ) => {
}); });
}; };
// Users for moderators configuration // Fetch Collections for listing repository filters, parent collection selection, importer destiny...
export const fetchUsers = ({ commit }, { search, exceptions }) => { 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) if (collectionsIds != undefined && collectionsIds.length > 0) {
endpoint += '&exclude=' + exceptions.toString(); 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(); const source = axios.CancelToken.source();
return new Object({ return new Object({
request: new Promise((resolve, reject) => { 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 => { .then(res => {
let collections = res.data; const collections = res.data;
commit('setCollections', collections);
resolve( collections ); resolve( collections );
}) })
.catch((error) => { .catch((error) => {

View File

@ -14,14 +14,6 @@ export const getCollection = state => {
return state.collection; return state.collection;
} }
export const getCollectionName = state => {
return state.collectionName;
}
export const getCollectionURL = state => {
return state.collectionURL;
}
export const getAttachments = state => { export const getAttachments = state => {
return state.attachments; return state.attachments;
} }
@ -30,18 +22,6 @@ export const getFiles = state => {
return state.files; 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) => { export const getRepositoryTotalCollections = (state) => {
return state.repositoryTotalCollections; return state.repositoryTotalCollections;
} }

View File

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

View File

@ -4,10 +4,6 @@ export const setRepositoryTotalCollections = (state, repositoryTotalCollections)
state.repositoryTotalCollections = repositoryTotalCollections; state.repositoryTotalCollections = repositoryTotalCollections;
} }
export const setCollectionTotalItems = (state, collectionTotalItems) => {
state.collectionTotalItems = collectionTotalItems;
}
export const setItems = ( state, items ) => { export const setItems = ( state, items ) => {
state.items = items; state.items = items;
} }
@ -49,18 +45,6 @@ export const cleanCollection = (state) => {
state.collection = []; state.collection = [];
} }
export const setCollectionName = (state, collectionName) => {
state.collectionName = collectionName;
}
export const setCollectionURL = (state, collectionURL) => {
state.collectionURL = collectionURL;
}
export const cleanCollectionName = (state) => {
state.collectionName = '';
}
export const setSingleAttachment = ( state, attachment ) => { export const setSingleAttachment = ( state, attachment ) => {
let index = state.attachments.findIndex(newAttachment => newAttachment.id === attachment.id); let index = state.attachments.findIndex(newAttachment => newAttachment.id === attachment.id);
if ( index >= 0){ if ( index >= 0){
@ -92,19 +76,3 @@ export const setAttachments = ( state, attachments ) => {
export const cleanAttachments = (state) => { export const cleanAttachments = (state) => {
state.attachments = []; state.attachments = [];
} }
export const setCollectionCommentStatus = (state, collectionCommentStatus) => {
state.collectionCommentStatus = collectionCommentStatus;
}
export const cleanCollectionCommentStatus = (state) => {
state.collectionCommentStatus = '';
}
export const setCollectionAllowComments = (state, collectionAllowComments) => {
state.collectionAllowComments = collectionAllowComments;
}
export const cleanCollectionAllowComments = (state) => {
state.collectionAllowComments = '';
}

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