Merge branch 'develop' into feature/241
This commit is contained in:
commit
53e783c511
|
@ -67,7 +67,8 @@ cache:
|
||||||
notifications:
|
notifications:
|
||||||
slack:
|
slack:
|
||||||
rooms:
|
rooms:
|
||||||
secure: WjgA4OIad0/Ai+x1TUd7KShXpaJco9E/MfI6DWJFnBUF5JLKOxymTGbR82wspmnEwzXGWn++co1wNJh20pWobaIToL/2OpBd7xo5MHglNobH4cidmSIDPOQM8ItqBneRjayhXtfShRqQDNCk1D3d5KDUacXXeCX+GLdRuwdc/YuG7Zh3y1KrF6l10cwD/5JOJECwwMbqeLOezvzGi8ITL6pqnEEeNTb0Zr+K2Ru91RBOars4TLK3f5CmX4XxA4oQSMsrHKzTO2OZLua5CRwcBwKmtSqr0Z/W+RrXu2TLxvHYJ39t9uwssI3HmGoB/uNcahBTcE0Vk/phi3nfFO8KUGNqp3xLgUsF/xlb+WiIip2xOf9jq+XNGWwIVB2vWqiQppm58h57LgDNTF21XJOtfVPEyf9Em+VSfNgACq47u4kktgqgZ47xNEBZuv1s3as8BcgdQH78hF+qguhhHJH6RgyHt+GYAq2uRLF8z3IM8hfKpb5mA9jD3UfW3pXEtKF9K+XqtNycX8KxJ2ldEJrMguaqW3sUHSLBo+r71OqldHeuwIfyIGIROTym11GWH56rIXxd51nJuVDWMvTg7bwcjN/d61LdfsaOAgqAYSUPlJjjp67GHZP/vson3uPr//NgnAJ/4gtjwReB7IX8oBLhB5s8lU6dnfnsLbY3Rt+THNA=
|
secure: WjgA4OIad0/Ai+x1TUd7KShXpaJco9E/MfI6DWJFnBUF5JLKOxymTGbR82wspmnEwzXGWn++co1wNJh20pWobaIToL/2OpBd7xo5MHglNobH4cidmSIDPOQM8ItqBneRjayhXtfShRqQDNCk1D3d5KDUacXXeCX+GLdRuwdc/YuG7Zh3y1KrF6l10cwD/5JOJECwwMbqeLOezvzGi8ITL6pqnEEeNTb0Zr+K2Ru91RBOars4TLK3f5CmX4XxA4oQSMsrHKzTO2OZLua5CRwcBwKmtSqr0Z/W+RrXu2TLxvHYJ39t9uwssI3HmGoB/uNcahBTcE0Vk/phi3nfFO8KUGNqp3xLgUsF/xlb+WiIip2xOf9jq+XNGWwIVB2vWqiQppm58h57LgDNTF21XJOtfVPEyf9Em+VSfNgACq47u4kktgqgZ47xNEBZuv1s3as8BcgdQH78hF+qguhhHJH6RgyHt+GYAq2uRLF8z3IM8hfKpb5mA9jD3UfW3pXEtKF9K+XqtNycX8KxJ2ldEJrMguaqW3sUHSLBo+r71OqldHeuwIfyIGIROTym11GWH56rIXxd51nJuVDWMvTg7bwcjN/d61LdfsaOAgqAYSUPlJjjp67GHZP/vson3uPr//NgnAJ/4gtjwReB7IX8oBLhB5s8lU6dnfnsLbY3Rt+THNA= #ANTIGO
|
||||||
|
secure: mX+veCaM90kFBRoiQ6XvPASbHzCdvhZBcTDnvOWhKKJ/NoYtfVglADC4qkoe4VoVtX7vJAG2aRDxf6oHYkEelDu4c6ke2xdaRxqLuCev9aMfLCSUgyOuJVTFBdbB8m//ddmZalH8K8dW7m6L109Fob459WShqDfic4FaDm+nB1hCvRaGljm9QiM0SKfQk0kXJXO4Gzdj522cNxCDkdybxGJPaWXcGEwYx4piiXCUw/BVeP04H/gWtBIDOERC8Se4YxTOYroXHCcuinz/N5nEkjCZ5a3qY63r89/emBgX/FYBpEVbVIl/E0BSYomSFtS9YhMiHweU/UoQeYdnrPGj0zfVminQHnIIOvYnQfVrVccJh2b5xc6gboJXWEoQuHhVwk+eVLCGqY9G7BxJSlWgXHedxLVJWTQRUkYDs+AV0t1Z27JG+CruRe44NWvyeGvSr3c9Q8tF0JtjG55CWDDqmDiL1CKEZh6sigK/wM44hlnpoS8zHt18Wak5dcN/sI0pqGt5CLO+mh1fhjcNcuOVYEQdN4iKXynBvWMivqUDuZ1ImsJeD0tFgQqHyZ5qMXidFQ9G6RjeArMvd/lsGWiZ192ophX1x0VGCNvR4jfPvJN2aGC+DTVzxC6UAt5/oFcYxutqOC/NSxAeVd4/Nh4k/cQXI1fWorXhKRSkTq/tM6I=
|
||||||
on_sucess: always
|
on_sucess: always
|
||||||
on_failure: always
|
on_failure: always
|
||||||
email:
|
email:
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
# Tainacan
|
# Tainacan
|
||||||
- Tainacan's [plugin](https://wordpress.org/plugins/tainacan/) and [theme](https://wordpress.org/themes/tainacan-interface/) on WordPress.org
|
- Tainacan's [plugin](https://wordpress.org/plugins/tainacan/) and [theme](https://wordpress.org/themes/tainacan-interface/) on WordPress.org
|
||||||
- [Access our homepage](http://tainacan.org)
|
- [Access our website](http://tainacan.org)
|
||||||
- [Read our official documentation](https://wiki.tainacan.org/)
|
- [Read our official documentation](https://wiki.tainacan.org/)
|
||||||
- [Explore our blog](http://tainacan.org/blog/)
|
- [Explore our blog](http://tainacan.org/blog/)
|
||||||
- [Join our mailing list](https://lists.riseup.net/www/info/tainacan)
|
- [Join our mailing list](https://lists.riseup.net/www/info/tainacan)
|
||||||
|
@ -42,7 +42,7 @@ In addition to [our documentation](https://wiki.tainacan.org/) and [instructiona
|
||||||
|
|
||||||
### Developers
|
### Developers
|
||||||
|
|
||||||
Please refer to our [Developers Documentation](docs/index.md) if you want to develop tainacan plugins, themes or if you want to contribute to the core.
|
Please refer to our [Developers Documentation](https://tainacan.github.io/tainacan-wiki/#/dev/) if you want to develop tainacan plugins, themes or if you want to contribute to the core.
|
||||||
|
|
||||||
### Contributing
|
### Contributing
|
||||||
Tainacan is a free, open source software licensed under **GPLv3**. Contributions to the codebase will abide to the same license; other contributions may be under additional or other terms.
|
Tainacan is a free, open source software licensed under **GPLv3**. Contributions to the codebase will abide to the same license; other contributions may be under additional or other terms.
|
||||||
|
|
|
@ -1,248 +1 @@
|
||||||
# Creating a custom Metadata type
|
This page was moved to https://tainacan.github.io/tainacan-wiki/
|
||||||
|
|
||||||
Metadata types are the objects that represent the types of metadata that can be used. Examples of Metadata Types are "Text", "Long text", "Date", "Relationship with another item", etc;
|
|
||||||
|
|
||||||
Each metadata type object have its own settings and web component that will be used to render the interface.
|
|
||||||
|
|
||||||
Developers can create custom Metadata Types via plugins. This article shows how to do this.
|
|
||||||
|
|
||||||
A Metadata Type is composed of a simple PHP class and a Vue Web Component.
|
|
||||||
|
|
||||||
|
|
||||||
## PHP Class
|
|
||||||
|
|
||||||
### Registering your Metadata Type
|
|
||||||
|
|
||||||
First of all, you have to register you Metadata type class. You do this by calling the `register_metadata_type` method of the `Metadata` Repository:
|
|
||||||
|
|
||||||
```PHP
|
|
||||||
add_action('init', 'my_custom_mt_registration');
|
|
||||||
|
|
||||||
function my_custom_mt_registration() {
|
|
||||||
$Tainacan_Metadata = \Tainacan\Repositories\Metadata::get_instance();
|
|
||||||
$Tainacan_Metadata->register_metadata_type('MyCustomMetadataTypeClass');
|
|
||||||
}
|
|
||||||
|
|
||||||
```
|
|
||||||
|
|
||||||
Now you have to create the class you just register, and use its constructor to set the basic features of you Metadata Type:
|
|
||||||
|
|
||||||
```PHP
|
|
||||||
class MyCustomMetadataTypeClass {
|
|
||||||
|
|
||||||
public function __construct() {
|
|
||||||
|
|
||||||
parent::__construct();
|
|
||||||
|
|
||||||
$this->set_name( __('My Custom Metadata Type', 'my-domain') );
|
|
||||||
$this->set_description( __('My Custom Metadata Type', 'my-domain') );
|
|
||||||
$this->set_primitive_type('string');
|
|
||||||
$this->set_component('my-custom-component');
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
These are the basic methods you have to call to set up you MT:
|
|
||||||
|
|
||||||
* **set_name(string $name)** - Sets the name of the Metadata type. This is the name the user will see in the front end and should be internationalized.
|
|
||||||
* **set_description(string $name)** - Sets the description of the Metadata type. This is the text the user will see in the front end and should be internationalized.
|
|
||||||
* **set_primitive(string $type)** - Inform what is the raw type of data that will be stored. This is used mainly by Filter Types to decide what kind of Filters will be available for each Metadata Type. Current used primitive types are: string, date, float, item, term and long_string (you can create new ones if you want, just note that not Filter Types will be available to them)
|
|
||||||
set_preview_template
|
|
||||||
|
|
||||||
You can also set a HTML preview of your Metadata Type to help users visualize how it works. This will be displayed in the Metadata configuration screen, when the user click on the "?" icon of a Metadata Type.
|
|
||||||
|
|
||||||
* **set_preview_template(string $template)** - The HTML code that previwes the plugin. Use [Buefy](https://buefy.github.io/) and [Bulma](http://bulma.io/) classes.
|
|
||||||
|
|
||||||
|
|
||||||
### Metadata type Options
|
|
||||||
|
|
||||||
When you register a new Metadata Type, it will be automatically added as an option in the Metadata configuration screen. It will also have the default metadata configuration form, where you set whether the metadata is required or not, accept multiple values or not, and so on.
|
|
||||||
|
|
||||||
However, you metadata type may have specific options you want to give to the users. For example: In the Selectbox Metadata type, you have the ability to set what will be the options in your Selectbox.
|
|
||||||
|
|
||||||
In order to do this, you have to declare what are the options your metadata type has, and prepare another web component to be rendered in the metadata form:
|
|
||||||
|
|
||||||
```PHP
|
|
||||||
class MyCustomMetadataTypeClass {
|
|
||||||
|
|
||||||
public function __construct() {
|
|
||||||
|
|
||||||
parent::__construct();
|
|
||||||
|
|
||||||
$this->set_name( __('My Custom Metadata Type', 'my-domain') );
|
|
||||||
$this->set_description( __('My Custom Metadata Type', 'my-domain') );
|
|
||||||
$this->set_primitive_type('string');
|
|
||||||
$this->set_component('my-custom-component');
|
|
||||||
|
|
||||||
// custom options
|
|
||||||
$this->set_form_component('my-custom-form-component');
|
|
||||||
$this->set_default_options([
|
|
||||||
'my_option_1' => 'value 1',
|
|
||||||
'my_option_2' => 'value 2'
|
|
||||||
]);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
Optionally you can implement `validate_options` method to validate the form before it gets saved:
|
|
||||||
|
|
||||||
```PHP
|
|
||||||
class MyCustomMetadataTypeClass {
|
|
||||||
|
|
||||||
public function __construct() {
|
|
||||||
|
|
||||||
parent::__construct();
|
|
||||||
|
|
||||||
$this->set_name( __('My Custom Metadata Type', 'my-domain') );
|
|
||||||
$this->set_description( __('My Custom Metadata Type', 'my-domain') );
|
|
||||||
$this->set_primitive_type('string');
|
|
||||||
$this->set_component('my-custom-component');
|
|
||||||
|
|
||||||
// custom options
|
|
||||||
$this->set_form_component('my-custom-form-component');
|
|
||||||
$this->set_default_options([
|
|
||||||
'my_option_1' => 'value 1',
|
|
||||||
'my_option_2' => 'value 2'
|
|
||||||
]);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public function validate_options( Metadatum $metadatum ) {
|
|
||||||
|
|
||||||
$option1 = $this->get_option('my_option_1');
|
|
||||||
|
|
||||||
if ($option1) { // some condition
|
|
||||||
return true; // validated!
|
|
||||||
} else {
|
|
||||||
return ['my_option_1' => __('This option is invald', 'my-domain')];
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
`validate_options` is expected to return `true` if valid, and an array where the keys are the option name and the values are the error messages.
|
|
||||||
|
|
||||||
### Additional Methods
|
|
||||||
|
|
||||||
There are few other methods you can implement that can change the items interact with metadata depending on the metadata type:
|
|
||||||
|
|
||||||
#### **validate( Item_Metadata_Entity $item_metadata)**
|
|
||||||
|
|
||||||
This method will override the validation of the Item Metadata Entity, which means every time Tainacan saves a value for a metadata of this type, it will call this method. For example, the Date Metadata Type override this method to make sure the date is in the correct format:
|
|
||||||
|
|
||||||
```PHP
|
|
||||||
public function validate( Item_Metadata_Entity $item_metadata) {
|
|
||||||
$value = $item_metadata->get_value();
|
|
||||||
$format = 'Y-m-d';
|
|
||||||
|
|
||||||
if (is_array($value)) {
|
|
||||||
foreach ($value as $date_value) {
|
|
||||||
$d = \DateTime::createFromFormat($format, $date_value);
|
|
||||||
if (!$d || $d->format($format) !== $date_value) {
|
|
||||||
$this->add_error(
|
|
||||||
sprintf(
|
|
||||||
__('Invalid date format. Expected format is YYYY-MM-DD, got %s.', 'tainacan'),
|
|
||||||
$date_value
|
|
||||||
)
|
|
||||||
);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return True;
|
|
||||||
}
|
|
||||||
|
|
||||||
$d = \DateTime::createFromFormat($format, $value);
|
|
||||||
|
|
||||||
if (!$d || $d->format($format) !== $value) {
|
|
||||||
$this->add_error(
|
|
||||||
sprintf(
|
|
||||||
__('Invalid date format. Expected format is YYYY-MM-DD, got %s.', 'tainacan'),
|
|
||||||
$value
|
|
||||||
)
|
|
||||||
);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
Note that it checks whether the value is a string (single value) or an array (multiple values).
|
|
||||||
|
|
||||||
|
|
||||||
#### **get_value_as_html( Item_Metadata_Entity $item_metadata)**
|
|
||||||
|
|
||||||
This method will change the way a value is converted to HTML for metadata of this metadata type. For example, Taxonomy and Relationship Metadata Type use this to add links to the related term/item in the HTML output.
|
|
||||||
|
|
||||||
## Creating Vue Web Component
|
|
||||||
|
|
||||||
*TODO: explain how to load the web component code.*
|
|
||||||
|
|
||||||
The Vue component is the chunk that will be rendered inside the Item Edition form so the user can edit it's metadata of your custom type.
|
|
||||||
|
|
||||||
As in any Vue component, you should provide a `template` with it's HTML content, a `script` with it's logic and optionally a `style`. Bellow are the template and script for the Selectbox metadata type:
|
|
||||||
|
|
||||||
```html
|
|
||||||
<template>
|
|
||||||
<b-input
|
|
||||||
:disabled="disabled"
|
|
||||||
:class="{'has-content': inputValue !== undefined && inputValue !== ''}"
|
|
||||||
:id="id"
|
|
||||||
type="number"
|
|
||||||
:value="inputValue"
|
|
||||||
step="0.01"
|
|
||||||
@blur="onBlur"
|
|
||||||
@change="onBlur"
|
|
||||||
@input="onInput($event)"/>
|
|
||||||
</template>
|
|
||||||
```
|
|
||||||
```javascript
|
|
||||||
<script>
|
|
||||||
export default {
|
|
||||||
created(){
|
|
||||||
if( this.value )
|
|
||||||
this.inputValue = this.value;
|
|
||||||
},
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
inputValue: ''
|
|
||||||
}
|
|
||||||
},
|
|
||||||
props: {
|
|
||||||
id: '',
|
|
||||||
metadatum: {
|
|
||||||
type: Object
|
|
||||||
},
|
|
||||||
value: [String, Number, Array],
|
|
||||||
disabled: false,
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
onBlur() {
|
|
||||||
this.$emit('blur');
|
|
||||||
},
|
|
||||||
onInput($event) {
|
|
||||||
this.inputValue = $event;
|
|
||||||
this.$emit('input', this.inputValue);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
```
|
|
||||||
|
|
||||||
Notice first the "props" on the component. They are passed to every metadata:
|
|
||||||
- `metadadum` is the metadatum object itself, wich also contains the `metadata_type_options`;
|
|
||||||
- `value` is the value used for binding whatever is the content of this metadatum;
|
|
||||||
- `id` is the metadatum id;
|
|
||||||
- `disabled` is a boolean handled by the Item's form, which can be used to disable any inner component in case the options are not loaded and other situations that might be desired;
|
|
||||||
|
|
||||||
The "data" here has only a copy of the input value, passed during the `created()` lifecycle. Props are usualy not to be modified so we use this as an internal variable.
|
|
||||||
|
|
||||||
The "methods" here simply delegate the blur and input events to the default parent component, which is responsible for passing this values to the Item's form. **Attention: all metadatum component must emit an input value, passing the updated value that they received from the props**.
|
|
||||||
|
|
||||||
In the above example, a custom component from [Buefy](https://buefy.github.io/), `b-input` is used. You can use any javascript available from your plugin here, or just try out theirs, as it's already loaded on Tainacan's plugin. The styling also come from this library, inheriting [Bulma](http://bulma.io/), and it's recommended the use of their classes as most are overrided by Tainacan stylesheets.
|
|
|
@ -1,110 +1 @@
|
||||||
# CSV Importer
|
This page was moved to https://tainacan.github.io/tainacan-wiki/
|
||||||
|
|
||||||
The CSV importer allows user to import items to a collection from a CSV (comma separated values) file.
|
|
||||||
|
|
||||||
The input file is a standard CSV file, where each line will hold one item information, and each column will hold the value for one specific metadata. Also, the first line brings the column titles.
|
|
||||||
|
|
||||||
When the user starts the importer process, he/she must choose the right file encoding the CSV was saved in (usually UTF-8), the column separator and the cell enclosure. All this options are set when the user generates the CSV file using a spreadsheet editor.
|
|
||||||
|
|
||||||
In this section user will also inform the character (or characters) used to separate values in a multi-valued cell.
|
|
||||||
|
|
||||||
After configuring the importer and choosing the target collection, the CSV file is uploaded and the user has the chance to map the columns present in the CSV to the metadata present in the target collection.
|
|
||||||
|
|
||||||
If the metadata was not created beforehand, user can create and map metadata in this screen, or choose the "Create metadatum" option in the mapper. If this option is selected, Tainacan will automatically create a metadatum when the importer runs (see "Creating metadata on the fly" section below to learn how to tell tainacan the type and other attributes of the metadatum that will be created).
|
|
||||||
|
|
||||||
|
|
||||||
## Special Columns
|
|
||||||
|
|
||||||
Each column of the CSV must be mapped to a metadatum in the target collection. However, there are special columns that can be used to set the value for other aspects of the item. For example, the item status can be set to say the item is public to everyone, in draft or private to editors.
|
|
||||||
|
|
||||||
The special columns that can be used are:
|
|
||||||
|
|
||||||
* **special_item_status** - Inform the item status. Possible values are draft, private or publish.
|
|
||||||
* **special_item_id** - Inform the item ID in the Tainacan database. This is useful when re-importing items and let the user decide wether to update existing items or ignore them and only add new items.
|
|
||||||
* **special_document** - let the user inform the item document. See "Importing files and attachments"
|
|
||||||
* **special_attachments** - let the user inform the attachments. See "Importing files and attachments"
|
|
||||||
* **special_comment_status** - Inform if the item is open for comments. Possible values are open or closed. Default is closed.
|
|
||||||
|
|
||||||
Example:
|
|
||||||
|
|
||||||
```
|
|
||||||
name, special_item_status, special_comment_status
|
|
||||||
item uno, draft, closed
|
|
||||||
item due, private, closed
|
|
||||||
item tre, publish, open
|
|
||||||
```
|
|
||||||
|
|
||||||
### Importing files and attachments
|
|
||||||
|
|
||||||
If you also have files you want to import, that are related to the items in your CSV, you can use some special columns in your csv to do so.
|
|
||||||
|
|
||||||
There are two special columns you can use: `special_document`, which will set the Document of your item, and `special_attachments` to add one or many attachments.
|
|
||||||
|
|
||||||
The values for the special_document must be prepended with 'url:'', 'file:'' or 'text:'. This will indicate the Document Type.
|
|
||||||
|
|
||||||
Example:
|
|
||||||
|
|
||||||
```
|
|
||||||
name, special_document
|
|
||||||
An image,file:http://example.com/image.jpg
|
|
||||||
A youtube video,url:http://youtube.com/?w=123456
|
|
||||||
A text,text:This is a sample text
|
|
||||||
```
|
|
||||||
|
|
||||||
The values for the special_attachments is just a list of files. If you want to add many attachments, use the separator you set in the Multivalued Delimiter option.
|
|
||||||
|
|
||||||
In either case, you can point to a file using a full URL, or just a file name. In this last case, you should set the option below to tell Tainacan where to find the files in your server. You can then upload them directly (via FTP for example) and tainacan will add them to your items.
|
|
||||||
|
|
||||||
Example:
|
|
||||||
|
|
||||||
```
|
|
||||||
name, special_attachments
|
|
||||||
An image,http://example.com/image.jpg
|
|
||||||
Many images,http://example.com/image.jpg||http://example.com/image2.jpg||http://example.com/image3.jpg
|
|
||||||
Images uploaded via FTP,myfolder/image.jpg||myfolder/image2.jpg
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
## Creating metadata on the fly
|
|
||||||
|
|
||||||
When the user maps the columns found in the CSV file to the metadata present in the collection, he/she has can choose the "Create metadatum" option, so the importer will automatically create the metadata as it process the file.
|
|
||||||
|
|
||||||
By default, it will create a public text metadatum, but you can inform tainacan the type and other features of the metadata in the header of the CSV.
|
|
||||||
|
|
||||||
In the first line, where you declare the name of each column, you can add some information that will be used by the importer to create the metadatum_id.
|
|
||||||
|
|
||||||
Each information about the metadatum will be separated by a pipe "|" character.
|
|
||||||
|
|
||||||
The first information must be the metadata name, and the second, the metadata type.
|
|
||||||
|
|
||||||
For example:
|
|
||||||
|
|
||||||
```
|
|
||||||
Name,Subject|taxonomy,Date of creation|date
|
|
||||||
```
|
|
||||||
|
|
||||||
The natively supported types right now are:
|
|
||||||
|
|
||||||
* text
|
|
||||||
* textarea
|
|
||||||
* taxonomy - when this type is used, a new taxonomy will be created
|
|
||||||
* date - Values must be informed in YYYY-MM-DD format
|
|
||||||
* numeric
|
|
||||||
* relationship - Values must be the ID of the related item
|
|
||||||
|
|
||||||
After the type, you can use keywords to inform other features:
|
|
||||||
|
|
||||||
* multiple - can have multiple values
|
|
||||||
* required - is required
|
|
||||||
* display_yes - display in lists: yes
|
|
||||||
* display_no - display in lists: not by default
|
|
||||||
* display_never - display in lists: never
|
|
||||||
* status_public - visible to everyone
|
|
||||||
* status_private - visible only to editors
|
|
||||||
* collection_key_yes - set this meta as a collection key (there cannot be two items with the same value).
|
|
||||||
|
|
||||||
Examples combining multiple features:
|
|
||||||
|
|
||||||
```
|
|
||||||
Name,Subject|taxonomy|multiple|required,Internal code|numeric|required|collection_key_yes|status_private
|
|
||||||
```
|
|
|
@ -1,40 +1 @@
|
||||||
# Templates
|
This page was moved to https://tainacan.github.io/tainacan-wiki/
|
||||||
|
|
||||||
Notes for a future documentation
|
|
||||||
|
|
||||||
Tainacan uses the ordinary WordPress template hierarchy for its templates. It adds additional templates in the hierarchy
|
|
||||||
|
|
||||||
* `tainacan/single-items.php` - Used in the single template for any item of any collection
|
|
||||||
* `tainacan/arhive-items.php` - Used in the list of items of any collection (e.g. both archive-tnc_col_4_item.php and archive-tnc_col_5_item.php)
|
|
||||||
* `tainacan/archive-taxonomy.php` - Used as a template for all Tainacan Taxonomies (list items based on a term)
|
|
||||||
* `tainacan/archive-repository.php` - Used in the `/items/` URL, which is added by the Tainacan plugin, and lists all items of all collections
|
|
||||||
|
|
||||||
Since each collection is a new custom post type, these templates are usefull to create a template that will be used by all collections.
|
|
||||||
|
|
||||||
Nevertheless, you are still able to create more specific templates, using the standard WordPress hierarchy.
|
|
||||||
|
|
||||||
Examples:
|
|
||||||
|
|
||||||
A template to list Collections:
|
|
||||||
|
|
||||||
`archive-tainacan-collection.php`
|
|
||||||
|
|
||||||
A template for single items in the collection with id 4:
|
|
||||||
|
|
||||||
`single-tnc_col_4_item.php`
|
|
||||||
|
|
||||||
A template for a single specific item:
|
|
||||||
|
|
||||||
`single-tnc_col_4_item-item-name.php`
|
|
||||||
|
|
||||||
A template for the list of items of the collection with id 4
|
|
||||||
|
|
||||||
`archive-tnc_col_4_item.php`
|
|
||||||
|
|
||||||
A template for a specific taxonomy
|
|
||||||
|
|
||||||
`taxonomy-tnc_tax_123.php`
|
|
||||||
|
|
||||||
A teplate for a specific term
|
|
||||||
|
|
||||||
`taxonomy-tnc_tax_123-term-name.php`
|
|
|
@ -1,231 +1 @@
|
||||||
# The Exporter flow
|
This page was moved to https://tainacan.github.io/tainacan-wiki/
|
||||||
|
|
||||||
This page describes how Tainacan exporters work and is a reference to write your own exporter.
|
|
||||||
|
|
||||||
This documentation is still in construction. A very effective way to learn more on how to build an exporter is to look at the source code of the CSV Exporter class that is included in the tainacan package.
|
|
||||||
|
|
||||||
## Introduction
|
|
||||||
|
|
||||||
Exporters can export items from a single collection, or even create a bunch of collections, taxonomies and items all at once.
|
|
||||||
|
|
||||||
In order to create an Exporter, you have to extend the `\Tainacan\Exporter` Class and register it using the global `Tainacan\Export_Handler->register_exporter()` method.
|
|
||||||
|
|
||||||
This method takes an array as argument, with the defintion of your exporter. These are the expected attributes.
|
|
||||||
|
|
||||||
```
|
|
||||||
@type string $name The name of the exporter. e.g. 'Example exporter'
|
|
||||||
@type string $slug A unique slug for the exporter. e.g. 'example-exporter'
|
|
||||||
@type string $description The exporter description. e.g. 'This is an example exporter description'
|
|
||||||
@type string $class_name The Exporter Class. e.g. '\Tainacan\Exporter\Test_Exporter'
|
|
||||||
@type bool $manual_mapping Wether Tainacan must present the user with an interface to manually choose
|
|
||||||
a mapping standard. This will allow them to export the items mapped to a
|
|
||||||
chosen standard instead of in its original form.
|
|
||||||
|
|
||||||
The exporter will also be able to inform what mappers it supports. See `set_accepted_mapping_methods`
|
|
||||||
and `accept_no_mapping` below.
|
|
||||||
|
|
||||||
Note that this will only work when exporting items from one single collection.
|
|
||||||
|
|
||||||
@type bool $manual_collection Wether Tainacan will let the user choose the source collection.
|
|
||||||
If set to true, Tainacan give the user a select box from where he/she will Choose
|
|
||||||
one (and only one) Collection to export items from.
|
|
||||||
|
|
||||||
Otherwise, the child exporter class must choose the collections somehow
|
|
||||||
(it could be in its options form) and add them to the collections property using `add_collection()`
|
|
||||||
```
|
|
||||||
|
|
||||||
Note that depending on the value of `manual_mapping` and `manual_collection` you will have to implement some methods in your exporter class.
|
|
||||||
|
|
||||||
## Initializing a new exporter
|
|
||||||
|
|
||||||
When the user starts a new export process, he/she first choose which export to use.
|
|
||||||
|
|
||||||
Once the Exporter is chosen, the first thing that happens is the creation of a new instance of the chosen Exporter. This fires the `__construct()` method.
|
|
||||||
|
|
||||||
|
|
||||||
## Choose a collection (if `manual_collection` is true)
|
|
||||||
|
|
||||||
After choosing the exporter, user will be given the choice to choose the source collection.
|
|
||||||
|
|
||||||
If called from inside a collection, this step is skipped and the current collection is set as source.
|
|
||||||
|
|
||||||
## Set options
|
|
||||||
|
|
||||||
Now its time to set the exporter options. Each exporter may have its own set of options, that will be used during the export process. It could be anything, from the delimiter character in a CSV exporter, to an API key for a exporter that sends data to an external API.
|
|
||||||
|
|
||||||
exporter classes should declare the default values for its options in the `__construct()` method, by calling `set_default_options()`.
|
|
||||||
|
|
||||||
```
|
|
||||||
namespace Tainacan\exporter;
|
|
||||||
|
|
||||||
class Myexporter extends Exporter {
|
|
||||||
function __construct() {
|
|
||||||
parent::construct();
|
|
||||||
$this->set_default_options(['
|
|
||||||
'foo' => 'bar'
|
|
||||||
']);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
The exporter classes must also implement the `options_form` method, in which it will echo the form that the user will interact to set the options.
|
|
||||||
|
|
||||||
```
|
|
||||||
function options_form() {
|
|
||||||
$form = '<div class="field">';
|
|
||||||
$form .= '<label class="label">' . __('My exporter Option 1', 'tainacan') . '</label>';
|
|
||||||
$form .= '<div class="control">';
|
|
||||||
$form .= '<input type="text" class="input" name="my_exporter_option_1" value="' . $this->get_option('my_exporter_option_1') . '" />';
|
|
||||||
$form .= '</div>';
|
|
||||||
$form .= '</div>';
|
|
||||||
|
|
||||||
return $form;
|
|
||||||
}
|
|
||||||
```
|
|
||||||
## Accepting mappers
|
|
||||||
|
|
||||||
If you set `manual_mapping` as true and want to give users the ability to export a mapped version of their collections, you can also
|
|
||||||
define which mappers you accept.
|
|
||||||
|
|
||||||
```
|
|
||||||
public function __construct($attributes = array()) {
|
|
||||||
parent::__construct($attributes);
|
|
||||||
$this->set_accepted_mapping_methods('any'); // set all method to mapping
|
|
||||||
$this->accept_no_mapping = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
```
|
|
||||||
|
|
||||||
`$this->set_accepted_mapping_methods()` lets you do that. If you set it to "any", all mappers will be available. If you set it to "list", you can then pass a second argument with the list of mappers you want to be available for the users.
|
|
||||||
|
|
||||||
```
|
|
||||||
public function __construct($attributes = array()) {
|
|
||||||
parent::__construct($attributes);
|
|
||||||
$this->set_accepted_mapping_methods('list', [ "dublin-core" ]); // set specific list of methods to mapping
|
|
||||||
}
|
|
||||||
|
|
||||||
```
|
|
||||||
|
|
||||||
Finally, `$this->accept_no_mapping = true;` informs that you also allow users to export items in their original form, without any mapping. In other words, to choose a Mapper is not mandatory if this is set to true.
|
|
||||||
|
|
||||||
## exporter steps
|
|
||||||
|
|
||||||
An exporter may have several steps, that will handle different parts of the process. Each step will be handled by a different callback in the exporter class.
|
|
||||||
|
|
||||||
First, lets have a look at a simple CSV exporter, that only have one step, in which it exports the items from the source collection into a CSV file. After that we will have a look on how to create custom steps.
|
|
||||||
|
|
||||||
### Simple exporter - One step that exports items
|
|
||||||
|
|
||||||
By default, exporters must implement 3 methods: `output_header`, `output_footer` and `process_item`.
|
|
||||||
|
|
||||||
`output_header` and `output_footer` are invoked at the beginning and end of each collection being exported. It can be useful, for example, to write the header and footer of a csv or xml file.
|
|
||||||
|
|
||||||
Inside this methods you may do whatever you want. It could be a POST to an API or to write a new file (see handling files below).
|
|
||||||
|
|
||||||
The `process_item` method will be invoked for every item being exported. Tainacan will automatically loop through the collections in the queue, fetch and prepare the items, and then send them, one at a time, to this method. (see adding collections below).
|
|
||||||
|
|
||||||
This method gets two parameters, the `$item` object (instance of \Tainacan\Entities\Item), and its `$metadata`, with an array of '\Tainacan\Entities\Item_Metadata_Entity' objects.
|
|
||||||
|
|
||||||
Note that, in this method, you dont need to bother about mapping. If a mapper was chosen by the user, you will receive this array of metadata already mapped, which means they will be only the ones defined in the mapper. (Note that, if a collection fail to map one of its metadata to a metadata expected by the chosen mapper, they will be empy elements in this array).
|
|
||||||
|
|
||||||
Now you can loop through the metadata and access any propery you want from the item to do whatever you like. In the case of the CSV exporter, it will add one line to the CSV file.
|
|
||||||
|
|
||||||
If you need to access the Mapper object, with everything about the the chosen mapper standard, you can do so by calling `$this->get_current_mapper()`. If a object is not returned, it means no mapper was selected by the user.
|
|
||||||
|
|
||||||
#### Adding collections
|
|
||||||
|
|
||||||
If your exporter does not use the `manual_collection` option, you might want to programatically add collections to be exported to the queue.
|
|
||||||
|
|
||||||
To add or remove a collection from the queue, use the `add_collection()` and `remove_collection()` methods passing the collection definition.
|
|
||||||
|
|
||||||
The collection definition is an array with their IDs and the total number of items to be exported.
|
|
||||||
|
|
||||||
|
|
||||||
Example of the structure of this propery for one collection:
|
|
||||||
|
|
||||||
```
|
|
||||||
[
|
|
||||||
'id' => 12,
|
|
||||||
'total_items' => 1234,
|
|
||||||
]
|
|
||||||
```
|
|
||||||
|
|
||||||
#### Handling files
|
|
||||||
|
|
||||||
Your export may generate one or more files, that will be downloaded by the user as a package at the end.
|
|
||||||
|
|
||||||
To create and write contents to a file, use the `append_to_file()` method.
|
|
||||||
|
|
||||||
It takes 2 arguments: `$key` and `$data`. `$key` is the file identifier and will serve as the filename, prepended with the ID of the process. If a file with this key does not exist yet, it will be created. `$data` is the content to be appended to the file.
|
|
||||||
|
|
||||||
Dont forget to add a link to the generated file at the output at the end of the process. This will be the only way users will have to get them. See "Final output" below).
|
|
||||||
|
|
||||||
TODO: If more than one file was created, Tainacan will create a zip of them at the end. Yet to be implemented.
|
|
||||||
|
|
||||||
#### Using transients
|
|
||||||
|
|
||||||
Since exporters are handled as background processes, they will run accross multiple requests. For that reason, you can not simply add properties to your class and trust their values will be kept during all the time the process is running.
|
|
||||||
|
|
||||||
If you want a value to persist so you can use it accross all methods of your exporter, at any time, you should use `transients` to store them in the database.
|
|
||||||
|
|
||||||
This is as simple as calling `set_transient()` and `get_transient()`. See the example below:
|
|
||||||
|
|
||||||
|
|
||||||
```
|
|
||||||
|
|
||||||
function callback_for_step_1() {
|
|
||||||
|
|
||||||
|
|
||||||
$this->add_transient('time_step_1', time());
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// ...
|
|
||||||
|
|
||||||
function callback_for_step_5() {
|
|
||||||
|
|
||||||
$time_step_1 = get_transient('time_step_1');
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
#### Handling user feedback
|
|
||||||
|
|
||||||
There are two information Tainacan exporters give to the user about the status of the process while it is running in feedback. the `progress label` and the `progress value`.
|
|
||||||
|
|
||||||
The `progress label` is a string that can be anything that tells the user what is going on. By default, it is the Step Name, but you can inform a specific `progress_label` attribute when registering the steps.
|
|
||||||
|
|
||||||
The `progress value` is a number between 0 and 100 that indicates the progress of the current step or the whole exporter, Thats up to you. By default, it calculates it automatically using the `total` attribute registered with the steps, against the `$this->get_in_step_count()` value. In the case of the default Process Items callback, it calculates based on the number of items found in each collection.
|
|
||||||
|
|
||||||
See the `finish_processing` dummy callback in the Test Importer. You will notice that when we registered the step, we informed a `total` attribute to this step with the value of 5. This will tell Tainacan that the total number iterations this step need to complete is 5 and allow it to calculate the progress.
|
|
||||||
|
|
||||||
If it is no possible to know `total` of a step beforehand, you can set it at any time, even inside the step callback itself, using the `set_current_step_total($value)` or `set_step_total($step, $value)` methods.
|
|
||||||
|
|
||||||
|
|
||||||
##### Final output
|
|
||||||
|
|
||||||
When the process finishes, Background processes define an "output", which is a final report to the user of what happened.
|
|
||||||
|
|
||||||
This could be simply a message saying "All good", or could be a report with the names and links to the collections created. HTML is welcome.
|
|
||||||
|
|
||||||
Remember that for a more detailed and technical report, you should use Logs (see Logs below). This output is meant as a short but descriptive user friendly message.
|
|
||||||
|
|
||||||
In order to achieve this, exporters must implement a method called `get_output()` that returns a string.
|
|
||||||
|
|
||||||
This method will be called only once, when the exporter ends, so you might need to save information using `transients` during the process and use them in `get_output()` to compose your message.
|
|
||||||
|
|
||||||
To get a list of the generated files to display to users, use `$this->get_output_files()`.
|
|
||||||
|
|
||||||
#### Logs
|
|
||||||
|
|
||||||
There are two useful methods to write information to the logs: `add_log()` and `add_error_log()`. These are written into a log file related to the exporter background process and a link to it will be presented to the user.
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## Run exporter
|
|
||||||
|
|
||||||
Finally, everything is ready. The exporter runs.
|
|
||||||
|
|
||||||
This will trigger a Background Process (documentation needed) and the exporter will run through as many background requests as needed.
|
|
|
@ -1,57 +1 @@
|
||||||
# Exporting and Exposing your Repository
|
This page was moved to https://tainacan.github.io/tainacan-wiki/
|
||||||
|
|
||||||
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 don’t 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
|
|
||||||
|
|
||||||
With Tainacan you have the possibility to map your collection structure to one or more known standards you may want to be compatible with. So even if you use a custom set of metadata to describe your collection, you may be compatible and interoperate with other repositories.
|
|
||||||
|
|
||||||
You do it by informing, for each metadatum you create, what is it relative in each format you want to map your collection to. You may say for example, that you "Name" Metadatum is the equivalent to the dc:Title attribute in Dublin Core and some another attribute in other format you choose.
|
|
||||||
|
|
||||||
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 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 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.
|
|
||||||
|
|
||||||
## Exposing
|
|
||||||
|
|
||||||
Tainacan is powered with an API that allows other applications to search and consume the content of your repository. By default, this API serves the content in JSON format, preserving the metadata in the collections the way you created them.
|
|
||||||
|
|
||||||
In the same way you can choose the format of the file when you export your collection, one can choose the format he/she want to consume yout content in. This is what exposers are for.
|
|
||||||
|
|
||||||
Each exposer implements a different way of presenting your data in the API response, and may support one or many mappings. See more [details about exposers](exposers.md).
|
|
||||||
|
|
||||||
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 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.
|
|
||||||
Example using WordPress rest server:
|
|
||||||
|
|
||||||
$item_exposer_json = json_encode([
|
|
||||||
'exposer' => 'Xml',
|
|
||||||
'mapper' => 'Dublin Core',
|
|
||||||
]);
|
|
||||||
$request = new \WP_REST_Request('GET', '/wp-json/tainacan/v2/items/123' );
|
|
||||||
$request->set_body($item_exposer_json);
|
|
||||||
$response = $this->server->dispatch($request);
|
|
||||||
|
|
||||||
|
|
||||||
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
|
|
||||||
|
|
261
docs/exposers.md
261
docs/exposers.md
|
@ -1,260 +1 @@
|
||||||
# How to create an Exposer
|
This page was moved to https://tainacan.github.io/tainacan-wiki/
|
||||||
|
|
||||||
Exposers are classes that implement a new Exposer that can be used by Tainacan to expose the content of a repository via API.
|
|
||||||
|
|
||||||
In order to create a new exposer you basically have to create an Exposer class and register it.
|
|
||||||
|
|
||||||
## Creating an Exposer class
|
|
||||||
|
|
||||||
Create a class that extends `\Tainacan\Exposers\Exposer`.
|
|
||||||
|
|
||||||
```PHP
|
|
||||||
<?php
|
|
||||||
|
|
||||||
class MyExposer extends \Tainacan\Exposers\Exposer {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
```
|
|
||||||
|
|
||||||
In this class you will have to set up some attributes and methods:
|
|
||||||
|
|
||||||
### Attributes
|
|
||||||
|
|
||||||
#### Slug
|
|
||||||
|
|
||||||
**String $slug**
|
|
||||||
|
|
||||||
A URL friendly version of the Exposer name, to be used as a parameter to the API request informing you want to get the data using this exposer.
|
|
||||||
|
|
||||||
```PHP
|
|
||||||
<?php
|
|
||||||
|
|
||||||
class MyExposer extends \Tainacan\Exposers\Exposer {
|
|
||||||
|
|
||||||
public $slug = 'my-exposer';
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
```
|
|
||||||
|
|
||||||
#### Supported Mapping Standard
|
|
||||||
|
|
||||||
**Array or true $mappers**
|
|
||||||
|
|
||||||
A list of mapping standards that is exposer supports. This means that whenever someone makes a request to receive data via this exposer, he/she will also be able to choose in which mapping standard they want the content to be served. If set to `true` the exposer will accept all mapping standards.
|
|
||||||
|
|
||||||
```PHP
|
|
||||||
<?php
|
|
||||||
|
|
||||||
class MyExposer extends \Tainacan\Exposers\Exposer {
|
|
||||||
|
|
||||||
public $slug = 'my-exposer';
|
|
||||||
public $mappers = true;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
```
|
|
||||||
|
|
||||||
or
|
|
||||||
|
|
||||||
```PHP
|
|
||||||
<?php
|
|
||||||
|
|
||||||
class MyExposer extends \Tainacan\Exposers\Exposer {
|
|
||||||
|
|
||||||
public $slug = 'my-exposer';
|
|
||||||
public $mappers = ['dublin-core']; // indicates that this exposer will only serve data mapped to dublin core mapper
|
|
||||||
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
#### Accept no Mapping Standards
|
|
||||||
|
|
||||||
**Bool $accept_no_mapper**
|
|
||||||
|
|
||||||
Indicates whether this exposer accept to serve data in its native form, without any mapping standards.
|
|
||||||
|
|
||||||
```PHP
|
|
||||||
<?php
|
|
||||||
|
|
||||||
class MyExposer extends \Tainacan\Exposers\Exposer {
|
|
||||||
|
|
||||||
public $slug = 'my-exposer';
|
|
||||||
public $mappers = ['dublin-core']; // indicates that this exposer will only serve data mapped to dublin core mapper
|
|
||||||
public $accept_no_mapper = true;
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### Methods
|
|
||||||
|
|
||||||
Now that you have declared the basic attributes of your Exposer, there are two methods you must implement.
|
|
||||||
|
|
||||||
|
|
||||||
#### __construct()
|
|
||||||
|
|
||||||
In this method you must call `set_name()` and `set_description()` to identify your exposer.
|
|
||||||
|
|
||||||
```PHP
|
|
||||||
<?php
|
|
||||||
|
|
||||||
class MyExposer extends \Tainacan\Exposers\Exposer {
|
|
||||||
|
|
||||||
public $slug = 'my-exposer';
|
|
||||||
public $mappers = ['dublin-core']; // indicates that this exposer will only serve data mapped to dublin core mapper
|
|
||||||
public $accept_no_mapper = true;
|
|
||||||
|
|
||||||
public function __construct() {
|
|
||||||
$this->set_name( __('My Exposer', 'my-test-plugin-namespace') );
|
|
||||||
$this->set_description( __('This exposer server the data in a very different way', 'my-test-plugin-namespace') );
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
**Note**: The reason Name and Description are declared this way, and not as attributes, is to give you the opportunity to localize your strings to different languages. Please refer to the WordPress documentation to learn how to internationalize your plugin.
|
|
||||||
|
|
||||||
|
|
||||||
#### rest_request_after_callbacks()
|
|
||||||
|
|
||||||
Now this is where all the magic happens!
|
|
||||||
|
|
||||||
This method will be called right before the API returns the data to the client.
|
|
||||||
|
|
||||||
It will give you all the items it received, in the way they were about to be served in the default JSON format, and give you the opportunity to transform it.
|
|
||||||
|
|
||||||
It receives 3 parameters:
|
|
||||||
|
|
||||||
* $response: an instance of the `\WP_REST_Response` object
|
|
||||||
* $handler: an instance of the `\WP_REST_Server` object
|
|
||||||
* $request: an instance of the `\WP_REST_Request` object
|
|
||||||
|
|
||||||
This method have to return the modified version of the `\WP_REST_Response` object.
|
|
||||||
|
|
||||||
```PHP
|
|
||||||
<?php
|
|
||||||
|
|
||||||
class MyExposer extends \Tainacan\Exposers\Exposer {
|
|
||||||
|
|
||||||
public $slug = 'my-exposer';
|
|
||||||
public $mappers = ['dublin-core']; // indicates that this exposer will only serve data mapped to dublin core mapper
|
|
||||||
public $accept_no_mapper = true;
|
|
||||||
|
|
||||||
public function __construct() {
|
|
||||||
$this->set_name( __('My Exposer', 'my-test-plugin-namespace') );
|
|
||||||
$this->set_description( __('This exposer server the data in a very different way', 'my-test-plugin-namespace') );
|
|
||||||
}
|
|
||||||
|
|
||||||
public function rest_request_after_callbacks( $response, $handler, $request ) {
|
|
||||||
|
|
||||||
// Set the headers to another content type, if applicable
|
|
||||||
$response->set_headers( ['Content-Type: text/plain; charset=' . get_option( 'blog_charset' )] );
|
|
||||||
|
|
||||||
$items = $response->get_data();
|
|
||||||
|
|
||||||
// Transform the items somehow ...
|
|
||||||
// ...
|
|
||||||
|
|
||||||
$response->set_data($items);
|
|
||||||
|
|
||||||
return $response;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
### Registering a new exposer
|
|
||||||
|
|
||||||
To register a new exposer, the action need to be added to the `init` hook, like:
|
|
||||||
```PHP
|
|
||||||
<?php
|
|
||||||
function registerMyExposer() {
|
|
||||||
$exposers = \Tainacan\Exposers_Handler::get_instance();
|
|
||||||
$exposers->register_exposer_type('MyExposer');
|
|
||||||
}
|
|
||||||
add_action('init', 'registerMyExposer');
|
|
||||||
```
|
|
||||||
|
|
||||||
### Full Example
|
|
||||||
|
|
||||||
This is a full example of a plugin that implements a simple text exposer
|
|
||||||
|
|
||||||
```PHP
|
|
||||||
<?php
|
|
||||||
/*
|
|
||||||
Plugin Name: Tainacan TXT Exposer
|
|
||||||
Description: This is a sample exposer class
|
|
||||||
*/
|
|
||||||
|
|
||||||
function myNewExposer($exposers) {
|
|
||||||
|
|
||||||
class TxtExposer extends \Tainacan\Exposers\Exposer {
|
|
||||||
|
|
||||||
public $slug = 'txt'; // type slug for url safe
|
|
||||||
public $name = 'TXT';
|
|
||||||
protected $mappers = true;
|
|
||||||
public $accept_no_mapper = true;
|
|
||||||
|
|
||||||
private $identation = '';
|
|
||||||
|
|
||||||
function __construct() {
|
|
||||||
$this->set_name( 'TXT' );
|
|
||||||
$this->set_description( __('A simple TXT table', 'my-test-plugin-namespace') );
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* {@inheritDoc}
|
|
||||||
* @see \Tainacan\Exposers\Types\Type::rest_request_after_callbacks()
|
|
||||||
*/
|
|
||||||
public function rest_request_after_callbacks( $response, $handler, $request ) {
|
|
||||||
$response->set_headers( ['Content-Type: text/plain; charset=' . get_option( 'blog_charset' )] );
|
|
||||||
|
|
||||||
$txt = $this->array_to_txt($response->get_data(), false);
|
|
||||||
|
|
||||||
$response->set_data($txt);
|
|
||||||
return $response;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Convert Array to Txt
|
|
||||||
* @param array $data
|
|
||||||
* @param string $txt
|
|
||||||
* @return string
|
|
||||||
*/
|
|
||||||
protected function array_to_txt( $data, $addlevel = true ) {
|
|
||||||
|
|
||||||
$return = '';
|
|
||||||
|
|
||||||
if ($addlevel) {
|
|
||||||
$this->identation .= ' ';
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach( $data as $key => $value ) {
|
|
||||||
|
|
||||||
|
|
||||||
$return .= $this->identation . $key . ': ';
|
|
||||||
if (is_array($value)) {
|
|
||||||
$return .= "\n" . $this->array_to_txt($value);
|
|
||||||
} else {
|
|
||||||
$return .= $value . "\n";
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($addlevel) {
|
|
||||||
$this->identation = substr($this->identation, 0, strlen($this->identation) - 4 );
|
|
||||||
}
|
|
||||||
|
|
||||||
return $return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$exposers = \Tainacan\Exposers_Handler::get_instance();
|
|
||||||
$exposers->register_exposer('TxtExposer');
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
add_action('init', 'myNewExposer');
|
|
||||||
```
|
|
|
@ -1,152 +1 @@
|
||||||
# Extra View Modes
|
This page was moved to https://tainacan.github.io/tainacan-wiki/
|
||||||
|
|
||||||
Extra View Modes are a way of creating your own templates for items list visualization. By default, Tainacan offers the following view modes:
|
|
||||||
|
|
||||||
- Table
|
|
||||||
- Cards
|
|
||||||
- Records
|
|
||||||
- Masonry
|
|
||||||
- Thumbnails
|
|
||||||
- Slideshow
|
|
||||||
|
|
||||||
Each has it's specificities, but in case you're not satisfied with them, a developer can easily create a plugin to offer a custom template for displaying items list.
|
|
||||||
|
|
||||||
## Creating your extra view mode
|
|
||||||
|
|
||||||
As shown in [our post for extra view modes](http://tainacan.org/2018/06/13/custom-view-modes-how-will-the-world-see-your-collection/), we've created [a sample plugin](https://github.com/tainacan/tainacan-extra-viewmodes) with some inspirational ideas for custom view modes. We here describe the process to create such plugin. You will basically need three files:
|
|
||||||
|
|
||||||
1. The .php file for registering the plugin and view mode;
|
|
||||||
2. The .php file with the items list template;
|
|
||||||
3. The .css file with the styling for your template;
|
|
||||||
|
|
||||||
### Registering your plugin
|
|
||||||
|
|
||||||
As in any WordPress Plugin, you'll first need to create a php header as follows:
|
|
||||||
|
|
||||||
```php
|
|
||||||
<?php
|
|
||||||
/*
|
|
||||||
Plugin Name: Name of Your Extra View Mode Plugin
|
|
||||||
Plugin URI: A URL for a related link
|
|
||||||
Description: An extra view modes plugin for Tainacan
|
|
||||||
Author: Your Name Here
|
|
||||||
Version: 0.1
|
|
||||||
License: GPLv2 or later
|
|
||||||
License URI: http://www.gnu.org/licenses/gpl-3.0.html
|
|
||||||
*/
|
|
||||||
|
|
||||||
defined( 'ABSPATH' ) or die( 'No script kiddies please!' );
|
|
||||||
```
|
|
||||||
|
|
||||||
Only Plugin Name is obligatory. Save the file with a name unique to your plugin (a good practice is to use the plugin name, such as `name-of-your-extra-view-mode-plugin.php`).
|
|
||||||
|
|
||||||
The rest of the file will contain the register function for your view mode. We discuss it in details ahead:
|
|
||||||
|
|
||||||
```php
|
|
||||||
add_action( 'after_setup_theme', function() {
|
|
||||||
|
|
||||||
tainacan_register_view_mode('MyViewMode', [
|
|
||||||
'label' => 'My View Mode',
|
|
||||||
'icon' => '<span class="icon"><i class="mdi mdi-view-quilt mdi-24px"></i></span>',
|
|
||||||
'description' => 'This is my view mode. It looks great the way it is.',
|
|
||||||
'dynamic_metadata' => false,
|
|
||||||
'template' => __DIR__ . './my-view-mode.php',
|
|
||||||
]);
|
|
||||||
|
|
||||||
} );
|
|
||||||
|
|
||||||
?> /* End of file */
|
|
||||||
```
|
|
||||||
|
|
||||||
The function `tainacan_register_view_mode` is part of Tainacan's plugin. It's first paramether is a unique *slug* that will be used to identify your view mode. Then follows an array of paramethers:
|
|
||||||
|
|
||||||
| Type | Name | Description | Default |
|
|
||||||
|--------|------------------|-------------|-------------------------------------------|
|
|
||||||
| string | label | Label, visible to users on the view modes dropdown. | Same of the variable $slug |
|
|
||||||
| string | icon | HTML that outputs an icon that represents the view mode. Visible to users on view modes dropdown. | None |
|
|
||||||
| string | description | Description, visible only to editors in the admin. | None |
|
|
||||||
| string | type | Type. Accepted values are 'template' or 'component'. | 'template' |
|
|
||||||
| string | template | Full path to the template file to be used. Required if $type is set to template. | 'theme-path/tainacan/view-mode-{$slug}.php' |
|
|
||||||
| string | component | Component tag name. The web component js must be included and must accept two props: 1) items: the list of items to be rendered ; 2) displayed-metadata: list of metadata to be displayed; | 'view-mode-{$slug}' |
|
|
||||||
| string | thumbnail | Full URL to a thumbnail that represents the view mode. Displayed only in Admin. | None |
|
|
||||||
| string | skeleton_template | HTML that outputs a preview of the items to be loaded, such as gray blocks, mimicking the shape of the items. | None |
|
|
||||||
| bool | show_pagination | Wether to display or not pagination controls. | true |
|
|
||||||
| bool | full-screen | Wether the view mode will display full screen or not. | false |
|
|
||||||
| bool | dynamic_metadata | Wether to display or not (and use or not) the "displayed metadata" selector. | false |
|
|
||||||
| bool | implements_skeleton | Wheter the view modes takes care of showing it's own Skeleton/Ghost css classes for loading items. | false |
|
|
||||||
|
|
||||||
The `type` parameter is one of the most relevants here. When registering view modes, you can either create a simple PHP `template` using WordPress functions (as the ones in our sample plugin), or more complex Vue.js `component`. When passing a template, the file path should be provided, while for components the name of previously loaded .vue component must be provided. Vue components must also have two props, one for receiving the items list as a parsed JSON Object and other for an array of metadata that will be displayed.
|
|
||||||
|
|
||||||
View modes as Cards and Grid do not allow users to choose which metadata should be displayed, but rather decide that only certain will be visible. For this kind of view mode, it is used the `dynamic_metadata` parameter as `false`.
|
|
||||||
|
|
||||||
By default a spinning circle loading animation is shown when items are being fetch. Most of our oficial view modes override this by implementing a so called skeleton/ghost list, that mimics a list of gray blocks in the shape of items before they arrived. If your view mode does that, you should set `implements_skeleton` to true and provide a HTML template with such skeleton to `skeleton_template`. Tainacan will take care of rendering this skeleton while items are being loaded.
|
|
||||||
|
|
||||||
## Making an extra view mode template
|
|
||||||
|
|
||||||
The file path indicated on the register above should point to the .php file where your template lives. It will probably have some structure similar to the following sample:
|
|
||||||
|
|
||||||
```php
|
|
||||||
<?php if ( have_posts() ) : ?>
|
|
||||||
|
|
||||||
<div class="my-view-mode-container">
|
|
||||||
|
|
||||||
<?php while ( have_posts() ) : the_post(); ?>
|
|
||||||
|
|
||||||
<a class="my-view-mode-item" href="<?php the_permalink(); ?>">
|
|
||||||
<div class="media">
|
|
||||||
|
|
||||||
<?php if ( tainacan_current_view_displays('thumbnail') ): ?>
|
|
||||||
<a href="<?php the_permalink(); ?>">
|
|
||||||
<?php if ( has_post_thumbnail() ): ?>
|
|
||||||
<?php the_post_thumbnail('tainacan-medium', array('class' => 'mr-4')); ?>
|
|
||||||
<?php else: ?>
|
|
||||||
<?php echo '<div class="mr-4"><img alt="Thumbnail placeholder" src="'.get_stylesheet_directory_uri().'/assets/images/thumbnail_placeholder.png"></div>'?>
|
|
||||||
<?php endif; ?>
|
|
||||||
|
|
||||||
</a>
|
|
||||||
<?php endif; ?>
|
|
||||||
|
|
||||||
<div class="list-metadata media-body">
|
|
||||||
<?php if ( tainacan_current_view_displays('title') ): ?>
|
|
||||||
<p class="metadata-title">
|
|
||||||
<a href="<?php the_permalink(); ?>">
|
|
||||||
<?php the_title(); ?>
|
|
||||||
</a>
|
|
||||||
</p>
|
|
||||||
<?php endif; ?>
|
|
||||||
<?php tainacan_the_metadata(array('metadata__in' => $view_mode_displayed_metadata['meta'], 'exclude_title' => true, 'before_title' => '<h3 class="metadata-label">', 'before_value' => '<p class="metadata-value">')); ?>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</a>
|
|
||||||
|
|
||||||
<?php endwhile; ?>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<?php else : ?>
|
|
||||||
|
|
||||||
<div class="my-view-mode-container">
|
|
||||||
<section class="section">
|
|
||||||
<div class="content has-text-gray4 has-text-centered">
|
|
||||||
<p>
|
|
||||||
<span class="icon is-large">
|
|
||||||
<i class="mdi mdi-48px mdi-file-multiple"></i>
|
|
||||||
</span>
|
|
||||||
</p>
|
|
||||||
<p>'No item was found.','tainacan-interface'</p>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<?php endif; ?>
|
|
||||||
```
|
|
||||||
|
|
||||||
The classes `my-view-mode-container` and `my-view-mode-item`, and so forth should be implemented by you on your style file. Other classes as the `skeleton`, `section` are part of Tainacan's plugin CSS, and can be used if you wish to keep a standard.
|
|
||||||
|
|
||||||
Thumbnail is obtained via the function `the_post_thumbnail()`, which accepts as first parameter any of the following:
|
|
||||||
|
|
||||||
- 'tainacan-small' (40px width, 40px height, cropped);
|
|
||||||
- 'tainacan-medium' (275px width, 275 height, cropped);
|
|
||||||
- 'tainacan-medium-full', (max. 205px width, max. 1500px height, not cropped );
|
|
||||||
|
|
||||||
You can see that this view mode displays the array of metadata obtained from `tainacan_the_metadata()` function.
|
|
|
@ -1,21 +1 @@
|
||||||
# Faceted Search
|
This page was moved to https://tainacan.github.io/tainacan-wiki/
|
||||||
|
|
||||||
Tainacan implements a faceted search for your collections.
|
|
||||||
|
|
||||||
Filters are displayed in the left side of the screen and allow users to filter the results.
|
|
||||||
|
|
||||||
By default, tainacan will filter the available values in each facet depending on the current filters selected. It means it will only display values that will bring at least one item in the results. Everytime the user selects on value from one filter, all the other values from all other filters are reduced. This helps users to refine their filters.
|
|
||||||
|
|
||||||
If you want to disable this behavior, and always display all possible values for a filter, even if there is no items that will meet the criteria, add this to you `wp-config.php`:
|
|
||||||
|
|
||||||
```
|
|
||||||
define('TAINACAN_FACETS_DISABLE_FILTER_ITEMS', true);
|
|
||||||
```
|
|
||||||
|
|
||||||
Tainacan will also add the number of items found for each value of each filter. This, when used without a Elastic Search* integration, might have performance issues. So if you want to remove the items count from the filters, add this to your `wp-config.php`:
|
|
||||||
|
|
||||||
```
|
|
||||||
define('TAINACAN_FACETS_DISABLE_COUNT_ITEMS', true);
|
|
||||||
```
|
|
||||||
|
|
||||||
\* Note: Elastic Search integration for this feature is still under development
|
|
|
@ -1,21 +1 @@
|
||||||
#Garbage Collector
|
This page was moved to https://tainacan.github.io/tainacan-wiki/
|
||||||
|
|
||||||
There is a first, experimental version of a WP CLI command for that:
|
|
||||||
|
|
||||||
```
|
|
||||||
wp tainacan garbage-collector
|
|
||||||
```
|
|
||||||
|
|
||||||
For more info:
|
|
||||||
```
|
|
||||||
wp help tainacan garbage-collector
|
|
||||||
```
|
|
||||||
|
|
||||||
It will clean:
|
|
||||||
|
|
||||||
* items from deleted collections (and its comments and metadata)
|
|
||||||
* documents and attachments from deleted items
|
|
||||||
* deleted metadata (currently there is interface to untrash them)
|
|
||||||
* post_meta of deleted metadata
|
|
||||||
* orphan terms (with taxonomy that does not exist)
|
|
||||||
* tnc_bulk postmeta (temporary metadata used to group items for bulk edit operations)
|
|
|
@ -1,31 +1 @@
|
||||||
Documentar os hooks do tainacan:
|
This page was moved to https://tainacan.github.io/tainacan-wiki/
|
||||||
|
|
||||||
**Actions**
|
|
||||||
|
|
||||||
tainacan-insert
|
|
||||||
|
|
||||||
tainacan-insert-Item_Metadata_Entity
|
|
||||||
|
|
||||||
tainacan-insert-Item
|
|
||||||
|
|
||||||
register_field_types
|
|
||||||
|
|
||||||
tainacan-insert-'.$obj->get_post_type()
|
|
||||||
|
|
||||||
tainacan-insert-Term
|
|
||||||
|
|
||||||
**Filters**
|
|
||||||
|
|
||||||
rest_{$this->collection->get_post_type()}_collection_params
|
|
||||||
|
|
||||||
tainacan-get-map-'.$this->get_name()
|
|
||||||
|
|
||||||
tainacan-insert-log-message-title
|
|
||||||
|
|
||||||
tainacan-set-post-status
|
|
||||||
|
|
||||||
tainacan-get-post-status
|
|
||||||
|
|
||||||
tainacan-status-validation
|
|
||||||
|
|
||||||
|
|
|
@ -1,330 +1 @@
|
||||||
# The Importer flow
|
This page was moved to https://tainacan.github.io/tainacan-wiki/
|
||||||
|
|
||||||
This page describes how Tainacan importer works and is a reference to write your own importer.
|
|
||||||
|
|
||||||
This documentation is still in construction. A very effective way to learn more on how to build an importer is to look at the source code of the Test Importer and the CSV Importer classes that are included in the tainacan package.
|
|
||||||
|
|
||||||
## Introduction
|
|
||||||
|
|
||||||
Importers can import items inside a collection, or even create a bunch of collections, taxonomies and items all at once.
|
|
||||||
|
|
||||||
In order to create an Importer, you have to extend the `\Tainacan\Importer` Class and register it using the global `Tainacan_Importer_Handler->register_importer()` method.
|
|
||||||
|
|
||||||
This method takes an array as argument, with the defintion of your importer. These are the expected attributes.
|
|
||||||
|
|
||||||
```
|
|
||||||
@type string $name The name of the importer. e.g. 'Example Importer'
|
|
||||||
@type string $slug A unique slug for the importer. e.g. 'example-importer'
|
|
||||||
@type string $description The importer description. e.g. 'This is an example importer description'
|
|
||||||
@type string $class_name The Importer Class. e.g. '\Tainacan\Importer\Test_Importer'
|
|
||||||
@type bool $manual_mapping Wether Tainacan must present the user with an interface to manually map
|
|
||||||
the metadata from the source to the target collection.
|
|
||||||
If set to true, Importer Class must implement the method
|
|
||||||
get_source_metadata() to return the metadata found in the source.
|
|
||||||
|
|
||||||
Note that this will only work when importing items to one single collection.
|
|
||||||
|
|
||||||
@type bool $manual_collection Wether Tainacan will let the user choose a destination collection.
|
|
||||||
If set to true, Tainacan will handle Collection creation and will assign it to
|
|
||||||
the importer object using add_collection() method.
|
|
||||||
|
|
||||||
Otherwise, the child importer class must create the collections and add them to the collections property also using add_collection()
|
|
||||||
```
|
|
||||||
|
|
||||||
Note that depending on the value of `manual_mapping` and `manual_collection` you will have to implement some methods in your importer class.
|
|
||||||
|
|
||||||
## Initializing a new importer
|
|
||||||
|
|
||||||
When the user starts a new import process, he/she first choose which import to use.
|
|
||||||
|
|
||||||
Once the Importer is chosen, the first thing that happens is the creation of a new instance of the chosen Importer. This fires the `__construct()` method.
|
|
||||||
|
|
||||||
|
|
||||||
## Choose a collection (if `manual_collection` is true)
|
|
||||||
|
|
||||||
After choosing the importer, user will be given the choice to choose the destination collection.
|
|
||||||
|
|
||||||
If called from inside a collection, this step is skipped and the current collection is set as destination.
|
|
||||||
|
|
||||||
## Set options
|
|
||||||
|
|
||||||
Now its time to set the importer options. Each importer may have its own set of options, that will be used during the import process. It could be anything, from the delimiter character in a CSV importer, to an API key for a importer that fetches something from an API.
|
|
||||||
|
|
||||||
Importer classes should declare the default values for its options in the `__construct()` method, by calling `set_default_options()`.
|
|
||||||
|
|
||||||
```
|
|
||||||
namespace Tainacan\Importer;
|
|
||||||
|
|
||||||
class MyImporter extends Importer {
|
|
||||||
function __construct() {
|
|
||||||
parent::construct();
|
|
||||||
$this->set_default_options(['
|
|
||||||
'foo' => 'bar'
|
|
||||||
']);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
The Importer classes must also implement the `options_form` method, in which it will echo the form that the user will interact to set the options.
|
|
||||||
|
|
||||||
```
|
|
||||||
function options_form() {
|
|
||||||
$form = '<div class="field">';
|
|
||||||
$form .= '<label class="label">' . __('My Importer Option 1', 'tainacan') . '</label>';
|
|
||||||
$form .= '<div class="control">';
|
|
||||||
$form .= '<input type="text" class="input" name="my_importer_option_1" value="' . $this->get_option('my_importer_option_1') . '" />';
|
|
||||||
$form .= '</div>';
|
|
||||||
$form .= '</div>';
|
|
||||||
|
|
||||||
return $form;
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## Fetch source
|
|
||||||
|
|
||||||
Next, users will choose the source. Each importer declares the kind of sources they accpet: URL, file, or both.
|
|
||||||
|
|
||||||
Importers do this by calling the `add_import_method()` and `remove_import_method` in its construction. By default, importers will support only `file` method. Here is an example of what an importer thar accepts only URLs should do:
|
|
||||||
|
|
||||||
```
|
|
||||||
function __construct() {
|
|
||||||
parent::construct();
|
|
||||||
|
|
||||||
$this->add_import_method('url');
|
|
||||||
$this->remove_import_method('file');
|
|
||||||
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
If the Importer accepts the `file` method, user will be prompted with a file input to upload a file. The file will then be saved and will be accessible via the `$this->get_tmp_file()` method.
|
|
||||||
|
|
||||||
If the importer accepts the `url` method, user will be prompted with an text input to enter an URL. By default, the importer will fetch any given URL to the same `file` attrribute, as if the user had uploaded it. However, each importer may override the `fetch_from_remote()` method and do whatever it want to create the file. For example, it could make several paged requests.
|
|
||||||
|
|
||||||
From that point forward, the importer will behave just as if it was using the file method.
|
|
||||||
|
|
||||||
|
|
||||||
## Mapping
|
|
||||||
|
|
||||||
At this point, if the Importer definition has `manual_mapping` set to `true`, the user is presented with an interface to map the metadata from the source to the metadata present in the chosen collection.
|
|
||||||
|
|
||||||
The Importer class must implement the `get_source_metadata()` method, that will return an array of the metadata found in the source. It can either return an hard coded array or an array that is read from the source file. For example, an importer that fetches data from an api knows beforehand what are the metadata the api will return, however, an importer that reads from a csv, may want to return whatever is found in the first line of the array.
|
|
||||||
|
|
||||||
```
|
|
||||||
// Example 1: returns a hard coded set of metadata
|
|
||||||
function get_source_metadata() {
|
|
||||||
return [
|
|
||||||
'title',
|
|
||||||
'description'
|
|
||||||
'date',
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
// Example 2: returns the columns of the first line of a CSV file
|
|
||||||
public function get_source_metadata(){
|
|
||||||
$file = new \SplFileObject( $this->tmp_file, 'r' );
|
|
||||||
$file->seek(0);
|
|
||||||
return $file->fgetcsv( $this->get_option('delimiter') );
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## Importer steps
|
|
||||||
|
|
||||||
An Importer may have several steps, that will handle different parts of the process. Each step will be handled by a different callback in the Importer class.
|
|
||||||
|
|
||||||
First, lets have a look at a simple CSV importer, that only have one step, in which it imports the items from the source into a chosen collection. After that we will have a look on how to create custom steps.
|
|
||||||
|
|
||||||
### Simple Importer - One step that imports items
|
|
||||||
|
|
||||||
By default, the only method an Importer class must implement to function is the `process_item()` class.
|
|
||||||
|
|
||||||
This method gets two parameters, the `$index` of the item to be inserted, and the `$collection_definition`, with information on the target collection.
|
|
||||||
|
|
||||||
Inside this method you must fetch the item from the source and format it according to the `mapping` definition of the collection.
|
|
||||||
|
|
||||||
The `mapping` defines how the item metadata from the source should be mapped to the metadata present in the target collection. It was created either manually, by the user, or programatically by the importer in an earlier step (see advanced importers below). This is an array where the keys are the `metadata IDs` and the values are the `identifers` found in source.
|
|
||||||
|
|
||||||
All this method should do is return the item as an associative array, where the keys are the metadata `identifiers`, and the values are the values tha should be stored.
|
|
||||||
|
|
||||||
And that's it. Behind the scenes the Importer super class is handling everyhting and it will call `process_item()` as many times as needed to import all items into the collection
|
|
||||||
|
|
||||||
### Advanced Importer - Many steps
|
|
||||||
|
|
||||||
By default, Tainacan Importer super class is registering one single step to the importer:
|
|
||||||
|
|
||||||
```
|
|
||||||
[
|
|
||||||
'name' => 'Import Items',
|
|
||||||
'progress_label' => 'Importing Items',
|
|
||||||
'callback' => 'process_collections'
|
|
||||||
]
|
|
||||||
```
|
|
||||||
|
|
||||||
This step will lopp through all the collections added to the importer (manuall or programatically) and add the items to it.
|
|
||||||
|
|
||||||
You may register as many steps and callbacks as you want in your importer, but you should consider keeping this default step at some point to handle the items insertion. For example, see how the Test Importer adds other steps before and after but keeps this default step in the middle:
|
|
||||||
|
|
||||||
```
|
|
||||||
class Test_Importer extends Importer {
|
|
||||||
|
|
||||||
protected $steps = [
|
|
||||||
|
|
||||||
[
|
|
||||||
'name' => 'Create Taxonomies',
|
|
||||||
'progress_label' => 'Creating taxonomies',
|
|
||||||
'callback' => 'create_taxonomies'
|
|
||||||
],
|
|
||||||
[
|
|
||||||
'name' => 'Create Collections',
|
|
||||||
'progress_label' => 'Creating Collections',
|
|
||||||
'callback' => 'create_collections'
|
|
||||||
],
|
|
||||||
|
|
||||||
// we keep the default step
|
|
||||||
[
|
|
||||||
'name' => 'Import Items',
|
|
||||||
'progress_label' => 'Importing items',
|
|
||||||
'callback' => 'process_collections'
|
|
||||||
],
|
|
||||||
|
|
||||||
[
|
|
||||||
'name' => 'Post-configure taxonomies',
|
|
||||||
'progress_label' => 'post processing taxonomies',
|
|
||||||
'callback' => 'close_taxonomies'
|
|
||||||
],
|
|
||||||
[
|
|
||||||
'name' => 'Finalize',
|
|
||||||
'progress_label' => 'Finalizing',
|
|
||||||
'callback' => 'finish_processing',
|
|
||||||
'total' => 5
|
|
||||||
]
|
|
||||||
|
|
||||||
];
|
|
||||||
|
|
||||||
//...
|
|
||||||
|
|
||||||
```
|
|
||||||
|
|
||||||
#### Steps callbacks
|
|
||||||
|
|
||||||
Each step has its own callback. The callback may do anything necessary, just keep in mind that you should allow the importer to break very long processes into several requests.
|
|
||||||
|
|
||||||
In order to that, your step callback might be called several times, and each time run a part of the process and return its current status, until its done.
|
|
||||||
|
|
||||||
When you run the importer, Tainacan will automatically iterate over your steps. If a step callback returns `false`, it assumes the step is over and it will pass to the next step in the next iteration. If the step callback returns an integer, it will keep the pointer in this step and call the same step again in the next iteration. The current position, which is the integer returned the last time the callback was invoked, will be accessible via the `$this->get_in_step_count()` method.
|
|
||||||
|
|
||||||
See this example found in the Test Importer:
|
|
||||||
|
|
||||||
```
|
|
||||||
public function finish_processing() {
|
|
||||||
|
|
||||||
// Lets just pretend we are doing something really important
|
|
||||||
$important_stuff = 5;
|
|
||||||
$current = $this->get_in_step_count();
|
|
||||||
if ($current <= $important_stuff) {
|
|
||||||
// This is very important
|
|
||||||
sleep(5);
|
|
||||||
$current ++;
|
|
||||||
return $current;
|
|
||||||
} else {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
#### Adding collections
|
|
||||||
|
|
||||||
If your importer does not use the `manual_collection` option, you might have to create the collection on your own.
|
|
||||||
|
|
||||||
You will do this using the [Tainacan internal API](internal-api.md).
|
|
||||||
|
|
||||||
After you've created one or more collections, you will have to add them to the importer queue, registering some information about them. This only if you want (and most likely you should) rely on the default step for processing items into the collections.
|
|
||||||
|
|
||||||
To add or remove a collection from the queue, use the `add_collection()` and `remove_collection()` methods passing the collection definition.
|
|
||||||
|
|
||||||
The collection definition is an array with their IDs, an identifier from the source, the total number of items to be imported, the mapping array from the source structure to the ID of the metadata metadata in tainacan.
|
|
||||||
|
|
||||||
The format of the map is an array where the keys are the metadata IDs of the destination collection and the values are the identifier from the source. This could be an ID or a string or whatever the importer finds appropriate to handle.
|
|
||||||
|
|
||||||
The source_id can be anyhting you like, that helps you relate this collection to your source. You will use it in you `process_item` method to know where to fetch the item from.
|
|
||||||
|
|
||||||
Example of the structure of this propery for one collection:
|
|
||||||
|
|
||||||
```
|
|
||||||
[
|
|
||||||
'id' => 12,
|
|
||||||
'mapping' => [
|
|
||||||
30 => 'column1'
|
|
||||||
31 => 'column2'
|
|
||||||
],
|
|
||||||
'total_items' => 1234,
|
|
||||||
'source_id' => 55
|
|
||||||
]
|
|
||||||
```
|
|
||||||
|
|
||||||
#### Using transients
|
|
||||||
|
|
||||||
Since Importers are handled as background processes, they will run accross multiple requests. For that reason, you can not simply add properties to your class and trust their values will be kept during all the time the process is running.
|
|
||||||
|
|
||||||
If you want a value to persist so you can use it accross all methods of your importer, at any time, you should use `transients` to store them in the database.
|
|
||||||
|
|
||||||
This is as simple as calling `set_transient()` and `get_transient()`. See the example below:
|
|
||||||
|
|
||||||
|
|
||||||
```
|
|
||||||
|
|
||||||
function callback_for_step_1() {
|
|
||||||
|
|
||||||
|
|
||||||
$this->add_transient('time_step_1', time());
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// ...
|
|
||||||
|
|
||||||
function callback_for_step_5() {
|
|
||||||
|
|
||||||
$time_step_1 = get_transient('time_step_1');
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
#### Handling user feedback
|
|
||||||
|
|
||||||
There are two information Tainacan Importers give to the user about the status of the process while it is running in feedback. the `progress label` and the `progress value`.
|
|
||||||
|
|
||||||
The `progress label` is a string that can be anything that tells the user what is going on. By default, it is the Step Name, but you can inform a specific `progress_label` attribute when registering the steps.
|
|
||||||
|
|
||||||
The `progress value` is a number between 0 and 100 that indicates the progress of the current step or the whole importer, Thats up to you. By default, it calculates it automatically using the `total` attribute registered with the steps, against the `$this->get_in_step_count()` value. In the case of the default Process Items callback, it calculates based on the number of items found in each collection.
|
|
||||||
|
|
||||||
Remember the `finish_processing` dummy callback we saw in the Test Importer. You might have also noticed that when we registered the step, we informed a `total` attribute to this step with the value of 5. This will tell Tainacan that the total number iterations this step need to complete is 5 and allow it to calculate the progress.
|
|
||||||
|
|
||||||
If it is no possible to know `total` of a step beforehand, you can set it at any time, even inside the step callback itself, using the `set_current_step_total($value)` or `set_step_total($step, $value)` methods.
|
|
||||||
|
|
||||||
|
|
||||||
##### Final output
|
|
||||||
|
|
||||||
When the process finishes, Background processes define an "output", which is a final report to the user of what happened.
|
|
||||||
|
|
||||||
This could be simply a message saying "All good", or could be a report with the names and links to the collections created. HTML is welcome.
|
|
||||||
|
|
||||||
Remember that for a more detailed and technical report, you should use Logs (see Logs below). This output is meant as a short but descriptive user friendly message.
|
|
||||||
|
|
||||||
In order to achieve this, Importers must implement a method called `get_output()` that returns a string.
|
|
||||||
|
|
||||||
This method will be called only once, when the importer ends, so you might need to save information using `transients` during the process and use them in `get_output()` to compose your message.
|
|
||||||
|
|
||||||
|
|
||||||
#### Logs
|
|
||||||
|
|
||||||
There are two useful methods to write information to the logs: `add_log()` and `add_error_log()`. These are written into a log file related to the importer background process and a link to it will be presented to the user.
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## Run importer
|
|
||||||
|
|
||||||
Finally, everything is ready. The importer runs.
|
|
||||||
|
|
||||||
This will trigger a Background Process (documentation needed) and the importer will run through as many background requests as needed.
|
|
|
@ -1,45 +1 @@
|
||||||
# Tainacan for developers
|
This page was moved to https://tainacan.github.io/tainacan-wiki/
|
||||||
|
|
||||||
As you know, Tainacan is a WordPress plugin and is built on the top of this very well known platform. If you know WordPress it will be very easy for you to understand how Tainacan is organized, how it interacts with the database and how to build your own features on the top of it.
|
|
||||||
|
|
||||||
## Well, but I'm new to WordPress
|
|
||||||
|
|
||||||
If you don't have expirience with WordPress and would like to develop a plugin for Tainacan, or to contribute to the Tainacan plugin, it's a good idea to get to know some WordPress fundamentals. Those will be useful to everything you will deal with while working with Tainacan.
|
|
||||||
|
|
||||||
This is a non-exhaustive list of the most important topics you should look into:
|
|
||||||
|
|
||||||
* [WP_Query](https://codex.wordpress.org/Class_Reference/WP_Query) class - This is the heart of WordPress, the class that gives you the interface fo query for posts in the database. All interaction with the database in Tainacan uses this class.
|
|
||||||
* [Custom Post types](https://codex.wordpress.org/Post_Types) and [taxonomies](https://codex.wordpress.org/Taxonomies) - All Tainacan entities, such as collections, metadata, filters and items, are WordPress Custom post types. To understand how WordPress handles custom post types and custom taxonomies is very helpful.
|
|
||||||
* [The Loop](https://codex.wordpress.org/The_Loop) - One of the main WordPress elements used to interact through posts. Useful specially if you are tweaking with themes.
|
|
||||||
* [Template Tags](https://codex.wordpress.org/Template_Tags) - Simple functions used by theme developers to display dynamic content. Usually these function are used inside "The Loop" and Tainacan implements [it's own Template tags](../src/theme-helper/template-tags.php).
|
|
||||||
* [Template Hierarchy](https://developer.wordpress.org/themes/basics/template-hierarchy/) - Crucial if working with themes.
|
|
||||||
* Custom queries and loops - Based on the topics above, it is good to understand how to build custom queries and loops to list posts.
|
|
||||||
|
|
||||||
## Resources
|
|
||||||
|
|
||||||
### Development Resources
|
|
||||||
|
|
||||||
* [Key Concepts](key-concepts.md) - First things first. Let's understand what is what in Tainacan.
|
|
||||||
* [Tainacan internals](internal-api.md) - Reference on Tainacan main classes and how to use them.
|
|
||||||
* [Setting up local environment](setup-local.md) - If you want to contribute to Tainacan core, you must set up your local environment. Alternatively, you can use our [Docker repository](https://github.com/tainacan/tainacan-docker). **If you want to develop themes or plugins, you don't need this**.
|
|
||||||
* [Tainacan Custom templates](custom-templates.md) - Custom templates that Tainacan add to WordPress Template Hierarchy
|
|
||||||
* [Tainacan Template Tags](../src/theme-helper/template-tags.php) - Template tags useful to use in templates
|
|
||||||
* Tainacan Hooks - soon
|
|
||||||
* [Tainacan API](https://tainacan.org/api-docs/) - (Under construction)
|
|
||||||
* [Exporting and Exposing](exporting-and-exposing.md)
|
|
||||||
* [Creating a new Metadata Type](creating-metadata-type.md)
|
|
||||||
* [How to create Exporters](exporter-flow.md)
|
|
||||||
* [How to create Importers](importer-flow.md)
|
|
||||||
* [How to create Exposers](exposers.md)
|
|
||||||
* [Creating new View Modes](https://wiki.tainacan.org/index.php?title=Extra_View_Modes)
|
|
||||||
|
|
||||||
### Configuration and performance
|
|
||||||
|
|
||||||
* [Faceted Search](faceted-search.md)
|
|
||||||
* [Search Engine](search-engine.md)
|
|
||||||
* [Garbage Collector](garbage-collector.md)
|
|
||||||
|
|
||||||
### Other
|
|
||||||
|
|
||||||
* [Tainacan release process](release.md)
|
|
||||||
|
|
|
@ -1,286 +1 @@
|
||||||
# Tainacan Internal API
|
This page was moved to https://tainacan.github.io/tainacan-wiki/
|
||||||
|
|
||||||
This page shows how the internal API works and how to create and fetch all kinds of entities in Tainacan: Collections, items, taxnomies, metadata, filters, terms, item metadata and logs.
|
|
||||||
|
|
||||||
Its important that you first get familiar with the [key concepts](key-concepts.md) of tainacan.
|
|
||||||
|
|
||||||
|
|
||||||
## Overview
|
|
||||||
|
|
||||||
Tainacan adds a tiny layer of abstraction over WordPress to handle all its entities, but at the end of the day, everything is stored as a post of a specific post type (except terms). So for someone used to the way WordPress works, the data structure have no misteries at all.
|
|
||||||
|
|
||||||
This layer is based on a Repository pattern. Each entity Tainacan deals with have a correspondent Repository class and a correspondent Entity class.
|
|
||||||
|
|
||||||
Repositories are the classes that comunicate with the database and know where everything is stored and how to find things. It is a singleton class, so it have only one instance available to be used by any part of the application.
|
|
||||||
|
|
||||||
```PHP
|
|
||||||
$metadata_repo = Tainacan\Repositories\Metadata::get_instance();
|
|
||||||
```
|
|
||||||
Entities classes are the representation of the individual of each repository.
|
|
||||||
|
|
||||||
This abstraction allows us to easily manipulate entities without worrying how to save or fetch them.
|
|
||||||
|
|
||||||
For example, Metadata have many attributes, such as `name` and `required` (indicating wether this metadatum is required or not). As mentioned before, Metadata are stored as posts of a special post type called `tainacan-metadatum`. The name of the metadatum is, of course, the `post_title` and this `required` attribute is stored as a `post_meta`.
|
|
||||||
|
|
||||||
However, you dont need to bother about that. This pattern allows you to manipulate a Field entity and it attributes in a transparent way, such as:
|
|
||||||
|
|
||||||
```PHP
|
|
||||||
$metadatum->get_name(); // returns the metadatum name
|
|
||||||
$metadatum->get_required(); // returns the required value
|
|
||||||
$metadatum->set_name('new name');
|
|
||||||
$metadatum->set_required('yes');
|
|
||||||
```
|
|
||||||
|
|
||||||
![Entities and the database](assets/entity-database.png)
|
|
||||||
|
|
||||||
|
|
||||||
Tainacan will automatically map the values of the attributes to and from where they are stored.
|
|
||||||
|
|
||||||
When you want to fetch entities from the database, this abstraction layer steps aside and let you use all the power and flexibility of `WP_Query`, which you know and love. For example:
|
|
||||||
|
|
||||||
```PHP
|
|
||||||
Repositories\Metadata::get_instance()->fetch('s=test');
|
|
||||||
```
|
|
||||||
|
|
||||||
The `fetch` method from the repositories accept exactly the same arguments accepted by `WP_Query` and uses it internally. In fact, you could use `WP_Query` directly if you prefer, but using the repository class gives you some advantages. You dont have to know the name of the post type, you can also fetch by some mapped attribute calling it directly, without having to use `meta_query` (or even know wether a property is stored as a post attribute or post_meta). See more details in the Fetch section below.
|
|
||||||
|
|
||||||
Documentation for each repository:
|
|
||||||
|
|
||||||
* [Collections](repository-collections.md)
|
|
||||||
* [Metadata'](repository-metadata.md)
|
|
||||||
* [Filters](repository-filters.md)
|
|
||||||
* [Taxonomies](repository-taxonomies.md)
|
|
||||||
* [Items](repository-items.md)
|
|
||||||
* [Terms](repository-terms.md)
|
|
||||||
|
|
||||||
## Fetching data
|
|
||||||
|
|
||||||
Every repository have a `fetch()` method to fetch data from the database. Some repositories may have other fetch methods, such as `fetch_by_collection`, please refer to their reference to find out.
|
|
||||||
|
|
||||||
### Getting one single individual
|
|
||||||
|
|
||||||
If you have the ID or the `WP_Post` object, you can get the item by initializing a new instance of the Entity class:
|
|
||||||
|
|
||||||
```PHP
|
|
||||||
$collection = new Tainacan\Entities\Collection($collection_post);
|
|
||||||
$collection = new Tainacan\Entities\Collection($collection_id);
|
|
||||||
```
|
|
||||||
|
|
||||||
This will have the same effect as calling the `fetch` method from the repository passing an integer as argument. The repository will assume it is the collection ID.
|
|
||||||
|
|
||||||
```PHP
|
|
||||||
$collection = Tainacan\Repositories\Collections::get_instance()->fetch($collection_id);
|
|
||||||
```
|
|
||||||
|
|
||||||
### Fethcing many individuals
|
|
||||||
|
|
||||||
To fetch collections (or any other entity) based on a query search, you may call the `fetch` method from the repository and use any paramater of the `WP_Query` class.
|
|
||||||
|
|
||||||
> the only exception for this are terms, which are saved as WordPress terms and gets paramaters from the `get_terms()` function instead
|
|
||||||
|
|
||||||
Examples:
|
|
||||||
|
|
||||||
```PHP
|
|
||||||
$repo = Tainacan\Repositories\Collections::get_instance();
|
|
||||||
$items_repo = Tainacan\Repositories\Collections::get_instance();
|
|
||||||
|
|
||||||
|
|
||||||
$collections = $repo->fetch(); // get all public collections (or any private collections current user can view. It works exactly the same way WP_Query)
|
|
||||||
|
|
||||||
/**
|
|
||||||
* fetch all items with title equal to 'test'
|
|
||||||
*/
|
|
||||||
$items = $items_repo->fetch([
|
|
||||||
'post_title' => 'test'
|
|
||||||
]);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* fetch all items with title equal to 'test'
|
|
||||||
* but using the mapped property name instead of the real name
|
|
||||||
*/
|
|
||||||
$items = $items_repo->fetch([
|
|
||||||
'title' => 'test'
|
|
||||||
]);
|
|
||||||
|
|
||||||
```
|
|
||||||
|
|
||||||
Note that you can use the mapped attribute names to build your query, but it is just fine if you want to use the native WordPress names. The same can be achievied with attributes stored as post_meta:
|
|
||||||
|
|
||||||
```PHP
|
|
||||||
$repo = Tainacan\Repositories\Metadata::get_instance();
|
|
||||||
|
|
||||||
$metadata = $repo->fetch([
|
|
||||||
'required' => 'yes'
|
|
||||||
]);
|
|
||||||
|
|
||||||
// is the same as
|
|
||||||
|
|
||||||
$metadata = $repo->fetch([
|
|
||||||
'meta_query' => [
|
|
||||||
[
|
|
||||||
'key' => 'required',
|
|
||||||
'value' => 'yes'
|
|
||||||
]
|
|
||||||
]
|
|
||||||
]);
|
|
||||||
```
|
|
||||||
|
|
||||||
### Output
|
|
||||||
|
|
||||||
Fetch methods accept an attribute to choose how you want your output.
|
|
||||||
|
|
||||||
By default, it is a `WP_Query` object, which you can use to build your loop just as if you had called `WP_Query` your self.
|
|
||||||
|
|
||||||
But it also can be an array of Taincan Entities objects. This is very useful when you want to manipulate them.
|
|
||||||
|
|
||||||
## Inserting
|
|
||||||
|
|
||||||
All repositories have an `insert()` method that gets an Entity as argument and save it in the database. If the entity has an ID, this method will update the entity. (yes, the same way `wp_insert_post` works)
|
|
||||||
|
|
||||||
Each repository will get as a parameter an instace of its correspondent entity. For example, Collections repository `insert()` will get an instace of `Tainacan\Entities\Collection` and return the updated entity.
|
|
||||||
|
|
||||||
However, before insertion, you must validate the entity, calling the `validate()` method every entity has. You can only insert valid entities.
|
|
||||||
|
|
||||||
So this is a typical routine for creating an entity:
|
|
||||||
|
|
||||||
```PHP
|
|
||||||
$collectionsRepo = \Tainacan\Repositories\Collections::get_instance();
|
|
||||||
$collection = new \Tainacan\Entities\Collection();
|
|
||||||
$collection->set_name('New Collection');
|
|
||||||
|
|
||||||
if ($collection->validate()) {
|
|
||||||
$insertedCollection = $collectionsRepo->insert($collection);
|
|
||||||
echo 'Now I have an ID! ' . $insertedCollection->get_id();
|
|
||||||
|
|
||||||
// Lets update something
|
|
||||||
$insertedCollection->set_description('new description');
|
|
||||||
if ($insertedCollection->validate()) {
|
|
||||||
$insertedCollection = $collectionsRepo->insert($insertedCollection);
|
|
||||||
echo 'I still have the same ID! ' . $insertedCollection->get_id();
|
|
||||||
} else {
|
|
||||||
$errors = $insertedCollection->get_errors();
|
|
||||||
}
|
|
||||||
|
|
||||||
} else {
|
|
||||||
$validationErrors = $collection->get_errors();
|
|
||||||
// Do something!
|
|
||||||
}
|
|
||||||
|
|
||||||
```
|
|
||||||
> IMPORTANT: Repositories `insert()` methods do not check for user permissions. If you call it, it will save entities to the database no matter who is logged in (or even if some is logged in). Again, this works the same way WordPress works with its internal functions. All permission checks must be done before you call the insertion methods. See "checking for permissions" section below.
|
|
||||||
|
|
||||||
The example above shows how to create and update a Collection, but it applies to every entity. They all work in the very same way.
|
|
||||||
|
|
||||||
Well, Item Metadata Entity is slightly different.
|
|
||||||
|
|
||||||
### Handling Item Metadata
|
|
||||||
|
|
||||||
`Item Metada` is a special kind of entity, because it is not an actual entity itself. Rather, it is the relationship between an Item and a Field. And this relationship has a value.
|
|
||||||
|
|
||||||
So imagine a Collection of pens has a Metadata called "color". This means that an item of this collection will have a relationship with this metadatum, and this relation will have a value. "Red", for example.
|
|
||||||
|
|
||||||
So the Item Metadata Entity constructor gets two entities: an item and a metadatum. Lets see an example, considering I alredy have a collection with metadata and an item.
|
|
||||||
|
|
||||||
```PHP
|
|
||||||
// Considering $item is an existing Item Entity an $metadatum an existing Field Entity
|
|
||||||
$itemMetadada = new \Tainacan\Entities\ItemMetadataEntity($item, $metadatum);
|
|
||||||
|
|
||||||
$itemMetadata->set_value('Red');
|
|
||||||
|
|
||||||
if ($itemMetadata->validate()) {
|
|
||||||
$ItemMetadataRepo = \Tainacan\Repositories\ItemMetadata::get_instance();
|
|
||||||
$ItemMetadata = $ItemMetadataRepo->insert($ItemMetadata);
|
|
||||||
} else {
|
|
||||||
$errors = $ItemMetadata->get_errors();
|
|
||||||
}
|
|
||||||
|
|
||||||
```
|
|
||||||
|
|
||||||
> Note: "Multiple" Metadata, which can have more than one value for the same item, work exactly the same way, with the difference that its value is an array of values, and not just one single value.
|
|
||||||
|
|
||||||
If you want to iterate over all metadata of an item or a collection, there are 2 usefull methods you can use. Metadata repository have a `fetch_by_collection()` method that will fetch all metadata from a given collection and return them in the right order.
|
|
||||||
|
|
||||||
Also, ItemMetadata Repository `fetch()` method will return an array of ItemMetadata Entities related to a given item.
|
|
||||||
|
|
||||||
|
|
||||||
### More about validating
|
|
||||||
|
|
||||||
TODO: document the validation chains
|
|
||||||
|
|
||||||
All validations validate the property with the validation declared in the get_map() method of the repository.
|
|
||||||
|
|
||||||
Validate item -> call ItemMetadata->validate() for each metadatum
|
|
||||||
|
|
||||||
Validate ItemMetadata -> call $metadatumType->validate() for the Field type of the metadatum.
|
|
||||||
|
|
||||||
Validate Field -> call validate_options() of the Field type
|
|
||||||
|
|
||||||
## Checking for permissions
|
|
||||||
|
|
||||||
Each entity type is stored as a post type and has its own set of capabilities. For example, Collections are posts of the `tainacan-collection` post type, and have associated capabilities such as `edit_tainacan-collections` and `edit_others_tainacan-collections`.
|
|
||||||
|
|
||||||
If you are familiar with WordPress Roles and capabilities and, more specifically, with custom post types capabilities, this is very easy to understand. If you are not, its best if you first learn how WordPress handles custom post types capabilities and you will easily understand how tainacan works with it.
|
|
||||||
|
|
||||||
To see a complete list of tainacan capabilities see [Tainacan Permissions](permissions.md).
|
|
||||||
|
|
||||||
When you use WordPress custom post types, you dont need to know the exact name of the capabilities of a post type to check for them. The post type object has a property called `cap` that informs you what are the specific capabilities that the post type have for the post type actions.
|
|
||||||
|
|
||||||
For example, for a post type called `book`, that have capabilities such as `edit_books`, you could:
|
|
||||||
|
|
||||||
```PHP
|
|
||||||
if (current_user_can('edit_books'))
|
|
||||||
// do something
|
|
||||||
```
|
|
||||||
OR
|
|
||||||
```PHP
|
|
||||||
$book_cpt = get_post_type_object('book');
|
|
||||||
if (current_user_can( $book_cpt->cap->edit_posts ))
|
|
||||||
// do something
|
|
||||||
```
|
|
||||||
|
|
||||||
This makes life easier and Tainacan works exacty the same way.
|
|
||||||
|
|
||||||
```PHP
|
|
||||||
|
|
||||||
$collection = new \Tainacan\Entities\Collection(23);
|
|
||||||
|
|
||||||
var_dump( $collection->get_capabilities() ); // all capabilities of the collections post type
|
|
||||||
|
|
||||||
```
|
|
||||||
|
|
||||||
This is specially usefull when handling Items, because they are posts of dynamic created post types, and it would cost too much to find out the correct capabilties names.
|
|
||||||
|
|
||||||
Also, every entity implement 3 methods to check for the Meta Capabilities `edit_post`, `delete_post` and `read_post`, so intead of:
|
|
||||||
|
|
||||||
```PHP
|
|
||||||
current_user_can( $item->get_capabilities()->edit_post, $item->get_id() );
|
|
||||||
```
|
|
||||||
|
|
||||||
You can simply
|
|
||||||
```PHP
|
|
||||||
$item->can_edit();
|
|
||||||
```
|
|
||||||
|
|
||||||
So now you know how to check the permision when a user wants to update an item. Here is the complete code:
|
|
||||||
|
|
||||||
```PHP
|
|
||||||
$collectionsRepo = \Tainacan\Repositories\Collections::get_instance();
|
|
||||||
$collection = new \Tainacan\Entities\Collection(23);
|
|
||||||
|
|
||||||
if ($collection->can_edit()) {
|
|
||||||
|
|
||||||
$collection->set_description('new description');
|
|
||||||
|
|
||||||
if ($collection->validate()) {
|
|
||||||
|
|
||||||
$collection = $collectionsRepo->insert($collection);
|
|
||||||
|
|
||||||
} else {
|
|
||||||
$validationErrors = $collection->get_errors();
|
|
||||||
// Do something!
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
echo 'Sorry, you dont have permission to do that';
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
|
@ -1,100 +1 @@
|
||||||
# Understanding Tainacan
|
This page was moved to https://tainacan.github.io/tainacan-wiki/
|
||||||
|
|
||||||
The typical workflow when you create a Digital Repository with Tainacan is:
|
|
||||||
|
|
||||||
* Create a collection
|
|
||||||
* Choose which metadata the item in this collections will have
|
|
||||||
* Choose which filters will be used when browsing the collection
|
|
||||||
* Insert items to the collection or import them from an external source
|
|
||||||
|
|
||||||
## Collections
|
|
||||||
|
|
||||||
A collection is a group of items, that share the same set of metadata. Every item uploaded to your digital repository will be part of a collection - and only one collection. For instance, you could have a "paintings" collections, with metadata such as Title, Author, Country, Tecnique, etc and another collection for "films", with Title, Director, Country and Genre.
|
|
||||||
|
|
||||||
For each collection you can set a different set of metadata and they can share common taxonomies, which means you could browse for items in a specific Country, and get both paintings and films in your results.
|
|
||||||
|
|
||||||
Collections can also have child collections, which will inherit parent's collection metadata and can add their own set of additional information.
|
|
||||||
|
|
||||||
## Items
|
|
||||||
|
|
||||||
Items are the actual content of yout repository. The painting, the film, the book and so on. They belong to a collection and have all the metadata configured in the collection it belongs to.
|
|
||||||
|
|
||||||
In WordPress language, each item is a post and its post type represents its collection.
|
|
||||||
|
|
||||||
### Document
|
|
||||||
|
|
||||||
Document is the main information of the item. It is the object which all metadata refer to. Tainacan accepts 3 different document types:
|
|
||||||
|
|
||||||
* File: A file attached to the item. An image, video, pdf, audio or other media file.
|
|
||||||
* URL: An URL pointing to an external website or file. It can be a general website, a specific file, or media services. In the case of media services, Tainacan recognizes addresses and displays the appropriate player, using [oEmbed](https://oembed.com/). A list of services automatically conveted to embeds by WordPress can be found [here](https://codex.wordpress.org/Embeds).
|
|
||||||
* Text: A plain text, stored directly in the database, the user can type upon creating or editing an item
|
|
||||||
|
|
||||||
## Metadata
|
|
||||||
|
|
||||||
Data about the Document.
|
|
||||||
|
|
||||||
Every collection declare a set of metadata to describe its documents. This means that the collection the item belongs to will detemine the metadata it will have.
|
|
||||||
|
|
||||||
Each metadata has a set of settings. Is it required? Is it supposed to be unique (an ID number for example)? Does it accept multiple values? What is it Metadata Type? (TODO: see the complete list of metadata attributes).
|
|
||||||
|
|
||||||
You may have repository-level metadata, that will be inherited by all collections of your repository. In the same way, collections inherit metadata from their parent collection.
|
|
||||||
|
|
||||||
(Note: you can use and import/export presets of metadata)
|
|
||||||
|
|
||||||
## Metadata Types
|
|
||||||
|
|
||||||
Metadata types are the objects that represent the types of metadata that can be used. Examples of Metadata Types are "Text", "Long text", "Date", "Relationship with another item", etc (TODO: see full list).
|
|
||||||
|
|
||||||
Each metadata type object have its own settings and web component that will be used to render the interface.
|
|
||||||
|
|
||||||
Metadata Types can be created via plugins and extend the default set of types shipped with Tainacan.
|
|
||||||
|
|
||||||
## Filters
|
|
||||||
|
|
||||||
For every collection, you may choose which metadata will be used to filter the results in a faceted search interface. These are the filters.
|
|
||||||
|
|
||||||
Filters give the ability to the user to filter items in a collection using a Filter Type.
|
|
||||||
|
|
||||||
## Filter Types
|
|
||||||
|
|
||||||
Filter types are the different types of interfaces to filter items in a collections based on one specific Metadatum. Examples of Filter Types are "input text", "datepicker", "date range picker", "number range slider", "list of checkboxes", etc.
|
|
||||||
|
|
||||||
Each Filter Type object have its own settings and web component that will be used to render the interface.
|
|
||||||
|
|
||||||
Filter Types can be created via plugins and extend the default set of types shipped with Tainacan.
|
|
||||||
|
|
||||||
## Taxonomies
|
|
||||||
|
|
||||||
Taxonomies can be created and used to classify items. Typical Taxonomies are Genre, Country, etc.
|
|
||||||
|
|
||||||
In WordPress language, they are Custom Taxonomies, and they can be shared among many collections.
|
|
||||||
|
|
||||||
Each Taxonomy has a set of terms. For example, the taxonomy Genre may have terms like "drama" and "comedy".
|
|
||||||
|
|
||||||
Terms can have hierarchy, which means that when you browse for items that have a parente term (for instance, "Rock"), the results will include items that have any of the child terms (for instance, "Progressive Rock" and "Classic Rock").
|
|
||||||
|
|
||||||
Terms also have a description, may have an icon or image that represents it and may also be linked to a existing concept of an ontology. They can have their own URL in your site, with a page listing all items that are related to them, despite their collection. In that way, they behave as if they were a collection themselves.
|
|
||||||
|
|
||||||
## Under discussion
|
|
||||||
|
|
||||||
### Item types
|
|
||||||
|
|
||||||
Item types gives you the abiity to specialize the desription of an item based on its nature. So, inside the same collection you may have items that vary in its nature and, therefore, have a different set of metadata.
|
|
||||||
|
|
||||||
For each item type you can choose a group of Metadata, in the same way you do for a collection. When you create an item inside a collection, it will have all the metadata chosen for that collection plus the metadata related to its type.
|
|
||||||
|
|
||||||
An item type can be anything. For example, LPs, Books and paintings are õbvious distinct item types that may have specific metadata. But it could also more abstract concepts, such as "financial transactions".
|
|
||||||
|
|
||||||
### Desktop
|
|
||||||
|
|
||||||
Desktop holds items that are not part of any collections yet. You might want to use it when you want to upload a bunch of items and organize them afterwards, instead of having to thinh an prepare all the collections beforehand.
|
|
||||||
|
|
||||||
Items in Desktop are not publicly visible and have only the metadata configured in repository level.
|
|
||||||
|
|
||||||
### Thematic Collections
|
|
||||||
|
|
||||||
Thematic Collections is another way to organize the items in your repository. In essence, each thematic collection is a term inside a taxonomy called "Thematic Collections", that can hold items from any collections, and an item can be part of many Thematic Collections.
|
|
||||||
|
|
||||||
It work in exactly the same way as any other taxonomy, the only difference is that you have another way to manage it,..
|
|
||||||
|
|
||||||
Another idea here is, intead of having a "fixed taxonomy" called thematic collections, we could just have a menu item "Taxonomies" or "Organize by taxonomies" that lets you browse the items by taxonomy, instead of by collections, and manipulate them.
|
|
|
@ -1,132 +1 @@
|
||||||
# Mapping Standards
|
This page was moved to https://tainacan.github.io/tainacan-wiki/
|
||||||
|
|
||||||
Mapping Standards are declarations of standards of metadata. Once they are available and activated in your repository, you can map the metadata of your collections to them.
|
|
||||||
|
|
||||||
## Structure
|
|
||||||
|
|
||||||
A Mapping Standard has the following attributes.
|
|
||||||
|
|
||||||
### Name
|
|
||||||
|
|
||||||
String $name
|
|
||||||
|
|
||||||
The name of the Mapping Standard.
|
|
||||||
|
|
||||||
### Metadata
|
|
||||||
|
|
||||||
Array or false $metadata
|
|
||||||
|
|
||||||
A list of metadata, terms or attributes that this mapping have. These are the element you will be able to map your Collection's Metadata.
|
|
||||||
|
|
||||||
Each metadatum has the following attributes:
|
|
||||||
|
|
||||||
* slug - The metadatum name, that refers to the name of the attribute in the origin vocabulary or ontology (e.g. title)
|
|
||||||
* label - The human readable name
|
|
||||||
* URI - The URI of this term/attribute in the origin Ontoloy/Vocabulary
|
|
||||||
* metadata_type - The prefered type for the metadatum
|
|
||||||
|
|
||||||
Array of:
|
|
||||||
```
|
|
||||||
['slug'] => [
|
|
||||||
'URI' => 'http://...',
|
|
||||||
'label' => 'Label',
|
|
||||||
'metadata_type' => 'date',
|
|
||||||
]
|
|
||||||
```
|
|
||||||
### Allow additional custom metadata
|
|
||||||
|
|
||||||
Boolean $allow_extra_metadata
|
|
||||||
|
|
||||||
Boolen indicating wether this mapping allows additional custom metadata to be added.
|
|
||||||
|
|
||||||
### Context URL / Vocab URL
|
|
||||||
|
|
||||||
String $context_url
|
|
||||||
|
|
||||||
The URL of the Ontology or vocabulary. For example `http://schema.org` or `http://dublincore.org/documents/dcmi-terms/`
|
|
||||||
|
|
||||||
### Type
|
|
||||||
|
|
||||||
String $type
|
|
||||||
|
|
||||||
The Class of the ontology that this mapping refers to. For example `CreativeWork`, which is a class of Schema.org, if applied
|
|
||||||
|
|
||||||
### Header
|
|
||||||
|
|
||||||
String $header
|
|
||||||
|
|
||||||
The header to be append to API answer, like for Dublin Core, if we need to add RDF to xml header when using Dublin Core as mapper, so:
|
|
||||||
```
|
|
||||||
public $header = '<?xml version="1.0"?><!DOCTYPE rdf:RDF SYSTEM "http://dublincore.org/2000/12/01-dcmes-xml-dtd.dtd"><rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:dc="http://purl.org/dc/elements/1.1/" ></rdf:RDF>';
|
|
||||||
```
|
|
||||||
|
|
||||||
### Prefix
|
|
||||||
|
|
||||||
String $prefix
|
|
||||||
|
|
||||||
The optional prefix for the key labels, like ```dc:``` for Dublin Core in XML exposing.
|
|
||||||
|
|
||||||
### Registering a new mapper
|
|
||||||
For register a new mapper, the action need to be added to `tainacan-register-exposer-mappers` hook, like:
|
|
||||||
```
|
|
||||||
function myNewMapper($exposers) {
|
|
||||||
$exposers->register_exposer_mapper('Tainacan\Exposers\Mappers\NewMapper');
|
|
||||||
}
|
|
||||||
add_action('tainacan-register-exposer-mappers', 'myNewMapper');
|
|
||||||
```
|
|
||||||
|
|
||||||
## Examples
|
|
||||||
|
|
||||||
```
|
|
||||||
namespace Tainacan\Exposers\Mappers;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Support Dublin Core Mapping
|
|
||||||
* http://purl.org/dc/elements/1.1/
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
class Dublin_Core extends Mapper {
|
|
||||||
public $slug = 'dublin-core';
|
|
||||||
public $name = 'Dublin Core';
|
|
||||||
public $allow_extra_metadata = true;
|
|
||||||
public $context_url = 'http://dublincore.org/documents/dcmi-terms/';
|
|
||||||
public $header = '<?xml version="1.0"?><!DOCTYPE rdf:RDF SYSTEM "http://dublincore.org/2000/12/01-dcmes-xml-dtd.dtd"><rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:dc="http://purl.org/dc/elements/1.1/" ></rdf:RDF>';
|
|
||||||
public $prefix = 'dc:';
|
|
||||||
public $metadata = [
|
|
||||||
'contributor' => [
|
|
||||||
'URI' => 'http://purl.org/dc/elements/1.1/contributor',
|
|
||||||
'label' => 'Contributor'
|
|
||||||
],
|
|
||||||
'coverage' => [
|
|
||||||
'URI' => 'http://purl.org/dc/elements/1.1/coverage',
|
|
||||||
'label' => 'Coverage'
|
|
||||||
],
|
|
||||||
'creator' => [
|
|
||||||
'URI' => 'http://purl.org/dc/elements/1.1/creator',
|
|
||||||
'label' => 'Creator'
|
|
||||||
],
|
|
||||||
...
|
|
||||||
```
|
|
||||||
|
|
||||||
```
|
|
||||||
namespace Tainacan\Exposers\Mappers;
|
|
||||||
|
|
||||||
class CreativeWorks extends Mapper {
|
|
||||||
public $name = 'Schema.org Creative Works';
|
|
||||||
public $slug = 'creative-works';
|
|
||||||
public $metadata = [
|
|
||||||
'name' => [
|
|
||||||
'label': 'Name',
|
|
||||||
'URI': 'http://schema.org/name'
|
|
||||||
],
|
|
||||||
'alternativeHeadline' => [
|
|
||||||
'label': 'Alternative Headline',
|
|
||||||
'URI': 'http://schema.org/alternativeHeadline'
|
|
||||||
],
|
|
||||||
... And so on...
|
|
||||||
],
|
|
||||||
public $allow_extra_fields = false;
|
|
||||||
public $context_url = 'http://schema.org';
|
|
||||||
public $type = 'CreativeWork';
|
|
||||||
}
|
|
||||||
```
|
|
|
@ -1,217 +1 @@
|
||||||
# Tainacan Users Permissions
|
This page was moved to https://tainacan.github.io/tainacan-wiki/
|
||||||
|
|
||||||
This page explains how permissions are handled in Tainacan. What are the users roles available and what each one of them can do.
|
|
||||||
|
|
||||||
Tainacan handles user permissions in the very same way WordPress does, so if you are used to WordPress Roles and Permissions, you wont have any trouble.
|
|
||||||
|
|
||||||
Default WordPress roles are assigned with new capabilities to work with Collections, Items and other Tainacan specific operations. Additionaly Tainacan creates new roles, relative to the core WordPress roles, that have the same Tainacan specific capabilities, but dont have acces to the rest of the administrative panel of WordPress. For example, WordPress Editor can manage everything inside Tainacan, and they can also create and publish pages, in the other hand, Tainacan Editors can't.
|
|
||||||
|
|
||||||
If you want to change permissions for specific roles or users, or even create new roles, you can allways use one of the many WordPress plugins available for that.
|
|
||||||
|
|
||||||
In short, these are the roles and their main characteristics. A detailed description can be found in the next session.
|
|
||||||
|
|
||||||
* Subscriber: Can't really do anything inside tainacan.
|
|
||||||
* Colaborator / Tainacan Colaborator: Can create collections and items, but not to publish them.
|
|
||||||
* Author / Tainacan Author: Can create and publish collections and items, but can not edit published items nor edit other user's items.
|
|
||||||
* Editor / Tainacan Editor: Can create, publish and edit other users's collections and items.
|
|
||||||
* Administrator: Rules the world.
|
|
||||||
|
|
||||||
## Roles and permissions
|
|
||||||
|
|
||||||
Here you will find a detailed explanation of what each role can do with each part of Tainacan.
|
|
||||||
|
|
||||||
### Collections
|
|
||||||
|
|
||||||
These are the capabilities related to collection management.
|
|
||||||
|
|
||||||
**Note about Collection moderators**: Collection moderators have the same capabilities an editor has, but only in relation to the collections he or she is moderating. Even if the user is a subscriber, he will act as if he/she was an editor for that specific collection.
|
|
||||||
|
|
||||||
| | Admin | Editor | Author | Collaborator |
|
|
||||||
|------------------------------|-------|--------|--------|--------------|
|
|
||||||
| Edit Collections | y | y | y | |
|
|
||||||
| Delete Collections | y | y | y | |
|
|
||||||
| Publish Collections | y | y | y | |
|
|
||||||
| Edit Published Collections | y | y | y | |
|
|
||||||
| Delete Published Collections | y | y | y | |
|
|
||||||
| Edit Others Collections | y | y | | |
|
|
||||||
| Delete Others Collections | y | y | | |
|
|
||||||
| Read Private Collections | y | y | | |
|
|
||||||
| Edit Private Collections | y | y | | |
|
|
||||||
| Delete Private Collections | y | y | | |
|
|
||||||
|
|
||||||
#### Edit Collections
|
|
||||||
|
|
||||||
> Capability name: edit_tainacan-collections
|
|
||||||
Who's got it: Everyone but subscribers and colaborators
|
|
||||||
|
|
||||||
Allows to create and edit one's own collections details. Does not allow to publish them.
|
|
||||||
|
|
||||||
#### Edit Others Collections
|
|
||||||
|
|
||||||
> Capability name: edit_others_tainacan-collections
|
|
||||||
Who's got it: Administrators, Editors and Tainacan Editors
|
|
||||||
|
|
||||||
Allows to edit other user's Collections details.
|
|
||||||
|
|
||||||
#### Edit Published Collections
|
|
||||||
|
|
||||||
> Capability name: edit_published_tainacan-collections
|
|
||||||
Who's got it: Administrators, Editors and Tainacan Editors
|
|
||||||
|
|
||||||
Allows to edit collections details after they were published.
|
|
||||||
|
|
||||||
#### Edit Private Collections
|
|
||||||
|
|
||||||
> Capability name: edit_private_tainacan-collections
|
|
||||||
Who's got it: Administrators, Editors and Tainacan Editors
|
|
||||||
|
|
||||||
Allows to edit details of collections marked as private.
|
|
||||||
|
|
||||||
#### Publish Collections
|
|
||||||
|
|
||||||
> Capability name: publish_tainacan-collections
|
|
||||||
Who's got it: Administrators, Editors, Tainacan Editors, Authors and Tainacan Authors
|
|
||||||
|
|
||||||
Allows to publish one's own collections.
|
|
||||||
|
|
||||||
#### Delete Collections
|
|
||||||
|
|
||||||
> Capability name: delete_tainacan-collections
|
|
||||||
Who's got it: Everyone but subscribers and colaborators
|
|
||||||
|
|
||||||
Allows to delete one's own collections.
|
|
||||||
|
|
||||||
#### Delete Others Collections
|
|
||||||
|
|
||||||
> Capability name: delete_others_tainacan-collections
|
|
||||||
Who's got it: Administrators, Editors and Tainacan Editors
|
|
||||||
|
|
||||||
Allows to delete other user's Collections.
|
|
||||||
|
|
||||||
#### Delete Published Collections
|
|
||||||
|
|
||||||
> Capability name: delete_published_tainacan-collections
|
|
||||||
Who's got it: Administrators, Editors and Tainacan Editors
|
|
||||||
|
|
||||||
Allows to delete collections after they were published.
|
|
||||||
|
|
||||||
#### Delete Private Collections
|
|
||||||
|
|
||||||
> Capability name: delete_private_tainacan-collections
|
|
||||||
Who's got it: Administrators, Editors and Tainacan Editors
|
|
||||||
|
|
||||||
Allows to delete collections marked as private.
|
|
||||||
|
|
||||||
#### Read Private Collections
|
|
||||||
|
|
||||||
> Capability name: read_private_tainacan-collections
|
|
||||||
Who's got it: Administrators, Editors and Tainacan Editors
|
|
||||||
|
|
||||||
Allows to view collections marked as private and its items.
|
|
||||||
|
|
||||||
### Items
|
|
||||||
|
|
||||||
These are the capabilities related to items management.
|
|
||||||
|
|
||||||
Every user in tainacan is granted a set of capabilities, for every collection in the repository, depending on his/her role.
|
|
||||||
|
|
||||||
Capabilities are independent for each collection. So a user may be editor in one collection but have no rights whatsoever in another collection.
|
|
||||||
|
|
||||||
Permissions to a specific collection may be granted to a user adding him/her as a moderator of the collection. In that case, he/she will have the same rights as an editor, but only for that specific collection.
|
|
||||||
|
|
||||||
Also, you may use a WordPress plugin to have granular control of capabilities for each user in each collection.
|
|
||||||
|
|
||||||
In the description below you will find the characteristics of all the capabilities that are applied for each collection. Each user and role have a set of all these 10 capabilities for each collection.
|
|
||||||
|
|
||||||
| | Admin | Editor | Author | Collaborator |
|
|
||||||
|------------------------|-------|--------|--------|--------------|
|
|
||||||
| Edit Items | y | y | y | y |
|
|
||||||
| Delete Items | y | y | y | y |
|
|
||||||
| Publish Items | y | y | y | |
|
|
||||||
| Edit Published Items | y | y | y | |
|
|
||||||
| Delete Published Items | y | y | y | |
|
|
||||||
| Edit Others Items | y | y | | |
|
|
||||||
| Delete Others Items | y | y | | |
|
|
||||||
| Read Private Items | y | y | | |
|
|
||||||
| Edit Private Items | y | y | | |
|
|
||||||
| Delete Private Items | y | y | | |
|
|
||||||
|
|
||||||
#### Edit Items
|
|
||||||
|
|
||||||
> Capability name: edit_%collection_id%_items
|
|
||||||
Who's got it: Everyone but subscribers
|
|
||||||
|
|
||||||
Allows to create and edit one's own items. Does not allow to publish them.
|
|
||||||
|
|
||||||
#### Edit Others Items
|
|
||||||
|
|
||||||
> Capability name: edit_others_%collection_id%_items
|
|
||||||
Who's got it: Administrators, Editors and Tainacan Editors
|
|
||||||
|
|
||||||
Allows to edit other user's Items.
|
|
||||||
|
|
||||||
#### Edit Published Items
|
|
||||||
|
|
||||||
> Capability name: edit_published_%collection_id%_items
|
|
||||||
Who's got it: Administrators, Editors and Tainacan Editors
|
|
||||||
|
|
||||||
Allows to edit Items after they were published.
|
|
||||||
|
|
||||||
#### Edit Private Items
|
|
||||||
|
|
||||||
> Capability name: edit_private_%collection_id%_items
|
|
||||||
Who's got it: Administrators, Editors and Tainacan Editors
|
|
||||||
|
|
||||||
Allows to edit Items marked as private.
|
|
||||||
|
|
||||||
#### Publish Items
|
|
||||||
|
|
||||||
> Capability name: publish_%collection_id%_items
|
|
||||||
Who's got it: Administrators, Editors, Tainacan Editors, Authors and Tainacan Authors
|
|
||||||
|
|
||||||
Allows to publish one's own Items.
|
|
||||||
|
|
||||||
#### Delete Items
|
|
||||||
|
|
||||||
> Capability name: delete_%collection_id%_items
|
|
||||||
Who's got it: Everyone but subscribers
|
|
||||||
|
|
||||||
Allows to delete one's own Items.
|
|
||||||
|
|
||||||
#### Delete Others Items
|
|
||||||
|
|
||||||
> Capability name: delete_others_%collection_id%_items
|
|
||||||
Who's got it: Administrators, Editors and Tainacan Editors
|
|
||||||
|
|
||||||
Allows to delete other user's Items.
|
|
||||||
|
|
||||||
#### Delete Published Items
|
|
||||||
|
|
||||||
> Capability name: delete_published_%collection_id%_items
|
|
||||||
Who's got it: Administrators, Editors and Tainacan Editors
|
|
||||||
|
|
||||||
Allows to delete Items after they were published.
|
|
||||||
|
|
||||||
#### Delete Private Items
|
|
||||||
|
|
||||||
> Capability name: delete_private_%collection_id%_items
|
|
||||||
Who's got it: Administrators, Editors and Tainacan Editors
|
|
||||||
|
|
||||||
Allows to delete Items marked as private.
|
|
||||||
|
|
||||||
#### Read Private Items
|
|
||||||
|
|
||||||
> Capability name: read_private_%collection_id%_items
|
|
||||||
Who's got it: Administrators, Editors and Tainacan Editors
|
|
||||||
|
|
||||||
Allows to view Items marked as private and its items.
|
|
||||||
|
|
||||||
|
|
||||||
### Taxonomies
|
|
||||||
|
|
||||||
### Metadata
|
|
||||||
|
|
||||||
### Filters
|
|
||||||
|
|
||||||
|
|
||||||
|
|
186
docs/release.md
186
docs/release.md
|
@ -1,185 +1 @@
|
||||||
# Releasing a new version
|
This page was moved to https://tainacan.github.io/tainacan-wiki/
|
||||||
|
|
||||||
This is a work in progress documentaion on how to release a new version.
|
|
||||||
|
|
||||||
Assuming:
|
|
||||||
|
|
||||||
* $CURRENT_VERSION is the current "old" version (e.g. 0.2)
|
|
||||||
* $NEW_VERSION is the version we are releasing (e.g. 0.3)
|
|
||||||
* $GIT_PATH is where our repository is cloned
|
|
||||||
* $BUILD_PATH is where the plugin is condigured to buid
|
|
||||||
* $SVN_PATH is where the WordPress.org SVN repo is
|
|
||||||
|
|
||||||
## Pre-release
|
|
||||||
|
|
||||||
Before we publish a new version, we always release one or more Release Candidates so the community have time to test and make sure the new version of Tainacan is clean and ready to reach the world.
|
|
||||||
|
|
||||||
### Start in the git repository
|
|
||||||
|
|
||||||
```
|
|
||||||
cd $GIT_PATH
|
|
||||||
git checkout develop
|
|
||||||
git pull
|
|
||||||
```
|
|
||||||
|
|
||||||
### Create a new Release branch
|
|
||||||
|
|
||||||
Create a new `release/$NEW_VERSION` branch. If using git flow:
|
|
||||||
|
|
||||||
```
|
|
||||||
git flow release start $NEW_VERSION
|
|
||||||
```
|
|
||||||
|
|
||||||
### Edit version numbers
|
|
||||||
|
|
||||||
Edit `src/readme.txt` and `src/tainacan.php` and change the version numbers to `$NEW_VERSION`. In `tainacan.php` also change the `TAINACAN_VERSION` constant after the comments section
|
|
||||||
|
|
||||||
When releasing a RC version, append RC (number) to the version.
|
|
||||||
|
|
||||||
Also increase the `Tested Up` version, if applicable.
|
|
||||||
|
|
||||||
### Set build to production mode
|
|
||||||
|
|
||||||
Edit `webpack.config.js` to set production mode.
|
|
||||||
|
|
||||||
### Build
|
|
||||||
|
|
||||||
```
|
|
||||||
./build.sh
|
|
||||||
cd $BUILD_PATH
|
|
||||||
```
|
|
||||||
### Set build to development mode
|
|
||||||
|
|
||||||
```
|
|
||||||
cd $GIT_PATH
|
|
||||||
git checkout webpack.config.js
|
|
||||||
```
|
|
||||||
|
|
||||||
### Publish the RC
|
|
||||||
|
|
||||||
Create a ZIP package with the built plugin and publish a blog post calling for tests.
|
|
||||||
|
|
||||||
Commit and push to Git!
|
|
||||||
|
|
||||||
### Test
|
|
||||||
|
|
||||||
Using the Test guide as a resource, test every feature of the plugin, including importers and exporters. With time, we can build more detailed test scripts and distribute tests among people in the community.
|
|
||||||
|
|
||||||
|
|
||||||
### Fix and publish new RC, or finish
|
|
||||||
|
|
||||||
If bugs are found, fix them and commit to the release branch. Publish a new RC, following the steps above, until we have a stable version we feel confident to release.
|
|
||||||
|
|
||||||
## Release
|
|
||||||
|
|
||||||
The plugin is ready to go. We have published one or more RCs and the community have tested it. Lets get it live to the world!
|
|
||||||
|
|
||||||
### Finish version number
|
|
||||||
|
|
||||||
Back to the git repository, make sure everything is up to date:
|
|
||||||
|
|
||||||
```
|
|
||||||
cd $GIT_PATH
|
|
||||||
git checkout release/$NEW_VERSION
|
|
||||||
git pull
|
|
||||||
```
|
|
||||||
Edit `src/readme.txt` and `src/tainacan.php` and change the version numbers to `$NEW_VERSION`.
|
|
||||||
|
|
||||||
Commit and push.
|
|
||||||
|
|
||||||
### Set build to production mode
|
|
||||||
|
|
||||||
Edit `webpack.config.js` to set production mode.
|
|
||||||
|
|
||||||
### Build
|
|
||||||
|
|
||||||
```
|
|
||||||
./build.sh
|
|
||||||
cd $BUILD_PATH
|
|
||||||
```
|
|
||||||
### Set build to development mode
|
|
||||||
|
|
||||||
```
|
|
||||||
cd $GIT_PATH
|
|
||||||
git checkout webpack.config.js
|
|
||||||
```
|
|
||||||
|
|
||||||
### Prepare SVN repo
|
|
||||||
|
|
||||||
clean trunk
|
|
||||||
|
|
||||||
```
|
|
||||||
rm -rf $SVN_PATH/trunk/*
|
|
||||||
```
|
|
||||||
|
|
||||||
### Copy new files
|
|
||||||
|
|
||||||
```
|
|
||||||
cp -R $BUILD_PATH/* $SVN_PATH/trunk/
|
|
||||||
```
|
|
||||||
|
|
||||||
Update assets
|
|
||||||
|
|
||||||
```
|
|
||||||
cp $GIT_PATH/wp-repo-assets/* $SVN_PATH/assets/
|
|
||||||
```
|
|
||||||
|
|
||||||
Create tag folder
|
|
||||||
|
|
||||||
```
|
|
||||||
cd $SVN_PATH
|
|
||||||
mkdir tags/$NEW_VERSION
|
|
||||||
cp -R trunk/* tags/$NEW_VERSION/
|
|
||||||
svn add tags/$NEW_VERSION
|
|
||||||
```
|
|
||||||
|
|
||||||
### Finish and commit
|
|
||||||
|
|
||||||
Go to the SVN folder
|
|
||||||
|
|
||||||
```
|
|
||||||
cd $SVN_PATH
|
|
||||||
```
|
|
||||||
|
|
||||||
`svn rm` all files that have been removed
|
|
||||||
|
|
||||||
```
|
|
||||||
svn st | grep '^!' | awk '{print $2}' | xargs svn rm
|
|
||||||
```
|
|
||||||
|
|
||||||
`svn add` all new files
|
|
||||||
|
|
||||||
```
|
|
||||||
svn st | grep '^?' | awk '{print $2}' | xargs svn add
|
|
||||||
```
|
|
||||||
|
|
||||||
Commit!
|
|
||||||
|
|
||||||
```
|
|
||||||
svn ci
|
|
||||||
```
|
|
||||||
|
|
||||||
### Check
|
|
||||||
|
|
||||||
In few minutes the new release should be available in the WordPress directory.
|
|
||||||
|
|
||||||
Check if everything is ok.
|
|
||||||
|
|
||||||
### Commit and create tag on git
|
|
||||||
|
|
||||||
Once the release is tested and confirmed, commit and create the tag on git.
|
|
||||||
|
|
||||||
Merge the release branch into master and develop branches and create a tag.
|
|
||||||
|
|
||||||
Using git flow:
|
|
||||||
|
|
||||||
```
|
|
||||||
cd $GIT_PATH
|
|
||||||
|
|
||||||
git flow release finish $NEW_VERSION
|
|
||||||
git push --all
|
|
||||||
git push --tags
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,122 +1 @@
|
||||||
|
This page was moved to https://tainacan.github.io/tainacan-wiki/
|
||||||
# Collections Repository
|
|
||||||
|
|
||||||
## Main Methods
|
|
||||||
|
|
||||||
These are the most used methods of this repository. For a complete list see [the repository file](../src/classes/repositories/class-tainacan-collections.php).
|
|
||||||
|
|
||||||
|
|
||||||
### fetch()
|
|
||||||
|
|
||||||
|
|
||||||
fetch collection based on ID or WP_Query args
|
|
||||||
|
|
||||||
Collections are stored as posts. Check WP_Query docs
|
|
||||||
to learn all args accepted in the $args parameter (@see https://developer.wordpress.org/reference/classes/wp_query/)
|
|
||||||
You can also use a mapped property, such as name and description, as an argument and it will be mapped to the
|
|
||||||
appropriate WP_Query argument
|
|
||||||
|
|
||||||
If a number is passed to $args, it will return a \Tainacan\Entities\Collection object. But if the post is not found or
|
|
||||||
does not match the entity post type, it will return an empty array
|
|
||||||
|
|
||||||
@param array $args WP_Query args || int $args the collection id
|
|
||||||
@param string $output The desired output format (@see \Tainacan\Repositories\Repository::fetch_output() for possible values)
|
|
||||||
|
|
||||||
@return \WP_Query|Array an instance of wp query OR array of entities;
|
|
||||||
|
|
||||||
|
|
||||||
### fetch_one()
|
|
||||||
|
|
||||||
|
|
||||||
Fetch one Entity based on query args.
|
|
||||||
|
|
||||||
Note: Does not work with Item_Metadata Repository
|
|
||||||
|
|
||||||
@param array $args Query Args as expected by fetch
|
|
||||||
|
|
||||||
@return false|\Tainacan\Entities The entity or false if none was found
|
|
||||||
|
|
||||||
|
|
||||||
### insert()
|
|
||||||
|
|
||||||
|
|
||||||
@param \Tainacan\Entities\Collection $collection
|
|
||||||
|
|
||||||
@return \Tainacan\Entities\Collection
|
|
||||||
{@inheritDoc}
|
|
||||||
@see \Tainacan\Repositories\Repository::insert()
|
|
||||||
|
|
||||||
|
|
||||||
### update()
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
### delete()
|
|
||||||
|
|
||||||
|
|
||||||
@param $collection_id
|
|
||||||
|
|
||||||
@return mixed|Collection
|
|
||||||
|
|
||||||
|
|
||||||
### trash()
|
|
||||||
|
|
||||||
|
|
||||||
@param $collection_id
|
|
||||||
|
|
||||||
@return mixed|Collection
|
|
||||||
|
|
||||||
|
|
||||||
## Usage
|
|
||||||
|
|
||||||
```PHP
|
|
||||||
$repository = \Tainacan\Repositories\Collections::get_instance();
|
|
||||||
```
|
|
||||||
|
|
||||||
## Entity Properties
|
|
||||||
|
|
||||||
These are the Entity attributes for this repository. The Entity class is at [classes/entities folder](../src/classes/entities/class-tainacan-collection.php)
|
|
||||||
|
|
||||||
Property | Description | Slug | Getter | Setter | Stored as
|
|
||||||
--- | --- | --- | --- | --- | ---
|
|
||||||
Status|The current situation of the post|status|`$entity->get_status()`|`$entity->set_status()`|post_status
|
|
||||||
ID|Unique identifier|id|`$entity->get_id()`|`$entity->set_id()`|ID
|
|
||||||
Name|The title of the collection|name|`$entity->get_name()`|`$entity->set_name()`|post_title
|
|
||||||
Author ID|The collection author's user ID (numeric string)|author_id|`$entity->get_author_id()`|`$entity->set_author_id()`|post_author
|
|
||||||
Creation Date|The collection creation date|creation_date|`$entity->get_creation_date()`|`$entity->set_creation_date()`|post_date
|
|
||||||
Modification Date|The collection modification date|modification_date|`$entity->get_modification_date()`|`$entity->set_modification_date()`|post_modified
|
|
||||||
Order|Collection order. This metadata is used if collections are manually ordered.|order|`$entity->get_order()`|`$entity->set_order()`|order
|
|
||||||
Parent Collection|Original collection from which features are inherited|parent|`$entity->get_parent()`|`$entity->set_parent()`|post_parent
|
|
||||||
Description|An introductory text describing the collection|description|`$entity->get_description()`|`$entity->set_description()`|post_content
|
|
||||||
Slug|An unique and sanitized string representation of the collection, used to build the collection URL. It must not contain any special characters or spaces.|slug|`$entity->get_slug()`|`$entity->set_slug()`|post_name
|
|
||||||
Default Order metadata|Default property items in this collections will be ordered by|default_orderby|`$entity->get_default_orderby()`|`$entity->set_default_orderby()`|meta
|
|
||||||
Default order|Default order for items in this collection. ASC or DESC|default_order|`$entity->get_default_order()`|`$entity->set_default_order()`|meta
|
|
||||||
Default Displayed Metadata|List of collection properties that will be displayed in the table view|default_displayed_metadata|`$entity->get_default_displayed_metadata()`|`$entity->set_default_displayed_metadata()`|meta
|
|
||||||
Default view mode|Collection default visualization mode|default_view_mode|`$entity->get_default_view_mode()`|`$entity->set_default_view_mode()`|meta
|
|
||||||
Enabled view modes|Which visualization modes will be available for the public to choose from|enabled_view_modes|`$entity->get_enabled_view_modes()`|`$entity->set_enabled_view_modes()`|meta
|
|
||||||
Ordination metadata|Collection metadata ordination|metadata_order|`$entity->get_metadata_order()`|`$entity->set_metadata_order()`|meta
|
|
||||||
Ordination filters|Collection filters ordination|filters_order|`$entity->get_filters_order()`|`$entity->set_filters_order()`|meta
|
|
||||||
Enable Cover Page|To use this page as the home page of this collection|enable_cover_page|`$entity->get_enable_cover_page()`|`$entity->set_enable_cover_page()`|meta
|
|
||||||
Cover Page ID|If enabled, this custom page will be used as cover for this collection, instead of default items list.|cover_page_id|`$entity->get_cover_page_id()`|`$entity->set_cover_page_id()`|meta
|
|
||||||
Header Image|The image to be used in collection header|header_image_id|`$entity->get_header_image_id()`|`$entity->set_header_image_id()`|meta
|
|
||||||
Moderators|To assign users as Moderators of this collection|moderators_ids|`$entity->get_moderators_ids()`|`$entity->set_moderators_ids()`|meta_multi
|
|
||||||
Thumbnail|Squared reduced-size version of a picture that helps recognizing and organizing files|_thumbnail_id|`$entity->get__thumbnail_id()`|`$entity->set__thumbnail_id()`|meta
|
|
||||||
Comment Status|Collection comment status: "open" means comments are allowed, "closed" means comments are not allowed.|comment_status|`$entity->get_comment_status()`|`$entity->set_comment_status()`|comment_status
|
|
||||||
Allow Items Comments|Collection items comment status: "open" means comments are allowed, "closed" means comments are not allowed.|allow_comments|`$entity->get_allow_comments()`|`$entity->set_allow_comments()`|meta
|
|
||||||
|
|
||||||
### Entity usage
|
|
||||||
|
|
||||||
|
|
||||||
Create new
|
|
||||||
|
|
||||||
```PHP
|
|
||||||
$entity = new \Tainacan\Entities\Collection();
|
|
||||||
```
|
|
||||||
|
|
||||||
Get existing by ID
|
|
||||||
```PHP
|
|
||||||
$repository = \Tainacan\Repositories\Collections::get_instance();
|
|
||||||
$entity = $repository->fetch(12);
|
|
||||||
echo 'My ID is ' . $entity->get_id(); // 12
|
|
||||||
```
|
|
||||||
|
|
|
@ -1,149 +1 @@
|
||||||
|
This page was moved to https://tainacan.github.io/tainacan-wiki/
|
||||||
# Filters Repository
|
|
||||||
|
|
||||||
## Main Methods
|
|
||||||
|
|
||||||
These are the most used methods of this repository. For a complete list see [the repository file](../src/classes/repositories/class-tainacan-filters.php).
|
|
||||||
|
|
||||||
|
|
||||||
### fetch()
|
|
||||||
|
|
||||||
|
|
||||||
fetch filter based on ID or WP_Query args
|
|
||||||
|
|
||||||
Filters are stored as posts. Check WP_Query docs
|
|
||||||
to learn all args accepted in the $args parameter (@see https://developer.wordpress.org/reference/classes/wp_query/)
|
|
||||||
You can also use a mapped property, such as name and description, as an argument and it will be mapped to the
|
|
||||||
appropriate WP_Query argument
|
|
||||||
|
|
||||||
If a number is passed to $args, it will return a \Tainacan\Entities\Filter object. But if the post is not found or
|
|
||||||
does not match the entity post type, it will return an empty array
|
|
||||||
|
|
||||||
@param array $args WP_Query args || int $args the filter id
|
|
||||||
@param string $output The desired output format (@see \Tainacan\Repositories\Repository::fetch_output() for possible values)
|
|
||||||
|
|
||||||
@return \WP_Query|Array an instance of wp query OR array of entities;
|
|
||||||
|
|
||||||
|
|
||||||
### fetch_one()
|
|
||||||
|
|
||||||
|
|
||||||
Fetch one Entity based on query args.
|
|
||||||
|
|
||||||
Note: Does not work with Item_Metadata Repository
|
|
||||||
|
|
||||||
@param array $args Query Args as expected by fetch
|
|
||||||
|
|
||||||
@return false|\Tainacan\Entities The entity or false if none was found
|
|
||||||
|
|
||||||
|
|
||||||
### fetch_ids()
|
|
||||||
|
|
||||||
|
|
||||||
fetch filters IDs based on WP_Query args
|
|
||||||
|
|
||||||
to learn all args accepted in the $args parameter (@see https://developer.wordpress.org/reference/classes/wp_query/)
|
|
||||||
You can also use a mapped property, such as name and description, as an argument and it will be mapped to the
|
|
||||||
appropriate WP_Query argument
|
|
||||||
|
|
||||||
@param array $args WP_Query args || int $args the item id
|
|
||||||
|
|
||||||
@return Array array of IDs;
|
|
||||||
@throws \Exception
|
|
||||||
|
|
||||||
|
|
||||||
### fetch_by_collection()
|
|
||||||
|
|
||||||
|
|
||||||
fetch filters by collection, searches all filters available
|
|
||||||
|
|
||||||
@param Entities\Collection $collection
|
|
||||||
@param array $args WP_Query args plus disabled_metadata
|
|
||||||
@param string $output The desired output format (@see \Tainacan\Repositories\Repository::fetch_output() for possible values)
|
|
||||||
|
|
||||||
@return array Entities\Metadatum
|
|
||||||
@throws \Exception
|
|
||||||
|
|
||||||
|
|
||||||
### fetch_ids_by_collection()
|
|
||||||
|
|
||||||
|
|
||||||
fetch filters IDs by collection, considering inheritance
|
|
||||||
|
|
||||||
@param Entities\Collection|int $collection object or ID
|
|
||||||
@param array $args WP_Query args plus disabled_metadata
|
|
||||||
|
|
||||||
@return array List of metadata IDs
|
|
||||||
@throws \Exception
|
|
||||||
|
|
||||||
|
|
||||||
### insert()
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@param \Tainacan\Entities\Entity $obj
|
|
||||||
|
|
||||||
@return \Tainacan\Entities\Entity | bool
|
|
||||||
@throws \Exception
|
|
||||||
|
|
||||||
|
|
||||||
### update()
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
### delete()
|
|
||||||
|
|
||||||
|
|
||||||
@param $filter_id
|
|
||||||
|
|
||||||
@return Entities\Filter
|
|
||||||
|
|
||||||
|
|
||||||
### trash()
|
|
||||||
|
|
||||||
|
|
||||||
@param $filter_id
|
|
||||||
|
|
||||||
@return mixed|Entities\Filter
|
|
||||||
|
|
||||||
|
|
||||||
## Usage
|
|
||||||
|
|
||||||
```PHP
|
|
||||||
$repository = \Tainacan\Repositories\Filters::get_instance();
|
|
||||||
```
|
|
||||||
|
|
||||||
## Entity Properties
|
|
||||||
|
|
||||||
These are the Entity attributes for this repository. The Entity class is at [classes/entities folder](../src/classes/entities/class-tainacan-filter.php)
|
|
||||||
|
|
||||||
Property | Description | Slug | Getter | Setter | Stored as
|
|
||||||
--- | --- | --- | --- | --- | ---
|
|
||||||
Status|Status|status|`$entity->get_status()`|`$entity->set_status()`|post_status
|
|
||||||
ID|Unique identifier|id|`$entity->get_id()`|`$entity->set_id()`|ID
|
|
||||||
Name|Name of the filter|name|`$entity->get_name()`|`$entity->set_name()`|post_title
|
|
||||||
Order|Filter order. This metadata is used if filters were manually ordered.|order|`$entity->get_order()`|`$entity->set_order()`|menu_order
|
|
||||||
Description|The filter description|description|`$entity->get_description()`|`$entity->set_description()`|post_content
|
|
||||||
Filter type options|The filter type options|filter_type_options|`$entity->get_filter_type_options()`|`$entity->set_filter_type_options()`|meta
|
|
||||||
Type|The filter type|filter_type|`$entity->get_filter_type()`|`$entity->set_filter_type()`|meta
|
|
||||||
Collection|The collection ID|collection_id|`$entity->get_collection_id()`|`$entity->set_collection_id()`|meta
|
|
||||||
Color|Filter color|color|`$entity->get_color()`|`$entity->set_color()`|meta
|
|
||||||
Metadata|Filter metadata|metadatum|`$entity->get_metadatum()`|`$entity->set_metadatum()`|meta
|
|
||||||
Max of options|The max number of options to be showed in filter sidebar.|max_options|`$entity->get_max_options()`|`$entity->set_max_options()`|meta
|
|
||||||
|
|
||||||
### Entity usage
|
|
||||||
|
|
||||||
|
|
||||||
Create new
|
|
||||||
|
|
||||||
```PHP
|
|
||||||
$entity = new \Tainacan\Entities\Filter();
|
|
||||||
```
|
|
||||||
|
|
||||||
Get existing by ID
|
|
||||||
```PHP
|
|
||||||
$repository = \Tainacan\Repositories\Filters::get_instance();
|
|
||||||
$entity = $repository->fetch(12);
|
|
||||||
echo 'My ID is ' . $entity->get_id(); // 12
|
|
||||||
```
|
|
||||||
|
|
|
@ -1,137 +1 @@
|
||||||
|
This page was moved to https://tainacan.github.io/tainacan-wiki/
|
||||||
# Items Repository
|
|
||||||
|
|
||||||
## Main Methods
|
|
||||||
|
|
||||||
These are the most used methods of this repository. For a complete list see [the repository file](../src/classes/repositories/class-tainacan-items.php).
|
|
||||||
|
|
||||||
|
|
||||||
### fetch()
|
|
||||||
|
|
||||||
|
|
||||||
fetch items based on ID or WP_Query args
|
|
||||||
|
|
||||||
Items are stored as posts. Check WP_Query docs
|
|
||||||
to learn all args accepted in the $args parameter (@see https://developer.wordpress.org/reference/classes/wp_query/)
|
|
||||||
You can also use a mapped property, such as name and description, as an argument and it will be mapped to the
|
|
||||||
appropriate WP_Query argument
|
|
||||||
|
|
||||||
If a number is passed to $args, it will return a \Tainacan\Entities\Item object. But if the post is not found or
|
|
||||||
does not match the entity post type, it will return an empty array
|
|
||||||
|
|
||||||
The second paramater specifies from which collections item should be fetched.
|
|
||||||
You can pass the Collection ID or object, or an Array of IDs or collection objects
|
|
||||||
|
|
||||||
@param array $args WP_Query args || int $args the item id
|
|
||||||
@param array $collections Array Entities\Collection || Array int collections IDs || int collection id || Entities\Collection collection object
|
|
||||||
@param string $output The desired output format (@see \Tainacan\Repositories\Repository::fetch_output() for possible values)
|
|
||||||
|
|
||||||
@return \WP_Query|Array|Item an instance of wp query OR array of entities OR a Item;
|
|
||||||
|
|
||||||
|
|
||||||
### fetch_one()
|
|
||||||
|
|
||||||
|
|
||||||
Fetch one Entity based on query args.
|
|
||||||
|
|
||||||
Note: Does not work with Item_Metadata Repository
|
|
||||||
|
|
||||||
@param array $args Query Args as expected by fetch
|
|
||||||
|
|
||||||
@return false|\Tainacan\Entities The entity or false if none was found
|
|
||||||
|
|
||||||
|
|
||||||
### fetch_ids()
|
|
||||||
|
|
||||||
|
|
||||||
fetch items IDs based on WP_Query args
|
|
||||||
|
|
||||||
to learn all args accepted in the $args parameter (@see https://developer.wordpress.org/reference/classes/wp_query/)
|
|
||||||
You can also use a mapped property, such as name and description, as an argument and it will be mapped to the
|
|
||||||
appropriate WP_Query argument
|
|
||||||
|
|
||||||
The second paramater specifies from which collections item should be fetched.
|
|
||||||
You can pass the Collection ID or object, or an Array of IDs or collection objects
|
|
||||||
|
|
||||||
@param array $args WP_Query args || int $args the item id
|
|
||||||
@param array $collections Array Entities\Collection || Array int collections IDs || int collection id || Entities\Collection collection object
|
|
||||||
|
|
||||||
@return Array array of IDs;
|
|
||||||
|
|
||||||
|
|
||||||
### get_thumbnail_id_from_document()
|
|
||||||
|
|
||||||
|
|
||||||
Get a default thumbnail ID from the item document.
|
|
||||||
|
|
||||||
@param Entities\Item $item The item
|
|
||||||
|
|
||||||
@return int|null The thumbnail ID or null if it was not possible to find a thumbnail
|
|
||||||
|
|
||||||
|
|
||||||
### insert()
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
### update()
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
### delete()
|
|
||||||
|
|
||||||
|
|
||||||
@param $item_id
|
|
||||||
|
|
||||||
@return mixed|Item
|
|
||||||
|
|
||||||
|
|
||||||
### trash()
|
|
||||||
|
|
||||||
|
|
||||||
@param $item_id
|
|
||||||
|
|
||||||
@return mixed|Item
|
|
||||||
|
|
||||||
|
|
||||||
## Usage
|
|
||||||
|
|
||||||
```PHP
|
|
||||||
$repository = \Tainacan\Repositories\Items::get_instance();
|
|
||||||
```
|
|
||||||
|
|
||||||
## Entity Properties
|
|
||||||
|
|
||||||
These are the Entity attributes for this repository. The Entity class is at [classes/entities folder](../src/classes/entities/class-tainacan-item.php)
|
|
||||||
|
|
||||||
Property | Description | Slug | Getter | Setter | Stored as
|
|
||||||
--- | --- | --- | --- | --- | ---
|
|
||||||
Status|The posts status|status|`$entity->get_status()`|`$entity->set_status()`|post_status
|
|
||||||
ID|Unique identifier|id|`$entity->get_id()`|`$entity->set_id()`|ID
|
|
||||||
Title|Title of the item|title|`$entity->get_title()`|`$entity->set_title()`|post_title
|
|
||||||
Description|The item description|description|`$entity->get_description()`|`$entity->set_description()`|post_content
|
|
||||||
Collection|The collection ID|collection_id|`$entity->get_collection_id()`|`$entity->set_collection_id()`|meta
|
|
||||||
Author|The item author's user ID (numeric string)|author_id|`$entity->get_author_id()`|`$entity->set_author_id()`|post_author
|
|
||||||
Creation Date|The item creation date|creation_date|`$entity->get_creation_date()`|`$entity->set_creation_date()`|post_date
|
|
||||||
Modification Date|The item modification date|modification_date|`$entity->get_modification_date()`|`$entity->set_modification_date()`|post_modified
|
|
||||||
Term IDs|The item term IDs|terms|`$entity->get_terms()`|`$entity->set_terms()`|terms
|
|
||||||
Document Type|The document type, can be a local attachment, an external URL or a text|document_type|`$entity->get_document_type()`|`$entity->set_document_type()`|meta
|
|
||||||
Document|The document itself. An ID in case of attachment, an URL in case of link or a text in the case of text.|document|`$entity->get_document()`|`$entity->set_document()`|meta
|
|
||||||
Thumbnail|Squared reduced-size version of a picture that helps recognizing and organizing files|_thumbnail_id|`$entity->get__thumbnail_id()`|`$entity->set__thumbnail_id()`|meta
|
|
||||||
Comment Status|Item comment status: "open" means comments are allowed, "closed" means comments are not allowed.|comment_status|`$entity->get_comment_status()`|`$entity->set_comment_status()`|comment_status
|
|
||||||
|
|
||||||
### Entity usage
|
|
||||||
|
|
||||||
|
|
||||||
Create new
|
|
||||||
|
|
||||||
```PHP
|
|
||||||
$entity = new \Tainacan\Entities\Item();
|
|
||||||
```
|
|
||||||
|
|
||||||
Get existing by ID
|
|
||||||
```PHP
|
|
||||||
$repository = \Tainacan\Repositories\Items::get_instance();
|
|
||||||
$entity = $repository->fetch(12);
|
|
||||||
echo 'My ID is ' . $entity->get_id(); // 12
|
|
||||||
```
|
|
||||||
|
|
|
@ -1,252 +1 @@
|
||||||
|
This page was moved to https://tainacan.github.io/tainacan-wiki/
|
||||||
# Metadata Repository
|
|
||||||
|
|
||||||
## Main Methods
|
|
||||||
|
|
||||||
These are the most used methods of this repository. For a complete list see [the repository file](../src/classes/repositories/class-tainacan-metadata.php).
|
|
||||||
|
|
||||||
|
|
||||||
### fetch()
|
|
||||||
|
|
||||||
|
|
||||||
fetch metadatum based on ID or WP_Query args
|
|
||||||
|
|
||||||
metadatum are stored as posts. Check WP_Query docs
|
|
||||||
to learn all args accepted in the $args parameter (@see https://developer.wordpress.org/reference/classes/wp_query/)
|
|
||||||
You can also use a mapped property, such as name and description, as an argument and it will be mapped to the
|
|
||||||
appropriate WP_Query argument
|
|
||||||
|
|
||||||
If a number is passed to $args, it will return a \Tainacan\Entities\Metadatum object. But if the post is not found or
|
|
||||||
does not match the entity post type, it will return an empty array
|
|
||||||
|
|
||||||
@param array $args WP_Query args || int $args the metadatum id
|
|
||||||
@param string $output The desired output format (@see \Tainacan\Repositories\Repository::fetch_output() for possible values)
|
|
||||||
|
|
||||||
@return Entities\Metadatum|\WP_Query|Array an instance of wp query OR array of entities;
|
|
||||||
@throws \Exception
|
|
||||||
|
|
||||||
|
|
||||||
### fetch_one()
|
|
||||||
|
|
||||||
|
|
||||||
Fetch one Entity based on query args.
|
|
||||||
|
|
||||||
Note: Does not work with Item_Metadata Repository
|
|
||||||
|
|
||||||
@param array $args Query Args as expected by fetch
|
|
||||||
|
|
||||||
@return false|\Tainacan\Entities The entity or false if none was found
|
|
||||||
|
|
||||||
|
|
||||||
### fetch_ids()
|
|
||||||
|
|
||||||
|
|
||||||
fetch metadata IDs based on WP_Query args
|
|
||||||
|
|
||||||
to learn all args accepted in the $args parameter (@see https://developer.wordpress.org/reference/classes/wp_query/)
|
|
||||||
You can also use a mapped property, such as name and description, as an argument and it will be mapped to the
|
|
||||||
appropriate WP_Query argument
|
|
||||||
|
|
||||||
@param array $args WP_Query args || int $args the item id
|
|
||||||
|
|
||||||
@return Array array of IDs;
|
|
||||||
@throws \Exception
|
|
||||||
|
|
||||||
|
|
||||||
### fetch_by_collection()
|
|
||||||
|
|
||||||
|
|
||||||
fetch metadatum by collection, considering inheritance and order
|
|
||||||
|
|
||||||
@param Entities\Collection $collection
|
|
||||||
@param array $args WP_Query args plus disabled_metadata
|
|
||||||
@param string $output The desired output format (@see \Tainacan\Repositories\Repository::fetch_output() for possible values)
|
|
||||||
|
|
||||||
@return array Entities\Metadatum
|
|
||||||
@throws \Exception
|
|
||||||
|
|
||||||
|
|
||||||
### fetch_ids_by_collection()
|
|
||||||
|
|
||||||
|
|
||||||
fetch metadata IDs by collection, considering inheritance
|
|
||||||
|
|
||||||
@param Entities\Collection|int $collection object or ID
|
|
||||||
@param array $args WP_Query args plus disabled_metadata
|
|
||||||
|
|
||||||
@return array List of metadata IDs
|
|
||||||
@throws \Exception
|
|
||||||
|
|
||||||
|
|
||||||
### fetch_metadata_types()
|
|
||||||
|
|
||||||
|
|
||||||
fetch all registered metadatum type classes
|
|
||||||
|
|
||||||
Possible outputs are:
|
|
||||||
CLASS (default) - returns the Class name of of metadatum types registered
|
|
||||||
NAME - return an Array of the names of metadatum types registered
|
|
||||||
|
|
||||||
@param $output string CLASS | NAME
|
|
||||||
|
|
||||||
@return array of Entities\Metadata_Types\Metadata_Type classes path name
|
|
||||||
|
|
||||||
|
|
||||||
### get_core_metadata()
|
|
||||||
|
|
||||||
|
|
||||||
returns all core metadata from a specific collection
|
|
||||||
|
|
||||||
@param Entities\Collection $collection
|
|
||||||
|
|
||||||
@return Array|\WP_Query
|
|
||||||
@throws \Exception
|
|
||||||
|
|
||||||
|
|
||||||
### get_core_title_metadatum()
|
|
||||||
|
|
||||||
|
|
||||||
Get the Core Title Metadatum for a collection
|
|
||||||
|
|
||||||
@param Entities\Collection $collection
|
|
||||||
|
|
||||||
@return \Tainacan\Entities\Metadatum The Core Title Metadatum
|
|
||||||
@throws \Exception
|
|
||||||
|
|
||||||
|
|
||||||
### get_core_description_metadatum()
|
|
||||||
|
|
||||||
|
|
||||||
Get the Core Description Metadatum for a collection
|
|
||||||
|
|
||||||
@param Entities\Collection $collection
|
|
||||||
|
|
||||||
@return \Tainacan\Entities\Metadatum The Core Description Metadatum
|
|
||||||
@throws \Exception
|
|
||||||
|
|
||||||
|
|
||||||
### fetch_all_metadatum_values()
|
|
||||||
|
|
||||||
|
|
||||||
Return all possible values for a metadatum
|
|
||||||
|
|
||||||
Each metadata is a label with the metadatum name and the value.
|
|
||||||
|
|
||||||
If an ID, a slug or a Tainacan\Entities\Metadatum object is passed in the 'metadata' argument, it returns only one metadata, otherwise
|
|
||||||
it returns all metadata
|
|
||||||
|
|
||||||
@param int $metadatum_id The ID of the metadata to fetch values from
|
|
||||||
@param array|string $args {
|
|
||||||
Optional. Array or string of arguments.
|
|
||||||
|
|
||||||
@type mixed $collection_idThe collection ID you want to consider or null for all collections. If a collectoin is set
|
|
||||||
then only values applied to items in this collection will be returned
|
|
||||||
|
|
||||||
@type int $numberThe number of values to return (for pagination). Default empty (unlimited)
|
|
||||||
|
|
||||||
@type int $offsetThe offset (for pagination). Default 0
|
|
||||||
|
|
||||||
@type array|bool $items_filterArray in the same format used in @see \Tainacan\Repositories\Items::fetch(). It will filter the results to only return values used in the items inside this criteria. If false, it will return all values, even unused ones. Defatul [] (all items)
|
|
||||||
|
|
||||||
@type array $includeArray if ids to be included in the result. Default [] (nothing)
|
|
||||||
|
|
||||||
@type array $searchString to search. It will only return values that has this string. Default '' (nothing)
|
|
||||||
|
|
||||||
@type array $parent_idUsed by taxonomy metadata. The ID of the parent term to retrieve terms from. Default 0
|
|
||||||
|
|
||||||
* @type bool $count_itemsInclude the count of items that can be found in each value (uses $items_filter as well). Default false
|
|
||||||
*
|
|
||||||
* @type string $last_termThe last term returned when using a elasticsearch for calculates the facet.
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@return array Array with the total number of values found. The total number of pages with the current number and the results with id and label for each value. Terms also include parent, taxonomy and number of children.
|
|
||||||
|
|
||||||
|
|
||||||
### insert()
|
|
||||||
|
|
||||||
|
|
||||||
@param \Tainacan\Entities\Metadatum $metadatum
|
|
||||||
|
|
||||||
@return \Tainacan\Entities\Metadatum
|
|
||||||
{@inheritDoc}
|
|
||||||
@see \Tainacan\Repositories\Repository::insert()
|
|
||||||
|
|
||||||
|
|
||||||
### update()
|
|
||||||
|
|
||||||
|
|
||||||
@param $object
|
|
||||||
@param $new_values
|
|
||||||
|
|
||||||
@return mixed|string|Entities\Entity
|
|
||||||
@throws \Exception
|
|
||||||
|
|
||||||
|
|
||||||
### delete()
|
|
||||||
|
|
||||||
|
|
||||||
@param $metadatum_id
|
|
||||||
|
|
||||||
@return mixed|void
|
|
||||||
@throws \Exception
|
|
||||||
|
|
||||||
|
|
||||||
### trash()
|
|
||||||
|
|
||||||
|
|
||||||
@param $metadatum_id
|
|
||||||
|
|
||||||
@return mixed|Entities\Metadatum
|
|
||||||
@throws \Exception
|
|
||||||
|
|
||||||
|
|
||||||
## Usage
|
|
||||||
|
|
||||||
```PHP
|
|
||||||
$repository = \Tainacan\Repositories\Metadata::get_instance();
|
|
||||||
```
|
|
||||||
|
|
||||||
## Entity Properties
|
|
||||||
|
|
||||||
These are the Entity attributes for this repository. The Entity class is at [classes/entities folder](../src/classes/entities/class-tainacan-metadatum.php)
|
|
||||||
|
|
||||||
Property | Description | Slug | Getter | Setter | Stored as
|
|
||||||
--- | --- | --- | --- | --- | ---
|
|
||||||
Status|Status|status|`$entity->get_status()`|`$entity->set_status()`|post_status
|
|
||||||
ID|Unique identifier|id|`$entity->get_id()`|`$entity->set_id()`|ID
|
|
||||||
Name|Name of the metadata|name|`$entity->get_name()`|`$entity->set_name()`|post_title
|
|
||||||
Slug|A unique and santized string representation of the metadata|slug|`$entity->get_slug()`|`$entity->set_slug()`|post_name
|
|
||||||
Order|Metadata order. This metadata will be used if collections were manually ordered.|order|`$entity->get_order()`|`$entity->set_order()`|menu_order
|
|
||||||
Parent|Parent metadata|parent|`$entity->get_parent()`|`$entity->set_parent()`|post_parent
|
|
||||||
Description|The metadata description|description|`$entity->get_description()`|`$entity->set_description()`|post_content
|
|
||||||
Type|The metadata type|metadata_type|`$entity->get_metadata_type()`|`$entity->set_metadata_type()`|meta
|
|
||||||
Required|The metadata is required. All items in this collection must fill this field|required|`$entity->get_required()`|`$entity->set_required()`|meta
|
|
||||||
Unique value|Metadata value should be unique accross all items in this collection|collection_key|`$entity->get_collection_key()`|`$entity->set_collection_key()`|meta
|
|
||||||
Multiple|Allow items to have more than one value for this metadatum|multiple|`$entity->get_multiple()`|`$entity->set_multiple()`|meta
|
|
||||||
Cardinality|Number of multiples possible metadata|cardinality|`$entity->get_cardinality()`|`$entity->set_cardinality()`|meta
|
|
||||||
Mask|The mask to be used in the metadata|mask|`$entity->get_mask()`|`$entity->set_mask()`|meta
|
|
||||||
Default value|The default value for the metadata|default_value|`$entity->get_default_value()`|`$entity->set_default_value()`|meta
|
|
||||||
Metadata type options|Specific options for metadata type|metadata_type_options|`$entity->get_metadata_type_options()`|`$entity->set_metadata_type_options()`|meta
|
|
||||||
Collection|The collection ID|collection_id|`$entity->get_collection_id()`|`$entity->set_collection_id()`|meta
|
|
||||||
Metadata Value Accepts Suggestions|Allow community to suggest different values for the metadata|accept_suggestion|`$entity->get_accept_suggestion()`|`$entity->set_accept_suggestion()`|meta
|
|
||||||
Relationship metadata mapping|The metadata mapping options. Metadata can be configured to match another type of data distribution.|exposer_mapping|`$entity->get_exposer_mapping()`|`$entity->set_exposer_mapping()`|meta
|
|
||||||
Display|Display by default on listing or do not display or never display.|display|`$entity->get_display()`|`$entity->set_display()`|meta
|
|
||||||
The semantic metadatum description URI|The semantic metadatum description URI like: https://schema.org/URL|semantic_uri|`$entity->get_semantic_uri()`|`$entity->set_semantic_uri()`|meta
|
|
||||||
|
|
||||||
### Entity usage
|
|
||||||
|
|
||||||
|
|
||||||
Create new
|
|
||||||
|
|
||||||
```PHP
|
|
||||||
$entity = new \Tainacan\Entities\Metadatum();
|
|
||||||
```
|
|
||||||
|
|
||||||
Get existing by ID
|
|
||||||
```PHP
|
|
||||||
$repository = \Tainacan\Repositories\Metadata::get_instance();
|
|
||||||
$entity = $repository->fetch(12);
|
|
||||||
echo 'My ID is ' . $entity->get_id(); // 12
|
|
||||||
```
|
|
||||||
|
|
|
@ -1,126 +1 @@
|
||||||
|
This page was moved to https://tainacan.github.io/tainacan-wiki/
|
||||||
# Taxonomies Repository
|
|
||||||
|
|
||||||
## Main Methods
|
|
||||||
|
|
||||||
These are the most used methods of this repository. For a complete list see [the repository file](../src/classes/repositories/class-tainacan-taxonomies.php).
|
|
||||||
|
|
||||||
|
|
||||||
### fetch()
|
|
||||||
|
|
||||||
|
|
||||||
fetch taxonomies based on ID or WP_Query args
|
|
||||||
|
|
||||||
Taxonomies are stored as posts. Check WP_Query docs
|
|
||||||
to learn all args accepted in the $args parameter (@see https://developer.wordpress.org/reference/classes/wp_query/)
|
|
||||||
You can also use a mapped property, such as name and description, as an argument and it will be mapped to the
|
|
||||||
appropriate WP_Query argument
|
|
||||||
|
|
||||||
If a number is passed to $args, it will return a \Tainacan\Entities\Taxonomy object. But if the post is not found or
|
|
||||||
does not match the entity post type, it will return an empty array
|
|
||||||
|
|
||||||
@param array $args WP_Query args | int $args the taxonomy id
|
|
||||||
@param string $output The desired output format (@see \Tainacan\Repositories\Repository::fetch_output() for possible values)
|
|
||||||
|
|
||||||
@return \WP_Query|Array an instance of wp query OR array of entities;
|
|
||||||
|
|
||||||
|
|
||||||
### fetch_one()
|
|
||||||
|
|
||||||
|
|
||||||
Fetch one Entity based on query args.
|
|
||||||
|
|
||||||
Note: Does not work with Item_Metadata Repository
|
|
||||||
|
|
||||||
@param array $args Query Args as expected by fetch
|
|
||||||
|
|
||||||
@return false|\Tainacan\Entities The entity or false if none was found
|
|
||||||
|
|
||||||
|
|
||||||
### fetch_by_collection()
|
|
||||||
|
|
||||||
|
|
||||||
fetch taxonomies by collection, considering inheritance
|
|
||||||
|
|
||||||
@param Entities\Collection $collection
|
|
||||||
@param array $args WP_Query args plus disabled_metadata
|
|
||||||
@param string $output The desired output format (@see \Tainacan\Repositories\Repository::fetch_output() for possible values)
|
|
||||||
|
|
||||||
@return array Entities\Metadatum
|
|
||||||
@throws \Exception
|
|
||||||
|
|
||||||
|
|
||||||
### term_exists()
|
|
||||||
|
|
||||||
|
|
||||||
* Check if a term already exists
|
|
||||||
*
|
|
||||||
* @param Entities\Taxonomy $taxonomy The taxonomy object where to look for terms
|
|
||||||
* @param string $term_name The term name
|
|
||||||
* @param int|null $parent The ID of the parent term to look for children or null to look for terms in any hierarchical position. Default is null
|
|
||||||
* @param bool $return_term wether to return the term object if it exists. default is to false
|
|
||||||
*
|
|
||||||
* @return bool|WP_Term return boolean indicating if term exists. If $return_term is true and term exists, return WP_Term object
|
|
||||||
|
|
||||||
|
|
||||||
### insert()
|
|
||||||
|
|
||||||
|
|
||||||
@param Entities\Taxonomy $taxonomy
|
|
||||||
|
|
||||||
@return Entities\Entity
|
|
||||||
|
|
||||||
|
|
||||||
### update()
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
### delete()
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
### trash()
|
|
||||||
|
|
||||||
|
|
||||||
@param $taxonomy_id
|
|
||||||
|
|
||||||
@return mixed|Entities\Taxonomy
|
|
||||||
|
|
||||||
|
|
||||||
## Usage
|
|
||||||
|
|
||||||
```PHP
|
|
||||||
$repository = \Tainacan\Repositories\Taxonomies::get_instance();
|
|
||||||
```
|
|
||||||
|
|
||||||
## Entity Properties
|
|
||||||
|
|
||||||
These are the Entity attributes for this repository. The Entity class is at [classes/entities folder](../src/classes/entities/class-tainacan-taxonomy.php)
|
|
||||||
|
|
||||||
Property | Description | Slug | Getter | Setter | Stored as
|
|
||||||
--- | --- | --- | --- | --- | ---
|
|
||||||
Status|Status|status|`$entity->get_status()`|`$entity->set_status()`|post_status
|
|
||||||
ID|Unique identifier|id|`$entity->get_id()`|`$entity->set_id()`|ID
|
|
||||||
Name|Name of the taxonomy|name|`$entity->get_name()`|`$entity->set_name()`|post_title
|
|
||||||
Description|The taxonomy description|description|`$entity->get_description()`|`$entity->set_description()`|post_content
|
|
||||||
Slug|The taxonomy slug|slug|`$entity->get_slug()`|`$entity->set_slug()`|post_name
|
|
||||||
Allow insert|Allow/Deny the creation of new terms in the taxonomy|allow_insert|`$entity->get_allow_insert()`|`$entity->set_allow_insert()`|meta
|
|
||||||
Enabled for post types|Also enable this taxonomy for other WordPress post types|enabled_post_types|`$entity->get_enabled_post_types()`|`$entity->set_enabled_post_types()`|meta_multi
|
|
||||||
Collections|The IDs of collection where the taxonomy is used|collections_ids|`$entity->get_collections_ids()`|`$entity->set_collections_ids()`|meta_multi
|
|
||||||
|
|
||||||
### Entity usage
|
|
||||||
|
|
||||||
|
|
||||||
Create new
|
|
||||||
|
|
||||||
```PHP
|
|
||||||
$entity = new \Tainacan\Entities\Taxonomy();
|
|
||||||
```
|
|
||||||
|
|
||||||
Get existing by ID
|
|
||||||
```PHP
|
|
||||||
$repository = \Tainacan\Repositories\Taxonomies::get_instance();
|
|
||||||
$entity = $repository->fetch(12);
|
|
||||||
echo 'My ID is ' . $entity->get_id(); // 12
|
|
||||||
```
|
|
||||||
|
|
|
@ -1,104 +1 @@
|
||||||
|
This page was moved to https://tainacan.github.io/tainacan-wiki/
|
||||||
# Terms Repository
|
|
||||||
|
|
||||||
## Main Methods
|
|
||||||
|
|
||||||
These are the most used methods of this repository. For a complete list see [the repository file](../src/classes/repositories/class-tainacan-terms.php).
|
|
||||||
|
|
||||||
|
|
||||||
### fetch()
|
|
||||||
|
|
||||||
|
|
||||||
fetch terms based on ID or get terms args
|
|
||||||
|
|
||||||
Terms are stored as WordPress regular terms. Check (@see https://developer.wordpress.org/reference/functions/get_terms/) get_terms() docs
|
|
||||||
to learn all args accepted in the $args parameter
|
|
||||||
|
|
||||||
The second paramater specifies from which taxonomies terms should be fetched.
|
|
||||||
You can pass the Taxonomy ID or object, or an Array of IDs or taxonomies objects
|
|
||||||
|
|
||||||
@param array $args WP_Query args || int $args the term id
|
|
||||||
@param array $taxonomies Array Entities\Taxonomy || Array int terms IDs || int collection id || Entities\Taxonomy taxonomy object
|
|
||||||
|
|
||||||
@return array of Entities\Term objects || Entities\Term
|
|
||||||
|
|
||||||
|
|
||||||
### fetch_one()
|
|
||||||
|
|
||||||
|
|
||||||
Fetch one Entity based on query args.
|
|
||||||
|
|
||||||
Note: Does not work with Item_Metadata Repository
|
|
||||||
|
|
||||||
@param array $args Query Args as expected by fetch
|
|
||||||
|
|
||||||
@return false|\Tainacan\Entities The entity or false if none was found
|
|
||||||
|
|
||||||
|
|
||||||
### insert()
|
|
||||||
|
|
||||||
|
|
||||||
@param Entities\Entity $term
|
|
||||||
|
|
||||||
@return Entities\Entity|Entities\Term
|
|
||||||
@throws \Exception
|
|
||||||
|
|
||||||
|
|
||||||
### update()
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
### delete()
|
|
||||||
|
|
||||||
|
|
||||||
@param Array $delete_args has ['term_id', 'taxonomy']
|
|
||||||
|
|
||||||
@return bool|int|mixed|\WP_Error
|
|
||||||
|
|
||||||
|
|
||||||
### trash()
|
|
||||||
|
|
||||||
|
|
||||||
@param $term_id
|
|
||||||
|
|
||||||
@return mixed|void
|
|
||||||
|
|
||||||
|
|
||||||
## Usage
|
|
||||||
|
|
||||||
```PHP
|
|
||||||
$repository = \Tainacan\Repositories\Terms::get_instance();
|
|
||||||
```
|
|
||||||
|
|
||||||
## Entity Properties
|
|
||||||
|
|
||||||
These are the Entity attributes for this repository. The Entity class is at [classes/entities folder](../src/classes/entities/class-tainacan-term.php)
|
|
||||||
|
|
||||||
Property | Description | Slug | Getter | Setter | Stored as
|
|
||||||
--- | --- | --- | --- | --- | ---
|
|
||||||
Status|Status|status|`$entity->get_status()`|`$entity->set_status()`|post_status
|
|
||||||
ID|Unique identifier|term_id|`$entity->get_term_id()`|`$entity->set_term_id()`|term_id
|
|
||||||
Name|Name of the term|name|`$entity->get_name()`|`$entity->set_name()`|name
|
|
||||||
Parent|The parent of the term|parent|`$entity->get_parent()`|`$entity->set_parent()`|parent
|
|
||||||
Description|The term description|description|`$entity->get_description()`|`$entity->set_description()`|description
|
|
||||||
Taxonomy|The term taxonomy|taxonomy|`$entity->get_taxonomy()`|`$entity->set_taxonomy()`|taxonomy
|
|
||||||
User|The term creator|user|`$entity->get_user()`|`$entity->set_user()`|termmeta
|
|
||||||
Header Image|The image to be used in term header|header_image_id|`$entity->get_header_image_id()`|`$entity->set_header_image_id()`|termmeta
|
|
||||||
Hide empty|Hide empty terms|hide_empty|`$entity->get_hide_empty()`|`$entity->set_hide_empty()`|hide_empty
|
|
||||||
|
|
||||||
### Entity usage
|
|
||||||
|
|
||||||
|
|
||||||
Create new
|
|
||||||
|
|
||||||
```PHP
|
|
||||||
$entity = new \Tainacan\Entities\Term();
|
|
||||||
```
|
|
||||||
|
|
||||||
Get existing by ID
|
|
||||||
```PHP
|
|
||||||
$repository = \Tainacan\Repositories\Terms::get_instance();
|
|
||||||
$entity = $repository->fetch(12);
|
|
||||||
echo 'My ID is ' . $entity->get_id(); // 12
|
|
||||||
```
|
|
||||||
|
|
|
@ -1,25 +1 @@
|
||||||
# Search engine
|
This page was moved to https://tainacan.github.io/tainacan-wiki/
|
||||||
|
|
||||||
In addition to the [faceted search](faceted-search.md), which allows users to filter items by specific metadata, Tainacan also offers a free textual search.
|
|
||||||
|
|
||||||
By default, when using this option to search, WordPress searches only inside the Title (post_title) and Description (post_content). This, of course, is very limited, and this article presents and discusses the approach Tainacan will take to face this issue.
|
|
||||||
|
|
||||||
There is'nt one silver bullet to solve this problem. In some cases, perhaps for small repositories, a simple change in the way WordPress queries for posts, including relation to metadata and taxonomies, can give users the results they were looking for. In other cases, repository managers may want to use sophisticated solutions such as Elastic Search or Solr to enable Full Text Search for their users.
|
|
||||||
|
|
||||||
An intermediary approach could be creating index tables and tokenizing strings. This would allow even to order results based on relevance. (There is at least one paid WordPress plugin that does that)
|
|
||||||
|
|
||||||
Considering all these options, our current approach was to filter the SQL query built by the WordPress WP_Query object and include all the joins and wheres needed to search also in metadata and taxonomies values. This approach is the same of the "[Search Everything](https://wordpress.org/plugins/search-everything/)" plugin.
|
|
||||||
|
|
||||||
This approach might slow down search queries, specially the open keyword search input.
|
|
||||||
|
|
||||||
If you want to disable this change to the default WordPress behavior you can do this by adding the following line to you `wp-config.php`. You should do this if you are going to use another plugin for this purpose to avoid conflicts.
|
|
||||||
|
|
||||||
```
|
|
||||||
define('TAINACAN_DISABLE_DEFAULT_SEARCH_ENGINE', true);
|
|
||||||
```
|
|
||||||
Our efforts right now are to improve the compatibility with [ElasticPress](https://wordpress.org/plugins/elasticpress/) plugin. Its already working for searches and most parts of the plugin. We are now starting to make it work to build the facets.
|
|
||||||
|
|
||||||
|
|
||||||
Our understanding is that, if a reposiory gets too big, it might need a more robust infrastructure and Elastic Search is our call. Therefore we are working to better integrate it with the ElasticPress plugin.
|
|
||||||
|
|
||||||
However, since we made sure to build things in the "WordPress way", and since Tainacan search uses the native `WP_Query` class to make it queries, any plugin that filters its behavior might work with Tainacan. So feel free to try other search plugins for WordPress and please let us know how well they work!
|
|
|
@ -1,113 +1 @@
|
||||||
# Setting up your local enviroment
|
This page was moved to https://tainacan.github.io/tainacan-wiki/
|
||||||
|
|
||||||
This document will run you through setting up your local enviroment and running the tests. If you haven't done it yet, please have a look at [key concepts](key-concepts.md) so you can have a better Understanding of the project.
|
|
||||||
|
|
||||||
## Overview
|
|
||||||
|
|
||||||
This is the development repository for the Tainacan WordPress plugin.
|
|
||||||
|
|
||||||
Overview of folders:
|
|
||||||
|
|
||||||
* `docs` - This technical documentation
|
|
||||||
* `src` - The actual plugin. Everything outside this folder is not part of the distribution package
|
|
||||||
* `tests` - phpunit tests
|
|
||||||
* `cypress` - integration tests
|
|
||||||
|
|
||||||
This repository includes all the tools needed to develop Tainacan, such as tests and other scripts to compile sass and other things.
|
|
||||||
|
|
||||||
## Before you start
|
|
||||||
|
|
||||||
Tainacan is a WordPress plugin, so you will need all the basic dependencies you usually have to run a WordPress site, such as PHP and MySQL.
|
|
||||||
|
|
||||||
You wil also need:
|
|
||||||
|
|
||||||
* `Composer` to manage dependencies
|
|
||||||
* `Sass` to compile sass into css files
|
|
||||||
* `WP-Cli` to configure the test environment
|
|
||||||
* `Phpunit` to run unit tests
|
|
||||||
* `Node` to handle dependencies and vuild the JS application (at least version 5.7.0)
|
|
||||||
|
|
||||||
```
|
|
||||||
sudo apt-get install phpunit composer ruby ruby-dev nodejs npm
|
|
||||||
sudo gem install sass
|
|
||||||
```
|
|
||||||
|
|
||||||
* To install WP-Cli, check [the official documentation](https://wp-cli.org/#installing).
|
|
||||||
|
|
||||||
|
|
||||||
## Setting up
|
|
||||||
|
|
||||||
First of all, clone this repository.
|
|
||||||
|
|
||||||
Note that you can NOT clone it directly in the WordPress `plugins` directory. Clone it in a folder of its own and configure your build to point to your local WordPress `plugins` folder.
|
|
||||||
|
|
||||||
```
|
|
||||||
git clone git@git.github.com:tainacan/tainacan.git
|
|
||||||
```
|
|
||||||
|
|
||||||
Set up a WordPress installation. This could be a dedicated installation to develop tainacan or you can use an existing instance you have. Its up to you, but you will need one, both for developing and manually testing, as well to run automated integration tests.
|
|
||||||
|
|
||||||
## Build
|
|
||||||
|
|
||||||
When we want to build the plugin, we run `build.sh` that basically installs any dependencies, compiles all the assets (sass and js) and moves the files to the plugin directory. This compiled version of the plugin is the one added to the official WordPress Plugin repository.
|
|
||||||
|
|
||||||
In order to use it, make a copy of `build-config-sample.cfg` and name it only `build-config.cfg`. Edit and fill in your environment details:
|
|
||||||
|
|
||||||
* `wp_base_dir`: The base directory for you local WordPress installation, used for development and testing. e.g `~/develop/wordpress`
|
|
||||||
* `wp_url`: The base URL for your local WordPress installation/ e.g `http://localhost/wp`
|
|
||||||
* `wp_plugin_dir`: The directory for your plugin build. Should be a directory inside `wp_base_dir`. e.g `~/develop/wordpress/wp-content/plugins/test-tainacan`
|
|
||||||
|
|
||||||
Once you are ready, you can run:
|
|
||||||
|
|
||||||
```
|
|
||||||
./build.sh
|
|
||||||
```
|
|
||||||
|
|
||||||
While developing, you might want to run `build-watch.sh`. This script will watch your development folder for changes and automatically build the plugin so you don't have to do it manually every time you modify a file.
|
|
||||||
|
|
||||||
## Tests
|
|
||||||
|
|
||||||
Tainacan uses `phpunit` to run tests for the backend and the API. This is a very important part of the development proccess! Never commit anything before run all the tests to make sure you did not break anything. If you are developing a new feature, you must write tests for it. If you are fixing a bug, you should first write a test that reproduces the bug and then make it pass.
|
|
||||||
|
|
||||||
To execute all the tests, simply execute the `phpunit` command from the project root folder. But first you need to configure PHPUnit.
|
|
||||||
|
|
||||||
#### Preparing PHPUnit
|
|
||||||
|
|
||||||
To run the unit tests it is necessary to create a new MySQL database for your unit tests. This database will be cleaned and restored every time you run PHPUnit.
|
|
||||||
|
|
||||||
Install the WordPress test library by running the script provided in the `tests/bin` folder, by running the following command:
|
|
||||||
|
|
||||||
```
|
|
||||||
tests/bin/install-wp-tests.sh wordpress_test root root /path/to/wordpress-test-folder localhost latest
|
|
||||||
```
|
|
||||||
The parameters are:
|
|
||||||
|
|
||||||
* Database name
|
|
||||||
* MySQL username
|
|
||||||
* MySQL password
|
|
||||||
* WordPress Test Directory*
|
|
||||||
* MySQL host
|
|
||||||
* WordPress version
|
|
||||||
* Optional: skip create database
|
|
||||||
|
|
||||||
\* `WordPress Test Directory` will be created with 2 sub folders:
|
|
||||||
|
|
||||||
* `wordpress-test` - An installation of WordPress
|
|
||||||
* `wordpress-tests-lib` - As the name says, the WordPress Tests Library
|
|
||||||
|
|
||||||
Inside `tests` folder, edit the file called `bootstrap-config-sample.php` and inform the folder where you installed your WordPress Test Library. This will be `/path/to/wordpress-test-folder/wodpress-tests-lib`. Save the file as `bootstrap-config.php`.
|
|
||||||
|
|
||||||
Note that the installation script will create a config file in the destination folder with your database credentials. If you have to change it, you will need to edit it there.
|
|
||||||
|
|
||||||
You only need to do all this once, and now you are ready to run tests.
|
|
||||||
|
|
||||||
#### Running tests
|
|
||||||
|
|
||||||
Simply type this command from the project root folder:
|
|
||||||
|
|
||||||
```
|
|
||||||
phpunit
|
|
||||||
```
|
|
||||||
|
|
||||||
(Note that `phpunit` accpets several parametrs, for example if you want to run just a specific group of tests).
|
|
||||||
|
|
|
@ -1,426 +1 @@
|
||||||
# Tainacan Application Programming Interface
|
This page was moved to https://tainacan.github.io/tainacan-wiki/
|
||||||
|
|
||||||
### Brief Description
|
|
||||||
|
|
||||||
A REST API for Tainacan Plugin. This API uses the Wordpress REST API.
|
|
||||||
|
|
||||||
------
|
|
||||||
### Routes and Endpoints
|
|
||||||
|
|
||||||
#### Summary
|
|
||||||
|
|
||||||
1. [Collections](#collections)
|
|
||||||
1. [Items](#items)
|
|
||||||
1. [Metadata](#metadata)
|
|
||||||
1. [Metadatum Types](#metadatum-types)
|
|
||||||
1. [Item Metadata](#item-metadata)
|
|
||||||
1. [Taxonomies](#taxonomies)
|
|
||||||
1. [Filters](#filters)
|
|
||||||
1. [Filter Types](#filter-types)
|
|
||||||
1. [Terms](#terms)
|
|
||||||
1. [Logs](#logs)
|
|
||||||
1. [Others](#others)
|
|
||||||
|
|
||||||
------
|
|
||||||
#### Collections
|
|
||||||
|
|
||||||
1. Route `wp-json/tainacan/v2/collections/(?P<collection_id>[\d]+)`
|
|
||||||
|
|
||||||
1. Endpoints:
|
|
||||||
|
|
||||||
1. GET (Fetch a collection)
|
|
||||||
|
|
||||||
1. DELETE (Delete or Trash a collection and all your dependencies)
|
|
||||||
|
|
||||||
*To delete pass in body of a requisition the parameter `is_permanently` as true. To only trash pass false.*
|
|
||||||
|
|
||||||
1. PATCH or PUT (Update a collection)
|
|
||||||
|
|
||||||
Example of JSON passed in body for updating a collection:
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
{
|
|
||||||
"name": "string",
|
|
||||||
"description": "string",
|
|
||||||
...
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
2. Route `wp-json/tainacan/v2/collections`
|
|
||||||
|
|
||||||
1. Endpoints:
|
|
||||||
|
|
||||||
1. GET (Fetch all collections)
|
|
||||||
|
|
||||||
1. POST (Create a collection).
|
|
||||||
|
|
||||||
Example of JSON passed in body for creating a collection:
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
{
|
|
||||||
"name": "string",
|
|
||||||
"description": "string",
|
|
||||||
"status": "string",
|
|
||||||
"order": "string",
|
|
||||||
"parent": "integer",
|
|
||||||
"slug": "string",
|
|
||||||
"default_orderby": "string",
|
|
||||||
"default_order": "string",
|
|
||||||
"columns": "string",
|
|
||||||
"default_view_mode": "string"
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
------
|
|
||||||
#### Items
|
|
||||||
|
|
||||||
1. Route `wp-json/tainacan/v2/collection/(?P<collection_id>[\d]+)/items`
|
|
||||||
|
|
||||||
1. Endpoints:
|
|
||||||
|
|
||||||
1. GET (Fetch all items from a collection)
|
|
||||||
|
|
||||||
1. POST (Create a item in a collection)
|
|
||||||
|
|
||||||
Example of JSON passed in body for creating a item:
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
{
|
|
||||||
"title": "string",
|
|
||||||
"description": "string",
|
|
||||||
"status": "string",
|
|
||||||
"terms": ["integer", "integer", ...]
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
2. Route `wp-json/tainacan/v2/items/(?P<item_id>[\d]+)`
|
|
||||||
|
|
||||||
1. Endpoints:
|
|
||||||
|
|
||||||
1. GET (Fetch a item)
|
|
||||||
|
|
||||||
1. DELETE (Delete or Trash a item and all your dependencies)
|
|
||||||
|
|
||||||
*To delete pass in body of a requisition the parameter is_permanently as true. To only trash pass false.*
|
|
||||||
|
|
||||||
1. PATCH or PUT (Update a item)
|
|
||||||
|
|
||||||
Example of JSON passed in body for updating a item:
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
{
|
|
||||||
"title": "string",
|
|
||||||
"description": "string",
|
|
||||||
"terms": ["integer", "integer", ...]
|
|
||||||
...
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
------
|
|
||||||
#### Metadata
|
|
||||||
|
|
||||||
1. Route `wp-json/tainacan/v2/collection/(?P<collection_id>[\d]+)/metadata`
|
|
||||||
|
|
||||||
1. Endpoints:
|
|
||||||
1. GET (Fetch all collection metadatum)
|
|
||||||
|
|
||||||
1. POST (Create a metadatum in collection and all it items)
|
|
||||||
|
|
||||||
In body of requisition pass a JSON with the attributes of metadatum like:
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
{
|
|
||||||
"name": "string",
|
|
||||||
"description": "string",
|
|
||||||
"metadatum_type": "string",
|
|
||||||
"order": "string",
|
|
||||||
"parent": "integer",
|
|
||||||
"required": "string",
|
|
||||||
"collection_key": "string",
|
|
||||||
"multiple": "string",
|
|
||||||
"cardinality": "string",
|
|
||||||
"mask": "string",
|
|
||||||
"default_value": "string",
|
|
||||||
"field_type_options": "string",
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
2. Route `wp-json/tainacan/v2/collection/(?P<collection_id>[\d]+)/metadata/(?P<metadatum_id>[\d]+)`
|
|
||||||
|
|
||||||
1. Endpoints:
|
|
||||||
|
|
||||||
1. GET (Fetch a metadatum from a collection or Fetch all metadatum values)
|
|
||||||
|
|
||||||
To fetch all metadatum values from a metadatum of a collection in all it items, pass a query like `?fetch=all_field_values`
|
|
||||||
|
|
||||||
1. PATCH or PUT (Update a metadatum in a collection and all it items)
|
|
||||||
|
|
||||||
In body of requisition pass a JSON with the attributes you need to update, like:
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
{
|
|
||||||
"name": "string",
|
|
||||||
"description": "string",
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
3. Route `wp-json/tainacan/v2/metadata`
|
|
||||||
|
|
||||||
1. Endpoints:
|
|
||||||
|
|
||||||
1. GET (Fetch all default metadata)
|
|
||||||
|
|
||||||
1. POST (Create a default metadatum)
|
|
||||||
|
|
||||||
In body of requisition pass a JSON with the attributes of metadatum.
|
|
||||||
|
|
||||||
4. Route `wp-json/tainacan/v2/metadata/(?P(<metadatum_id>[\d]+))`
|
|
||||||
|
|
||||||
1. Endpoints:
|
|
||||||
|
|
||||||
1. DELETE (Trash a default metadatum)
|
|
||||||
|
|
||||||
1. PATCH or PUT (Update a default metadatum)
|
|
||||||
|
|
||||||
In body of requisition pass a JSON with the attributes you need to update.
|
|
||||||
|
|
||||||
------
|
|
||||||
#### Metadatum Types
|
|
||||||
1. Route `wp-json/tainacan/v2/metadatum-types`
|
|
||||||
|
|
||||||
1. Endpoint:
|
|
||||||
|
|
||||||
1. GET (Fetch all metadatum types)
|
|
||||||
------
|
|
||||||
#### Item Metadata
|
|
||||||
|
|
||||||
1. Route `wp-json/tainacan/v2/item/(?P<item_id>[\d]+)/metadata/(?P<metadatum_id>[\d]+)`
|
|
||||||
|
|
||||||
1. Endpoints:
|
|
||||||
|
|
||||||
1. PATCH or PUT (Set value of a metadata)
|
|
||||||
|
|
||||||
In body of requisition pass a JSON with values of metadata, like:
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
{
|
|
||||||
"values": ["any", "type"]
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
2. Route `wp-json/tainacan/v2/item/(?P<item_id>[\d]+)/metadata`
|
|
||||||
|
|
||||||
1. Endpoint:
|
|
||||||
|
|
||||||
1. GET (Fetch all item metadata, with it values)
|
|
||||||
|
|
||||||
------
|
|
||||||
#### Taxonomies
|
|
||||||
|
|
||||||
1. Route `wp-json/tainacan/v2/taxonomies`
|
|
||||||
|
|
||||||
1. Endpoints:
|
|
||||||
|
|
||||||
1. GET (Fetch all taxonomies)
|
|
||||||
|
|
||||||
1. POST (Create a taxonomy)
|
|
||||||
|
|
||||||
Example of JSON passed in body for creating a taxonomy:
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
{
|
|
||||||
"name": "string",
|
|
||||||
"description": "string",
|
|
||||||
"status": "string",
|
|
||||||
"parent": "string",
|
|
||||||
"slug": "string",
|
|
||||||
"allow_insert": "string",
|
|
||||||
"collections_ids": "array"
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
2. Route `wp-json/tainacan/v2/taxonomies/(?P<taxonomy_id>[\d]+)`
|
|
||||||
|
|
||||||
1. Endpoints:
|
|
||||||
|
|
||||||
1. GET (Fetch a taxonomy)
|
|
||||||
|
|
||||||
1. DELETE (Delete or trash a taxonomy)
|
|
||||||
|
|
||||||
*To delete pass in body of requisition the parameter is_permanently as true. To only trash pass false.*
|
|
||||||
|
|
||||||
|
|
||||||
1. PATCH or PUT (Update a taxonomy)
|
|
||||||
|
|
||||||
Example of JSON passed in body for updating a taxonomy:
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
{
|
|
||||||
"name": "string",
|
|
||||||
"description": "string",
|
|
||||||
...
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
3. Route `wp-json/tainacan/v2/taxonomies/(?P<taxonomy_id>[\d]+)/collection/(?P<collection_id>[\d]+)`
|
|
||||||
|
|
||||||
1. Endpoints:
|
|
||||||
|
|
||||||
1. PATCH or PUT (Add a Collection in a Taxonomy)
|
|
||||||
|
|
||||||
------
|
|
||||||
#### Filters
|
|
||||||
|
|
||||||
1. Route `wp-json/tainacan/v2/collection/(?P<collection_id>[\d]+)/metadatum/(?P<metadatum_id>[\d]+)/filters`
|
|
||||||
|
|
||||||
1. Endpoints:
|
|
||||||
|
|
||||||
1. POST (Create a filter)
|
|
||||||
|
|
||||||
Example of JSON passed in body for creating a filter:
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
{
|
|
||||||
"filter_type": "string",
|
|
||||||
"filter": {
|
|
||||||
"name": "string",
|
|
||||||
"description": "string",
|
|
||||||
...
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
2. Route `wp-json/tainacan/v2/filters/(?P<filter_id>[\d]+)`
|
|
||||||
|
|
||||||
1. Endpoints:
|
|
||||||
|
|
||||||
1. GET (Fetch a filter)
|
|
||||||
|
|
||||||
1. DELETE (Delete or trash a filter)
|
|
||||||
|
|
||||||
*To delete pass in body of requisition the parameter is_permanently as true. To only trash pass false.*
|
|
||||||
|
|
||||||
1. PATCH or PUT (Update a filter)
|
|
||||||
|
|
||||||
Example of JSON passed in body for updating a filter:
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
{
|
|
||||||
"name": "string",
|
|
||||||
...
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
3. Route `wp-json/tainacan/v2/filters`
|
|
||||||
|
|
||||||
1. Endpoints:
|
|
||||||
|
|
||||||
1. GET (Fetch all repository filters)
|
|
||||||
|
|
||||||
1. POST (Create a filter in repository. Without metadatum and collection associations)
|
|
||||||
|
|
||||||
Example of JSON passed in body for creating a filter:
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
{
|
|
||||||
"filter_type": "string",
|
|
||||||
"filter": {
|
|
||||||
"name": "string",
|
|
||||||
"description": "string",
|
|
||||||
...
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
4. Route `wp-json/tainacan/v2/collection/(?P<collection_id>[\d]+)/filters`
|
|
||||||
|
|
||||||
1. Endpoints:
|
|
||||||
|
|
||||||
1. GET (Fetch all collection filters)
|
|
||||||
|
|
||||||
1. POST (Create a filter in a collection, without metadatum association)
|
|
||||||
|
|
||||||
Example of JSON passed in body for creating a filter:
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
{
|
|
||||||
"filter_type": "string",
|
|
||||||
"filter": {
|
|
||||||
"name": "string",
|
|
||||||
"description": "string",
|
|
||||||
...
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
------
|
|
||||||
#### Filter Types
|
|
||||||
1. Route `wp-json/tainacan/v2/filter-types`
|
|
||||||
|
|
||||||
1. Endpoint:
|
|
||||||
|
|
||||||
1. GET (Fetch all filter types)
|
|
||||||
------
|
|
||||||
|
|
||||||
#### Terms
|
|
||||||
|
|
||||||
1. Route `wp-json/tainacan/v2/taxonomy/(?P<taxonomy_id>[\d]+)/terms`
|
|
||||||
|
|
||||||
1. Endpoints:
|
|
||||||
|
|
||||||
1. GET (Fetch all tems of a taxonomy)
|
|
||||||
|
|
||||||
1. POST (Create a term in a taxonomy)
|
|
||||||
|
|
||||||
Example of JSON passed in body for creating a term:
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
{
|
|
||||||
"name": "string",
|
|
||||||
"user": "int",
|
|
||||||
...
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
2. Route `wp-json/tainacan/v2/taxonomy/(?P<taxonomy_id>[\d]+)/terms/(?P<term_id>[\d]+)`
|
|
||||||
|
|
||||||
1. Endpoints:
|
|
||||||
|
|
||||||
1. GET (Fecth a term of a taxonomy)
|
|
||||||
|
|
||||||
1. DELETE (Delete a term of a taxonoy)
|
|
||||||
|
|
||||||
1. PATCH or PUT (Update a term in a taxonomy)
|
|
||||||
|
|
||||||
Example of JSON passed in body for updating a term:
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
{
|
|
||||||
"name": "string",
|
|
||||||
...
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
------
|
|
||||||
#### Logs
|
|
||||||
|
|
||||||
1. Route `wp-json/tainacan/v2/logs`
|
|
||||||
|
|
||||||
1. Endpoints:
|
|
||||||
|
|
||||||
1. GET (Get all logs)
|
|
||||||
|
|
||||||
2. Route `wp-json/tainacan/v2/logs/(?P<log_id>[\d]+)`
|
|
||||||
|
|
||||||
1. Endpoints:
|
|
||||||
|
|
||||||
1. GET (Get a log)
|
|
||||||
|
|
||||||
------
|
|
||||||
#### Others
|
|
||||||
|
|
||||||
To Create, Read, Update or Delete Media or Users you can use the default routes of Wordpress.
|
|
||||||
|
|
||||||
See about Media in [Media | REST API Handbook](https://developer.wordpress.org/rest-api/reference/media/);
|
|
||||||
|
|
||||||
See about Users in [Users | REST API Handbook](https://developer.wordpress.org/rest-api/reference/users/).
|
|
|
@ -1,35 +1 @@
|
||||||
# Vocabulary Importer
|
This page was moved to https://tainacan.github.io/tainacan-wiki/
|
||||||
|
|
||||||
This importer allows user to add terms to a given taxonomy. It's useful to import controlled vocabularies to a Tainacan installation.
|
|
||||||
|
|
||||||
The file format used to import vocabularies is a CSV - comma separated values. Each line of the file will represent one term.
|
|
||||||
|
|
||||||
For each term, you can inform the name of the term and it's definition.
|
|
||||||
|
|
||||||
For example:
|
|
||||||
|
|
||||||
```
|
|
||||||
Term 1,Definition of term 1
|
|
||||||
Term 2,Definition of term 2
|
|
||||||
Term 3,Definition of term 3
|
|
||||||
```
|
|
||||||
|
|
||||||
It's also possible to inform hierarchy. You do so by leaving empty cells to the left, indicating the level in the hierarchy the term is in.
|
|
||||||
|
|
||||||
Your spreadsheet will look like this:
|
|
||||||
|
|
||||||
![Vocabulary spreadsheet](assets/vocabulary-importer-sample.png)
|
|
||||||
|
|
||||||
This same spreadsheet, saved in CSV format, will look like this:
|
|
||||||
|
|
||||||
```
|
|
||||||
Term 1,Definition of term 1,,
|
|
||||||
Term 2,Definition of term 2,,
|
|
||||||
,1st Child of term 2,Definition of this term,
|
|
||||||
,2nd Child of term 2,Definition of this term,
|
|
||||||
,,Gran child,Definition of grand child
|
|
||||||
Term 3,Definition of term 3,,
|
|
||||||
Term 4,Definition of term 4,,
|
|
||||||
```
|
|
||||||
|
|
||||||
Once you have your CSV ready, fire the Vocabulary CSV Importer, choose the target Taxonomy (or create one), and hit "Run".
|
|
|
@ -309,6 +309,7 @@
|
||||||
if ((metadatum.metadata_type_object && metadatum.metadata_type_object.form_component) || metadatum.edit_form == '') {
|
if ((metadatum.metadata_type_object && metadatum.metadata_type_object.form_component) || metadatum.edit_form == '') {
|
||||||
|
|
||||||
this.fillExtraFormData(this.editForm);
|
this.fillExtraFormData(this.editForm);
|
||||||
|
|
||||||
this.updateMetadatum({
|
this.updateMetadatum({
|
||||||
collectionId: this.collectionId,
|
collectionId: this.collectionId,
|
||||||
metadatumId: metadatum.id,
|
metadatumId: metadatum.id,
|
||||||
|
|
|
@ -160,7 +160,18 @@
|
||||||
|
|
||||||
<!-- Submit -->
|
<!-- Submit -->
|
||||||
<div class="field is-grouped form-submit">
|
<div class="field is-grouped form-submit">
|
||||||
<div class="control">
|
<div
|
||||||
|
v-if="$route.query.recent"
|
||||||
|
class="control">
|
||||||
|
<button
|
||||||
|
id="button-another-taxonomy-creation"
|
||||||
|
@click.prevent="goToCreateAnotherTaxonomy()"
|
||||||
|
class="button is-secondary">{{ $i18n.get('label_create_another_taxonomy') }}</button>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
v-if="!$route.query.recent"
|
||||||
|
style="margin-right: auto;"
|
||||||
|
class="control">
|
||||||
<button
|
<button
|
||||||
id="button-cancel-taxonomy-creation"
|
id="button-cancel-taxonomy-creation"
|
||||||
class="button is-outlined"
|
class="button is-outlined"
|
||||||
|
@ -187,6 +198,7 @@
|
||||||
<b-tab-item :label="$i18n.get('terms')">
|
<b-tab-item :label="$i18n.get('terms')">
|
||||||
<!-- Terms List -->
|
<!-- Terms List -->
|
||||||
<terms-list
|
<terms-list
|
||||||
|
:key="shouldReloadTermsList ? 'termslistreloaded' : 'termslist'"
|
||||||
@isEditingTermUpdate="isEditingTermUpdate"
|
@isEditingTermUpdate="isEditingTermUpdate"
|
||||||
:taxonomy-id="taxonomyId"/>
|
:taxonomy-id="taxonomyId"/>
|
||||||
</b-tab-item>
|
</b-tab-item>
|
||||||
|
@ -215,7 +227,7 @@
|
||||||
taxonomy: null,
|
taxonomy: null,
|
||||||
isLoadingTaxonomy: false,
|
isLoadingTaxonomy: false,
|
||||||
isUpdatingSlug: false,
|
isUpdatingSlug: false,
|
||||||
isEditinTerm: false,
|
isEditingTerm: false,
|
||||||
form: {
|
form: {
|
||||||
name: String,
|
name: String,
|
||||||
status: String,
|
status: String,
|
||||||
|
@ -228,7 +240,8 @@
|
||||||
editFormErrors: {},
|
editFormErrors: {},
|
||||||
formErrorMessage: '',
|
formErrorMessage: '',
|
||||||
entityName: 'taxonomy',
|
entityName: 'taxonomy',
|
||||||
updatedAt: undefined
|
updatedAt: undefined,
|
||||||
|
shouldReloadTermsList: false
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
components: {
|
components: {
|
||||||
|
@ -263,7 +276,7 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
} else if (this.isEditinTerm) {
|
} else if (this.isEditingTerm) {
|
||||||
this.$modal.open({
|
this.$modal.open({
|
||||||
parent: this,
|
parent: this,
|
||||||
component: CustomDialog,
|
component: CustomDialog,
|
||||||
|
@ -335,6 +348,10 @@
|
||||||
// Updates saved at message
|
// Updates saved at message
|
||||||
let now = new Date();
|
let now = new Date();
|
||||||
this.updatedAt = now.toLocaleString();
|
this.updatedAt = now.toLocaleString();
|
||||||
|
|
||||||
|
if (this.$route.name == 'TaxonomyCreationForm') {
|
||||||
|
this.$router.push(this.$routerHelper.getTaxonomyEditPath(this.taxonomyId, true));
|
||||||
|
}
|
||||||
})
|
})
|
||||||
.catch((errors) => {
|
.catch((errors) => {
|
||||||
for (let error of errors.errors) {
|
for (let error of errors.errors) {
|
||||||
|
@ -398,6 +415,7 @@
|
||||||
this.form.status = 'publish';
|
this.form.status = 'publish';
|
||||||
|
|
||||||
this.isLoadingTaxonomy = false;
|
this.isLoadingTaxonomy = false;
|
||||||
|
this.shouldReloadTermsList = false;
|
||||||
|
|
||||||
})
|
})
|
||||||
.catch(error => this.$console.error(error));
|
.catch(error => this.$console.error(error));
|
||||||
|
@ -412,7 +430,31 @@
|
||||||
return ( this.form.allowInsert === 'yes' ) ? this.$i18n.get('label_yes') : this.$i18n.get('label_no');
|
return ( this.form.allowInsert === 'yes' ) ? this.$i18n.get('label_yes') : this.$i18n.get('label_no');
|
||||||
},
|
},
|
||||||
isEditingTermUpdate (value) {
|
isEditingTermUpdate (value) {
|
||||||
this.isEditinTerm = value;
|
this.isEditingTerm = value;
|
||||||
|
},
|
||||||
|
goToCreateAnotherTaxonomy() {
|
||||||
|
this.$router.push(this.$routerHelper.getNewTaxonomyPath());
|
||||||
|
|
||||||
|
this.taxonomyId = undefined;
|
||||||
|
this.taxonomy = null;
|
||||||
|
this.isUpdatingSlug = false;
|
||||||
|
this.isEditingTerm = false,
|
||||||
|
this.form = {
|
||||||
|
name: String,
|
||||||
|
status: String,
|
||||||
|
description: String,
|
||||||
|
slug: String,
|
||||||
|
allowInsert: String,
|
||||||
|
enabledPostTypes: Array
|
||||||
|
};
|
||||||
|
this.editFormErrors = {};
|
||||||
|
this.formErrorMessage = '';
|
||||||
|
this.updatedAt = undefined;
|
||||||
|
|
||||||
|
// Forces terms list to reload
|
||||||
|
this.shouldReloadTermsList = true;
|
||||||
|
|
||||||
|
this.createNewTaxonomy();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
mounted(){
|
mounted(){
|
||||||
|
@ -420,9 +462,9 @@
|
||||||
if (this.$route.query.tab == 'terms')
|
if (this.$route.query.tab == 'terms')
|
||||||
this.tabIndex = 1;
|
this.tabIndex = 1;
|
||||||
|
|
||||||
if (this.$route.path.split("/").pop() === "new") {
|
if (this.$route.name == "TaxonomyCreationForm") {
|
||||||
this.createNewTaxonomy();
|
this.createNewTaxonomy();
|
||||||
} else if (this.$route.path.split("/").pop() === "edit") {
|
} else if (this.$route.name == "TaxonomyEditionForm") {
|
||||||
|
|
||||||
this.isLoadingTaxonomy = true;
|
this.isLoadingTaxonomy = true;
|
||||||
|
|
||||||
|
|
|
@ -8,11 +8,15 @@
|
||||||
<hr>
|
<hr>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<b-loading
|
||||||
|
:is-full-page="false"
|
||||||
|
:active.sync="isLoading" />
|
||||||
|
|
||||||
<!-- Name -------------- -->
|
<!-- Name -------------- -->
|
||||||
<b-field
|
<b-field
|
||||||
:addons="false"
|
:addons="false"
|
||||||
:type="((formErrors.name !== '' || formErrors.repeated !== '') && (formErrors.name !== undefined || formErrors.repeated !== undefined )) ? 'is-danger' : ''"
|
:type="((formErrors.name !== '' || formErrors.repeated !== '') && (formErrors.name !== undefined || formErrors.repeated !== undefined )) ? 'is-danger' : ''"
|
||||||
:message="formErrors.name ? formErrors : formErrors.repeated">
|
:message="formErrors.name ? formErrors.name : formErrors.repeated">
|
||||||
<label class="label is-inline">
|
<label class="label is-inline">
|
||||||
{{ $i18n.get('label_name') }}
|
{{ $i18n.get('label_name') }}
|
||||||
<span class="required-term-asterisk">*</span>
|
<span class="required-term-asterisk">*</span>
|
||||||
|
@ -217,7 +221,8 @@
|
||||||
hasParent: false,
|
hasParent: false,
|
||||||
hasChangedParent: false,
|
hasChangedParent: false,
|
||||||
initialParentId: undefined,
|
initialParentId: undefined,
|
||||||
entityName: 'term'
|
entityName: 'term',
|
||||||
|
isLoading: false,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
props: {
|
props: {
|
||||||
|
@ -245,6 +250,7 @@
|
||||||
header_image: this.editForm.header_image,
|
header_image: this.editForm.header_image,
|
||||||
};
|
};
|
||||||
this.fillExtraFormData(data);
|
this.fillExtraFormData(data);
|
||||||
|
this.isLoading = true;
|
||||||
this.sendChildTerm({
|
this.sendChildTerm({
|
||||||
taxonomyId: this.taxonomyId,
|
taxonomyId: this.taxonomyId,
|
||||||
term: data
|
term: data
|
||||||
|
@ -253,6 +259,7 @@
|
||||||
this.$emit('onEditionFinished', {term: term, hasChangedParent: this.hasChangedParent });
|
this.$emit('onEditionFinished', {term: term, hasChangedParent: this.hasChangedParent });
|
||||||
this.editForm = {};
|
this.editForm = {};
|
||||||
this.formErrors = {};
|
this.formErrors = {};
|
||||||
|
this.isLoading = false;
|
||||||
})
|
})
|
||||||
.catch((errors) => {
|
.catch((errors) => {
|
||||||
for (let error of errors.errors) {
|
for (let error of errors.errors) {
|
||||||
|
@ -260,6 +267,7 @@
|
||||||
this.$set(this.formErrors, metadatum, (this.formErrors[metadatum] !== undefined ? this.formErrors[metadatum] : '') + error[metadatum] + '\n');
|
this.$set(this.formErrors, metadatum, (this.formErrors[metadatum] !== undefined ? this.formErrors[metadatum] : '') + error[metadatum] + '\n');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
this.isLoading = false;
|
||||||
this.$emit('onErrorFound');
|
this.$emit('onErrorFound');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -274,6 +282,7 @@
|
||||||
header_image: this.editForm.header_image,
|
header_image: this.editForm.header_image,
|
||||||
}
|
}
|
||||||
this.fillExtraFormData(data);
|
this.fillExtraFormData(data);
|
||||||
|
this.isLoading = true;
|
||||||
this.updateChildTerm({
|
this.updateChildTerm({
|
||||||
taxonomyId: this.taxonomyId,
|
taxonomyId: this.taxonomyId,
|
||||||
term: data
|
term: data
|
||||||
|
@ -288,6 +297,7 @@
|
||||||
this.$set(this.formErrors, metadatum, (this.formErrors[metadatum] !== undefined ? this.formErrors[metadatum] : '') + error[metadatum] + '\n');
|
this.$set(this.formErrors, metadatum, (this.formErrors[metadatum] !== undefined ? this.formErrors[metadatum] : '') + error[metadatum] + '\n');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
this.isLoading = false;
|
||||||
this.$emit('onErrorFound');
|
this.$emit('onErrorFound');
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -107,11 +107,11 @@
|
||||||
v-if="contextMenuItem != null && contextMenuItem.current_user_can_edit && !$route.query.iframemode">
|
v-if="contextMenuItem != null && contextMenuItem.current_user_can_edit && !$route.query.iframemode">
|
||||||
{{ $i18n.getFrom('items','edit_item') }}
|
{{ $i18n.getFrom('items','edit_item') }}
|
||||||
</b-dropdown-item>
|
</b-dropdown-item>
|
||||||
<!-- <b-dropdown-item
|
<b-dropdown-item
|
||||||
@click="duplicateOneItem(contextMenuItem.id)"
|
@click="duplicateOneItem(contextMenuItem.id)"
|
||||||
v-if="contextMenuItem != null && contextMenuItem.current_user_can_edit && !$route.query.iframemode">
|
v-if="contextMenuItem != null && contextMenuItem.current_user_can_edit && !$route.query.iframemode">
|
||||||
{{ $i18n.get('label_duplicate_item') }}
|
{{ $i18n.get('label_duplicate_item') }}
|
||||||
</b-dropdown-item> -->
|
</b-dropdown-item>
|
||||||
<b-dropdown-item
|
<b-dropdown-item
|
||||||
@click="deleteOneItem(contextMenuItem.id)"
|
@click="deleteOneItem(contextMenuItem.id)"
|
||||||
v-if="contextMenuItem != null && contextMenuItem.current_user_can_edit && !$route.query.iframemode">
|
v-if="contextMenuItem != null && contextMenuItem.current_user_can_edit && !$route.query.iframemode">
|
||||||
|
@ -712,7 +712,10 @@
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
<tr
|
<tr
|
||||||
:class="{ 'selected-row': selectedItems[index] }"
|
:class="{
|
||||||
|
'selected-row': selectedItems[index],
|
||||||
|
'highlighted-item': highlightedItem == item.id
|
||||||
|
}"
|
||||||
:key="index"
|
:key="index"
|
||||||
v-for="(item, index) of items">
|
v-for="(item, index) of items">
|
||||||
<!-- Checking list -->
|
<!-- Checking list -->
|
||||||
|
@ -900,6 +903,7 @@
|
||||||
<script>
|
<script>
|
||||||
import { mapActions, mapGetters } from 'vuex';
|
import { mapActions, mapGetters } from 'vuex';
|
||||||
import CustomDialog from '../other/custom-dialog.vue';
|
import CustomDialog from '../other/custom-dialog.vue';
|
||||||
|
import DuplicationDialog from '../other/duplication-dialog.vue';
|
||||||
import BulkEditionModal from '../bulk-edition/bulk-edition-modal.vue';
|
import BulkEditionModal from '../bulk-edition/bulk-edition-modal.vue';
|
||||||
import { dateInter } from "../../../admin/js/mixins";
|
import { dateInter } from "../../../admin/js/mixins";
|
||||||
|
|
||||||
|
@ -939,6 +943,14 @@ export default {
|
||||||
this.selectedItemsIDs.push(false);
|
this.selectedItemsIDs.push(false);
|
||||||
this.selectedItems.push(false);
|
this.selectedItems.push(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (this.highlightsItem)
|
||||||
|
setTimeout(() => this.$eventBusSearch.highlightsItem(null), 3000);
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
highlightedItem () {
|
||||||
|
return this.getHighlightedItem();
|
||||||
|
}
|
||||||
},
|
},
|
||||||
watch: {
|
watch: {
|
||||||
selectedItems() {
|
selectedItems() {
|
||||||
|
@ -972,18 +984,18 @@ export default {
|
||||||
'createEditGroup',
|
'createEditGroup',
|
||||||
'trashItemsInBulk',
|
'trashItemsInBulk',
|
||||||
'deleteItemsInBulk',
|
'deleteItemsInBulk',
|
||||||
'untrashItemsInBulk',
|
'untrashItemsInBulk'
|
||||||
]),
|
]),
|
||||||
...mapGetters('bulkedition', [
|
...mapGetters('bulkedition', [
|
||||||
'getGroupID'
|
'getGroupID'
|
||||||
]),
|
]),
|
||||||
...mapActions('item', [
|
...mapActions('item', [
|
||||||
'fetchItem',
|
'fetchItem'
|
||||||
'duplicateItem'
|
|
||||||
]),
|
]),
|
||||||
...mapGetters('search', [
|
...mapGetters('search', [
|
||||||
'getOrder',
|
'getOrder',
|
||||||
'getOrderBy'
|
'getOrderBy',
|
||||||
|
'getHighlightedItem'
|
||||||
]),
|
]),
|
||||||
openBulkEditionModal(){
|
openBulkEditionModal(){
|
||||||
this.$modal.open({
|
this.$modal.open({
|
||||||
|
@ -1026,15 +1038,24 @@ export default {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
duplicateOneItem(itemId) {
|
duplicateOneItem(itemId) {
|
||||||
this.fetchItem({ itemId: itemId, contextEdit: true })
|
|
||||||
.then((item) => {
|
this.$modal.open({
|
||||||
this.duplicateItem({ item: item })
|
parent: this,
|
||||||
.then((duplicatedItem) => {
|
component: DuplicationDialog,
|
||||||
this.$console.log(duplicatedItem);
|
canCancel: false,
|
||||||
})
|
props: {
|
||||||
.catch((error) => this.$console.error(error));
|
icon: 'items',
|
||||||
})
|
collectionId: this.collectionId,
|
||||||
.catch(error => this.$console.error("Error fetching item for duplicate: " + error));
|
itemId: itemId,
|
||||||
|
onConfirm: (duplicatedItemId) => {
|
||||||
|
if (duplicatedItemId != null && duplicatedItemId != undefined)
|
||||||
|
this.$eventBusSearch.highlightsItem(duplicatedItemId);
|
||||||
|
|
||||||
|
this.$eventBusSearch.loadItems();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
this.clearContextMenu();
|
this.clearContextMenu();
|
||||||
},
|
},
|
||||||
untrashOneItem(itemId) {
|
untrashOneItem(itemId) {
|
||||||
|
@ -1278,6 +1299,23 @@ export default {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@keyframes highlight {
|
||||||
|
from {
|
||||||
|
background-color: $blue1;
|
||||||
|
}
|
||||||
|
to {
|
||||||
|
background-color: initial;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.highlighted-item {
|
||||||
|
transition: background-color 0.5s;
|
||||||
|
animation-name: highlight;
|
||||||
|
animation-duration: 1s;
|
||||||
|
animation-iteration-count: 2;
|
||||||
|
}
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -525,7 +525,7 @@
|
||||||
|
|
||||||
.highlighted-process {
|
.highlighted-process {
|
||||||
&>.process-handler {
|
&>.process-handler {
|
||||||
transition: background-color 0.5s;
|
transition: background-color 0.8s;
|
||||||
animation-name: highlight;
|
animation-name: highlight;
|
||||||
animation-duration: 1s;
|
animation-duration: 1s;
|
||||||
animation-iteration-count: 2;
|
animation-iteration-count: 2;
|
||||||
|
|
|
@ -0,0 +1,115 @@
|
||||||
|
<template>
|
||||||
|
<div
|
||||||
|
aria-labelledby="alert-dialog-title"
|
||||||
|
aria-modal="true"
|
||||||
|
role="alertdialog"
|
||||||
|
class="tainacan-form dialog">
|
||||||
|
<div
|
||||||
|
class="modal-card"
|
||||||
|
style="width: auto">
|
||||||
|
<div
|
||||||
|
v-if="icon != undefined && icon != ''"
|
||||||
|
class="modal-custom-icon">
|
||||||
|
<span class="icon is-large">
|
||||||
|
<i
|
||||||
|
:class="'tainacan-icon-' + icon"
|
||||||
|
class="tainacan-icon"/>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<section
|
||||||
|
tabindex="1"
|
||||||
|
class="modal-card-body">
|
||||||
|
<header
|
||||||
|
class="modal-card-head">
|
||||||
|
<h1
|
||||||
|
id="alert-dialog-title"
|
||||||
|
class="modal-card-title">
|
||||||
|
{{ $i18n.get('label_duplicating_item') }}
|
||||||
|
</h1>
|
||||||
|
</header>
|
||||||
|
{{ message }}
|
||||||
|
</section>
|
||||||
|
<footer class="modal-card-foot form-submit">
|
||||||
|
<button
|
||||||
|
type="submit"
|
||||||
|
class="button is-outlined"
|
||||||
|
:disabled="isLoading"
|
||||||
|
@click="onConfirm(duplicatedItemId); $parent.close();">
|
||||||
|
{{ $i18n.get('label_items_list') }}
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
type="submit"
|
||||||
|
class="button is-success"
|
||||||
|
:disabled="isLoading"
|
||||||
|
@click="$router.push($routerHelper.getItemEditPath(collectionId, duplicatedItemId)); $parent.close();">
|
||||||
|
{{ $i18n.getFrom('items','edit_item') }}
|
||||||
|
</button>
|
||||||
|
</footer>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { mapActions } from 'vuex';
|
||||||
|
export default {
|
||||||
|
name: 'DuplicationDialog',
|
||||||
|
props: {
|
||||||
|
icon: String,
|
||||||
|
onConfirm: {
|
||||||
|
type: Function,
|
||||||
|
default: () => {}
|
||||||
|
},
|
||||||
|
collectionId: String,
|
||||||
|
itemId: String
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
isLoading: Boolean,
|
||||||
|
message: String,
|
||||||
|
duplicatedItemId: String
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
...mapActions('item', [
|
||||||
|
'fetchItem',
|
||||||
|
'duplicateItem'
|
||||||
|
]),
|
||||||
|
},
|
||||||
|
created() {
|
||||||
|
this.isLoading = true;
|
||||||
|
this.message = this.$i18n.get('info_await_while_item_duplication');
|
||||||
|
|
||||||
|
this.duplicateItem({ collectionId: this.collectionId, itemId: this.itemId })
|
||||||
|
.then((duplicatedItem) => {
|
||||||
|
this.isLoading = false;
|
||||||
|
this.message = this.$i18n.get('label_item_duplication_success');
|
||||||
|
|
||||||
|
if (duplicatedItem.id)
|
||||||
|
this.duplicatedItemId = duplicatedItem.id;
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
this.$console.error('Error fetching item for duplicate ' + error);
|
||||||
|
this.isLoading = false;
|
||||||
|
this.message = this.$i18n.get('label_item_duplication_failure');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
|
||||||
|
i.tainacan-icon,
|
||||||
|
i.tainacan-icon::before {
|
||||||
|
font-size: 42px;
|
||||||
|
}
|
||||||
|
|
||||||
|
button.is-success {
|
||||||
|
margin-left: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-card-foot {
|
||||||
|
margin-top: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
</style>
|
||||||
|
|
|
@ -305,8 +305,8 @@ RouterHelperPlugin.install = function (Vue, options = {}) {
|
||||||
getFilterEditPath(id) {
|
getFilterEditPath(id) {
|
||||||
return '/filters/' + id + '/edit';
|
return '/filters/' + id + '/edit';
|
||||||
},
|
},
|
||||||
getTaxonomyEditPath(id) {
|
getTaxonomyEditPath(id, isRecent) {
|
||||||
return '/taxonomies/' + id + '/edit';
|
return isRecent != undefined ? '/taxonomies/' + id + '/edit?recent=true' : '/taxonomies/' + id + '/edit';
|
||||||
},
|
},
|
||||||
getTermEditPath(taxonomyId, termId) {
|
getTermEditPath(taxonomyId, termId) {
|
||||||
return '/taxonomies/' + taxonomyId + '/terms/' + termId + '/edit';
|
return '/taxonomies/' + taxonomyId + '/terms/' + termId + '/edit';
|
||||||
|
|
|
@ -53,7 +53,7 @@
|
||||||
<span class="text">{{ $i18n.get('filters') }}</span>
|
<span class="text">{{ $i18n.get('filters') }}</span>
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<!-- Side bar with search and filters -->
|
<!-- Sidebar with search and filters -->
|
||||||
<!-- <transition name="filters-menu"> -->
|
<!-- <transition name="filters-menu"> -->
|
||||||
<aside
|
<aside
|
||||||
:aria-busy="isLoadingFilters"
|
:aria-busy="isLoadingFilters"
|
||||||
|
@ -148,18 +148,70 @@
|
||||||
|
|
||||||
</aside>
|
</aside>
|
||||||
<!-- </transition> -->
|
<!-- </transition> -->
|
||||||
|
|
||||||
<!-- ITEMS LIST AREA (ASIDE THE ASIDE) ------------------------- -->
|
<!-- ITEMS LIST AREA (ASIDE THE ASIDE) ------------------------- -->
|
||||||
<div
|
<div
|
||||||
id="items-list-area"
|
id="items-list-area"
|
||||||
class="items-list-area"
|
class="items-list-area"
|
||||||
:class="{ 'spaced-to-right': !isFiltersMenuCompressed && !openAdvancedSearch && !(registeredViewModes[viewMode] != undefined && registeredViewModes[viewMode].full_screen)}">
|
:class="{ 'spaced-to-right': !isFiltersMenuCompressed && !openAdvancedSearch && !(registeredViewModes[viewMode] != undefined && registeredViewModes[viewMode].full_screen)}">
|
||||||
|
|
||||||
|
<!-- ADVANCED SEARCH -->
|
||||||
|
<div
|
||||||
|
id="advanced-search-container"
|
||||||
|
role="search"
|
||||||
|
v-if="openAdvancedSearch">
|
||||||
|
|
||||||
|
<div class="tnc-advanced-search-close">
|
||||||
|
<div class="advanced-search-criteria-title">
|
||||||
|
<div class="is-flex">
|
||||||
|
<h1>{{ $i18n.get('info_search_criteria') }}</h1>
|
||||||
|
<div
|
||||||
|
:style="{'margin-bottom': 'auto'}"
|
||||||
|
class="field is-grouped">
|
||||||
|
<a
|
||||||
|
class="back-link"
|
||||||
|
@click="openAdvancedSearch = false">
|
||||||
|
{{ $i18n.get('back') }}
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<hr>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
<advanced-search
|
||||||
|
:collection-id="collectionId"
|
||||||
|
:is-repository-level="isRepositoryLevel"
|
||||||
|
:advanced-search-results="advancedSearchResults"
|
||||||
|
:open-form-advanced-search="openFormAdvancedSearch"
|
||||||
|
:is-do-search="isDoSearch"/>
|
||||||
|
|
||||||
|
<div class="advanced-search-form-submit">
|
||||||
|
<p
|
||||||
|
v-if="advancedSearchResults"
|
||||||
|
class="control">
|
||||||
|
<button
|
||||||
|
aria-controls="items-list-results"
|
||||||
|
@click="advancedSearchResults = !advancedSearchResults"
|
||||||
|
class="button is-outlined">{{ $i18n.get('edit_search') }}</button>
|
||||||
|
</p>
|
||||||
|
<p
|
||||||
|
v-if="advancedSearchResults"
|
||||||
|
class="control">
|
||||||
|
<button
|
||||||
|
aria-controls="items-list-results"
|
||||||
|
@click="isDoSearch = !isDoSearch"
|
||||||
|
class="button is-success">{{ $i18n.get('search') }}</button>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<!-- SEARCH CONTROL ------------------------- -->
|
<!-- SEARCH CONTROL ------------------------- -->
|
||||||
<div
|
<div
|
||||||
aria-labelledby="search-control-landmark"
|
aria-labelledby="search-control-landmark"
|
||||||
role="region"
|
role="region"
|
||||||
ref="search-control"
|
ref="search-control"
|
||||||
v-if="!openAdvancedSearch && !(registeredViewModes[viewMode] != undefined && registeredViewModes[viewMode].full_screen)"
|
v-if="!(registeredViewModes[viewMode] != undefined && registeredViewModes[viewMode].full_screen) && ((openAdvancedSearch && advancedSearchResults) || !openAdvancedSearch)"
|
||||||
class="search-control">
|
class="search-control">
|
||||||
|
|
||||||
<h3
|
<h3
|
||||||
|
@ -174,7 +226,9 @@
|
||||||
<!-- Item Creation Dropdown, only on Admin -->
|
<!-- Item Creation Dropdown, only on Admin -->
|
||||||
<div
|
<div
|
||||||
class="search-control-item"
|
class="search-control-item"
|
||||||
v-if="!isOnTheme && !$route.query.iframemode">
|
v-if="!isOnTheme &&
|
||||||
|
!$route.query.iframemode &&
|
||||||
|
!openAdvancedSearch">
|
||||||
<b-dropdown
|
<b-dropdown
|
||||||
:mobile-modal="true"
|
:mobile-modal="true"
|
||||||
id="item-creation-options-dropdown"
|
id="item-creation-options-dropdown"
|
||||||
|
@ -561,57 +615,6 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- ADVANCED SEARCH -->
|
|
||||||
<div
|
|
||||||
id="advanced-search-container"
|
|
||||||
role="search"
|
|
||||||
v-if="openAdvancedSearch">
|
|
||||||
|
|
||||||
<div class="tnc-advanced-search-close">
|
|
||||||
<div class="advanced-search-criteria-title">
|
|
||||||
<div class="is-flex">
|
|
||||||
<h1>{{ $i18n.get('info_search_criteria') }}</h1>
|
|
||||||
<div
|
|
||||||
:style="{'margin-bottom': 'auto'}"
|
|
||||||
class="field is-grouped">
|
|
||||||
<a
|
|
||||||
class="back-link"
|
|
||||||
@click="openAdvancedSearch = false">
|
|
||||||
{{ $i18n.get('back') }}
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<hr>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
<advanced-search
|
|
||||||
:collection-id="collectionId"
|
|
||||||
:is-repository-level="isRepositoryLevel"
|
|
||||||
:advanced-search-results="advancedSearchResults"
|
|
||||||
:open-form-advanced-search="openFormAdvancedSearch"
|
|
||||||
:is-do-search="isDoSearch"/>
|
|
||||||
|
|
||||||
<div class="advanced-search-form-submit">
|
|
||||||
<p
|
|
||||||
v-if="advancedSearchResults"
|
|
||||||
class="control">
|
|
||||||
<button
|
|
||||||
aria-controls="items-list-results"
|
|
||||||
@click="advancedSearchResults = !advancedSearchResults"
|
|
||||||
class="button is-outlined">{{ $i18n.get('edit_search') }}</button>
|
|
||||||
</p>
|
|
||||||
<p
|
|
||||||
v-if="advancedSearchResults"
|
|
||||||
class="control">
|
|
||||||
<button
|
|
||||||
aria-controls="items-list-results"
|
|
||||||
@click="isDoSearch = !isDoSearch"
|
|
||||||
class="button is-success">{{ $i18n.get('search') }}</button>
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- STATUS TABS, only on Admin -------- -->
|
<!-- STATUS TABS, only on Admin -------- -->
|
||||||
<div
|
<div
|
||||||
v-if="!isOnTheme && !openAdvancedSearch"
|
v-if="!isOnTheme && !openAdvancedSearch"
|
||||||
|
@ -701,6 +704,34 @@
|
||||||
<!-- <skeleton-items-list v-if="!isOnTheme"/> -->
|
<!-- <skeleton-items-list v-if="!isOnTheme"/> -->
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- Alert if custom metada is being used for sorting -->
|
||||||
|
<div
|
||||||
|
v-if="hasAnOpenAlert &&
|
||||||
|
isSortingByCustomMetadata &&
|
||||||
|
!showLoading &&
|
||||||
|
((openAdvancedSearch && advancedSearchResults) || !openAdvancedSearch)"
|
||||||
|
class="metadata-alert">
|
||||||
|
<p class="text">
|
||||||
|
{{
|
||||||
|
totalItems > 0 ?
|
||||||
|
$i18n.getWithVariables('info_sorting_by_metadata_value_%s', [orderByName]) :
|
||||||
|
$i18n.getWithVariables('info_sorting_by_metadata_value_%s_empty_list', [orderByName])
|
||||||
|
}}
|
||||||
|
</p>
|
||||||
|
<div>
|
||||||
|
<button
|
||||||
|
@click="openMetatadaSortingWarningDialog({ offerCheckbox: false })"
|
||||||
|
class="button">
|
||||||
|
{{ $i18n.get('label_view_more') }}
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
@click="hasAnOpenAlert = false"
|
||||||
|
class="button icon">
|
||||||
|
<i class="tainacan-icon tainacan-icon-close"/>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<!-- Admin View Modes-->
|
<!-- Admin View Modes-->
|
||||||
<items-list
|
<items-list
|
||||||
v-if="!isOnTheme &&
|
v-if="!isOnTheme &&
|
||||||
|
@ -883,7 +914,8 @@
|
||||||
sortingMetadata: [],
|
sortingMetadata: [],
|
||||||
isFilterModalActive: false,
|
isFilterModalActive: false,
|
||||||
collection: undefined,
|
collection: undefined,
|
||||||
hasAnOpenModal: false
|
hasAnOpenModal: false,
|
||||||
|
hasAnOpenAlert: true
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
props: {
|
props: {
|
||||||
|
@ -996,6 +1028,10 @@
|
||||||
} else {
|
} else {
|
||||||
this.$eventBusSearch.clearAllFilters();
|
this.$eventBusSearch.clearAllFilters();
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
orderByName() {
|
||||||
|
if (this.isSortingByCustomMetadata)
|
||||||
|
this.hasAnOpenAlert = true;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
@ -1084,7 +1120,7 @@
|
||||||
},
|
},
|
||||||
onChangeOrderBy(metadatum) {
|
onChangeOrderBy(metadatum) {
|
||||||
this.$eventBusSearch.setOrderBy(metadatum);
|
this.$eventBusSearch.setOrderBy(metadatum);
|
||||||
this.showItemsHiddingDueSorting();
|
this.showItemsHiddingDueSortingDialog();
|
||||||
},
|
},
|
||||||
onChangeOrder() {
|
onChangeOrder() {
|
||||||
this.order == 'DESC' ? this.$eventBusSearch.setOrder('ASC') : this.$eventBusSearch.setOrder('DESC');
|
this.order == 'DESC' ? this.$eventBusSearch.setOrder('ASC') : this.$eventBusSearch.setOrder('DESC');
|
||||||
|
@ -1398,14 +1434,18 @@
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
showItemsHiddingDueSorting() {
|
showItemsHiddingDueSortingDialog() {
|
||||||
|
|
||||||
if (this.isSortingByCustomMetadata &&
|
if (this.isSortingByCustomMetadata &&
|
||||||
this.$userPrefs.get('neverShowItemsHiddenDueSortingDialog') != true) {
|
this.$userPrefs.get('neverShowItemsHiddenDueSortingDialog') != true) {
|
||||||
|
|
||||||
this.hasAnOpenModal = true;
|
this.hasAnOpenModal = true;
|
||||||
|
|
||||||
this.$modal.open({
|
this.openMetatadaSortingWarningDialog({ offerCheckbox: true });
|
||||||
|
}
|
||||||
|
},
|
||||||
|
openMetatadaSortingWarningDialog({ offerCheckbox }) {
|
||||||
|
this.$modal.open({
|
||||||
parent: this,
|
parent: this,
|
||||||
component: CustomDialog,
|
component: CustomDialog,
|
||||||
props: {
|
props: {
|
||||||
|
@ -1416,11 +1456,10 @@
|
||||||
this.hasAnOpenModal = false;
|
this.hasAnOpenModal = false;
|
||||||
},
|
},
|
||||||
hideCancel: true,
|
hideCancel: true,
|
||||||
showNeverShowAgainOption: tainacan_plugin.user_caps != undefined && tainacan_plugin.user_caps.length != undefined && tainacan_plugin.user_caps.length > 0,
|
showNeverShowAgainOption: offerCheckbox && tainacan_plugin.user_caps != undefined && tainacan_plugin.user_caps.length != undefined && tainacan_plugin.user_caps.length > 0,
|
||||||
messageKeyForUserPrefs: 'ItemsHiddenDueSorting'
|
messageKeyForUserPrefs: 'ItemsHiddenDueSorting'
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
|
||||||
},
|
},
|
||||||
adjustSearchControlHeight: _.debounce( function() {
|
adjustSearchControlHeight: _.debounce( function() {
|
||||||
this.$nextTick(() => {
|
this.$nextTick(() => {
|
||||||
|
@ -1532,7 +1571,7 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
this.showItemsHiddingDueSorting();
|
this.showItemsHiddingDueSortingDialog();
|
||||||
|
|
||||||
// Watches window resize to adjust filter's top position and compression on mobile
|
// Watches window resize to adjust filter's top position and compression on mobile
|
||||||
this.adjustSearchControlHeight();
|
this.adjustSearchControlHeight();
|
||||||
|
@ -1912,6 +1951,38 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.metadata-alert {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
margin: 6px $page-side-padding;
|
||||||
|
border-radius: 3px;
|
||||||
|
padding: 4px 12px;
|
||||||
|
color: $yellow2;
|
||||||
|
background: $yellow1;
|
||||||
|
animation-name: appear;
|
||||||
|
animation-duration: 0.5s;
|
||||||
|
|
||||||
|
p {
|
||||||
|
margin: 0 auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
&>div {
|
||||||
|
display: flex;
|
||||||
|
|
||||||
|
.button,
|
||||||
|
.button:hover,
|
||||||
|
.button:active,
|
||||||
|
.button:focus {
|
||||||
|
background: none;
|
||||||
|
color:$yellow2;
|
||||||
|
font-weight: bold;
|
||||||
|
border: none;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.items-list-area {
|
.items-list-area {
|
||||||
margin-left: 0;
|
margin-left: 0;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
|
|
|
@ -52,7 +52,7 @@
|
||||||
<span class="text">{{ $i18n.get('filters') }}</span>
|
<span class="text">{{ $i18n.get('filters') }}</span>
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<!-- Side bar with search and filters -->
|
<!-- Sidebar with search and filters -->
|
||||||
<aside
|
<aside
|
||||||
:aria-busy="isLoadingFilters"
|
:aria-busy="isLoadingFilters"
|
||||||
id="filters-desktop-aside"
|
id="filters-desktop-aside"
|
||||||
|
@ -152,12 +152,64 @@
|
||||||
class="items-list-area"
|
class="items-list-area"
|
||||||
:class="{ 'spaced-to-right': !isFiltersMenuCompressed && !openAdvancedSearch && !(registeredViewModes[viewMode] != undefined && registeredViewModes[viewMode].full_screen)}">
|
:class="{ 'spaced-to-right': !isFiltersMenuCompressed && !openAdvancedSearch && !(registeredViewModes[viewMode] != undefined && registeredViewModes[viewMode].full_screen)}">
|
||||||
|
|
||||||
|
<!-- ADVANCED SEARCH -->
|
||||||
|
<div
|
||||||
|
id="advanced-search-container"
|
||||||
|
role="search"
|
||||||
|
v-if="openAdvancedSearch">
|
||||||
|
|
||||||
|
<div class="tnc-advanced-search-close">
|
||||||
|
<div class="advanced-search-criteria-title">
|
||||||
|
<div class="is-flex">
|
||||||
|
<h1>{{ $i18n.get('info_search_criteria') }}</h1>
|
||||||
|
<div
|
||||||
|
:style="{'margin-bottom': 'auto'}"
|
||||||
|
class="field is-grouped">
|
||||||
|
<a
|
||||||
|
class="back-link"
|
||||||
|
@click="openAdvancedSearch = false">
|
||||||
|
{{ $i18n.get('back') }}
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<hr>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<advanced-search
|
||||||
|
:is-repository-level="isRepositoryLevel"
|
||||||
|
:collection-id="collectionId"
|
||||||
|
:advanced-search-results="advancedSearchResults"
|
||||||
|
:open-form-advanced-search="openFormAdvancedSearch"
|
||||||
|
:is-do-search="isDoSearch"
|
||||||
|
:metadata="metadata"/>
|
||||||
|
|
||||||
|
<div class="advanced-searh-form-submit">
|
||||||
|
<p
|
||||||
|
v-if="advancedSearchResults"
|
||||||
|
class="control">
|
||||||
|
<button
|
||||||
|
aria-controls="items-list-results"
|
||||||
|
@click="advancedSearchResults = !advancedSearchResults"
|
||||||
|
class="button is-outlined">{{ $i18n.get('edit_search') }}</button>
|
||||||
|
</p>
|
||||||
|
<p
|
||||||
|
v-if="advancedSearchResults"
|
||||||
|
class="control">
|
||||||
|
<button
|
||||||
|
aria-controls="items-list-results"
|
||||||
|
@click="isDoSearch = !isDoSearch"
|
||||||
|
class="button is-success">{{ $i18n.get('search') }}</button>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<!-- SEARCH CONTROL ------------------------- -->
|
<!-- SEARCH CONTROL ------------------------- -->
|
||||||
<div
|
<div
|
||||||
:aria-label="$i18n.get('label_sort_visualization')"
|
:aria-label="$i18n.get('label_sort_visualization')"
|
||||||
role="region"
|
role="region"
|
||||||
ref="search-control"
|
ref="search-control"
|
||||||
v-if="!openAdvancedSearch && !(registeredViewModes[viewMode] != undefined && registeredViewModes[viewMode].full_screen)"
|
v-if="!(registeredViewModes[viewMode] != undefined && registeredViewModes[viewMode].full_screen) && ((openAdvancedSearch && advancedSearchResults) || !openAdvancedSearch)"
|
||||||
class="search-control">
|
class="search-control">
|
||||||
<!-- <b-loading
|
<!-- <b-loading
|
||||||
:is-full-page="false"
|
:is-full-page="false"
|
||||||
|
@ -165,7 +217,9 @@
|
||||||
<!-- Item Creation Dropdown, only on Admin -->
|
<!-- Item Creation Dropdown, only on Admin -->
|
||||||
<div
|
<div
|
||||||
class="search-control-item"
|
class="search-control-item"
|
||||||
v-if="!isOnTheme && !$route.query.iframemode">
|
v-if="!isOnTheme &&
|
||||||
|
!$route.query.iframemode &&
|
||||||
|
!openAdvancedSearch">
|
||||||
<b-dropdown
|
<b-dropdown
|
||||||
:mobile-modal="true"
|
:mobile-modal="true"
|
||||||
id="item-creation-options-dropdown"
|
id="item-creation-options-dropdown"
|
||||||
|
@ -538,58 +592,6 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- ADVANCED SEARCH -->
|
|
||||||
<div
|
|
||||||
id="advanced-search-container"
|
|
||||||
role="search"
|
|
||||||
v-if="openAdvancedSearch">
|
|
||||||
|
|
||||||
<div class="tnc-advanced-search-close">
|
|
||||||
<div class="advanced-search-criteria-title">
|
|
||||||
<div class="is-flex">
|
|
||||||
<h1>{{ $i18n.get('info_search_criteria') }}</h1>
|
|
||||||
<div
|
|
||||||
:style="{'margin-bottom': 'auto'}"
|
|
||||||
class="field is-grouped">
|
|
||||||
<a
|
|
||||||
class="back-link"
|
|
||||||
@click="openAdvancedSearch = false">
|
|
||||||
{{ $i18n.get('back') }}
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<hr>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<advanced-search
|
|
||||||
:is-repository-level="isRepositoryLevel"
|
|
||||||
:collection-id="collectionId"
|
|
||||||
:advanced-search-results="advancedSearchResults"
|
|
||||||
:open-form-advanced-search="openFormAdvancedSearch"
|
|
||||||
:is-do-search="isDoSearch"
|
|
||||||
:metadata="metadata"/>
|
|
||||||
|
|
||||||
<div class="advanced-searh-form-submit">
|
|
||||||
<p
|
|
||||||
v-if="advancedSearchResults"
|
|
||||||
class="control">
|
|
||||||
<button
|
|
||||||
aria-controls="items-list-results"
|
|
||||||
@click="advancedSearchResults = !advancedSearchResults"
|
|
||||||
class="button is-outlined">{{ $i18n.get('edit_search') }}</button>
|
|
||||||
</p>
|
|
||||||
<p
|
|
||||||
v-if="advancedSearchResults"
|
|
||||||
class="control">
|
|
||||||
<button
|
|
||||||
aria-controls="items-list-results"
|
|
||||||
@click="isDoSearch = !isDoSearch"
|
|
||||||
class="button is-success">{{ $i18n.get('search') }}</button>
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- STATUS TABS, only on Admin -------- -->
|
<!-- STATUS TABS, only on Admin -------- -->
|
||||||
<div
|
<div
|
||||||
v-if="!isOnTheme && !openAdvancedSearch"
|
v-if="!isOnTheme && !openAdvancedSearch"
|
||||||
|
@ -678,6 +680,34 @@
|
||||||
<!-- <skeleton-items-list v-if="!isOnTheme"/> -->
|
<!-- <skeleton-items-list v-if="!isOnTheme"/> -->
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- Alert if custom metada is being used for sorting -->
|
||||||
|
<div
|
||||||
|
v-if="hasAnOpenAlert &&
|
||||||
|
isSortingByCustomMetadata &&
|
||||||
|
!showLoading &&
|
||||||
|
((openAdvancedSearch && advancedSearchResults) || !openAdvancedSearch)"
|
||||||
|
class="metadata-alert">
|
||||||
|
<p class="text">
|
||||||
|
{{
|
||||||
|
totalItems > 0 ?
|
||||||
|
$i18n.getWithVariables('info_sorting_by_metadata_value_%s', [orderByName]) :
|
||||||
|
$i18n.getWithVariables('info_sorting_by_metadata_value_%s_empty_list', [orderByName])
|
||||||
|
}}
|
||||||
|
</p>
|
||||||
|
<div>
|
||||||
|
<button
|
||||||
|
@click="openMetatadaSortingWarningDialog({ offerCheckbox: false })"
|
||||||
|
class="button">
|
||||||
|
{{ $i18n.get('label_view_more') }}
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
@click="hasAnOpenAlert = false"
|
||||||
|
class="button icon">
|
||||||
|
<i class="tainacan-icon tainacan-icon-close"/>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<!-- Admin View Modes-->
|
<!-- Admin View Modes-->
|
||||||
<items-list
|
<items-list
|
||||||
v-if="!isOnTheme &&
|
v-if="!isOnTheme &&
|
||||||
|
@ -858,7 +888,9 @@
|
||||||
searchControlHeight: 0,
|
searchControlHeight: 0,
|
||||||
sortingMetadata: [],
|
sortingMetadata: [],
|
||||||
isFilterModalActive: false,
|
isFilterModalActive: false,
|
||||||
customFilters: []
|
customFilters: [],
|
||||||
|
hasAnOpenModal: false,
|
||||||
|
hasAnOpenAlert: true
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
props: {
|
props: {
|
||||||
|
@ -951,6 +983,10 @@
|
||||||
} else {
|
} else {
|
||||||
this.$eventBusSearch.clearAllFilters();
|
this.$eventBusSearch.clearAllFilters();
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
orderByName() {
|
||||||
|
if (this.isSortingByCustomMetadata)
|
||||||
|
this.hasAnOpenAlert = true;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
@ -1337,14 +1373,18 @@
|
||||||
this.isLoadingMetadata = false;
|
this.isLoadingMetadata = false;
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
showItemsHiddingDueSorting() {
|
showItemsHiddingDueSortingDialog() {
|
||||||
|
|
||||||
if (this.isSortingByCustomMetadata &&
|
if (this.isSortingByCustomMetadata &&
|
||||||
this.$userPrefs.get('neverShowItemsHiddenDueSortingDialog') != true) {
|
this.$userPrefs.get('neverShowItemsHiddenDueSortingDialog') != true) {
|
||||||
|
|
||||||
this.hasAnOpenModal = true;
|
this.hasAnOpenModal = true;
|
||||||
|
|
||||||
this.$modal.open({
|
this.openMetatadaSortingWarningDialog({ offerCheckbox: true });
|
||||||
|
}
|
||||||
|
},
|
||||||
|
openMetatadaSortingWarningDialog({ offerCheckbox }) {
|
||||||
|
this.$modal.open({
|
||||||
parent: this,
|
parent: this,
|
||||||
component: CustomDialog,
|
component: CustomDialog,
|
||||||
props: {
|
props: {
|
||||||
|
@ -1355,11 +1395,10 @@
|
||||||
this.hasAnOpenModal = false;
|
this.hasAnOpenModal = false;
|
||||||
},
|
},
|
||||||
hideCancel: true,
|
hideCancel: true,
|
||||||
showNeverShowAgainOption: tainacan_plugin.user_caps != undefined && tainacan_plugin.user_caps.length != undefined && tainacan_plugin.user_caps.length > 0,
|
showNeverShowAgainOption: offerCheckbox && tainacan_plugin.user_caps != undefined && tainacan_plugin.user_caps.length != undefined && tainacan_plugin.user_caps.length > 0,
|
||||||
messageKeyForUserPrefs: 'ItemsHiddenDueSorting'
|
messageKeyForUserPrefs: 'ItemsHiddenDueSorting'
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
|
||||||
},
|
},
|
||||||
adjustSearchControlHeight: _.debounce( function() {
|
adjustSearchControlHeight: _.debounce( function() {
|
||||||
this.$nextTick(() => {
|
this.$nextTick(() => {
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
.notices {
|
.notices {
|
||||||
z-index: 99999999;
|
z-index: 99999999;
|
||||||
|
|
||||||
.toast {
|
.toast, .snackbar {
|
||||||
font-size: 0.75rem;
|
font-size: 0.75rem;
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
box-shadow: none;
|
box-shadow: none;
|
||||||
|
|
|
@ -415,6 +415,10 @@ return apply_filters( 'tainacan-admin-i18n', [
|
||||||
'label_duplicate_item' => __( 'Duplicate item', 'tainacan' ),
|
'label_duplicate_item' => __( 'Duplicate item', 'tainacan' ),
|
||||||
'label_create_another_item' => __( 'Create another item', 'tainacan' ),
|
'label_create_another_item' => __( 'Create another item', 'tainacan' ),
|
||||||
'label_recent_collections' => __( 'Recent Collections', 'tainacan' ),
|
'label_recent_collections' => __( 'Recent Collections', 'tainacan' ),
|
||||||
|
'label_duplicating_item' => __( 'Duplicating item', 'tainacan' ),
|
||||||
|
'label_item_duplication_success' => __( 'The item was duplicated with success!', 'tainacan' ),
|
||||||
|
'label_item_duplication_failure' => __( 'Something wrong happened... Item duplication failed!', 'tainacan' ),
|
||||||
|
'label_create_another_taxonomy' => __( 'Create another Taxonomy', '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' ),
|
||||||
|
@ -625,6 +629,9 @@ return apply_filters( 'tainacan-admin-i18n', [
|
||||||
'info_send_email' => __('The exporter may take a while. Check this option to receive an e-mail when the process is done. You can also check the process status visiting the', 'tainacan'),
|
'info_send_email' => __('The exporter may take a while. Check this option to receive an e-mail when the process is done. You can also check the process status visiting the', 'tainacan'),
|
||||||
'info_tainacan_api' => __('Tainacan API on JSON format.', 'tainacan'),
|
'info_tainacan_api' => __('Tainacan API on JSON format.', 'tainacan'),
|
||||||
'info_items_hidden_due_sorting' => __('When ordering by metadata value, items that have no value for the chosen metadata will not be listed. This list may have less elements than the total existing for current search criteria.', 'tainacan'),
|
'info_items_hidden_due_sorting' => __('When ordering by metadata value, items that have no value for the chosen metadata will not be listed. This list may have less elements than the total existing for current search criteria.', 'tainacan'),
|
||||||
|
'info_sorting_by_metadata_value_%s' => __('Showing only items that have value for metadata %s.', 'tainacan'),
|
||||||
|
'info_sorting_by_metadata_value_%s_empty_list' => __('No item found, but only items with values for metadata %s are shown. Try sorting by other metatada.', 'tainacan'),
|
||||||
|
'info_await_while_item_duplication' => __('Please wait while item is being duplicated...', 'tainacan'),
|
||||||
|
|
||||||
// Tainacan Metadatum Types
|
// Tainacan Metadatum Types
|
||||||
'tainacan-text' => __( 'Text', 'tainacan' ),
|
'tainacan-text' => __( 'Text', 'tainacan' ),
|
||||||
|
|
|
@ -80,7 +80,7 @@ class REST_Items_Controller extends REST_Controller {
|
||||||
'permission_callback' => array($this, 'delete_item_permissions_check'),
|
'permission_callback' => array($this, 'delete_item_permissions_check'),
|
||||||
'args' => array(
|
'args' => array(
|
||||||
'permanently' => array(
|
'permanently' => array(
|
||||||
'description' => __('To delete permanently, you can pass \'permanently\' as true. By default this will only trash collection'),
|
'description' => __('To delete permanently, you can pass \'permanently\' as true. By default this will only trash collection', 'tainacan'),
|
||||||
'default' => 'false'
|
'default' => 'false'
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
@ -98,6 +98,27 @@ class REST_Items_Controller extends REST_Controller {
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
register_rest_route(
|
||||||
|
$this->namespace, '/collection/(?P<collection_id>[\d]+)/' . $this->rest_base . '/(?P<item_id>[\d]+)/duplicate',
|
||||||
|
array(
|
||||||
|
array(
|
||||||
|
'methods' => \WP_REST_Server::CREATABLE,
|
||||||
|
'callback' => array($this, 'duplicate_item'),
|
||||||
|
'permission_callback' => array($this, 'create_item_permissions_check'),
|
||||||
|
'args' => array(
|
||||||
|
'copies' => array(
|
||||||
|
'description' => __('Number of copies to be created', 'tainacan'),
|
||||||
|
'default' => 1,
|
||||||
|
'type' => 'integer'
|
||||||
|
),
|
||||||
|
'status' => array(
|
||||||
|
'description' => __('Try to assign the informed status to the duplicates if they validate. By default it will save them as drafts.', 'tainacan'),
|
||||||
|
'type' => 'string'
|
||||||
|
),
|
||||||
|
)
|
||||||
|
),
|
||||||
|
)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -628,6 +649,104 @@ class REST_Items_Controller extends REST_Controller {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param \WP_REST_Request $request
|
||||||
|
*
|
||||||
|
* @return \WP_Error|\WP_REST_Response
|
||||||
|
*/
|
||||||
|
public function duplicate_item( $request ) {
|
||||||
|
$item_id = $request['item_id'];
|
||||||
|
|
||||||
|
$item = $this->items_repository->fetch($item_id);
|
||||||
|
|
||||||
|
$defaults = [
|
||||||
|
'copies' => 1,
|
||||||
|
'status' => 'draft'
|
||||||
|
];
|
||||||
|
|
||||||
|
$body = json_decode($request->get_body(), true);
|
||||||
|
|
||||||
|
if (!is_array($body)) {
|
||||||
|
$body = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
$args = array_merge($defaults, $body);
|
||||||
|
|
||||||
|
if ($item) {
|
||||||
|
|
||||||
|
$response = [
|
||||||
|
'items' => []
|
||||||
|
];
|
||||||
|
|
||||||
|
for ($i=1; $i<=$args['copies']; $i++) {
|
||||||
|
|
||||||
|
$new_item = new Entities\Item();
|
||||||
|
$items_repo = Repositories\Items::get_instance();
|
||||||
|
|
||||||
|
$new_item->set_status( 'draft' );
|
||||||
|
|
||||||
|
$new_item->set_title( $item->get_title() );
|
||||||
|
$new_item->set_description( $item->get_description() );
|
||||||
|
$new_item->set_collection_id( $item->get_collection_id() );
|
||||||
|
|
||||||
|
if ( $new_item->validate() ) {
|
||||||
|
|
||||||
|
$new_item = $items_repo->insert($new_item);
|
||||||
|
|
||||||
|
$metadata = $item->get_metadata();
|
||||||
|
|
||||||
|
$errors = [];
|
||||||
|
|
||||||
|
foreach ($metadata as $item_metadatum) {
|
||||||
|
|
||||||
|
$new_item_metadatum = new Entities\Item_Metadata_Entity( $new_item, $item_metadatum->get_metadatum() );
|
||||||
|
$new_item_metadatum->set_value( $item_metadatum->get_value() );
|
||||||
|
|
||||||
|
if ( $new_item_metadatum->validate() ) {
|
||||||
|
Repositories\Item_Metadata::get_instance()->insert( $new_item_metadatum );
|
||||||
|
} else {
|
||||||
|
$errors[] = $new_item_metadatum->get_errors();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($args['status'] != 'draft') {
|
||||||
|
|
||||||
|
$new_item->set_status( $args['status'] );
|
||||||
|
|
||||||
|
if ( $new_item->validate() ) {
|
||||||
|
$new_item = $items_repo->insert($new_item);
|
||||||
|
} else {
|
||||||
|
$new_item->set_status( 'draft' );
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
$response['items'][] = $this->prepare_item_for_response($new_item, $request);
|
||||||
|
|
||||||
|
do_action('tainacan-api-item-duplicated', $item, $new_item);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
return new \WP_REST_Response([
|
||||||
|
'error_message' => __('Error duplicating item', 'tainacan'),
|
||||||
|
'errors' => $new_item->get_errors(),
|
||||||
|
'item' => $this->prepare_item_for_response($item, $request)
|
||||||
|
], 400);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return new \WP_REST_Response($response, 201);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return new \WP_REST_Response([
|
||||||
|
'error_message' => __('An item with this ID was not found', 'tainacan' ),
|
||||||
|
'item_id' => $item_id
|
||||||
|
], 400);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param string $method
|
* @param string $method
|
||||||
|
|
|
@ -28,11 +28,17 @@ class Media {
|
||||||
public function insert_attachment_from_url($url, $post_id = null) {
|
public function insert_attachment_from_url($url, $post_id = null) {
|
||||||
$filename = $this->save_remote_file($url);
|
$filename = $this->save_remote_file($url);
|
||||||
|
|
||||||
if( !file_exists($filename) ) {
|
if( !file_exists($filename) ) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return $this->insert_attachment_from_blob(fopen($filename,'r'), basename($url), $post_id);
|
$file = fopen($filename,'r');
|
||||||
|
|
||||||
|
if (false === $file) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->insert_attachment_from_blob($file, basename($url), $post_id);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -120,11 +126,11 @@ class Media {
|
||||||
}
|
}
|
||||||
|
|
||||||
if( @filesize($upload['file']) == 0 && is_resource($blob) ){
|
if( @filesize($upload['file']) == 0 && is_resource($blob) ){
|
||||||
$file_wordpress_stream = fopen( $upload['file'], 'r+');
|
$file_wordpress_stream = fopen( $upload['file'], 'r+');
|
||||||
stream_copy_to_stream($blob, $file_wordpress_stream);
|
stream_copy_to_stream($blob, $file_wordpress_stream);
|
||||||
|
|
||||||
if( file_exists(self::$file_name) ) unlink(self::$file_name);
|
if( file_exists(self::$file_name) ) unlink(self::$file_name);
|
||||||
}
|
}
|
||||||
|
|
||||||
$file_path = $upload['file'];
|
$file_path = $upload['file'];
|
||||||
$file_name = basename( $file_path );
|
$file_name = basename( $file_path );
|
||||||
|
@ -155,6 +161,22 @@ class Media {
|
||||||
return $attach_id;
|
return $attach_id;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add support to get mime type content even when mime_content_type function is not available
|
||||||
|
* @param string $filename The file name to check the mime type
|
||||||
|
* @return string mime type @see \mime_content_type()
|
||||||
|
*/
|
||||||
|
function get_mime_content_type( $filename ){
|
||||||
|
if (function_exists( 'mime_content_type' )) {
|
||||||
|
return mime_content_type($filename);
|
||||||
|
} else {
|
||||||
|
$finfo = finfo_open( FILEINFO_MIME_TYPE );
|
||||||
|
$mime_type = finfo_file( $finfo, $filename );
|
||||||
|
finfo_close( $finfo );
|
||||||
|
return $mime_type;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Extract an image from the first page of a pdf file
|
* Extract an image from the first page of a pdf file
|
||||||
*
|
*
|
||||||
|
@ -171,7 +193,7 @@ class Media {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( mime_content_type($filepath) != 'application/pdf') {
|
if ( $this->get_mime_content_type($filepath) != 'application/pdf') {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -224,7 +224,7 @@ class Term extends Entity {
|
||||||
|
|
||||||
if (false !== $term_exists) {
|
if (false !== $term_exists) {
|
||||||
if ($this->get_id() != $term_exists->term_id) {
|
if ($this->get_id() != $term_exists->term_id) {
|
||||||
$this->add_error( 'repeated', __('You can not have two terms with the same name at the same level', 'tainacan') );
|
$this->add_error( 'name', __('You can not have two terms with the same name at the same level', 'tainacan') );
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -175,7 +175,8 @@
|
||||||
compare: this.comparator,
|
compare: this.comparator,
|
||||||
metadatum_id: this.metadatum,
|
metadatum_id: this.metadatum,
|
||||||
collection_id: ( this.collection_id ) ? this.collection_id : this.filter.collection_id,
|
collection_id: ( this.collection_id ) ? this.collection_id : this.filter.collection_id,
|
||||||
value: ''
|
value: '',
|
||||||
|
type: 'NUMERIC'
|
||||||
});
|
});
|
||||||
|
|
||||||
this.value = null;
|
this.value = null;
|
||||||
|
@ -191,7 +192,8 @@
|
||||||
compare: this.comparator,
|
compare: this.comparator,
|
||||||
metadatum_id: this.metadatum,
|
metadatum_id: this.metadatum,
|
||||||
collection_id: ( this.collection_id ) ? this.collection_id : this.filter.collection_id,
|
collection_id: ( this.collection_id ) ? this.collection_id : this.filter.collection_id,
|
||||||
value: this.value
|
value: this.value,
|
||||||
|
type: 'NUMERIC'
|
||||||
});
|
});
|
||||||
|
|
||||||
this.$eventBusSearch.$emit( 'sendValuesToTags', {
|
this.$eventBusSearch.$emit( 'sendValuesToTags', {
|
||||||
|
|
|
@ -97,43 +97,64 @@
|
||||||
created(){
|
created(){
|
||||||
this.fetchTaxonomies();
|
this.fetchTaxonomies();
|
||||||
|
|
||||||
if ( this.value ) {
|
|
||||||
this.taxonomy_id = this.value.taxonomy_id;
|
|
||||||
}
|
|
||||||
|
|
||||||
if( this.value ) {
|
|
||||||
this.allow_new_terms = ( this.value.allow_new_terms ) ? this.value.allow_new_terms : 'no';
|
|
||||||
}
|
|
||||||
|
|
||||||
this.single_types['tainacan-taxonomy-radio'] = 'Radio';
|
this.single_types['tainacan-taxonomy-radio'] = 'Radio';
|
||||||
this.multiple_types['tainacan-taxonomy-tag-input'] = 'Tag Input';
|
this.multiple_types['tainacan-taxonomy-tag-input'] = 'Tag Input';
|
||||||
this.multiple_types['tainacan-taxonomy-checkbox'] = 'Checkbox';
|
this.multiple_types['tainacan-taxonomy-checkbox'] = 'Checkbox';
|
||||||
|
|
||||||
this.isReady = true;
|
if (this.value) {
|
||||||
},
|
|
||||||
watch: {
|
this.taxonomy_id = this.value.taxonomy_id;
|
||||||
input_type(val, oldValue) {
|
this.allow_new_terms = ( this.value.allow_new_terms ) ? this.value.allow_new_terms : 'no';
|
||||||
if (val != oldValue) {
|
|
||||||
this.emitValues();
|
if (this.metadatum && this.metadatum.multiple === 'no') {
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
computed: {
|
|
||||||
listInputType(){
|
|
||||||
if( this.metadatum && this.metadatum.multiple === 'no' ){
|
|
||||||
let types = Object.keys( this.single_types );
|
let types = Object.keys( this.single_types );
|
||||||
let hasValue = this.value && this.value.input_type && types.indexOf( this.value.input_type ) >= 0;
|
let hasValue = this.value && this.value.input_type && types.indexOf( this.value.input_type ) >= 0;
|
||||||
this.setInputType( ( hasValue ) ? this.value.input_type : 'tainacan-taxonomy-radio' );
|
this.setInputType( ( hasValue ) ? this.value.input_type : 'tainacan-taxonomy-radio' );
|
||||||
return true;
|
|
||||||
} else {
|
} else {
|
||||||
let types = Object.keys( this.multiple_types );
|
let types = Object.keys( this.multiple_types );
|
||||||
let hasValue = this.value && this.value.input_type && types.indexOf( this.value.input_type ) >= 0;
|
let hasValue = this.value && this.value.input_type && types.indexOf( this.value.input_type ) >= 0;
|
||||||
this.setInputType( ( hasValue ) ? this.value.input_type : 'tainacan-taxonomy-checkbox' );
|
this.setInputType( ( hasValue ) ? this.value.input_type : 'tainacan-taxonomy-checkbox' );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.isReady = true;
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
input_type:{
|
||||||
|
handler(val, oldValue) {
|
||||||
|
if (val != oldValue) {
|
||||||
|
this.emitValues();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
listInputType() {
|
||||||
|
if ( this.metadatum && this.metadatum.multiple === 'no' ){
|
||||||
|
let types = Object.keys( this.single_types );
|
||||||
|
let hasValue = this.value && this.value.input_type && types.indexOf( this.value.input_type ) >= 0;
|
||||||
|
|
||||||
|
if (hasValue)
|
||||||
|
this.setInputType(this.value.input_type)
|
||||||
|
else {
|
||||||
|
this.setInputType('tainacan-taxonomy-radio');
|
||||||
|
this.emitValues();
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
let types = Object.keys( this.multiple_types );
|
||||||
|
let hasValue = this.value && this.value.input_type && types.indexOf( this.value.input_type ) >= 0;
|
||||||
|
if (hasValue)
|
||||||
|
this.setInputType(this.value.input_type)
|
||||||
|
else
|
||||||
|
this.setInputType('tainacan-taxonomy-checkbox');
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
setError(){
|
setError(){
|
||||||
if( this.errors && this.errors.taxonomy_id !== '' ){
|
if (this.errors && this.errors.taxonomy_id !== '') {
|
||||||
this.setErrorsAttributes( 'is-danger', this.errors.taxonomy_id );
|
this.setErrorsAttributes( 'is-danger', this.errors.taxonomy_id );
|
||||||
} else {
|
} else {
|
||||||
this.setErrorsAttributes( '', '' );
|
this.setErrorsAttributes( '', '' );
|
||||||
|
|
|
@ -101,7 +101,7 @@
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
getComponent() {
|
getComponent() {
|
||||||
if(this.forcedComponentType){
|
if (this.forcedComponentType){
|
||||||
return this.forcedComponentType;
|
return this.forcedComponentType;
|
||||||
} else if( this.metadatum.metadatum
|
} else if( this.metadatum.metadatum
|
||||||
&& this.metadatum.metadatum.metadata_type_options && this.metadatum.metadatum.metadata_type_options.input_type ){
|
&& this.metadatum.metadatum.metadata_type_options && this.metadatum.metadatum.metadata_type_options.input_type ){
|
||||||
|
|
|
@ -175,8 +175,16 @@ class Item_Metadata extends Repository {
|
||||||
// $success = wp_set_object_terms( $item_metadata->get_item()->get_id(), $new_terms, $taxonomy->get_db_identifier() );
|
// $success = wp_set_object_terms( $item_metadata->get_item()->get_id(), $new_terms, $taxonomy->get_db_identifier() );
|
||||||
|
|
||||||
$insert = [];
|
$insert = [];
|
||||||
foreach ( (array) $new_terms as $new_term ) {
|
if ( !is_array($new_terms) ) {
|
||||||
$exists = Terms::get_instance()->term_exists($new_term, $taxonomy, null, true);
|
$new_terms = [ $new_terms ];
|
||||||
|
}
|
||||||
|
foreach ( $new_terms as $new_term ) {
|
||||||
|
if ( \is_object($new_term) && $new_term instanceof Entities\Term ) {
|
||||||
|
$exists = $new_term->WP_Term;
|
||||||
|
} else {
|
||||||
|
$exists = Terms::get_instance()->term_exists($new_term, $taxonomy, null, true);
|
||||||
|
}
|
||||||
|
|
||||||
if ( $exists ) {
|
if ( $exists ) {
|
||||||
$insert[] = $exists->term_id;
|
$insert[] = $exists->term_id;
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -303,6 +303,9 @@ export default {
|
||||||
this.$store.dispatch('search/setAdminViewMode', adminViewMode);
|
this.$store.dispatch('search/setAdminViewMode', adminViewMode);
|
||||||
this.updateURLQueries();
|
this.updateURLQueries();
|
||||||
},
|
},
|
||||||
|
highlightsItem(itemId) {
|
||||||
|
this.$store.dispatch('search/highlightsItem', itemId);
|
||||||
|
},
|
||||||
updateURLQueries() {
|
updateURLQueries() {
|
||||||
this.$router.replace({ query: {} });
|
this.$router.replace({ query: {} });
|
||||||
this.$router.replace({ query: this.$store.getters['search/getPostQuery'] });
|
this.$router.replace({ query: this.$store.getters['search/getPostQuery'] });
|
||||||
|
|
|
@ -134,14 +134,10 @@ export const updateItem = ({ commit }, item) => {
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
export const duplicateItem = ({ commit }, { item, attachment }) => {
|
export const duplicateItem = ({ commit }, { collectionId, itemId }) => {
|
||||||
delete item['id'];
|
|
||||||
|
|
||||||
if (item['terms'] == null)
|
|
||||||
item['terms'] = [];
|
|
||||||
|
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
axios.tainacan.post('/collection/' + item.collection_id + '/items/', item)
|
axios.tainacan.post('/collection/' + collectionId + '/items/' + itemId + '/duplicate')
|
||||||
.then( res => {
|
.then( res => {
|
||||||
resolve( res.data );
|
resolve( res.data );
|
||||||
}).catch( error => {
|
}).catch( error => {
|
||||||
|
|
|
@ -166,3 +166,7 @@ export const cleanTaxQueries = ({ commit }) => {
|
||||||
export const cleanFetchOnly = ({ commit }) => {
|
export const cleanFetchOnly = ({ commit }) => {
|
||||||
commit('cleanFetchOnly');
|
commit('cleanFetchOnly');
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const highlightsItem = ({ commit }, itemId ) => {
|
||||||
|
commit('setHighlightedItem', itemId);
|
||||||
|
};
|
|
@ -83,3 +83,7 @@ export const getFilterTags = state => {
|
||||||
export const getFacets = state => {
|
export const getFacets = state => {
|
||||||
return state.facets;
|
return state.facets;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const getHighlightedItem = state => {
|
||||||
|
return state.highlightedItem;
|
||||||
|
};
|
||||||
|
|
|
@ -23,7 +23,8 @@ const state = {
|
||||||
totalPages: 0,
|
totalPages: 0,
|
||||||
itemsPerPage: 12, // Not the same as postquery.perpage as API may have limited it's value
|
itemsPerPage: 12, // Not the same as postquery.perpage as API may have limited it's value
|
||||||
facets: {},
|
facets: {},
|
||||||
orderByName: ''
|
orderByName: '',
|
||||||
|
highlightedItem: null
|
||||||
};
|
};
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
|
|
|
@ -188,3 +188,7 @@ export const cleanFetchOnly = (state) => {
|
||||||
export const setFacets = (state, facets) => {
|
export const setFacets = (state, facets) => {
|
||||||
state.facets = facets;
|
state.facets = facets;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const setHighlightedItem = (state, itemId) => {
|
||||||
|
state.highlightedItem = itemId;
|
||||||
|
}
|
||||||
|
|
|
@ -476,6 +476,229 @@ class TAINACAN_REST_Items_Controller extends TAINACAN_UnitApiTestCase {
|
||||||
//$this->assertEquals(403, $response->get_status());
|
//$this->assertEquals(403, $response->get_status());
|
||||||
$this->assertEquals(201, $response->get_status());
|
$this->assertEquals(201, $response->get_status());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @group duplicate
|
||||||
|
*/
|
||||||
|
function test_duplicate() {
|
||||||
|
|
||||||
|
$collection = $this->tainacan_entity_factory->create_entity(
|
||||||
|
'collection',
|
||||||
|
array(
|
||||||
|
'name' => 'Agile',
|
||||||
|
'description' => 'Agile methods',
|
||||||
|
'status' => 'publish'
|
||||||
|
),
|
||||||
|
true
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
$public_meta = $this->tainacan_entity_factory->create_entity(
|
||||||
|
'metadatum',
|
||||||
|
array(
|
||||||
|
'name' => 'public_meta',
|
||||||
|
'collection' => $collection,
|
||||||
|
'metadata_type' => 'Tainacan\Metadata_Types\Text',
|
||||||
|
'status' => 'publish'
|
||||||
|
),
|
||||||
|
true
|
||||||
|
);
|
||||||
|
|
||||||
|
$multiple_meta = $this->tainacan_entity_factory->create_entity(
|
||||||
|
'metadatum',
|
||||||
|
array(
|
||||||
|
'name' => 'public_meta',
|
||||||
|
'collection' => $collection,
|
||||||
|
'metadata_type' => 'Tainacan\Metadata_Types\Text',
|
||||||
|
'multiple' => 'yes',
|
||||||
|
'status' => 'publish'
|
||||||
|
),
|
||||||
|
true
|
||||||
|
);
|
||||||
|
|
||||||
|
$taxonomy = $this->tainacan_entity_factory->create_entity(
|
||||||
|
'taxonomy',
|
||||||
|
array(
|
||||||
|
'name' => 'taxonomy_public',
|
||||||
|
'description' => 'taxonomy_public',
|
||||||
|
'status' => 'publish'
|
||||||
|
),
|
||||||
|
true
|
||||||
|
);
|
||||||
|
|
||||||
|
$taxonomy2 = $this->tainacan_entity_factory->create_entity(
|
||||||
|
'taxonomy',
|
||||||
|
array(
|
||||||
|
'name' => 'taxonomy_public2',
|
||||||
|
'description' => 'taxonomy_public2',
|
||||||
|
'status' => 'publish'
|
||||||
|
),
|
||||||
|
true
|
||||||
|
);
|
||||||
|
|
||||||
|
$tax_meta = $this->tainacan_entity_factory->create_entity(
|
||||||
|
'metadatum',
|
||||||
|
array(
|
||||||
|
'name' => 'metadata-public',
|
||||||
|
'status' => 'publish',
|
||||||
|
'collection' => $collection,
|
||||||
|
'metadata_type' => 'Tainacan\Metadata_Types\Taxonomy',
|
||||||
|
'metadata_type_options' => [
|
||||||
|
'allow_new_terms' => 'yes',
|
||||||
|
'taxonomy_id' => $taxonomy->get_id()
|
||||||
|
],
|
||||||
|
'multiple' => 'yes'
|
||||||
|
),
|
||||||
|
true
|
||||||
|
);
|
||||||
|
|
||||||
|
$tax_meta_single = $this->tainacan_entity_factory->create_entity(
|
||||||
|
'metadatum',
|
||||||
|
array(
|
||||||
|
'name' => 'metadata-public-single',
|
||||||
|
'status' => 'publish',
|
||||||
|
'collection' => $collection,
|
||||||
|
'metadata_type' => 'Tainacan\Metadata_Types\Taxonomy',
|
||||||
|
'metadata_type_options' => [
|
||||||
|
'allow_new_terms' => 'yes',
|
||||||
|
'taxonomy_id' => $taxonomy2->get_id()
|
||||||
|
],
|
||||||
|
'multiple' => 'no'
|
||||||
|
),
|
||||||
|
true
|
||||||
|
);
|
||||||
|
|
||||||
|
$item1 = $this->tainacan_entity_factory->create_entity(
|
||||||
|
'item',
|
||||||
|
array(
|
||||||
|
'title' => 'Lean Startup',
|
||||||
|
'description' => 'Um processo ágil de criação de novos negócios.',
|
||||||
|
'collection' => $collection,
|
||||||
|
'status' => 'publish'
|
||||||
|
),
|
||||||
|
true
|
||||||
|
);
|
||||||
|
|
||||||
|
$itemMetaRepo = \Tainacan\Repositories\Item_Metadata::get_instance();
|
||||||
|
|
||||||
|
$newMeta = new \Tainacan\Entities\Item_Metadata_Entity($item1, $public_meta);
|
||||||
|
$newMeta->set_value('test');
|
||||||
|
$newMeta->validate();
|
||||||
|
$itemMetaRepo->insert($newMeta);
|
||||||
|
|
||||||
|
$newMeta = new \Tainacan\Entities\Item_Metadata_Entity($item1, $multiple_meta);
|
||||||
|
$newMeta->set_value(['test1', 'test2']);
|
||||||
|
$newMeta->validate();
|
||||||
|
$itemMetaRepo->insert($newMeta);
|
||||||
|
|
||||||
|
$newMeta = new \Tainacan\Entities\Item_Metadata_Entity($item1, $tax_meta);
|
||||||
|
$newMeta->set_value(['blue', 'red']);
|
||||||
|
$newMeta->validate();
|
||||||
|
$itemMetaRepo->insert($newMeta);
|
||||||
|
|
||||||
|
$newMeta = new \Tainacan\Entities\Item_Metadata_Entity($item1, $tax_meta_single);
|
||||||
|
$newMeta->set_value('test term');
|
||||||
|
$newMeta->validate();
|
||||||
|
$itemMetaRepo->insert($newMeta);
|
||||||
|
|
||||||
|
$request = new \WP_REST_Request('POST', $this->namespace . '/collection/' . $collection->get_id() . '/items/' . $item1->get_id() . '/duplicate');
|
||||||
|
|
||||||
|
$response = $this->server->dispatch($request);
|
||||||
|
|
||||||
|
$this->assertEquals(201, $response->get_status());
|
||||||
|
|
||||||
|
$data = $response->get_data();
|
||||||
|
|
||||||
|
$this->assertEquals('Lean Startup', $data['items'][0]['title']);
|
||||||
|
|
||||||
|
$metadata_1 = $item1->get_metadata();
|
||||||
|
|
||||||
|
$duplicated = \Tainacan\Repositories\Items::get_instance()->fetch( (int) $data['items'][0]['id'] );
|
||||||
|
|
||||||
|
$metadata_2 = $duplicated->get_metadata();
|
||||||
|
|
||||||
|
foreach( $metadata_1 as $k => $m ) {
|
||||||
|
$this->assertEquals( $m->get_value(), $metadata_2[$k]->get_value() );
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
$request = new \WP_REST_Request('POST', $this->namespace . '/collection/' . $collection->get_id() . '/items/' . $item1->get_id() . '/duplicate');
|
||||||
|
|
||||||
|
$params = json_encode([
|
||||||
|
'copies' => 4,
|
||||||
|
'status' => 'publish'
|
||||||
|
]);
|
||||||
|
|
||||||
|
$request->set_body($params);
|
||||||
|
|
||||||
|
$response = $this->server->dispatch($request);
|
||||||
|
|
||||||
|
$data = $response->get_data();
|
||||||
|
|
||||||
|
$this->assertEquals(4, count($data['items']));
|
||||||
|
|
||||||
|
foreach ( $data['items'] as $created_item ) {
|
||||||
|
$duplicated = \Tainacan\Repositories\Items::get_instance()->fetch( (int) $created_item['id'] );
|
||||||
|
|
||||||
|
$this->assertEquals('publish', $created_item['status']);
|
||||||
|
|
||||||
|
$metadata_2 = $duplicated->get_metadata();
|
||||||
|
|
||||||
|
foreach( $metadata_1 as $k => $m ) {
|
||||||
|
$this->assertEquals( $m->get_value(), $metadata_2[$k]->get_value() );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Create a required metadata
|
||||||
|
$required_meta = $this->tainacan_entity_factory->create_entity(
|
||||||
|
'metadatum',
|
||||||
|
array(
|
||||||
|
'name' => 'required_meta',
|
||||||
|
'collection' => $collection,
|
||||||
|
'metadata_type' => 'Tainacan\Metadata_Types\Text',
|
||||||
|
'multiple' => 'no',
|
||||||
|
'required' => 'yes',
|
||||||
|
'status' => 'publish'
|
||||||
|
),
|
||||||
|
true
|
||||||
|
);
|
||||||
|
|
||||||
|
// $metas = \Tainacan\Repositories\Metadata::get_instance()->fetch_by_collection($collection, [], 'OBJECT');
|
||||||
|
//
|
||||||
|
// foreach ($metas as $m) {
|
||||||
|
// var_dump($m->get_name());
|
||||||
|
// }
|
||||||
|
|
||||||
|
$request = new \WP_REST_Request('POST', $this->namespace . '/collection/' . $collection->get_id() . '/items/' . $item1->get_id() . '/duplicate');
|
||||||
|
|
||||||
|
$params = json_encode([
|
||||||
|
'copies' => 3,
|
||||||
|
'status' => 'publish'
|
||||||
|
]);
|
||||||
|
|
||||||
|
$request->set_body($params);
|
||||||
|
|
||||||
|
$response = $this->server->dispatch($request);
|
||||||
|
|
||||||
|
$data = $response->get_data();
|
||||||
|
|
||||||
|
$this->assertEquals(3, count($data['items']));
|
||||||
|
|
||||||
|
foreach ( $data['items'] as $created_item ) {
|
||||||
|
$duplicated = \Tainacan\Repositories\Items::get_instance()->fetch( (int) $created_item['id'] );
|
||||||
|
|
||||||
|
// item does not validate because required meta is empty and stays draft
|
||||||
|
$this->assertEquals('draft', $duplicated->get_status());
|
||||||
|
$this->assertEquals('draft', $created_item['status']);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
?>
|
?>
|
Loading…
Reference in New Issue