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

This commit is contained in:
weryques 2018-06-06 09:30:23 -03:00
commit 8cae609ea7
14 changed files with 522 additions and 82 deletions

View File

@ -35,7 +35,93 @@
</div>
<div class="table-wrapper">
<table class="tainacan-table">
<!-- CARDS VIEW MODE -->
<div
class="tainacan-cards-container"
v-if="viewMode == 'cards'">
<div class="columns is-multiline is-gapless no-gutters">
<div
:key="index"
v-for="(item, index) of items"
class="column is-12-tablet is-half-desktop is-half-widescreen is-one-third-fullhd">
<div
:class="{ 'selected-card': selectedItems[index] }"
class="tainacan-card">
<!-- Checkbox -->
<div
:class="{ 'is-selecting': isSelectingItems }"
class="card-checkbox">
<b-checkbox
size="is-small"
v-model="selectedItems[index]"/>
</div>
<!-- Title -->
<p
v-for="(column, index) in tableFields"
:key="index"
v-if="column.display && column.field_type_object != undefined && (column.field_type_object.related_mapped_prop == 'title')"
class="metadata-title">
<a
v-html="item.metadata != undefined ? renderMetadata(item.metadata, column) : ''"
@click="goToItemPage(item)"/>
</p>
<!-- Actions -->
<div
v-if="item.current_user_can_edit"
class="actions-area"
:label="$i18n.get('label_actions')">
<a
id="button-edit"
:aria-label="$i18n.getFrom('items','edit_item')"
@click.prevent.stop="goToItemEditPage(item.id)">
<b-icon
type="is-secondary"
icon="pencil"/>
</a>
<a
id="button-delete"
:aria-label="$i18n.get('label_button_delete')"
@click.prevent.stop="deleteOneItem(item.id)">
<b-icon
type="is-secondary"
:icon="!isOnTrash ? 'delete' : 'delete-forever'"/>
</a>
</div>
<!-- Remaining metadata -->
<div class="media">
<a
v-if="item.thumbnail != undefined"
@click="goToItemPage(item)">
<img :src="item['thumbnail'].thumb">
</a>
<div class="list-metadata media-body">
<span
v-for="(column, index) in tableFields"
:key="index"
v-if="column.display && column.slug != 'thumbnail' && column.field_type_object != undefined && (column.field_type_object.related_mapped_prop != 'title')">
<h3 class="metadata-label">{{ column.name }}</h3>
<p
v-html="item.metadata != undefined ? renderMetadata(item.metadata, column) : ''"
class="metadata-value"/>
</span>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- TABLE VIEW MODE -->
<table
v-if="viewMode == 'table'"
class="tainacan-table">
<thead>
<tr>
<!-- Checking list -->
@ -84,8 +170,6 @@
v-for="(column, index) in tableFields"
v-if="column.display"
:label="column.name"
:aria-label="(column.field != 'row_thumbnail' && column.field != 'row_creation' && column.field != 'row_author')
? column.name + '' + (item.metadata ? item.metadata[column.slug].value_as_string : '') : ''"
class="column-default-width"
:class="{
'thumbnail-cell': column.field == 'row_thumbnail',
@ -182,7 +266,8 @@ export default {
tableFields: Array,
items: Array,
isLoading: false,
isOnTrash: false
isOnTrash: false,
viewMode: 'table'
},
mounted() {
this.selectedItems = [];
@ -311,6 +396,7 @@ export default {
<style lang="scss" scoped>
@import "../../scss/_variables.scss";
@import "../../scss/_view-mode-cards.scss";
.selection-control {

View File

@ -21,8 +21,8 @@
:is-full-page="false"
:active.sync="isLoadingFilters"/>
<b-field :style="{'margin-bottom': '0.25rem !important'}">
<div class="control is-small is-clearfix">
<div class="search-area">
<div class="control has-icons-right is-small is-clearfix">
<input
class="input is-small"
:placeholder="$i18n.get('instruction_search')"
@ -31,21 +31,13 @@
:value="searchQuery"
@input="futureSearchQuery = $event.target.value"
@keyup.enter="updateSearch()">
<span
@click="updateSearch()"
class="icon is-right">
<i class="mdi mdi-magnify" />
</span>
</div>
</div>
<p class="control">
<button
id="collection-search-button"
type="submit"
class="button"
@click="updateSearch()">
<b-icon
icon="magnify"
size="is-small"/>
</button>
</p>
</b-field>
<!-- Advanced search button -->
<a
@click="openAdvancedSearch = !openAdvancedSearch"
class="is-size-7 is-secondary is-pulled-right">{{ $i18n.get('advanced_search') }}</a>
@ -243,7 +235,41 @@
</b-dropdown>
</b-field>
</div>
<div
v-if="!isOnTheme"
class="search-control-item">
<b-field>
<b-dropdown
v-model="adminViewMode"
:mobile-modal="false"
position="is-bottom-left"
:aria-label="$i18n.get('label_view_mode')">
<button
class="button is-white"
slot="trigger">
<span>
<b-icon
:icon="(adminViewMode == 'table' || adminViewMode == undefined) ?
'table' : (adminViewMode == 'cards' ?
'view-list' : 'view-grid')"/>
</span>
<b-icon icon="menu-down" />
</button>
<b-dropdown-item :value="'table'">
<b-icon icon="table"/>
{{ $i18n.get('label_table') }}
</b-dropdown-item>
<b-dropdown-item :value="'cards'">
<b-icon icon="view-list"/>
{{ $i18n.get('label_cards') }}
</b-dropdown-item>
<b-dropdown-item :value="'grid'">
<b-icon icon="view-grid"/>
{{ $i18n.get('label_grid') }}
</b-dropdown-item>
</b-dropdown>
</b-field>
</div>
</div>
@ -284,7 +310,8 @@
:table-fields="tableFields"
:items="items"
:is-loading="isLoadingItems"
:is-on-trash="status == 'trash'"/>
:is-on-trash="status == 'trash'"
:view-mode="adminViewMode"/>
<!-- Theme View Modes -->
<div
@ -362,6 +389,7 @@
isHeaderShrinked: false,
localTableFields: [],
registeredViewModes: tainacan_plugin.registered_view_modes,
adminViewMode: 'table',
openAdvancedSearch: false,
}
},
@ -465,11 +493,15 @@
}
}
}
let thumbnailField = this.localTableFields.find(field => field.slug == 'thumbnail');
let creationDateField = this.localTableFields.find(field => field.slug == 'creation_date');
let authorNameField = this.localTableFields.find(field => field.slug == 'author_name');
this.$eventBusSearch.addFetchOnly({
'0': 'thumbnail',
'0': thumbnailField.display ? 'thumbnail' : null,
'meta': fetchOnlyFieldIds,
'1': 'creation_date',
'2': 'author_name'
'1': creationDateField.display ? 'creation_date' : null,
'2': authorNameField.display ? 'author_name': null
});
this.$refs.displayedFieldsDropdown.toggle();
},
@ -487,8 +519,8 @@
.catch(() => this.isLoadingFilters = false);
this.isLoadingFields = true;
this.tableFields = [];
// Processing is done inside a local variable
let fields = [];
this.fetchFields({
collectionId: this.collectionId,
isRepositoryLevel: this.isRepositoryLevel,
@ -496,7 +528,7 @@
})
.then(() => {
this.tableFields.push({
fields.push({
name: this.$i18n.get('label_thumbnail'),
field: 'row_thumbnail',
field_type: undefined,
@ -517,7 +549,7 @@
else if (field.display == 'yes')
display = true;
this.tableFields.push(
fields.push(
{
name: field.name,
field: field.description,
@ -533,7 +565,7 @@
}
}
this.tableFields.push({
fields.push({
name: this.$i18n.get('label_creation_date'),
field: 'row_creation',
field_type: undefined,
@ -541,7 +573,7 @@
id: undefined,
display: true
});
this.tableFields.push({
fields.push({
name: this.$i18n.get('label_created_by'),
field: 'row_author',
field_type: undefined,
@ -565,7 +597,7 @@
'2': 'author_name'
});
this.isLoadingFields = false;
this.tableFields = fields;
})
.catch(() => {
this.isLoadingFields = false;
@ -592,7 +624,7 @@
/* This condition is to prevent a incorrect fetch by filter or fields when we come from items
* at collection level to items page at repository level
*/
if(this.collectionId === to.params.collectionId) {
if (this.collectionId === to.params.collectionId) {
this.prepareFieldsAndFilters();
}
});
@ -648,12 +680,26 @@
margin-top: 48px;
}
#collection-search-button {
border-radius: 0 !important;
padding: 0 8px !important;
border-color: $tainacan-input-background;
&:focus, &:active {
border-color: none !important;
.search-area {
display: flex;
align-items: center;
margin-right: 36px;
.control {
input {
height: 27px;
font-size: 11px;
color: $gray-light;
width: 148px;
}
.icon {
pointer-events: all;
cursor: pointer;
color: $tertiary;
height: 27px;
font-size: 18px;
}
margin-bottom: 5px;
}
}

View File

@ -26,6 +26,7 @@
.b-checkbox { width: 100% };
&:hover { background-color: $primary-lighter; }
.is-small { color: gray; }
&.is-active { background-color: white; }
}
}
}

View File

@ -172,14 +172,13 @@
width: 80px;
.actions-container {
visibility: hidden;
display: flex;
position: relative;
padding: 0;
height: 100%;
width: 80px;
z-index: 9;
background-color: transparent;
background-color: white;
float: right;
}
@ -206,7 +205,6 @@
}
.actions-cell {
.actions-container {
visibility: visible;
background: $tainacan-input-background !important;
}

View File

@ -0,0 +1,81 @@
.tainacan-cards-container {
min-height: 200px;
padding: 0;
.selected-card {
background-color: $primary-lighter;
}
.tainacan-card {
padding: 16px 44px 24px 44px;
.card-checkbox {
position: absolute;
margin-left: -28px;
margin-top: -2px;
}
.actions-area {
position: relative;
float: right;
top: -37px;
right: -30px;
width: 60px;
display: flex;
justify-content: space-between;
visibility: hidden;
opacity: 0;
transition: visibility 0.2s, opacity 0.2s;
}
&:hover .actions-area {
visibility: visible;
opacity: 1.0;
}
img {
width: 130px;
height: auto;
border-radius: 2px;
margin-right: 1.5rem;
}
.metadata-title {
flex-shrink: 0;
font-size: 0.875rem;
margin-bottom: 0.875rem;
text-overflow: ellipsis;
white-space: nowrap;
overflow: hidden;
}
.media {
.media-body {
flex: 1;
font-size: 0.6875rem;
color: gray;
overflow: hidden;
.metadata-label {
font-size: 0.75rem;
line-height: 1.0;
margin-bottom: 0.2rem;
text-overflow: ellipsis;
white-space: nowrap;
overflow: hidden;
font-weight: 500;
color: $gray-light;
}
.metadata-value {
text-overflow: ellipsis;
white-space: nowrap;
overflow: hidden;
margin-bottom: 1rem;
}
}
}
}
}

View File

@ -201,6 +201,9 @@ return [
'label_view_modes_available' => __( 'View modes available on theme', 'tainacan' ),
'label_warning' => __( 'Warning', 'tainacan' ),
'label_error' => __( 'Erro', 'tainacan' ),
'label_grid' => __( 'Grid', 'tainacan' ),
'label_table' => __( 'Table', 'tainacan' ),
'label_cards' => __( 'Cards', 'tainacan' ),
// Instructions. More complex sentences to guide user and placeholders
'instruction_delete_selected_collections' => __( 'Delete selected collections', 'tainacan' ),

View File

@ -96,12 +96,6 @@ export default {
line-height: 20px !important;
font-size: 14px !important;
}
#collection-search-button {
border: 1px solid $secondary !important;
height: 32px !important;
background-color: $secondary;
color: white;
}
.input, .textarea {
font-size: 14px;
border: none;

View File

@ -268,6 +268,16 @@ class Capabilities {
]
],
];
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()
@ -349,6 +359,43 @@ class Capabilities {
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
*/
@ -368,6 +415,7 @@ class Capabilities {
foreach ($caps as $cap) {
$role->add_cap($entity_cap->$cap);
$this->check_dependencies($role, $post_type, $cap);
}
$tainacan_roles = $this->get_tainacan_roles();
@ -384,6 +432,7 @@ class Capabilities {
foreach ($caps as $cap) {
$tainacan_role->add_cap($entity_cap->$cap);
$this->check_dependencies($tainacan_role, $post_type, $cap);
}
}
@ -414,6 +463,7 @@ class Capabilities {
foreach ($caps as $cap) {
$role->add_cap($collection_items_caps->$cap);
$this->check_dependencies($role, 'tainacan-items', $cap);
}
}
@ -464,6 +514,17 @@ class Capabilities {
$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);
}
}
}
}
}
}
@ -487,6 +548,7 @@ class Capabilities {
$caps = $defaults_caps['tainacan-items']['editor'];
foreach ($caps as $cap) {
$user->add_cap($collection_items_caps->$cap);
$this->check_dependencies($user, 'tainacan-items', $cap);
}
}
}

90
src/importer/import.php Normal file
View File

@ -0,0 +1,90 @@
<?php
class ScriptTainacanOld {
var $step = 0;
var $url = '';
/**
* start the execution
*/
function __construct($argv) {
$this->parse_args($argv);
$this->run();
}
/**
* parse args from and set the attributs
* 1 - Old Tainacan url (required)
*/
function parse_args($argv) {
if (!is_array($argv))
return;
if (isset($argv[1])) {
if (filter_var($argv[1], FILTER_VALIDATE_URL)) {
$this->url = $argv[1];
}
}
if (isset($argv[2])) {
if (is_numeric($argv[2])) {
$this->step = $argv[2];
} else {
$this->run = '';
}
}
}
/**
* echo message in prompt line
*/
function log($msg) {
echo $msg . PHP_EOL;
}
function run() {
$start = $partial = microtime(true);
// Avoid warnings
$_SERVER['SERVER_PROTOCOL'] = "HTTP/1.1";
$_SERVER['REQUEST_METHOD'] = "GET";
define( 'WP_USE_THEMES', false );
define( 'SHORTINIT', false );
require( dirname(__FILE__) . '/../../../../wp-blog-header.php' );
$old_tainacan = new \Tainacan\Importer\Old_Tainacan();
$id = $old_tainacan->get_id();
$_SESSION['tainacan_importer'][$id]->set_url($url);
$_SESSION['tainacan_importer'][$id]->set_repository();
while (!$_SESSION['tainacan_importer'][$id]->is_finished()){
$_SESSION['tainacan_importer'][$id]->run();
}
$scripttime = microtime(true) - $start;
$this->log("==========================================================");
$this->log("==========================================================");
$this->log("=== Fim do script. Tempo de execução {$scripttime}s");
$this->log("==========================================================");
$this->log("==========================================================");
}
}
$x = new ScriptTainacanOld($argv);

View File

@ -122,9 +122,9 @@ export default {
loadItems(to) {
// Forces fetch_only to be filled before any search happens
if (this.$store.getters['search/getFetchOnly'] == undefined)
if (this.$store.getters['search/getFetchOnly'] == undefined) {
this.$emit( 'hasToPrepareFieldsAndFilters', to);
else {
} else {
this.$emit( 'isLoadingItems', true);
this.$store.dispatch('collection/fetchItems',
{ 'collectionId': this.collectionId,

View File

@ -13,6 +13,13 @@ export const fetchItems = ({ rootGetters, dispatch, commit }, { collectionId, is
if (postQueries.metaquery != undefined && postQueries.metaquery.length > 0)
hasFiltered = true;
// Garanttees at least empty fetch_only are passed in case none is found
if (qs.stringify(postQueries.fetch_only) == '')
dispatch('search/add_fetchonly', {} , { root: true });
if (qs.stringify(postQueries.fetch_only['meta']) == '')
dispatch('search/add_fetchonly_meta', 0 , { root: true });
// Differentiates between repository level and collection level queries
let endpoint = '/collection/'+collectionId+'/items?'
@ -22,7 +29,6 @@ export const fetchItems = ({ rootGetters, dispatch, commit }, { collectionId, is
if (!isOnTheme)
endpoint = endpoint + 'context=edit&'
if (qs.stringify(postQueries.fetch_only['meta']) != '') {
axios.tainacan.get(endpoint + qs.stringify(postQueries))
.then(res => {
@ -39,9 +45,7 @@ export const fetchItems = ({ rootGetters, dispatch, commit }, { collectionId, is
dispatch('search/setTotalItems', res.headers['x-wp-total'], { root: true } );
})
.catch(error => reject(error));
} else {
reject("No fecth_only meta was found.");
}
});
}

View File

@ -110,7 +110,8 @@ class TAINACAN_REST_Export_Controller extends TAINACAN_UnitApiTestCase {
$response = $this->server->dispatch($request);
$this->assertEquals(200, $response->get_status());
$data = $response->get_data();
print_r($data);
$this->assertInstanceOf('SimpleXMLElement', @simplexml_load_string($data));
}
}

View File

@ -49,10 +49,80 @@ class DefaultCapabilities extends TAINACAN_UnitTestCase {
$this->assertTrue(current_user_can($entity_cap->$cap), "tainacan-$role_name does not have capability {$entity_cap->$cap}");
}
}
}
}
}
/**
* @group capabilities_denpendecies
*/
function test_capabilities_denpendecies() {
$collection = $this->tainacan_entity_factory->create_entity(
'collection',
array(
'name' => 'test capabilities denpendecies',
),
true
);
$item = $this->tainacan_entity_factory->create_entity(
'item',
array(
'title' => 'test capabilities denpendecies Item',
'collection' => $collection,
),
true
);
$Tainacan_Capabilities = \Tainacan\Capabilities::get_instance();
$deps = $Tainacan_Capabilities::$dependencies;
$defaults_caps = $Tainacan_Capabilities->defaults;
$tainacan_roles = $Tainacan_Capabilities->get_tainacan_roles();
foreach ($defaults_caps as $post_type => $wp_append_roles) {
if(array_key_exists($post_type, $deps)) {
$entity = false;
$entity_cap = false;
if($post_type != 'tainacan-items') {
$entity = Repository::get_entity_by_post_type($post_type);
$entity_cap = $entity->get_capabilities();
}
foreach ($wp_append_roles as $role_name => $caps) {
$role = get_role($role_name);
$new_user = $this->factory()->user->create(array( 'role' => $role_name ));
wp_set_current_user($new_user);
foreach ($caps as $cap) {
if(array_key_exists($cap, $deps[$post_type])) {
$dep_cap = $deps[$post_type][$cap];
if($post_type == 'tainacan-items') {
$this->assertTrue(current_user_can($dep_cap), "$role_name does not have a dependency capability {$dep_cap} for tainacan-items" );
} else {
$this->assertTrue(current_user_can($dep_cap), "$role_name does not have a dependency capability {$dep_cap} for {$entity_cap->$cap}" );
}
}
}
$new_user = $this->factory()->user->create(array( 'role' => 'tainacan-' . $role_name ));
wp_set_current_user($new_user);
if(in_array($role_name, $tainacan_roles) ) {
foreach ($caps as $cap) {
if(array_key_exists($cap, $deps[$post_type])) {
$dep_cap = $deps[$post_type][$cap];
if($post_type == 'tainacan-items') {
$this->assertTrue(current_user_can($dep_cap), "tainaca-$role_name does not have a dependency capability {$dep_cap} for tainacan-items" );
} else {
$this->assertTrue(current_user_can($dep_cap), "tainaca-$role_name does not have a dependency capability {$dep_cap} for {$entity_cap->$cap}" );
}
}
}
}
}
}
}
}
}

View File

@ -55,6 +55,10 @@ class ImporterTests extends TAINACAN_UnitTestCase {
$_SESSION['tainacan_importer'][$id]->run();
}
$Tainacan_Collections = \Tainacan\Repositories\Collections::get_instance();
$collections = $Tainacan_Collections->fetch([], 'OBJECT');
$this->assertEquals(3, $collections, 'total collection in this url does not match');
$this->assertTrue(true);
}*/