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

This commit is contained in:
weryques 2018-07-17 12:56:17 -03:00
commit 666bfa5870
18 changed files with 282 additions and 65 deletions

View File

@ -1,4 +1,4 @@
sudo: false
sudo: true
language: php
php:
- 7.1
@ -23,6 +23,7 @@ apt:
services:
- mysql
before_install:
- sudo apt-get update
- sudo apt-get install sshpass
- sudo service mysql restart
- mysql -e 'CREATE DATABASE IF NOT EXISTS test;'

View File

@ -1,8 +1,8 @@
# Exporting and Exposing your Repository
When you buid a digital repository with Tainacan, you gain the ability to show it to the world in many different ways thanks to the power and flexibility of WordPress.
When you build a digital repository with Tainacan, you gain the ability to show it to the world in many different ways thanks to the power and flexibility of WordPress.
But sometimes you dont want just to have your collections browsable via web, you want to download a spreadsheet to work with or you want to make it availabe via APIs so it can be consumed by other applications or harvested by an aggregator. This page describe how Tainacan handle with these situations.
But sometimes you dont want just to have your collections browsable via web, you want to download a spreadsheet to work with or you want to make it available via APIs so it can be consumed by other applications or harvested by an aggregator. This page describe how Tainacan handle with these situations.
## Mapping
@ -12,13 +12,13 @@ You do it by informing, for each metadatum you create, what is it relative in ea
Tainacan is shipped with some Mapping standards that implement popular metadata standards. And it will be easy to create new standards. See more [details about mapping standards](mapping-standards.md).
You can also use these mapping standards as a pre-set when you create a new Collection.
You can also use these mapping standards as a preset when you create a new Collection.
## Exporting
Exporting allows you to download the content of your repository to a file - or to multiple files. The format of the package you will download depends on the exporter you will use. Tainacan ships with a simple CSV exporter and a Tainacan-Package exporter, that allows you to export all the content of your collections, including the attachments, to import in another Tainacan instance.
Whatever exporter you choose to you use, you will be able to choose wether you want to download the collection as it is, which means, with the metadata the way the were created in Tainacan, or wether you want to download it in a mapped version. For example, if you mapped your collection to Dublin Core, you can download a CSV file either in Dublin Core format or in the original format.
Whatever exporter you choose to you use, you will be able to choose wether you want to download the collection as it is, which means, with the metadata the way the were created in Tainacan, or if you want to download it in a mapped version. For example, if you mapped your collection to Dublin Core, you can download a CSV file either in Dublin Core format or in the original format.
Tainacan makes it very easy to developers to create new exporters and publish them as plugins anyone can use.
@ -32,6 +32,14 @@ Each exposer implements a different way of presenting your data in the API respo
For example, the default JSON exposer supports any mapping and can serve your content exposing any metadata standard you mapped your content to. The decision is in the hands of the application that makes the request to your API.
On the other hand, OAI-PMH exposer only supports Dublin Core mapping and will allways serve content this way.
On the other hand, OAI-PMH exposer only supports Dublin Core mapping and will always serve content this way.
Exposers are also really easy to develop and can be added to your Tainacan instance via plugins.
## Exposing API
Using exposing API is easy, need only to set some parameters to API call, for example to expose an Item with id 123 using XML format on site URI http://example.com, so we need to call API with this URI: http://example.com/wp-json/tainacan/v2/items/123 with this URI the Tainacan will return the Item 123 data, but with we send this parameter at body, exposer=xml, It will return a WordPress JSON with XML formatted in data propriety. Or with we want only the XML data, without JSON respond for example, we need only to put the expose parameter at URI, like:
http://example.com/wp-json/tainacan/v2/items/123?exposer=xml
Tainacan have support to metadata mapping, the parameter for using the mapper is mapper=[mapper], so for our last example, if we want a CSV, using dublin-core mapper exposing the content at CSV format, not JSON:
http://example.com/wp-json/tainacan/v2/items/123?exposer=xml&mapper=dublin-core

View File

@ -92,13 +92,13 @@
</button>
</div>
<div class="control">
<button
<a
type="button"
v-if="editForm.url != undefined && editForm.url!= ''"
class="button is-secondary"
:href="editForm.url">
{{ $i18n.get('see') + ' ' + $i18n.get('term') }}
</button>
</a>
</div>
<div class="control">
<button

View File

@ -57,6 +57,14 @@
<!--:style="{color: 'white'}"-->
<!--@click="toItemsPage">{{ $i18n.get('advanced_search') }}</a>-->
</div>
<button
@click="showProcesses = !showProcesses"
class="button is-small is-secondary level-item">
<b-icon icon="swap-vertical"/>
</button>
<processes-list
v-show="showProcesses"
@closeProcessesPopup="showProcesses = false"/>
<a
:style="{color: 'white'}"
class="level-item"
@ -68,8 +76,8 @@
</template>
<script>
import AdvancedSearch from '../advanced-search/advanced-search.vue';
import ProcessesList from '../other/processes-list.vue';
import { mapActions } from 'vuex';
export default {
@ -81,10 +89,13 @@
searchQuery: '',
futureSearchQuery: '',
metadata: Array,
showProcesses: false,
hasNewProcess: false
}
},
components: {
AdvancedSearch,
ProcessesList
},
methods: {
...mapActions('metadata', [

View File

@ -0,0 +1,166 @@
<template>
<div class="processes-popup">
<div class="popup-header">
<span class="header-title">{{ bgProcesses.length }}</span>
<a @click="showProcessesList = !showProcessesList">
<span class="icon has-text-tertiary">
<i
:class="{ 'mdi-chevron-up': showProcessesList,
'mdi-chevron-down': !showProcessesList }"
class="mdi"/>
</span>
</a>
<a @click="$emit('closeProcessesPopup')">
<span class="icon has-text-tertiary">
<i class="mdi mdi-close"/>
</span>
</a>
</div>
<div
v-if="showProcessesList"
class="popup-list">
<ul>
<li
v-if="bgProcess.progress_label"
:key="index"
v-for="(bgProcess, index) of bgProcesses">
<div class="progress-label">{{ bgProcess.progress_label }}</div>
<span
v-if="bgProcess.done > 0"
class="icon has-text-success">
<i class="mdi mdi-24px mdi-checkbox-marked-circle"/>
</span>
<span
v-if="bgProcess.done <= 0"
class="icon has-text-success">
<div class="control has-icons-right is-loading is-clearfix" />
</span>
</li>
</ul>
</div>
<div class="popup-footer"/>
</div>
</template>
<script>
import { mapGetters, mapActions } from 'vuex';
export default {
name: 'ProcessesList',
data() {
return {
showProcessesList: false
}
},
computed: {
bgProcesses() {
return this.getProcesses();
}
},
methods: {
...mapActions('bgprocess', [
'fetchProcesses'
]),
...mapGetters('bgprocess', [
'getProcesses',
])
},
mounted() {
this.fetchProcesses();
}
}
</script>
<style lang="scss">
@import "../../scss/_variables.scss";
@keyframes appear {
from {
top: 24px;
opacity: 0;
}
to {
top: 48px;
opacity: 1;
}
}
.processes-popup{
background-color: #c1dae0;
width: 320px;
max-width: 100%;
position: absolute;
top: 48px;
border-radius: 5px;
box-shadow: 0px 0px 10px -8px #222;
animation-name: appear;
animation-duration: 0.3s;
.update-warning {
color: $tertiary;
animation-name: blink;
animation-duration: 0.5s;
animation-delay: 0.5s;
align-items: center;
display: flex;
}
.popup-header {
display: flex;
justify-content: space-between;
color: $tertiary;
padding: 12px;
.header-title {
margin-right: auto;
}
}
.popup-list {
background-color: white;
color: black;
li {
padding: 6px 12px;
display: flex;
justify-content: space-between;
.progress-label {
margin-right: auto;
white-space: nowrap;
text-overflow: ellipsis;
overflow: hidden;
max-width: calc(100% - 40px);
}
.control.is-loading::after {
position: relative !important;
right: 0;
top: 0;
}
}
li:hover {
background-color: $tainacan-input-background;
}
}
&:before {
content: "";
display: block;
position: absolute;
right: 47px;
width: 0;
height: 0;
border-style: solid;
}
&:before {
border-color: transparent transparent $primary-light transparent;
border-right-width: 8px;
border-bottom-width: 8px;
border-left-width: 8px;
top: -10px;
}
}
</style>

View File

@ -216,12 +216,20 @@
</option> -->
</b-select>
<button
:disabled="totalItems <= 0"
class="button is-white is-small"
:disabled="totalItems <= 0 || order == 'DESC'"
@click="onChangeOrder()">
<span class="icon is-small gray-icon">
<i class="mdi mdi-sort-ascending"/>
</span>
</button>
<button
:disabled="totalItems <= 0 || order == 'ASC'"
class="button is-white is-small"
@click="onChangeOrder()">
<b-icon
class="gray-icon"
:icon="order === 'ASC' ? 'sort-ascending' : 'sort-descending'"/>
<span class="icon is-small gray-icon">
<i class="mdi mdi-sort-descending"/>
</span>
</button>
</b-field>
</div>
@ -247,6 +255,7 @@
<b-icon icon="menu-down" />
</button>
<b-dropdown-item
:class="{ 'is-active': viewModeOption == viewMode }"
v-for="(viewModeOption, index) of enabledViewModes"
:key="index"
:value="viewModeOption"
@ -272,38 +281,49 @@
class="button is-white"
slot="trigger">
<span>
<b-icon
class="gray-icon view-mode-icon"
:icon="(adminViewMode == 'table' || adminViewMode == undefined) ?
'table' : (adminViewMode == 'cards' ?
'view-list' : 'view-grid')"/>
<span class="icon is-small gray-icon">
<i
:class="{'mdi-view-list' : ( adminViewMode == 'table' || adminViewMode == undefined),
'mdi-view-module' : adminViewMode == 'cards',
'mdi-apps' : adminViewMode == 'grid',
'mdi-view-column' : adminViewMode == 'records'}"
class="mdi"/>
</span>
</span>
&nbsp;&nbsp;&nbsp;{{ $i18n.get('label_visualization') }}
<b-icon icon="menu-down" />
</button>
<b-dropdown-item :value="'table'">
<b-icon
class="gray-icon"
icon="table"/>
{{ $i18n.get('label_table') }}
</b-dropdown-item>
<b-dropdown-item :value="'cards'">
<b-dropdown-item
:class="{ 'is-active': adminViewMode == 'table' }"
:value="'table'">
<b-icon
class="gray-icon"
icon="view-list"/>
{{ $i18n.get('label_cards') }}
{{ $i18n.get('label_table') }}
</b-dropdown-item>
<b-dropdown-item :value="'records'">
<b-dropdown-item
:class="{ 'is-active': adminViewMode == 'cards' }"
:value="'cards'">
<b-icon
class="gray-icon"
icon="view-module"/>
{{ $i18n.get('label_records') }}
{{ $i18n.get('label_cards') }}
</b-dropdown-item>
<b-dropdown-item :value="'grid'">
<b-dropdown-item
:class="{ 'is-active': adminViewMode == 'grid' }"
:value="'grid'">
<b-icon
class="gray-icon"
icon="view-grid"/>
{{ $i18n.get('label_grid') }}
icon="apps"/>
{{ $i18n.get('label_thumbnails') }}
</b-dropdown-item>
<b-dropdown-item
:class="{ 'is-active': adminViewMode == 'records' }"
:value="'records'">
<b-icon
class="gray-icon"
icon="view-column"/>
{{ $i18n.get('label_records') }}
</b-dropdown-item>
</b-dropdown>
</b-field>
@ -1002,6 +1022,7 @@
.gray-icon, .gray-icon .icon {
color: $tainacan-placeholder-color !important;
padding-right: 10px;
}
.gray-icon .icon i::before, .gray-icon i::before {
font-size: 21px !important;
@ -1018,11 +1039,13 @@
div.dropdown-content {
padding: 0;
.metadata-options-container {
max-height: 240px;
overflow: auto;
}
.dropdown-item {
padding: 0.25rem 1.0rem 0.25rem 0.75rem;
}
.dropdown-item span{
vertical-align: sub;
}

View File

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

View File

@ -14,6 +14,8 @@
table.tainacan-table {
width: 100%;
border-collapse: separate;
border-spacing: 0;
.checkbox-cell {
min-width: 38px;

View File

@ -227,7 +227,7 @@ return apply_filters( 'tainacan-admin-i18n', [
'label_view_modes_available' => __( 'View modes available on theme', 'tainacan' ),
'label_warning' => __( 'Warning', 'tainacan' ),
'label_error' => __( 'Erro', 'tainacan' ),
'label_grid' => __( 'Grid', 'tainacan' ),
'label_thumbnails' => __( 'Thumbnails', 'tainacan' ),
'label_table' => __( 'Table', 'tainacan' ),
'label_cards' => __( 'Cards', 'tainacan' ),
'label_records' => __( 'Records', 'tainacan' ),

View File

@ -311,9 +311,16 @@ export default {
font-size: 21px;
}
}
.dropdown-item {
padding: 0.25rem 1.35rem 0.25rem 0.25rem;
}
.view-mode-icon {
margin-right: 8px !important;
margin-top: 2px;
margin-right: 0px !important;
margin-top: 1px;
&.icon i::before, .gray-icon i::before {
font-size: 19px !important;
}
}
}

View File

@ -230,8 +230,8 @@ class REST_Background_Processes_Controller extends REST_Controller {
$filename = 'bg-' . $action . '-' . $id . $suffix . '.log';
$upload_url = wp_upload_dir();
$upload_url = trailingslashit( $upload_url['url'] );
$logs_url = $upload_url . 'tainacan';
$upload_url = trailingslashit( $upload_url['baseurl'] );
$logs_url = $upload_url . 'tainacan/' . $filename;
return $logs_url;

View File

@ -102,7 +102,7 @@ $Tainacan_Metadata->register_metadata_type('Tainacan\Metadata_Types\Numeric');
$Tainacan_Metadata->register_metadata_type('Tainacan\Metadata_Types\Selectbox');
$Tainacan_Metadata->register_metadata_type('Tainacan\Metadata_Types\Relationship');
$Tainacan_Metadata->register_metadata_type('Tainacan\Metadata_Types\Taxonomy');
$Tainacan_Metadata->register_metadata_type('Tainacan\Metadata_Types\Compound');
//$Tainacan_Metadata->register_metadata_type('Tainacan\Metadata_Types\Compound');
$Tainacan_Filters = \Tainacan\Repositories\Filters::get_instance();

View File

@ -275,12 +275,12 @@ class Old_Tainacan extends Importer{
$id = $this->get_transient('item_' . $item_Old->ID . '_id');
if( $id && is_numeric( $id ) ) {
$this->add_log('Updating item ' . $item_Old->post_title . ' in collection ');
$this->add_log('Updating item ' . $item_Old->post_title );
$item = new Entities\Item( $id );
}
$item->set_title( $item_Old->post_title );
$this->add_log('Begin insert ' . $item_Old->post_title . ' in collection ' . $collection_id['id'] );
$this->add_log('Begin insert ' . $item_Old->ID . ': ' . $item_Old->post_title . ' in collection ' . $collection_id['id'] );
$item->set_description( (isset($item_Old->post_content)) ? $item_Old->post_content : '' );
$item->set_status('publish');
@ -293,7 +293,7 @@ class Old_Tainacan extends Importer{
$this->add_transient('item_' . $item_Old->ID . '_collection', $collection_id['id']); // add collection for relations
if( $insertedItem->get_id() && is_array($processed_item['item']->metadata) ){
$this->add_log('Item ' . $insertedItem->get_title() . ' inserted');
$this->add_log('Item ' . $insertedItem->get_id() . ': ' . $insertedItem->get_title() . ' inserted. ID in source: ' . $item_Old->ID);
$this->add_item_metadata( $insertedItem, $processed_item['item']->metadata, $collection_id );

View File

@ -8,14 +8,14 @@ export const cleanProcesses = ( state ) => {
state.bg_processes = [];
}
export const setProcess = ( state, process ) => {
let index = state.bg_processes.findIndex(newProcess => newProcess.id === process.id);
export const setProcess = ( state, bgProcess ) => {
let index = state.bg_processes.findIndex(newProcess => newProcess.id == bgProcess.id);
if ( index >= 0){
Vue.set( state.bg_processes, index, process );
Vue.set(state.bg_processes, index, bgProcess);
} else {
state.bg_processes.push( process );
state.bg_processes.push(bgProcess);
}
state.bg_process = process;
state.bg_process = bgProcess;
}
export const setProcessLog = ( state, log ) => {

View File

@ -148,14 +148,13 @@ export const fetchImporterSourceInfo = ({ commit }, sessionId ) => {
});
};
export const runImporter = ( { commit } , importerId ) => {
export const runImporter = ( { dispatch } , importerId ) => {
return new Promise(( resolve, reject ) => {
axios.tainacan.post('importers/session/' + importerId + '/run')
.then( res => {
let backgroundProcessId = res.data;
// probably send this a dedicated store to background process
//commit('background/addBackgroundProcess', backgroundProcessId);
dispatch('bgprocess/fetchProcesses', null, { root: true });
resolve( backgroundProcessId );
})
.catch(error => {

View File

@ -52,21 +52,21 @@ class Theme_Helper {
$this->register_view_mode('table', [
'label' => __('Table', 'tainacan'),
'dynamic_metadata' => true,
'icon' => '<span class="icon"><i class="mdi mdi-table mdi-24px"></i></span>',
'icon' => '<span class="icon"><i class="mdi mdi-view-list mdi-24px"></i></span>',
'type' => 'component',
]);
$this->register_view_mode('cards', [
'label' => __('Cards view', 'tainacan'),
'dynamic_metadata' => false,
'description' => 'A cards view, displaying title, description, author name and creation date.',
'icon' => '<span class="icon"><i class="mdi mdi-view-list mdi-24px"></i></span>',
'icon' => '<span class="icon"><i class="mdi mdi-view-module mdi-24px"></i></span>',
'type' => 'component'
]);
$this->register_view_mode('records', [
'label' => __('Records view', 'tainacan'),
'dynamic_metadata' => true,
'description' => 'A records view, similiar to cards, but flexible for metadata',
'icon' => '<span class="icon"><i class="mdi mdi-view-module mdi-24px"></i></span>',
'icon' => '<span class="icon"><i class="mdi mdi-view-column mdi-24px"></i></span>',
'type' => 'component'
]);
}

View File

@ -194,7 +194,7 @@ class Metadata extends TAINACAN_UnitTestCase {
*/
function test_metadata_metadata_type(){
$Tainacan_Metadata = \Tainacan\Repositories\Metadata::get_instance();
$this->assertEquals( 8, sizeof( $Tainacan_Metadata->fetch_metadata_types() ) );
$this->assertEquals( 7, sizeof( $Tainacan_Metadata->fetch_metadata_types() ) );
}