Merge branch 'develop' into feature/gutenberg-blocks

This commit is contained in:
weryques 2018-10-09 14:11:23 -03:00
commit e619fc3fa1
63 changed files with 3446 additions and 2002 deletions

View File

@ -1,70 +0,0 @@
# Collections
## Collection Repository
<!-- BEGIN DOC-COMMENT H3 src/classes/repositories/class-tainacan-collections.php -->
### `protected function __construct()`
Collections constructor.
### `public function get_map()`
{@inheritDoc}
### `public function get_cpt_labels()`
Get the labels for the custom post type of this repository
**Returns:** `array` — Labels in the format expected by register_post_type()
### `public function insert( $collection )`
{@inheritDoc}
**Parameters:**
* `$collection` — \Tainacan\Entities\Collection
**Returns:** \Tainacan\Entities\Collection
### `public function delete( $args )`
**Parameters:**
* `(` — $args — is a array like [post_id, [is_permanently => bool]] )
**Returns:** mixed|Collection
### `public function fetch( $args = [], $output = null )`
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
**Parameters:**
* `$args` — array — WP_Query args || int $args the collection id
* `$output` — string — The desired output format (@see \Tainacan\Repositories\Repository::fetch_output() for possible values)
**Returns:** `\WP_Query|Array` — an instance of wp query OR array of entities;
### `public function map_meta_cap( $caps, $cap, $user_id, $args )`
Filter to handle special permissions
<!-- END DOC-COMMENT -->
## Collection Entity
<!-- BEGIN DOC-COMMENT H3 src/classes/entities/class-tainacan-collection.php -->
### `function validate()`
Validate Collection
**Returns:** bool
<!-- END DOC-COMMENT -->

View File

@ -1,516 +0,0 @@
# Metadata
## Metadatum Repository
<!-- BEGIN DOC-COMMENT H3 src/classes/repositories/class-tainacan-metadata.php -->
### `class Metadata extends Repository`
Class Metadata
### `protected function __construct()`
Register specific hooks for metadatum repository
### `public function get_cpt_labels()`
Get the labels for the custom post type of this repository
**Returns:** `array` — Labels in the format expected by register_post_type()
### `public function get_default_metadata_attribute()`
constant used in default metadatum in attribute collection_id
**Returns:** `string` — the value of constant
### `public function register_field_type( $class_name )`
register metadatum types class on array of types
**Parameters:**
* `string` — $class_name — | object The class name or the instance
### `public function unregister_field_type( $class_name )`
register metadatum types class on array of types
**Parameters:**
* `string` — $class_name — | object The class name or the instance
### `public function fetch( $args, $output = null )`
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
**Parameters:**
* `$args` — array — WP_Query args || int $args the metadatum id
* `$output` — string — The desired output format (@see \Tainacan\Repositories\Repository::fetch_output() for possible values)
**Returns:** `Entities\Metadatum|\WP_Query|Array` — an instance of wp query OR array of entities;
### `public function fetch_by_collection(Entities\Collection $collection, $args = [], $output = null)`
fetch metadatum by collection, searches all metadatum available
**Parameters:**
* `$collection` — Entities\Collection
* `$args` — array — WP_Query args plus disabled_fields
* `$output` — string — The desired output format (@see \Tainacan\Repositories\Repository::fetch_output() for possible values)
**Returns:** `array` — Entities\Metadatum
### `public function order_result( $result, Entities\Collection $collection, $include_disabled = false )`
Ordinate the result from fetch response if $collection has an ordination, metadata not ordinated appear on the end of the list
**Parameters:**
* `Response` — $result — from method fetch
* `$collection` — Entities\Collection
* `$include_disabled` — bool — Wether to include disabled metadata in the results or not
**Returns:** `array` — or WP_Query ordinate
### `public function insert($metadatum)`
{@inheritDoc}
**Parameters:**
* `$metadatum` — \Tainacan\Entities\Metadatum
**Returns:** \Tainacan\Entities\Metadatum
### `public function update($object, $new_values = null)`
**Returns:** mixed|string|Entities\Entity
### `public function fetch_field_types( $output = 'CLASS')`
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
**Parameters:**
* `string` — $output — CLASS | NAME
**Returns:** `array` — of Entities\Metadatum_Types\Metadatum_Type classes path name
### `public function register_core_fields( Entities\Collection $collection )`
**Parameters:**
* `$collection` — Entities\Collection
**Returns:** bool
### `public function disable_delete_core_fields( $before, $post )`
block user from remove core metadata
**Parameters:**
* `wordpress` — $before — pass a null value
* `the` — $post — post which is moving to trash
**Returns:** null/bool
### `public function force_delete_core_fields( $before, $post, $force_delete )`
block user from remove core metadata ( if use wp_delete_post)
**Parameters:**
* `wordpress` — $before — pass a null value
* `the` — $post — post which is deleting
* `a` — $force_delete — boolean that force the deleting
**Returns:** `null` — /bool
### `public function get_core_fields( Entities\Collection $collection )`
returns all core items from a specific collection
**Parameters:**
* `$collection` — Entities\Collection
**Returns:** Array|\WP_Query
### `public function insert_array_field( $data )`
create a metadatum entity and insert by an associative array ( attribute => value )
**Parameters:**
* `$data` — Array — the array of attributes to insert a metadatum
**Returns:** `int` — the metadatum id inserted
### `public function fetch_all_field_values($collection_id, $metadatum_id)`
Fetch all values of a metadatum from a collection in all it collection items
**Returns:** array|null|object
### `private function pre_update_taxonomy_field($metadatum)`
Stores the value of the taxonomy_id option to use on update_taxonomy_field method.
### `private function update_taxonomy_field($metadatum)`
Triggers hooks when saving a Taxonomy Metadatum, indicating wich taxonomy was added or removed from a collection.
This is used by Taxonomies repository to update the collections_ids property of the taxonomy as a metadatum type taxonomy is inserted or removed
**Parameters:**
* `$metadatum` — [type] — [description]
**Returns:** `[type]` — [description]
<!-- END DOC-COMMENT -->
## Metadatum Entity
<!-- BEGIN DOC-COMMENT H3 src/classes/entities/class-tainacan-metadatum.php -->
### `class Metadatum extends Entity`
Represents the Entity Metadatum
### `protected $repository = 'Metadata'`
{@inheritDoc}
### `function get_name()`
Return the metadatum name
**Returns:** string
### `function get_slug()`
Get metadatum slug
**Returns:** string
### `function get_order()`
Return the metadatum order type
**Returns:** string
### `function get_parent()`
Return the parent ID
**Returns:** string
### `function get_description()`
Return the metadatum description
**Returns:** string
### `function get_required()`
Return if is a required metadatum
**Returns:** boolean
### `function get_multiple()`
Return if is a multiple metadatum
**Returns:** boolean
### `function get_cardinality()`
Return the cardinality
**Returns:** string
### `function get_collection_key()`
Return if metadatum is key
**Returns:** boolean
### `function get_mask()`
Return the mask
**Returns:** string
### `function get_default_value()`
Return the metadatum default value
**Returns:** `string` — || integer
### `function get_field_type_object()`
Return the an object child of \Tainacan\Metadatum_Types\Metadatum_Type with options
**Returns:** `\Tainacan\Metadatum_Types\Metadatum_Type` — The metadatum type class with filled options
### `function get_field_type()`
Return the class name for the metadatum type
**Returns:** `string` — The
### `function get_field_type_options()`
Return the actual options for the current metadatum type
**Returns:** `array` — Configurations for the metadatum type object
### `function set_name($value)`
Set the metadatum name
**Parameters:**
* `$value` — [string]
**Returns:** void
### `function get_accept_suggestion()`
Return true if this metadatum allow community suggestions, false otherwise
**Returns:** bool
### `function set_slug($value)`
Set the metadatum slug
If you dont set the metadatum slug, it will be set automatically based on the name and following WordPress default behavior of creating slugs for posts.
If you set the slug for an existing one, WordPress will append a number at the end of in order to make it unique (e.g slug-1, slug-2)
**Parameters:**
* `$value` — [string]
**Returns:** void
### `function set_order($value)`
Set manually the order of the metadatum
**Parameters:**
* `$value` — [string]
**Returns:** void
### `function set_parent($value)`
Set the metadatum parent ID
**Parameters:**
* `$value` — [integer] — The ID from parent
**Returns:** void
### `function set_description($value)`
Set metadatum description
**Parameters:**
* `$value` — [string] — The text description
**Returns:** void
### `function set_required( $value )`
Allow the metadatum be required
**Parameters:**
* `$value` — [boolean]
**Returns:** void
### `function set_multiple( $value )`
Allow multiple metadata
**Parameters:**
* `$value` — [boolean]
**Returns:** void
### `function set_cardinality( $value )`
The number of possible metadata
**Parameters:**
* `$value` — [string]
**Returns:** void
### `function set_collection_key( $value )`
Define if the value is key on the collection
**Parameters:**
* `$value` — [string]
**Returns:** void
### `function set_mask( $value )`
Set mask for the metadatum
**Parameters:**
* `$value` — [string]
**Returns:** void
### `function set_privacy( $value )`
Set privacy
**Parameters:**
* `$value` — [string]
**Returns:** void
### `function set_default_value( $value )`
Set default value
**Parameters:**
* `||` — [string — integer] $value
**Returns:** void
### `public function set_field_type( $value )`
set the metadatum type class name
**Parameters:**
* `|` — string — \Tainacan\Metadatum_Types\Metadatum_Type $value The name of the class or the instance
### `function set_accept_suggestion( $value )`
Set if this metadatum allow community suggestions
**Parameters:**
* `$value` — bool
### `function set_field_type_options( $value )`
Set Metadatum type options
**Parameters:**
* `||` — [string — integer] $value
**Returns:** void
### `public function get_enabled_for_collection()`
Transient property used to store the status of the metadatum for a particular collection
Used by the API to tell front end when a metadatum is disabled
### `function is_multiple()`
Return true if is multiple, else return false
**Returns:** boolean
### `function is_collection_key()`
Return true if is collection key, else return false
**Returns:** boolean
### `function is_required()`
Return true if is required, else return false
**Returns:** boolean
### `public function validate()`
{@inheritdoc }
Also validates the metadatum, calling the validate_options callback of the Metadatum Type
**Returns:** `bool` — valid or not
<!-- END DOC-COMMENT -->

View File

@ -1,239 +0,0 @@
# Filters
## Filter Repository
<!-- BEGIN DOC-COMMENT H3 src/classes/repositories/class-tainacan-filters.php -->
### `public function delete($args)`
**Parameters:**
* `$args` — array
**Returns:** Entities\Filter
### `public function fetch($args = [], $output = null)`
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
**Parameters:**
* `$args` — array — WP_Query args || int $args the filter id
* `$output` — string — The desired output format (@see \Tainacan\Repositories\Repository::fetch_output() for possible values)
**Returns:** `\WP_Query|Array` — an instance of wp query OR array of entities;
### `public function register_filter_type( $class_name )`
register metadatum types class on array of types
**Parameters:**
* `string` — $class_name — | object The class name or the instance
### `public function deregister_filter_type( $class_name )`
register metadatum types class on array of types
**Parameters:**
* `string` — $class_name — | object The class name or the instance
### `public function fetch_filter_types( $output = 'CLASS')`
fetch all registered filter type classes
Possible outputs are: CLASS (default) - returns the Class name of of filter types registered NAME - return an Array of the names of filter types registered
**Parameters:**
* `string` — $output — CLASS | NAME
**Returns:** `array` — of Entities\Filter_Types\Filter_Type classes path name
### `public function fetch_supported_filter_types($types)`
fetch only supported filters for the type specified
**Parameters:**
* `string` — ( — || array ) $types Primitve types of metadatum ( float, string, int)
**Returns:** `array` — Filters supported by the primitive types passed in $types
### `public function fetch_by_collection(Entities\Collection $collection, $args = [], $output = null)`
fetch filters by collection, searches all filters available
**Parameters:**
* `$collection` — Entities\Collection
* `$args` — array — WP_Query args plus disabled_fields
* `$output` — string — The desired output format (@see \Tainacan\Repositories\Repository::fetch_output() for possible values)
**Returns:** `Array` — Entities\Metadatum
### `public function order_result( $result, Entities\Collection $collection )`
Ordinate the result from fetch response if $collection has an ordination, filters not ordinated appear on the end of the list
**Parameters:**
* `Response` — $result — from method fetch
* `$collection` — Entities\Collection
**Returns:** `array` — or WP_Query ordinate
<!-- END DOC-COMMENT -->
## Filter Entity
<!-- BEGIN DOC-COMMENT H3 src/classes/entities/class-tainacan-filter.php -->
### `class Filter extends Entity`
Represents the entity Filter
### `protected $repository = 'Filters'`
{@inheritDoc}
### `public function _toArray()`
**Returns:** array
### `function get_name()`
Return the filter name
**Returns:** string
### `function get_description()`
**Returns:** mixed|null
### `function get_order()`
Return the filter order type
**Returns:** string
### `function get_color()`
Return the filter color
**Returns:** string
### `function get_field()`
Return the metadatum
**Returns:** Metadatum
### `function get_filter_type_object()`
Return the an object child of \Tainacan\Filter_Types\Filter_Type with options
**Returns:** `\Tainacan\Filter_Types\Filter_Type` — The filter type class with filled options
### `function get_filter_type()`
Return the class name for the filter type
**Returns:** `string` — The
### `function get_filter_options()`
Return the actual options for the current filter type
**Returns:** `array` — Configurations for the filter type object
### `function set_name($value)`
Define the filter name
**Parameters:**
* `$value` — [string]
**Returns:** void
### `function set_order($value)`
Define the filter order type
**Parameters:**
* `$value` — [string]
**Returns:** void
### `function set_description($value)`
Define the filter description
**Parameters:**
* `$value` — [string]
**Returns:** void
### `function set_color( $value )`
Define the filter color
**Parameters:**
* `$value` — [string]
**Returns:** void
### `function set_field( $value )`
Define the filter metadatum
**Returns:** void
### `public function set_filter_type($value)`
Save the filter type class name
**Parameters:**
* `|` — string — \Tainacan\Filter_Types\Filter_Type $value The name of the class or the instance
### `public function validate()`
{@inheritdoc }
Also validates the metadatum, calling the validate_options callback of the Metadatum Type
**Returns:** `bool` — valid or not
<!-- END DOC-COMMENT -->

View File

@ -1,216 +0,0 @@
# Item Metadata
## Item Metadata Repository
<!-- BEGIN DOC-COMMENT H3 src/classes/repositories/class-tainacan-item-metadata.php -->
### `if ( $item_metadata->get_field()->get_parent() > 0 && is_null($item_metadata->get_meta_id()) )`
When we are adding a metadatum that is child of another, this means it is inside a compound metadatum
In that case, if the Item_Metadata object is not set with a meta_id, it means we want to create a new one and not update an existing. This is the case of a multiple compound metadatum.
### `public function delete($item_metadata)`
**Returns:** mixed|void
### `public function add_compound_value(Entities\Item_Metadata_Entity $item_metadata, $meta_id)`
**Returns:** `null|ind` — the meta id of the created compound metadata
### `public function fetch($object, $output = null )`
Fetch Item Metadatum objects related to an Item
**Parameters:**
* `$object` — Entities\Item
**Returns:** array
### `public function get_value(Entities\Item_Metadata_Entity $item_metadata)`
Get the value for a Item metadatum.
**Parameters:**
* `$item_metadata` — Entities\Item_Metadata_Entity
**Returns:** mixed
### `private function extract_compound_value(array $ids, Entities\Item $item, $compund_meta_id)`
Transforms the array saved as meta_value with the IDs of post_meta saved as a value for compound metadata and converts it into an array of Item Metadatada Entitites
**Parameters:**
* `$ids` — array — The array of post_meta ids
* `$item` — Entities\Item — The item this post_meta is related to
* `$compund_meta_id` — int — the meta_id of the parent compound metadata
**Returns:** `array` — An array of Item_Metadata_Entity objects
### `public function update( $object, $new_values = null )`
**Returns:** mixed
### `public function suggest($item_metadata)`
Suggest a value to be inserted as a item Metadatum value, return a pending log
**Parameters:**
* `$item_metadata` — Entities\Item_Metadata_Entity
**Returns:** Entities\Log
<!-- END DOC-COMMENT -->
## Item Metadata Entity
<!-- BEGIN DOC-COMMENT H3 src/classes/entities/class-tainacan-item-metadata-entity.php -->
### `class Item_Metadata_Entity extends Entity`
Represents the Item Metadatum Entity
### `protected $repository = 'Item_Metadata'`
{@inheritDoc}
### `function __construct(Item $item, Metadatum $metadatum, $meta_id = null, $parent_meta_id = null)`
**Parameters:**
* `$item` — Item — Item Entity
* `$metadatum` — Metadatum — Metadatum Entity
* `$meta_id` — int — ID for a specific meta row
### `function set_item(Item $item)`
Define the item
**Parameters:**
* `$item` — Item
**Returns:** void
### `function set_value($value)`
Define the metadatum value
**Parameters:**
* `|` — [integer — string] $value
**Returns:** void
### `function set_field(Metadatum $metadatum)`
Define the metadatum
**Parameters:**
* `$metadatum` — Metadatum
**Returns:** void
### `function set_meta_id($meta_id)`
Set the specific meta ID for this metadata.
When this value is set, get_value() will use it to fetch the value from the post_meta table, instead of considering the item and metadatum IDs
**Parameters:**
* `$meta_id` — int — the ID of a specifica post_meta row
### `function set_parent_meta_id($parent_meta_id)`
Set parent_meta_id. Used when a item_metadata is inside a compound Metadatum
When you have a multiple compound metadatum, this indicates of which instace of the value this item_metadata is attached to
**Parameters:**
* `$parent_meta_id` — [type] — [description]
### `function get_item()`
Return the item
**Returns:** Item
### `function get_field()`
Return the metadatum
**Returns:** Metadatum
### `function get_meta_id()`
Return the meta_id
**Returns:** Metadatum
### `function get_parent_meta_id()`
Return the meta_id
**Returns:** Metadatum
### `function get_value()`
Return the metadatum value
**Returns:** `string` — | integer
### `function is_multiple()`
Return true if metadatum is multiple, else return false
**Returns:** boolean
### `function is_collection_key()`
Return true if metadatum is key
**Returns:** boolean
### `function is_required()`
Return true if metadatum is required
**Returns:** boolean
### `function validate()`
Validate attributes
**Returns:** boolean
<!-- END DOC-COMMENT -->

View File

@ -1,263 +0,0 @@
# Items
## Items Repository
<!-- BEGIN DOC-COMMENT H3 src/classes/repositories/class-tainacan-items.php -->
### `public function get_cpt_labels()`
Get generic labels for the custom post types created for each collection
**Returns:** `array` — Labels in the format expected by register_post_type()
### `public function register_post_type()`
Register each Item post_type {@inheritDoc}
### `public function fetch( $args = [], $collections = [], $output = null )`
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
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
**Parameters:**
* `$args` — array — WP_Query args || int $args the item id
* `$collections` — array — Array Entities\Collection || Array int collections IDs || int collection id || Entities\Collection collection object
* `$output` — string — The desired output format (@see \Tainacan\Repositories\Repository::fetch_output() for possible values)
**Returns:** `\WP_Query|Array` — an instance of wp query OR array of entities;
### `if ( ! isset( $args['post_status'] ) )`
If no specific status is defined in the query, WordPress will fetch public items and private items for users withe the correct permission.
If a collection is private, it must have the same behavior, despite its items are public or not.
### `public function fetch_ids( $args = [], $collections = [] )`
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
**Parameters:**
* `$args` — array — WP_Query args || int $args the item id
* `$collections` — array — Array Entities\Collection || Array int collections IDs || int collection id || Entities\Collection collection object
**Returns:** `Array` — array of IDs;
### `public function delete( $args )`
**Parameters:**
* `(` — $args — is a array like [post_id, [is_permanently => bool]] )
**Returns:** mixed|Entities\Item
### `public function title_in_posts_where( $where, $wp_query )`
allow wp query filter post by array of titles
**Returns:** string
### `public function content_in_posts_where( $where, $wp_query )`
allow wp query filter post by array of content
**Returns:** string
<!-- END DOC-COMMENT -->
## Item Entity
<!-- BEGIN DOC-COMMENT H3 src/classes/entities/class-tainacan-item.php -->
### `class Item extends Entity`
Represents the Entity Item
### `protected $repository = 'Items'`
{@inheritDoc}
### `function __construct( $which = 0 )`
{@inheritDoc}
### `function set_terms( $value )`
### `function get_terms()`
**Returns:** mixed|null
### `function get_attachments()`
**Returns:** array
### `function get_author_name()`
**Returns:** string
### `function get_featured_image()`
**Returns:** false|string
### `function set_featured_img_id( $id )`
### `function get_featured_img_id()`
**Returns:** int|string
### `function get_modification_date()`
**Returns:** mixed|null
### `function get_creation_date()`
**Returns:** mixed|null
### `function get_author_id()`
**Returns:** mixed|null
### `function get_url()`
**Returns:** mixed|null
### `function get_id()`
Return the item ID
**Returns:** integer
### `function get_title()`
Return the item title
**Returns:** string
### `function get_order()`
Return the item order type
**Returns:** string
### `function get_parent()`
Return the parent ID
**Returns:** integer
### `function get_description()`
Return the item description
**Returns:** string
### `public function get_db_identifier()`
{@inheritDoc}
### `public function get_capabilities()`
Use especial Item capabilities {@inheritDoc}
### `function set_title( $value )`
Define the title
**Parameters:**
* `$value` — [string]
**Returns:** void
### `function set_order( $value )`
Define the order type
**Parameters:**
* `$value` — [string]
**Returns:** void
### `function set_parent( $value )`
Define the parent ID
**Parameters:**
* `$value` — [integer]
**Returns:** void
### `function set_description( $value )`
Define the description
**Parameters:**
* `$value` — [string]
**Returns:** void
### `function get_fields()`
Return a List of ItemMetadata objects
It will return all metadata associeated with the collection this item is part of.
If the item already has a value for any of the metadata, it will be available.
**Returns:** `array` — Array of ItemMetadata objects
### `protected function set_cap()`
set meta cap object
### `function validate()`
{@inheritDoc}
### `public function validate_core_fields()`
{@inheritDoc}
<!-- END DOC-COMMENT -->

View File

@ -1,237 +0,0 @@
# Logs
## Logs Repository
<!-- BEGIN DOC-COMMENT H3 src/classes/repositories/class-tainacan-logs.php -->
### `class Logs extends Repository`
Implement a Logs system
### `public function register_post_type()`
{@inheritDoc}
### `public function fetch($args = [], $output = null)`
fetch logs based on ID or WP_Query args
Logs 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
**Parameters:**
* `$args` — array — WP_Query args || int $args the log id
* `$output` — string — The desired output format (@see \Tainacan\Repositories\Repository::fetch_output() for possible values)
**Returns:** `\WP_Query|Array` — an instance of wp query OR array of entities;
### `public function insert_log($new_value, $value = null)`
Insert a log when a new entity is inserted
**Parameters:**
* `$new_value` — Entity
* `$value` — Entity
**Returns:** `Entities\Log` — new created log
### `public function approve($log)`
**Parameters:**
* `$log` — Entities\Log
**Returns:** `Entities\Entity|boolean` — return insert/update valeu or false
### `$value = $log->get_value()`
<!-- END DOC-COMMENT -->
## Log Entity
<!-- BEGIN DOC-COMMENT H3 src/classes/entities/class-tainacan-log.php -->
### `class Log extends Entity`
Represents entity Log
### `protected $repository = 'Logs'`
{@inheritDoc}
### `function get_title()`
Return the Log title
**Returns:** string
### `function get_order()`
Return the log order type
**Returns:** string
### `function get_parent()`
Retun the parent ID
**Returns:** integer
### `function get_description()`
Return the Log description
**Returns:** string
### `function get_blog_id()`
Return the ID of blog
**Returns:** integer
### `function get_user_id()`
Return User Id of who make the action
**Returns:** `int` — User Id of logged action
### `public function get_value()`
Get value of log entry
**Parameters:**
* `$value` — mixed
**Returns:** void
### `public function get_old_value()`
Get old value of log entry object
**Parameters:**
* `$value` — mixed
**Returns:** void
### `function set_title($value)`
Set log tittle
**Parameters:**
* `$value` — string
**Returns:** void
### `function set_order($value)`
Define the order type
**Parameters:**
* `$value` — [string]
**Returns:** void
### `function set_parent($value)`
Define the parent ID
**Parameters:**
* `$value` — [integer]
**Returns:** void
### `function set_description($value)`
Define the Log description
**Parameters:**
* `$value` — [string]
**Returns:** void
### `protected function set_user_id($value = 0)`
Define the user ID of log entry
**Parameters:**
* `$value` — [integer]
**Returns:** void
### `protected function set_blog_id($value = 0)`
Define the blog ID of log entry
**Parameters:**
* `$value` — [integer]
**Returns:** void
### `protected function set_value($value = null)`
Define the value of log entry
**Parameters:**
* `$value` — [mixed]
**Returns:** void
### `protected function set_old_value($value = null)`
Set old value of log entry
**Parameters:**
* `$value` — [mixed]
**Returns:** void
### `public static function create($msn = false, $desc = '', $new_value = null, $old_value = null, $status = 'publish')`
**Parameters:**
* `$msn` — boolean|string
* `$desc` — string
* `$new_value` — mixed
* `$old_value` — mixed
* `$status` — string — 'publish', 'private' or 'pending'
**Returns:** \Tainacan\Entities\Log
### `public function approve()`
{@inheritDoc}
<!-- END DOC-COMMENT -->

View File

@ -1,143 +0,0 @@
# Taxonomies
## Taxonomies Repository
<!-- BEGIN DOC-COMMENT H3 src/classes/repositories/class-tainacan-taxonomies.php -->
### `class Taxonomies extends Repository`
Class Tainacan_Taxonomies
### `public function get_cpt_labels()`
Get the labels for the custom post type of this repository
**Returns:** `array` — Labels in the format expected by register_post_type()
### `public function insert($taxonomy)`
**Parameters:**
* `$taxonomy` — Entities\Taxonomy
**Returns:** Entities\Entity
### `public function fetch( $args = [], $output = null )`
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
**Parameters:**
* `$args` — array — WP_Query args | int $args the taxonomy id
* `$output` — string — The desired output format (@see \Tainacan\Repositories\Repository::fetch_output() for possible values)
**Returns:** `\WP_Query|Array` — an instance of wp query OR array of entities;
<!-- END DOC-COMMENT -->
## Taxonomy Entity
<!-- BEGIN DOC-COMMENT H3 src/classes/entities/class-tainacan-taxonomy.php -->
### `class Taxonomy extends Entity`
Represents the Entity Taxonomy
### `protected static $post_type = 'tainacan-taxonomy'`
{@inheritDoc}
### `protected static $capability_type = ['tainacan-taxonomy', 'tainacan-taxonomies']`
{@inheritDoc}
### `protected $repository = 'Taxonomies'`
{@inheritDoc}
### `function register_taxonomy()`
Register the taxonomy
**Returns:** bool
### `function get_name()`
Return the name
**Returns:** string
### `function get_description()`
Return the description
**Returns:** string
### `function get_allow_insert()`
Return true if allow insert or false if not allow insert
**Returns:** boolean
### `function get_slug()`
Return the slug
**Returns:** string
### `function get_db_identifier()`
Return the DB ID
**Returns:** bool|string
### `function set_name($value)`
Define the name of taxonomy
**Parameters:**
* `$value` — [string]
### `function set_slug($value)`
Define the slug
**Parameters:**
* `$value` — [string]
### `function set_description($value)`
Define the description
**Parameters:**
* `$value` — [string]
### `function set_allow_insert($value)`
Define if allow insert or not
**Parameters:**
* `$value` — [boolean]
### `function validate()`
Validate Taxonomy
**Returns:** bool
<!-- END DOC-COMMENT -->

View File

@ -1,142 +0,0 @@
# Terms
## Terms Repository
<!-- BEGIN DOC-COMMENT H3 src/classes/repositories/class-tainacan-terms.php -->
### `class Terms extends Repository`
Class Tainacan_Terms
### `public function insert($term)`
**Parameters:**
* `$term` — Entities\Entity
**Returns:** Entities\Entity|Entities\Term
### `public function fetch( $args = [], $taxonomies = [])`
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
**Parameters:**
* `$args` — array — WP_Query args || int $args the term id
* `$taxonomies` — array — Array Entities\Taxonomy || Array int terms IDs || int collection id || Entities\Taxonomy taxonomy object
**Returns:** `array` — of Entities\Term objects || Entities\Term
<!-- END DOC-COMMENT -->
## Term Entity
<!-- BEGIN DOC-COMMENT H3 src/classes/entities/class-tainacan-term.php -->
### `class Term extends Entity`
Represents the Entity Term
### `protected $repository = 'Terms'`
{@inheritDoc}
### `function __construct($which = 0, $taxonomy = false )`
Term constructor.
**Parameters:**
* `$which` — int
* `$taxonomy` — string
### `function get_id()`
Return the unique identifier
**Returns:** integer
### `function get_name()`
Return the name
**Returns:** string
### `function get_parent()`
Return the parent ID
**Returns:** integer
### `function get_description()`
Return the description
**Returns:** string
### `function get_user()`
Return the user ID
**Returns:** integer
### `function get_taxonomy()`
Return the taxonomy
**Returns:** integer
### `function set_name($value)`
Define the name
**Parameters:**
* `$value` — [string]
### `function set_parent($value)`
Define the parent ID
**Parameters:**
* `$value` — [integer]
### `function set_description($value)`
Define the description
**Parameters:**
* `$value` — [string]
### `function set_user($value)`
Define the user associated
**Parameters:**
* `$value` — [integer]
### `function set_taxonomy($value)`
Define the taxonomy associated
**Parameters:**
* `$value` — [integer]
<!-- END DOC-COMMENT -->

View File

@ -4,16 +4,6 @@ This page shows how the internal API works and how to create and fetch all kinds
Its important that you first get familiar with the [key concepts](key-concepts.md) of tainacan.
This page gives an overview of the API. Detailed documentation and reference on each entity can be found below:
* [Collections Reference](class-reference-collections.md)
* [Items Reference](class-reference-items.md)
* [Item Metadata Reference](class-reference-item-metadata.md)
* [Metadata Reference](class-reference-metadata.md)
* [Filters Reference](class-reference-filters.md)
* [Taxonomies Reference](class-reference-taxonomies.md)
* [Terms Reference](class-reference-terms.md)
* [Logs Reference](class-reference-logs.md)
## Overview

View File

@ -17,6 +17,7 @@
"t": "^0.5.1",
"v-tooltip": "^2.0.0-rc.33",
"vue": "^2.5.17",
"vue-awesome-swiper": "^3.1.3",
"vue-masonry-css": "^1.0.3",
"vue-router": "^3.0.1",
"vue-the-mask": "^0.11.1",

View File

@ -3,7 +3,7 @@
/**
* @see \Tainacan\Admin_Hooks->register()
*/
function register_admin_hook( $context, $callback, $position = 'end-left' ) {
function tainacan_register_admin_hook( $context, $callback, $position = 'end-left' ) {
$admin_hooks = \Tainacan\Admin_Hooks::get_instance();
return $admin_hooks->register( $context, $callback, $position );
}

View File

@ -2,7 +2,8 @@
<div
class="repository-level-page page-container">
<tainacan-title />
<form
<form
@click="formErrorMessage = ''"
class="tainacan-form"
label-width="120px"
v-if="importer != undefined && importer != null">
@ -116,11 +117,13 @@
type="button"
@click="cancelBack">{{ $i18n.get('cancel') }}</button>
</div>
<span class="help is-danger">{{ formErrorMessage }}</span>
<div
v-if="!importer.manual_mapping"
class="control">
<button
:disabled="
(formErrorMessage != undefined && formErrorMessage != '') ||
sessionId == undefined ||
importer == undefined ||
(importer.manual_collection && collectionId == undefined) ||
@ -137,6 +140,7 @@
class="control">
<button
:disabled="
(formErrorMessage != undefined && formErrorMessage != '') ||
sessionId == undefined ||
importer == undefined ||
(importer.manual_collection && collectionId == undefined) ||
@ -169,9 +173,8 @@ export default {
isLoadingRun: false,
isLoadingUpload: false,
isFetchingCollections: false,
form: {
},
form: {},
formErrorMessage: '',
mappedCollection: {
'id': Number,
'mapping': {},
@ -254,7 +257,8 @@ export default {
resolve();
})
.catch((errors) => {
this.$console.log(errors);
this.formErrorMessage = errors.error_message;
this.$console.error(errors);
reject(errors);
});
});
@ -267,7 +271,8 @@ export default {
resolve();
})
.catch((errors) => {
this.$console.log(errors);
this.formErrorMessage = errors.error_message;
this.$console.error(errors);
reject(errors);
});
});
@ -289,6 +294,7 @@ export default {
resolve();
})
.catch((errors) => {
this.formErrorMessage = errors.error_message;
this.$console.log(errors);
reject(errors);
});
@ -304,7 +310,7 @@ export default {
if (this.importer.accepts.file && !this.importer.accepts.url) {
this.onUploadFile()
.then(() => { this.isLoadingUpload = false; resolve(); })
.catch((errors) => { this.isLoadingUpload = false; this.$console.log(errors) });
.catch((errors) => { this.isLoadingUpload = false; this.$console.error(errors) });
} else if (!this.importer.accepts.file && this.importer.accepts.url) {
this.onInputURL()
.then(() => { this.isLoadingUpload = false; resolve() })

View File

@ -188,20 +188,20 @@
</b-field>
<b-field
:type="formErrors['unique'] != undefined ? 'is-danger' : ''"
:message="formErrors['unique'] != undefined ? formErrors['unique'] : ''">
:type="formErrors['collection_key'] != undefined ? 'is-danger' : ''"
:message="formErrors['collection_key'] != undefined ? formErrors['collection_key'] : ''">
<b-checkbox
size="is-small"
@input="clearErrors('unique')"
v-model="editForm.unique"
@input="clearErrors('collection_key')"
v-model="editForm.collection_key"
true-value="yes"
false-value="no"
class="is-inline-block"
name="collecion_key">
{{ $i18n.get('label_unique_value') }}
<help-button
:title="$i18n.getHelperTitle('metadata', 'unique')"
:message="$i18n.getHelperMessage('metadata', 'unique')"/>
:title="$i18n.getHelperTitle('metadata', 'collection_key')"
:message="$i18n.getHelperMessage('metadata', 'collection_key')"/>
</b-checkbox>
</b-field>
</b-field>

View File

@ -68,7 +68,7 @@
:active.sync="isLoading"/>
</div>
<!-- GRID VIEW MODE -->
<!-- GRID (THUMBNAILS) VIEW MODE -->
<div
class="tainacan-grid-container"
v-if="viewMode == 'grid'">

View File

@ -62,7 +62,9 @@
</a>
</span>
</div>
<transition-group name="filter-item">
<transition-group
class="children-area"
name="filter-item">
<div
class="term-item"
:style="{
@ -279,6 +281,7 @@ export default {
}
}
.children-icon {
color: $blue2;
position: absolute;

View File

@ -1,8 +1,8 @@
<template>
<div class="tainacan-modal-content">
<header class="tainacan-modal-title">
<h2 v-if="isFilter">{{ this.$i18n.get('filter') }} <em>{{ filter.name }}</em></h2>
<h2 v-else>{{ this.$i18n.get('metadatum') }} <em>{{ metadatum.name }}</em></h2>
<h2 v-if="isFilter">{{ $i18n.get('filter') }} <em>{{ filter.name }}</em></h2>
<h2 v-else>{{ $i18n.get('metadatum') }} <em>{{ metadatum.name }}</em></h2>
<hr>
</header>
<div class="tainacan-form">
@ -22,7 +22,6 @@
<b-tabs
v-if="!isSearching"
size="is-small"
expanded
animated
@input="fetchSelectedLabels()"
v-model="activeTab">
@ -147,13 +146,14 @@
grouped
group-multiline>
<div
v-for="(term, index) in (selected instanceof Array ? selected : [this.selected])"
v-for="(term, index) in (selected instanceof Array ? selected : [selected])"
:key="index"
class="control">
<b-tag
v-if="selected instanceof Array ? true : selected != ''"
attached
closable
@close="selected.splice(index, 1)">
@close="selected instanceof Array ? selected.splice(index, 1) : selected = ''">
{{ isTaxonomy ? selectedTagsName[term] : term }}
</b-tag>
</div>

View File

@ -0,0 +1,59 @@
<template>
<div class="circular-counter html">
<svg
width="48"
height="48"
xmlns="http://www.w3.org/2000/svg">
<g>
<circle
id="circle"
class="circle-animation"
:style="{ 'animation-duration': time + 's' }"
r="16"
cy="28"
cx="28"
stroke-width="3"
stroke="white"
fill="none"/>
</g>
</svg>
</div>
</template>
<script>
export default {
name: 'CircularCounter',
props: {
time: Number
}
}
</script>
<style lang="scss" scoped>
.circular-counter {
position: relative;
float: left;
}
svg {
-webkit-transform: rotate(-90deg);
transform: rotate(-90deg);
}
@keyframes loading-counter {
from {
stroke-dashoffset: -100;
}
to {
stroke-dashoffset: 0;
}
}
.circle-animation {
stroke-dasharray: 100;
animation-name: loading-counter;
animation-iteration-count: infinite;
}
</style>

View File

@ -0,0 +1,93 @@
<template>
<form action="">
<div
class="tainacan-modal-content"
style="width: auto">
<header class="tainacan-modal-title">
<h2>{{ this.$i18n.get('collections') }}</h2>
<hr>
</header>
<section class="tainacan-form">
<p>{{ $i18n.get('instruction_select_a_target_collection') }}</p>
<div
v-if="!isLoading"
class="collection-types-container">
<div
class="collection-type"
v-for="(collection, index) in collections"
:key="index"
@click="onSelectCollection(collection)">
<h4>{{ collection.name }}</h4>
<p>{{ collection.length > 200 ? (collection.description.substring(0,197) + '...') : collection.description }}</p>
</div>
</div>
<b-loading
:active.sync="isLoading"
:can-cancel="false"/>
</section>
</div>
</form>
</template>
<script>
import { mapActions } from 'vuex';
export default {
name: 'CollectionsModal',
data(){
return {
collections: [],
isLoading: false
}
},
methods: {
...mapActions('collection', [
'fetchCollections'
]),
onSelectCollection(collection) {
this.$router.push(this.$routerHelper.getNewItemPath(collection.id));
this.$parent.close();
}
},
mounted() {
this.isLoading = true;
this.fetchCollections({ page: 1, collectionsPerPage: 96 })
.then((res) => {
this.collections = res.collections;
this.isLoading = false;
}).catch((error) => {
this.$console.log(error);
this.isLoading = false;
});
}
}
</script>
<style lang="scss" scoped>
@import "../../scss/_variables.scss";
.collection-types-container {
.collection-type {
border-bottom: 1px solid $gray2;
padding: 15px 8.3333333%;
cursor: pointer;
&:first-child {
margin-top: 15px;
}
&:last-child {
border-bottom: none;
}
&:hover {
background-color: $gray2;
}
}
}
</style>

View File

@ -35,6 +35,7 @@ import ViewModeTable from '../../theme-helper/view-mode-table.vue';
import ViewModeCards from '../../theme-helper/view-mode-cards.vue';
import ViewModeRecords from '../../theme-helper/view-mode-records.vue';
import ViewModeMasonry from '../../theme-helper/view-mode-masonry.vue';
import ViewModeSlideshow from '../../theme-helper/view-mode-slideshow.vue';
// Remaining imports
import HelpButton from '../components/other/help-button.vue';
@ -89,6 +90,7 @@ Vue.component('view-mode-table', ViewModeTable);
Vue.component('view-mode-cards', ViewModeCards);
Vue.component('view-mode-records', ViewModeRecords);
Vue.component('view-mode-masonry', ViewModeMasonry);
Vue.component('view-mode-slideshow', ViewModeSlideshow);
Vue.use(eventBusSearch, { store: store, router: routerTheme});

View File

@ -1,7 +1,10 @@
<template>
<div
v-hammer:swipe="onSwipeFiltersMenu"
:class="{'repository-level-page': isRepositoryLevel}">
:class="{
'repository-level-page': isRepositoryLevel,
'is-fullscreen': registeredViewModes[viewMode] != undefined && registeredViewModes[viewMode].full_screen
}">
<!-- SEARCH AND FILTERS --------------------- -->
<!-- Filter menu compress button -->
@ -11,7 +14,7 @@
autoHide: false,
placement: 'auto-start'
}"
v-if="!openAdvancedSearch"
v-if="!openAdvancedSearch && !(registeredViewModes[viewMode] != undefined && registeredViewModes[viewMode].full_screen)"
class="is-hidden-mobile"
id="filter-menu-compress-button"
:style="{ top: !isOnTheme ? (isRepositoryLevel ? '172px' : '120px') : '76px' }"
@ -20,7 +23,7 @@
</button>
<!-- Filters mobile modal button -->
<button
v-if="!openAdvancedSearch"
v-if="!openAdvancedSearch && !(registeredViewModes[viewMode] != undefined && registeredViewModes[viewMode].full_screen)"
class="is-hidden-tablet"
id="filter-menu-compress-button"
:style="{ top: !isOnTheme ? (isRepositoryLevel ? (searchControlHeight + 100) : (searchControlHeight + 70) + 'px') : (searchControlHeight - 25) + 'px' }"
@ -33,7 +36,9 @@
<!-- <transition name="filters-menu"> -->
<aside
:style="{ top: searchControlHeight + 'px' }"
v-if="!isFiltersMenuCompressed && !openAdvancedSearch"
v-if="!isFiltersMenuCompressed &&
!openAdvancedSearch &&
!(registeredViewModes[viewMode] != undefined && registeredViewModes[viewMode].full_screen)"
class="filters-menu tainacan-form is-hidden-mobile">
<b-loading
:is-full-page="false"
@ -111,18 +116,20 @@
<div
id="items-list-area"
class="items-list-area"
:class="{ 'spaced-to-right': !isFiltersMenuCompressed && !openAdvancedSearch }">
:class="{ 'spaced-to-right': !isFiltersMenuCompressed && !openAdvancedSearch && !(registeredViewModes[viewMode] != undefined && registeredViewModes[viewMode].full_screen)}">
<!-- FILTERS TAG LIST-->
<filters-tags-list
class="filter-tags-list"
:filters="filters"
v-if="hasFiltered && !openAdvancedSearch">Teste</filters-tags-list>
v-if="hasFiltered &&
!openAdvancedSearch &&
!(registeredViewModes[viewMode] != undefined && registeredViewModes[viewMode].full_screen)" />
<!-- SEARCH CONTROL ------------------------- -->
<div
ref="search-control"
v-if="!openAdvancedSearch"
v-if="!openAdvancedSearch && !(registeredViewModes[viewMode] != undefined && registeredViewModes[viewMode].full_screen)"
class="search-control">
<b-loading
:is-full-page="false"
@ -141,7 +148,7 @@
<b-icon icon="menu-down"/>
</button>
<b-dropdown-item>
<b-dropdown-item v-if="!isRepositoryLevel">
<router-link
id="a-create-item"
tag="div"
@ -149,6 +156,14 @@
{{ $i18n.get('add_one_item') }}
</router-link>
</b-dropdown-item>
<b-dropdown-item v-if="isRepositoryLevel">
<div
id="a-create-item"
tag="div"
@click="onOpenCollectionsModal">
{{ $i18n.get('add_one_item') }}
</div>
</b-dropdown-item>
<b-dropdown-item disabled>
{{ $i18n.get('add_items_bulk') + ' (Not ready)' }}
</b-dropdown-item>
@ -294,7 +309,7 @@
v-for="(viewModeOption, index) of enabledViewModes"
:key="index"
:value="viewModeOption"
v-if="registeredViewModes[viewModeOption] != undefined">
v-if="registeredViewModes[viewModeOption] != undefined && registeredViewModes[viewModeOption].full_screen == false">
<span
class="gray-icon"
v-html="registeredViewModes[viewModeOption].icon"/>
@ -373,6 +388,24 @@
</b-field>
</div>
<!-- Theme Full Screen mode, it's just a special view mode -->
<div
v-if="isOnTheme"
class="search-control-item">
<button
class="button is-white"
@click="onChangeViewMode(viewModeOption)"
v-for="(viewModeOption, index) of enabledViewModes"
:key="index"
:value="viewModeOption"
v-if="registeredViewModes[viewModeOption] != undefined && registeredViewModes[viewModeOption].full_screen == true ">
<span
class="gray-icon"
v-html="registeredViewModes[viewModeOption].icon"/>
<span class="is-hidden-touch">{{ registeredViewModes[viewModeOption].label }}</span>
</button>
</div>
<!-- Text simple search (used on mobile, instead of the one from filter list)-->
<div class="is-hidden-tablet search-control-item">
<div class="search-area">
@ -465,7 +498,8 @@
<div class="above-search-control">
<div
v-show="isLoadingItems"
v-show="isLoadingItems &&
!(registeredViewModes[viewMode] != undefined && registeredViewModes[viewMode].full_screen)"
class="loading-container">
<b-loading
:is-full-page="false"
@ -547,9 +581,9 @@
<component
v-else-if="isOnTheme &&
!isLoadingItems &&
registeredViewModes[viewMode] != undefined &&
registeredViewModes[viewMode].type == 'component' &&
(!isLoadingItems || !registeredViewModes[viewMode].show_pagination) &&
!openAdvancedSearch"
:collection-id="collectionId"
:displayed-metadata="displayedMetadata"
@ -661,6 +695,7 @@
import Pagination from '../../components/search/pagination.vue'
import AdvancedSearch from '../../components/advanced-search/advanced-search.vue';
import AvailableImportersModal from '../../components/other/available-importers-modal.vue';
import CollectionsModal from '../../components/other/collections-modal.vue';
import { mapActions, mapGetters } from 'vuex';
export default {
@ -774,14 +809,21 @@
'getAdminViewMode'
]),
onSwipeFiltersMenu($event) {
let screenWidth = (window.innerWidth || document.documentElement.clientWidth || document.body.clientWidth);
if (this.registeredViewModes[this.viewMode] == undefined ||
(this.registeredViewModes[this.viewMode] != undefined &&
(this.registeredViewModes[this.viewMode].full_screen == false ||
this.registeredViewModes[this.viewMode].full_screen == undefined)
)
) {
let screenWidth = (window.innerWidth || document.documentElement.clientWidth || document.body.clientWidth);
if ($event.offsetDirection == 4 && screenWidth <= 768) {
if (!this.isFilterModalActive)
this.isFilterModalActive = true;
} else if ($event.offsetDirection == 2 && screenWidth <= 768) {
if (this.isFilterModalActive)
this.isFilterModalActive = false;
if ($event.offsetDirection == 4 && screenWidth <= 768) {
if (!this.isFilterModalActive)
this.isFilterModalActive = true;
} else if ($event.offsetDirection == 2 && screenWidth <= 768) {
if (this.isFilterModalActive)
this.isFilterModalActive = false;
}
}
},
onOpenImportersModal() {
@ -795,6 +837,13 @@
}
});
},
onOpenCollectionsModal() {
this.$modal.open({
parent: this,
component: CollectionsModal,
hasModalCard: true
});
},
updateSearch() {
this.$eventBusSearch.setSearchQuery(this.futureSearchQuery);
},
@ -812,9 +861,18 @@
this.prepareMetadata();
this.$eventBusSearch.setViewMode(viewMode);
// For view modes such as slides, we force pagination to request only 12 per page
let existingViewModeIndex = Object.keys(this.registeredViewModes).findIndex(aViewMode => aViewMode == viewMode);
if (existingViewModeIndex >= 0) {
if (!this.registeredViewModes[Object.keys(this.registeredViewModes)[existingViewModeIndex]].show_pagination) {
this.$eventBusSearch.setItemsPerPage(12);
}
}
// Updates searchControlHeight before in case we need to adjust filters position on mobile
setTimeout(() => {
this.searchControlHeight = this.$refs['search-control'].clientHeight;
if (this.$refs['search-control'] != undefined)
this.searchControlHeight = this.$refs['search-control'].clientHeight;
}, 500);
},
onChangeAdminViewMode(adminViewMode) {
@ -824,7 +882,8 @@
// Updates searchControlHeight before in case we need to adjust filters position on mobile
setTimeout(() => {
this.searchControlHeight = this.$refs['search-control'].clientHeight;
if (this.$refs['search-control'] != undefined)
this.searchControlHeight = this.$refs['search-control'].clientHeight;
}, 500);
},
onChangeDisplayedMetadata() {
@ -1077,7 +1136,8 @@
},
adjustSearchControlHeight() {
this.$nextTick(() => {
this.searchControlHeight = this.$refs['search-control'] ? this.$refs['search-control'].clientHeight + this.$refs['search-control'].offsetTop : 0;
if (this.$refs['search-control'] != undefined)
this.searchControlHeight = this.$refs['search-control'] ? this.$refs['search-control'].clientHeight + this.$refs['search-control'].offsetTop : 0;
this.isFiltersMenuCompressed = jQuery(window).width() <= 768;
});
}
@ -1142,6 +1202,15 @@
else
this.$eventBusSearch.setInitialViewMode(this.defaultViewMode);
}
// For view modes such as slides, we force pagination to request only 12 per page
let existingViewModeIndex = Object.keys(this.registeredViewModes).findIndex(viewMode => viewMode == this.$userPrefs.get(prefsViewMode));
if (existingViewModeIndex >= 0) {
if (!this.registeredViewModes[Object.keys(this.registeredViewModes)[existingViewModeIndex]].show_pagination) {
this.$eventBusSearch.setItemsPerPage(12);
}
}
} else {
let prefsAdminViewMode = !this.isRepositoryLevel ? 'admin_view_mode_' + this.collectionId : 'admin_view_mode';
if (this.$userPrefs.get(prefsAdminViewMode) == undefined)
@ -1174,6 +1243,31 @@
@import '../../scss/_variables.scss';
@keyframes open-full-screen {
from {
opacity: 0;
transform: scale(0.6);
}
to {
opacity: 1;
transform: scale(1.0);
}
}
.is-fullscreen {
position: absolute;
top: 0;
bottom: 0;
left: 0;
right: 0;
width: 100vw;
height: 100vh;
z-index: 999999999;
background-color: black;
transition: background-color 0.3s ease, width 0.3s ease, height 0.3s ease;
animation: open-full-screen 0.4s ease;
}
.collapse-all {
display: inline-flex;
align-items: center;

View File

@ -1,7 +1,10 @@
<template>
<div
v-hammer:swipe="onSwipeFiltersMenu"
:class="{'repository-level-page': isRepositoryLevel}">
:class="{
'repository-level-page': isRepositoryLevel,
'is-fullscreen': registeredViewModes[viewMode] != undefined && registeredViewModes[viewMode].full_screen
}">
<!-- SEARCH AND FILTERS --------------------- -->
<!-- Filter menu compress button -->
@ -11,7 +14,7 @@
autoHide: false,
placement: 'auto-start'
}"
v-if="!openAdvancedSearch"
v-if="!openAdvancedSearch && !(registeredViewModes[viewMode] != undefined && registeredViewModes[viewMode].full_screen)"
class="is-hidden-mobile"
id="filter-menu-compress-button"
:style="{ top: !isOnTheme ? (isRepositoryLevel ? '172px' : '120px') : '76px' }"
@ -20,7 +23,7 @@
</button>
<!-- Filters mobile modal button -->
<button
v-if="!openAdvancedSearch"
v-if="!openAdvancedSearch && !(registeredViewModes[viewMode] != undefined && registeredViewModes[viewMode].full_screen)"
class="is-hidden-tablet"
id="filter-menu-compress-button"
:style="{ top: !isOnTheme ? (isRepositoryLevel ? (searchControlHeight + 100) : (searchControlHeight + 70) + 'px') : (searchControlHeight - 25) + 'px' }"
@ -32,7 +35,9 @@
<!-- Side bar with search and filters -->
<aside
:style="{ top: searchControlHeight + 'px' }"
v-show="!isFiltersMenuCompressed && !openAdvancedSearch"
v-show="!isFiltersMenuCompressed &&
!openAdvancedSearch &&
!(registeredViewModes[viewMode] != undefined && registeredViewModes[viewMode].full_screen)"
class="filters-menu tainacan-form is-hidden-mobile">
<b-loading
:is-full-page="false"
@ -110,18 +115,20 @@
<div
id="items-list-area"
class="items-list-area"
:class="{ 'spaced-to-right': !isFiltersMenuCompressed && !openAdvancedSearch }">
:class="{ 'spaced-to-right': !isFiltersMenuCompressed && !openAdvancedSearch && !(registeredViewModes[viewMode] != undefined && registeredViewModes[viewMode].full_screen)}">
<!-- FILTERS TAG LIST-->
<filters-tags-list
class="filter-tags-list"
:filters="filters"
v-if="hasFiltered && !openAdvancedSearch">Teste</filters-tags-list>
v-if="hasFiltered &&
!openAdvancedSearch &&
!(registeredViewModes[viewMode] != undefined && registeredViewModes[viewMode].full_screen)" />
<!-- SEARCH CONTROL ------------------------- -->
<div
ref="search-control"
v-if="!openAdvancedSearch"
v-if="!openAdvancedSearch && !(registeredViewModes[viewMode] != undefined && registeredViewModes[viewMode].full_screen)"
class="search-control">
<b-loading
:is-full-page="false"
@ -295,7 +302,7 @@
v-for="(viewModeOption, index) of enabledViewModes"
:key="index"
:value="viewModeOption"
v-if="registeredViewModes[viewModeOption] != undefined">
v-if="registeredViewModes[viewModeOption] != undefined && registeredViewModes[viewModeOption].full_screen == false">
<span
class="gray-icon"
v-html="registeredViewModes[viewModeOption].icon"/>
@ -374,6 +381,24 @@
</b-field>
</div>
<!-- Theme Full Screen mode, it's just a special view mode -->
<div
v-if="isOnTheme"
class="search-control-item">
<button
class="button is-white"
@click="onChangeViewMode(viewModeOption)"
v-for="(viewModeOption, index) of enabledViewModes"
:key="index"
:value="viewModeOption"
v-if="registeredViewModes[viewModeOption] != undefined && registeredViewModes[viewModeOption].full_screen == true ">
<span
class="gray-icon"
v-html="registeredViewModes[viewModeOption].icon"/>
<span class="is-hidden-touch">{{ registeredViewModes[viewModeOption].label }}</span>
</button>
</div>
<!-- Text simple search (used on mobile, instead of the one from filter list)-->
<div class="is-hidden-tablet search-control-item">
<div class="search-area">
@ -772,14 +797,21 @@
'getAdminViewMode'
]),
onSwipeFiltersMenu($event) {
let screenWidth = (window.innerWidth || document.documentElement.clientWidth || document.body.clientWidth);
if (this.registeredViewModes[this.viewMode] == undefined ||
(this.registeredViewModes[this.viewMode] != undefined &&
(this.registeredViewModes[this.viewMode].full_screen == false ||
this.registeredViewModes[this.viewMode].full_screen == undefined)
)
) {
let screenWidth = (window.innerWidth || document.documentElement.clientWidth || document.body.clientWidth);
if ($event.offsetDirection == 4 && screenWidth <= 768) {
if (!this.isFilterModalActive)
this.isFilterModalActive = true;
} else if ($event.offsetDirection == 2 && screenWidth <= 768) {
if (this.isFilterModalActive)
this.isFilterModalActive = false;
if ($event.offsetDirection == 4 && screenWidth <= 768) {
if (!this.isFilterModalActive)
this.isFilterModalActive = true;
} else if ($event.offsetDirection == 2 && screenWidth <= 768) {
if (this.isFilterModalActive)
this.isFilterModalActive = false;
}
}
},
onOpenImportersModal() {
@ -810,6 +842,14 @@
this.prepareMetadata();
this.$eventBusSearch.setViewMode(viewMode);
// For view modes such as slides, we force pagination to request only 12 per page
let existingViewModeIndex = Object.keys(this.registeredViewModes).findIndex(aViewMode => aViewMode == viewMode);
if (existingViewModeIndex >= 0) {
if (!this.registeredViewModes[Object.keys(this.registeredViewModes)[existingViewModeIndex]].show_pagination) {
this.$eventBusSearch.setItemsPerPage(12);
}
}
// Updates searchControlHeight before in case we need to adjust filters position on mobile
setTimeout(() => {
this.searchControlHeight = this.$refs['search-control'].clientHeight;
@ -1139,6 +1179,15 @@
else
this.$eventBusSearch.setInitialViewMode(this.defaultViewMode);
}
// For view modes such as slides, we force pagination to request only 12 per page
let existingViewModeIndex = Object.keys(this.registeredViewModes).findIndex(viewMode => viewMode == this.$userPrefs.get(prefsViewMode));
if (existingViewModeIndex >= 0) {
if (!this.registeredViewModes[Object.keys(this.registeredViewModes)[existingViewModeIndex]].show_pagination) {
this.$eventBusSearch.setItemsPerPage(12);
}
}
} else {
let prefsAdminViewMode = !this.isRepositoryLevel ? 'admin_view_mode_' + this.collectionId : 'admin_view_mode';
if (this.$userPrefs.get(prefsAdminViewMode) == undefined)
@ -1171,6 +1220,20 @@
@import '../../scss/_variables.scss';
.is-fullscreen {
position: absolute;
top: 0;
bottom: 0;
left: 0;
right: 0;
width: 100vw;
height: 100vh;
z-index: 999999999;
background-color: black;
transition: all 0.3s ease;
}
.collapse-all {
display: inline-flex;
align-items: center;

View File

@ -306,7 +306,7 @@
</template>
<script>
import {mapActions, mapGetters} from 'vuex'
import {mapActions, mapGetters} from 'vuex';
import FileItem from '../../components/other/file-item.vue';
import DocumentItem from '../../components/other/document-item.vue';
import { formHooks } from '../../js/mixins';
@ -528,9 +528,9 @@
width: 100%;
}
}
.collapse .collapse-content {
margin-left: 30px;
}
.collapse .collapse-content {
margin-left: 30px;
}
}
.field {

View File

@ -69,8 +69,11 @@
color: $secondary;
border: none;
}
.pagination-link:active {
box-shadow: none;
}
.pagination-link.is-current {
color: $gray4;
color: $gray4 !important;
}
.pagination-link::after:not(:last-child) {
content: ',';

View File

@ -404,4 +404,87 @@ $modal-z: 9999999;
animation-name: filters-menu-out;
animation-duration: 0.2s;
animation-timing-function: ease;
}
// Slide
@keyframes slide-left-in {
from {
opacity: 0;
-ms-transform: translate(-5%, 0%) scale(0.95); /* IE 9 */
-webkit-transform: translate(-5%, 0%) scale(0.95); /* Safari */
transform: translate(-5%, 0%) scale(0.95);
}
to {
opacity: 1;
-ms-transform: translate(0, 0) scale(1); /* IE 9 */
-webkit-transform: translate(0, 0) scale(1); /* Safari */
transform: translate(0, 0) scale(1);
}
}
@keyframes slide-left-out {
from {
opacity: 1;
-ms-transform: translate(0, 0) scale(1); /* IE 9 */
-webkit-transform: translate(0, 0) scale(1); /* Safari */
transform: translate(0, 0) scale(1);
}
to {
opacity: 0;
-ms-transform: translate(10%, 0%) scale(0.95); /* IE 9 */
-webkit-transform: translate(10%, 0%) scale(0.95); /* Safari */
transform: translate(10%, 0%) scale(0.95);
}
}
.slide-left-enter-active {
animation-name: slide-left-in;
animation-duration: 0.4s;
animation-timing-function: ease;
}
.slide-left-leave-active {
animation-name: slide-left-out;
animation-duration: 0.4s;
animation-timing-function: ease;
}
@keyframes slide-right-in {
from {
opacity: 0;
-ms-transform: translate(10%, 0%) scale(0.95); /* IE 9 */
-webkit-transform: translate(10%, 0%) scale(0.95); /* Safari */
transform: translate(10%, 0%) scale(0.95);
}
to {
opacity: 1;
-ms-transform: translate(0, 0) scale(1); /* IE 9 */
-webkit-transform: translate(0, 0) scale(1); /* Safari */
transform: translate(0, 0) scale(1);
}
}
@keyframes slide-right-out {
from {
opacity: 1;
-ms-transform: translate(0, 0) scale(1); /* IE 9 */
-webkit-transform: translate(0, 0) scale(1); /* Safari */
transform: translate(0, 0) scale(1);
}
to {
opacity: 0;
-ms-transform: translate(-5%, 0%) scale(0.95); /* IE 9 */
-webkit-transform: translate(-5%, 0%) scale(0.95); /* Safari */
transform: translate(-5%, 0%) scale(0.95);
}
}
.slide-right-enter-active {
animation-name: slide-right-in;
animation-duration: 0.3s;
animation-timing-function: ease;
}
.slide-right-leave-active {
animation-name: slide-right-out;
animation-duration: 0.3s;
animation-timing-function: ease;
}

View File

@ -1,14 +1,19 @@
.tainacan-cards-container {
min-height: 50vh;
padding: 0;
display: flex;
flex-wrap: wrap;
flex-grow: 1;
flex-shrink: 1;
display: -ms-grid;
display: grid;
grid-template-columns: repeat(auto-fill, 455px);
grid-gap: 0px;
justify-content: space-evenly;
animation-name: item-appear;
animation-duration: 0.5s;
@media screen and (max-width: 480px) {
width: 91.666666667%;
grid-template-columns: repeat(auto-fill, 100%);
}
.selected-card {
background-color: $turquoise1;
.metadata-title {
@ -105,6 +110,7 @@
.media {
width: 100%;
display: flex;
margin: 0 !important;
.list-metadata {
padding: 0.75rem 1.375rem;

View File

@ -1,10 +1,10 @@
.tainacan-grid-container {
min-height: 50vh;
padding: 0;
display: flex;
flex-wrap: wrap;
flex-grow: 1;
flex-shrink: 1;
display: -ms-grid;
display: grid;
grid-template-columns: repeat(auto-fill, 285px);
grid-gap: 0px;
justify-content: space-evenly;
animation-name: item-appear;
animation-duration: 0.5s;
@ -18,7 +18,6 @@
flex-basis: 0;
margin: 15px;
text-align: center;
height: 100%;
&:hover {
background-color: $gray2;

View File

@ -0,0 +1,466 @@
#close-fullscren-button {
border-radius: 50px;
color: white;
background-color: transparent;
border: none;
position: absolute;
top: 0;
right: 0;
padding: 1rem;
cursor: pointer;
z-index: 9999999999;
&:focus, &:active {
outline: none;
}
}
.metadata-menu {
background-color: black;
position: absolute;
z-index: 10;
width: 20.83% !important;
min-width: 180px;
min-height: 100%;
height: 100%;
padding: $page-side-padding 25px $page-side-padding $page-side-padding;
float: left;
overflow-y: auto;
overflow-x: hidden;
visibility: visible;
display: block;
transition: visibility ease 0.5s, display ease 0.5s;
@media screen and (max-width: 768px) {
width: 100% !important;
padding: $page-small-side-padding !important;
h3 {
margin-top: 0 !important;
}
}
@media screen and (min-width: 769px) {
top: 0px !important;
}
.metadata-menu-header {
display: flex;
flex-wrap: wrap;
h2 {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
margin-right: auto;
color: white;
font-size: 1.25rem;
font-weight: bold;
margin-bottom: 0;
}
#close-metadata-button {
border-radius: 50px;
color: white;
background-color: transparent;
border: none;
padding: 0;
cursor: pointer;
z-index: 9999999999;
&:focus, &:active {
outline: none;
}
}
hr {
margin-top: 0;
width: 100%;
background-color: $turquoise5;
}
}
h3 {
font-size: 100%;
margin-top: 0px;
}
.collapse-all {
font-size: 12px;
display: inline-flex;
align-items: center;
margin-bottom: 1rem;
.icon {
vertical-align: bottom;
}
}
// Overrides bootstrap behavior
.collapse:not(.show) {
display: initial;
}
.field {
margin-bottom: 0px !important;
border-bottom: 1px solid $gray5;
padding: 1rem 0px 0.2rem 0px;
.label {
cursor: pointer;
font-size: 14px;
font-weight: 500;
margin-bottom: 0.75rem !important;
display: inline-flex;
align-items: center;
span {
margin-right: 5px;
margin-left: -5px;
}
}
.content {
font-size: 0.75rem;
}
}
.checkbox {
margin-bottom: 5px;
align-items: baseline;
}
}
#metadata-compress-button {
position: absolute;
z-index: 99;
top: 123px;
left: 0;
max-width: 33px;
height: 26px;
width: 33px;
border: none;
background-color: $gray5;
color: white;
padding: 0;
border-top-right-radius: 2px;
border-bottom-right-radius: 2px;
cursor: pointer;
transition: top 0.3s, left 0.3s, opacity 0.3s, visibility 0.3s;
&:focus, &:active {
outline: none;
}
.icon {
margin-top: 1px;
}
@media screen and (max-width: 768px) {
max-width: 100%;
width: auto;
padding: 3px 6px 3px 0px;
height: 24px;
.icon {
position: relative;
top: -3px;
}
.text {
position: relative;
top: -6px;
}
}
}
.fullscreen-spaced-to-right {
.table-wrapper {
margin-left: 20.83%;
width: calc(100vw - 20.83%) !important;
};
.tainacan-slide-main-view .arrow-left {
left: 20.83% !important;
}
.tainacan-slide-main-view .slide-main-content {
width: calc(100vw - 20.83%) !important;
}
@media screen and (max-width: 768px) {
.table-wrapper {
margin-left: 0;
width: 100vw !important;
};
.tainacan-slide-main-view .arrow-left {
left: 0 !important;
margin-left: 0;
}
.tainacan-slide-main-view .slide-main-content {
width: 100vw !important;
}
}
}
.tainacan-slide-main-view {
display: flex;
justify-content: space-between;
align-items: center;
min-height: calc(100vh - 142px);
transition: height 0.3s, min-height 0.3s;
.slide-main-content {
position: relative;
width: 100vw;
display: flex;
align-items: center;
justify-content: center;
&>div,
&>div audio,
&>div img,
&>div video,
&>div.tainacan-embed-container {
width: 100%;
max-width: 100vh;
text-align: center;
color: white;
max-height: calc(100vh - 115px);
overflow: hidden;
transition: height 0.3s, max-height 0.3s;
}
&>div img {
width: auto;
}
.empty-document {
font-size: 4rem;
font-weight: normal;
color: $gray4;
text-align: center;
position: relative;
max-height: calc(100vh - 115px);
overflow: hidden;
transition: height 0.3s, max-height 0.3s;
margin: auto;
img {
height: 100%;
}
p {
position: absolute;
margin: calc(50% - 4rem) auto;
width: 100%;
}
@media screen and (max-width: 1024px) and (min-width: 768px) {
font-size: 3rem;
p {
margin: calc(50% - 1rem) auto;
}
}
@media screen and (max-width: 768px) {
font-size: 2rem;
p {
margin: calc(50% - 2rem) auto;
}
}
}
}
}
.slide-title-area {
width: 100%;
position: absolute;
bottom: 115px;
// top: -85px;
// margin-bottom: -85px;
background-color: rgba(0,0,0,0.5);
padding: 1rem $page-side-padding;
display: flex;
justify-content: space-between;
align-items: center;
transition: opacity 0.3s, visibility 0.3s, display 0.3s;
h1 {
color: white;
font-size: 2rem;
font-weight: normal;
text-overflow: ellipsis;
white-space: nowrap;
overflow: hidden;
}
.play-button {
cursor: pointer;
border: 0;
background: transparent;
position: relative;
.icon {
border-radius: 100px;
background: white;
border: 3px solid $turquoise5;
}
&:focus, &:active {
outline: 0;
border: 0;
}
.circular-counter {
position: absolute;
right: calc(4.16667% + 1px);
top: -3px;
}
}
}
.slide-control-arrow {
cursor: pointer;
background-color: transparent;
border: none !important;
height: 3.5rem;
width: 3.5rem;
text-align: center;
vertical-align: middle;
position: absolute;
z-index: 9;
&:focus, &:active {
border: none;
outline: none;
}
.icon {
height: 3.5rem;
width: 2.5rem;
text-align: center;
.mdi::before {
line-height: 5rem;
font-size: 5rem;
color: $turquoise5;
text-shadow: 0px 0px 2px $gray5;
}
}
&.arrow-left {
left: 0;
margin-left: $page-side-padding;
transition: margin-left 0.3s;
}
&.arrow-right {
right: 0;
margin-right: $page-side-padding;
transition: margin-right 0.3s;
}
&.slide-group-arrow {
top: calc(50% - 33px);
.icon {
height: 2.5rem;
width: 1.5rem;
.mdi::before {
line-height: 3rem;
font-size: 3rem;
color: white;
text-shadow: 0px 0px 2px $gray5;
}
}
}
}
.tainacan-slides-list {
background-color: rgba(0,0,0,0.5);
position: relative;
transition: opacity 0.3s, visibility 0.3s, display 0.3s;
height: 115px;
min-height: 115px;
padding: 0.75rem 0;
.loading-icon {
width: 100%;
top: calc(50% - 33px);
position: absolute;
z-index: 9;
}
#tainacan-slide-container {
padding: 0 $page-side-padding;
display: flex;
flex-wrap: nowrap;
overflow: hidden;
flex-grow: 1;
flex-shrink: 1;
justify-content: space-evenly;
animation-name: item-appear;
animation-duration: 0.5s;
.selected-record {
background-color: $turquoise1;
.metadata-title {
background-color: $turquoise2;
}
.media {
background-color: $turquoise1;
}
}
.tainacan-slide-item {
cursor: pointer;
&.active-item img {
border-bottom: 4px solid $turquoise5;
}
&:hover img{
border-bottom: 4px solid $gray2;
}
img {
color: white;
width: 100%;//85px;
height: auto;
border-radius: 0px;
padding: 0 0 6px 0;
border-bottom: 4px solid transparent;
transition: border-bottom 0.4s;
}
}
}
}
.hide-controls {
// @media screen and (max-width: 768px) {
.slide-control-arrow {
visibility: hidden;
opacity: 0;
}
.arrow-right {
margin-right: 0%;
}
.arrow-left {
margin-left: 0%;
}
#metadata-compress-button {
visibility: hidden;
opacity: 0;
left: -20px;
}
.slide-title-area,
.tainacan-slides-list {
visibility: hidden;
opacity: 0;
}
.tainacan-slide-main-view {
min-height: 100vh;
.slide-main-content {
&>div,
&>div audio,
&>div img,
&>div video,
&>div.tainacan-embed-container {
max-height: 95vh;
}
}
}
.empty-document {
max-height: 95vh;
img {
width: auto;
}
}
@media screen and (max-width: 768px) {
.empty-document img { width: 100% !important; }
}
// }
}

View File

@ -32,7 +32,6 @@ body.tainacan-admin-page #adminmenumain, body.tainacan-admin-page #wpfooter, bod
html {
overflow-y: hidden;
}
#tainacan-admin-app {
background: #ffffff;
position: fixed;

View File

@ -306,6 +306,8 @@ return apply_filters( 'tainacan-admin-i18n', [
'label_untrash_selected_items' => __( 'Recover from trash', 'tainacan' ),
'label_value_not_informed' => __( 'Value not informed.', 'tainacan' ),
'label_description_not_informed' => __( 'Description not informed.', 'tainacan' ),
'label_hide_metadata' => __( 'Hide metadata', 'tainacan' ),
'label_show_metadata' => __( 'Show metadata', 'tainacan' ),
'label_all_terms' => __( 'All terms', 'tainacan' ),
'label_selected_terms' => __( 'Selected terms', 'tainacan'),

View File

@ -56,7 +56,7 @@ export default {
@import "../admin/scss/_tooltips.scss";
@import "../admin/scss/_tainacan-form.scss";
@import "../admin/scss/_filters-menu-modal.scss";
.theme-items-list {
position: relative;
display: flex;
@ -183,6 +183,7 @@ export default {
#items-list-area {
width: 100%;
overflow-y: hidden;
overflow-x: hidden;
-webkit-overflow-scrolling: touch;
margin-left: 0;
&.spaced-to-right {

View File

@ -36,6 +36,15 @@ class REST_Bulkedit_Controller extends REST_Controller {
'args' => $this->get_create_params()
),
)
);
register_rest_route($this->namespace, '/collection/(?P<collection_id>[\d]+)/' . $this->rest_base . '/(?P<group_id>[0-9a-f]+)',
array(
array(
'methods' => \WP_REST_Server::READABLE,
'callback' => array($this, 'get_item'),
'permission_callback' => array($this, 'bulk_edit_permissions_check'),
),
)
);
register_rest_route($this->namespace, '/collection/(?P<collection_id>[\d]+)/' . $this->rest_base . '/(?P<group_id>[0-9a-f]+)/add',
array(
@ -158,6 +167,15 @@ class REST_Bulkedit_Controller extends REST_Controller {
],
),
)
);
register_rest_route($this->namespace, '/collection/(?P<collection_id>[\d]+)/' . $this->rest_base . '/(?P<group_id>[0-9a-f]+)/sequence/(?P<sequence_index>[\d]+)',
array(
array(
'methods' => \WP_REST_Server::READABLE,
'callback' => array($this, 'get_item_in_sequence'),
'permission_callback' => array($this, 'bulk_edit_permissions_check'),
),
)
);
}
@ -181,6 +199,9 @@ class REST_Bulkedit_Controller extends REST_Controller {
if (isset($body['items_ids']) && is_array($body['items_ids']) && !empty($body['items_ids'])) {
$args['items_ids'] = $body['items_ids'];
if (isset($body['options'])) {
$args['options'] = $body['options'];
}
} elseif ( isset($body['use_query']) && $body['use_query'] ) {
unset($body['use_query']['paged']);
@ -205,14 +226,10 @@ class REST_Bulkedit_Controller extends REST_Controller {
$bulk = new \Tainacan\Bulk_Edit($args);
$response = [
'id' => $bulk->get_id()
];
$response = $this->prepare_item_for_response($bulk, $request);
$rest_response = new \WP_REST_Response($response, 200);
$rest_response->header('X-WP-Total', $bulk->count_posts());
return $rest_response;
}
@ -264,6 +281,40 @@ class REST_Bulkedit_Controller extends REST_Controller {
}
}
public function get_item($request) {
$group_id = $request['group_id'];
$args = ['id' => $group_id];
$bulk = new \Tainacan\Bulk_Edit($args);
$return = $this->prepare_item_for_response($bulk, $request);
if (0 === $return['items_count']) {
return new \WP_REST_Response([
'error_message' => __('Group not found', 'tainacan'),
], 404);
}
return new \WP_REST_Response($return, 200);
}
function prepare_item_for_response($bulk_object, $request) {
$count = $bulk_object->count_posts();
$options = $bulk_object->get_options();
$return = [
'id' => $bulk_object->get_id(),
'items_count' => $count,
'options' => $options
];
return $return;
}
public function trash_items($request) {
$group_id = $request['group_id'];
@ -377,6 +428,25 @@ class REST_Bulkedit_Controller extends REST_Controller {
}
}
public function get_item_in_sequence($request) {
$group_id = $request['group_id'];
$index = $request['sequence_index'];
$args = ['id' => $group_id];
$bulk = new \Tainacan\Bulk_Edit($args);
$item_id = $bulk->get_item_id_by_index( (int) $index );
if ( !$item_id ) {
return new \WP_REST_Response([
'error_message' => __('Item not found.', 'tainacan'),
], 404);
} else {
return new \WP_REST_Response($item_id, 200);
}
}
/**

View File

@ -0,0 +1,219 @@
<?php
namespace Tainacan\API\EndPoints;
use \Tainacan\API\REST_Controller;
use Tainacan\Repositories;
use Tainacan\Entities;
/**
* Represents the Exporters REST Controller
*
* */
class REST_Exporters_Controller extends REST_Controller {
/**
* REST_Exporters_Controller constructor.
* Define the namespace, rest base and instantiate your attributes.
*/
public function __construct() {
$this->rest_base = 'exporters';
if (session_status() == PHP_SESSION_NONE) {
@session_start(); // @ avoids Warnings when running phpunit tests
}
parent::__construct();
}
/**
* Register the collections route and their endpoints
*/
public function register_routes() {
register_rest_route($this->namespace, '/' . $this->rest_base . '/available', array(
array(
'methods' => \WP_REST_Server::READABLE,
'callback' => array($this, 'get_registered_exporters'),
'permission_callback' => array($this, 'export_permissions_check'),
),
));
register_rest_route($this->namespace, '/' . $this->rest_base . '/session', array(
array(
'methods' => \WP_REST_Server::CREATABLE,
'callback' => array($this, 'create_item'),
'permission_callback' => array($this, 'export_permissions_check'),
'args' => [
'importer_slug' => [
'type' => 'string',
'description' => __( 'The slug of the exporter to be initialized', 'tainacan' ),
]
],
),
));
register_rest_route($this->namespace, '/' . $this->rest_base . '/session/(?P<session_id>[0-9a-f]+)', array(
array(
'methods' => \WP_REST_Server::EDITABLE,
'callback' => array($this, 'update_item'),
'permission_callback' => array($this, 'export_permissions_check'),
'args' => [
'send_email' => [
'type' => 'string',
'description' => __( 'The e-mail to be used by the export to send a message when the process ends', 'tainacan' ),
],
'collection' => [
'type' => 'array/object',
'description' => __( 'The array describing the collection as expected by the exporter', 'tainacan' ),
],
'options' => [
'type' => 'array/object',
'description' => __( 'The importer options', 'tainacan' ),
]
],
),
));
register_rest_route($this->namespace, '/' . $this->rest_base . '/session/(?P<session_id>[0-9a-f]+)/run', array(
array(
'methods' => \WP_REST_Server::CREATABLE,
'callback' => array($this, 'run'),
'permission_callback' => array($this, 'export_permissions_check'),
),
));
}
/**
*
* @param \WP_REST_Request $request
*
* @return bool|\WP_Error
* @throws \Exception
*/
public function export_permissions_check($request) {
return true;
}
public function get_registered_exporters() {
global $Tainacan_Exporter_Handler;
$exporters = $Tainacan_Exporter_Handler->get_registered_exporters();
return new \WP_REST_Response( $exporters, 200 );
}
/**
* Creates a new instance of the desired exporter and returns its ID
*
* @param \WP_REST_Request $request
*
* @return array|\WP_Error|\WP_REST_Response
*/
public function create_item( $request ) {
$body = json_decode($request->get_body(), true);
if(empty($body)) {
return new \WP_REST_Response([
'error_message' => __('Body can not be empty.', 'tainacan'),
], 400);
}
$slug = $body['exporter_slug'];
global $Tainacan_Exporter_Handler;
if ($object = $Tainacan_Exporter_Handler->initialize_exporter($slug)) {
$response = $object->_to_Array();
return new \WP_REST_Response($response, 201);
} else {
return new \WP_REST_Response([
'error_message' => __('Exporter not found', 'tainacan'),
], 400);
}
}
/**
* Update a collection
*
* @param \WP_REST_Request $request
*
* @return string|\WP_Error|\WP_REST_Response
*/
public function update_item( $request ) {
$session_id = $request['session_id'];
$body = json_decode($request->get_body(), true);
if(!empty($body)) {
$attributes = [];
foreach ($body as $att => $value) {
$attributes[$att] = $value;
}
$importer = $_SESSION['tainacan_exporter'][$session_id];
if($importer) {
foreach ($body as $att => $value) {
if ($att == 'collection') {
if (is_array($value) && isset($value['id'])) {
$importer->add_collection($value);
continue;
} else {
return new \WP_REST_Response([
'error_message' => __('Invalid collection', 'tainacan' ),
'session_id' => $session_id
], 400);
}
}
$method = 'set_' . $att;
if (method_exists($importer, $method)) {
$importer->$method($value);
}
}
$response = $importer->_to_Array();
return new \WP_REST_Response( $response, 200 );
}
return new \WP_REST_Response([
'error_message' => __('Importer Session not found', 'tainacan' ),
'session_id' => $session_id
], 400);
}
return new \WP_REST_Response([
'error_message' => __('The body can not be empty', 'tainacan'),
'body' => $body
], 400);
}
/**
* Run a exporter
*
* @param \WP_REST_Request $request
*
* @return string|\WP_Error|\WP_REST_Response
*/
public function run($request) {
$session_id = $request['session_id'];
$exporter = $_SESSION['tainacan_exporter'][$session_id];
if(!$exporter) {
return new \WP_REST_Response([
'error_message' => __('Exporter Session not found', 'tainacan' ),
'session_id' => $session_id
], 400);
}
global $Tainacan_Exporter_Handler;
$process = $Tainacan_Exporter_Handler->add_to_queue($exporter);
if (false === $process) {
return new \WP_REST_Response([
'error_message' => __('Error starting exporter', 'tainacan' ),
'session_id' => $session_id
], 400);
}
$response = [
'bg_process_id' => $process->ID
];
return new \WP_REST_Response( $response, 200 );
}
}
?>

View File

@ -137,7 +137,7 @@ class REST_Facets_Controller extends REST_Controller {
wp_reset_postdata();
}
$this->total_items = $items->found_posts;
$this->total_items = $items->found_posts;
$this->total_pages = ceil($this->total_items / (int) $items->query_vars['posts_per_page']);
}
@ -168,7 +168,6 @@ class REST_Facets_Controller extends REST_Controller {
$term_selected = $this->terms_repository->fetch($term_id, $this->taxonomy);
$realResponse[] = $term_selected;
}
foreach( $terms as $index => $term ){
@ -423,7 +422,6 @@ class REST_Facets_Controller extends REST_Controller {
if( $metaquery['key'] == $metadatum_id ){
return $metaquery['value'];
}
}
@ -447,8 +445,7 @@ class REST_Facets_Controller extends REST_Controller {
foreach( $request['current_query']['metaquery'] as $metaquery ){
if( $metaquery['key'] == $metadatum_id ){
return $metaquery['value'];
return $metaquery['value'];
}
}
@ -482,6 +479,70 @@ class REST_Facets_Controller extends REST_Controller {
return $values;
}
/**
* method responsible to return the total of items for the facet value
*
* @param value string/int the facet value
* @param reference_id int the taxonomy or the metadataid
* @param is_taxonomy (default) false if the value param is a term
* @param query the actual request query to filter the items
*
* @return int total of items found
*/
private function add_items_count( $value, $reference_id, $is_taxonomy = false, $query, $collection_id){
$new_args = $query;
$has_value = false;
if( !$is_taxonomy ){
if( isset( $query['metaquery'] ) ){
foreach( $query['metaquery'] as $index => $metaquery ){
if( $metaquery['key'] == $metadatum_id ){
$has_value = true;
if( is_array($metaquery['value']) )
$new_args['metaquery'][$index]['value'][] = $value;
else
$new_args['metaquery'][$index]['value'] = $value;
}
}
}
if( !$has_value ){
$new_args['metaquery'][] = [
'key' => $reference_id,
'value' => $value
];
}
} else {
if( isset( $query['taxquery'] ) ){
foreach( $query['taxquery'] as $taxquery ){
if( $taxquery['taxonomy'] === 'tnc_tax_' . $reference_id ){
$has_value = true;
$new_args['taxquery'][$index]['terms'][] = $value;
}
}
}
if( !$has_value ){
$new_args['taxquery'][] = [
'taxonomy' => 'tnc_tax_' . $reference_id,
'value' => [$value]
];
}
}
$items = $this->items_repository->fetch($new_args, $collection_id, 'WP_Query');
return $items->found_posts;
}
}
?>

View File

@ -14,6 +14,7 @@ $rest_logs_controller = new \Tainacan\API\EndPoints\REST_Logs_Control
$rest_metadata_types_controller = new \Tainacan\API\EndPoints\REST_Metadata_Types_Controller();
$rest_filter_types_controller = new \Tainacan\API\EndPoints\REST_Filter_Types_Controller();
$rest_importers_controller = new \Tainacan\API\EndPoints\REST_Importers_Controller();
$rest_exporters_controller = new \Tainacan\API\EndPoints\REST_Exporters_Controller();
$rest_background_processes_controller = new \Tainacan\API\EndPoints\REST_Background_Processes_Controller();
$rest_bulkedit_controller = new \Tainacan\API\EndPoints\REST_Bulkedit_Controller();
new \Tainacan\API\EndPoints\REST_Export_Controller();

View File

@ -42,6 +42,7 @@ class Bulk_Edit {
* @type int $collection_id The items collection ID. Required if initializing using a query search
* @type array $query The query paramaters used to fetch items that will be part of this bulk edit group
* @type array $items_ids an array containing the IDs of items that will be part of this bulk edit group
* @type array $options an array containing additional options for this bulk edit group (currently used to store sorting information)
* @type string $id The ID of the Bulk edit group.
*
* }
@ -87,7 +88,10 @@ class Bulk_Edit {
$wpdb->query( "INSERT INTO $wpdb->postmeta (post_id, meta_key, meta_value) {$items_query->request}" );
return;
$bulk_params = [
'orderby' => isset($params['query']['orderby']) ? $params['query']['orderby'] : 'post_date',
'order' => isset($params['query']['order']) ? $params['query']['order'] : 'DESC'
];
} elseif (isset($params['items_ids']) && is_array($params['items_ids'])) {
$items_ids = array_filter($params['items_ids'], 'is_integer');
@ -100,10 +104,23 @@ class Bulk_Edit {
$wpdb->query( "INSERT INTO $wpdb->postmeta (post_id, meta_key, meta_value) VALUES $insert_q" );
return;
$bulk_params = [
'orderby' => isset($params['options']['orderby']) ? $params['options']['orderby'] : 'post_date',
'order' => isset($params['options']['order']) ? $params['options']['order'] : 'DESC'
];
}
/**
* This is stored to be used by the get_sequence_item_by_index() method, which is used
* by the sequence edit routine.
*
* For everything else, the order does not matter...
*/
$this->save_options($bulk_params);
return;
}
/**
@ -126,15 +143,63 @@ class Bulk_Edit {
return $this->id;
}
// return the number of items selected in the current bulk group
/**
* return the number of items selected in the current bulk group
* @return int number of items in the group
*/
public function count_posts() {
global $wpdb;
$id = $this->get_id();
if (!empty($id)) {
return $wpdb->get_var( $wpdb->prepare("SELECT COUNT(post_id) FROM $wpdb->postmeta WHERE meta_key = %s AND meta_value = %s", $this->meta_key, $id) );
return (int) $wpdb->get_var( $wpdb->prepare("SELECT COUNT(post_id) FROM $wpdb->postmeta WHERE meta_key = %s AND meta_value = %s", $this->meta_key, $id) );
}
return 0;
}
/**
* Gets the id of the item in a given position inside the group
*
* @param int $index THe position of the index to search for. From 1 to the length of the group
* @return int|bool Returns the ID of the item or false if the index is out of range
*/
public function get_item_id_by_index($index) {
if (!is_int($index)) {
throw new InvalidArgumentException('get_item_id_by_index function only accepts integers. Input was: '.$index);
}
$options = $this->get_options();
$query = [
'meta_query' => [
[
'key' => $this->meta_key,
'value' => $this->get_id()
]
],
'fields' => 'ids',
'post_type' => \Tainacan\Repositories\Repository::get_collections_db_identifiers(),
'posts_per_page' => 1,
'paged' => $index,
'orderby' => $options['orderby'],
'order' => $options['order']
];
$object = new \WP_Query($query);
if ( $object->have_posts() && isset($object->posts) && is_array($object->posts) && isset($object->posts[0]) && is_integer($object->posts[0]) ) {
return $object->posts[0];
}
return false;
}
public function save_options($value) {
update_option('tainacan_bulk_' . $this->get_id(), $value);
}
public function get_options() {
return get_option('tainacan_bulk_' . $this->get_id());
}
private function _build_select($fields) {
global $wpdb;

View File

@ -647,6 +647,9 @@ class Item extends Entity {
$img = wp_get_attachment_image($this->get_document(), $img_size);
$img_full = wp_get_attachment_url($this->get_document());
$image_attributes = wp_get_attachment_image_src( $this->get_document(), $img_size );
$img = "<img style='max-width: 100%;' src='" . $image_attributes[0] . "' />";
$output .= sprintf("<a href='%s' target='blank'>%s</a>", $img_full, $img);
} else {

View File

@ -10,7 +10,8 @@ defined( 'ABSPATH' ) or die( 'No script kiddies please!' );
* Class TainacanMetadatumType
*/
class Core_Description extends Metadata_Type {
use \Tainacan\Traits\Formatter_Text;
function __construct(){
// call metadatum type constructor
parent::__construct();
@ -78,6 +79,28 @@ class Core_Description extends Metadata_Type {
return true;
}
/**
* Get the value as a HTML string with links and breakline tag.
* @return string
*/
public function get_value_as_html(\Tainacan\Entities\Item_Metadata_Entity $item_metadata) {
$value = $item_metadata->get_value();
$return = '';
if ( $item_metadata->is_multiple() ) {
$total = sizeof($value);
$count = 0;
foreach ( $value as $el ) {
$return .= nl2br($this->make_clickable_links($el));
$count ++;
if ($count <= $total)
$return .= ', ';
}
} else {
$return = nl2br($this->make_clickable_links($value));
}
return $return;
}
}

View File

@ -1,11 +1,11 @@
<template>
<div :class="{ 'is-flex': metadatum.metadatum.multiple != 'yes' }">
<div :class="{ 'is-flex': metadatum.metadatum.multiple != 'yes' || maxtags != undefined }">
<b-taginput
:disabled="disabled"
:id="id"
v-model="selected"
:data="options"
:maxtags="metadatum.metadatum.multiple === 'yes' && allowNew === true ? 100 : 1"
:maxtags="maxtags != undefined ? maxtags : (metadatum.metadatum.multiple == 'yes' || allowNew === true ? 100 : 1)"
autocomplete
attached
:loading="loading"
@ -35,8 +35,9 @@
});
}
if( this.metadatum.metadatum.metadata_type_options
&& this.metadatum.metadatum.metadata_type_options.search.length > 0){
if( this.metadatum.metadatum.metadata_type_options &&
this.metadatum.metadatum.metadata_type_options.search &&
this.metadatum.metadatum.metadata_type_options.search.length > 0){
axios.get('/collection/'+ collectionId +'/metadata?context=edit')
.then( res => {
for (let item of res.data) {
@ -70,6 +71,7 @@
type: Number
},
id: '',
maxtags: undefined,
disabled: false,
allowNew: true,
},
@ -101,14 +103,16 @@
}
if (query !== '') {
let metaquery = this.mountQuery( query );
this.loading = true;
this.options = [];
let metaquery = this.mountQuery( query );
let collectionId = ( this.metadatum && this.metadatum.metadatum.metadata_type_options.collection_id ) ? this.metadatum.metadatum.metadata_type_options.collection_id : this.collection_id;
axios.get('/collection/'+collectionId+'/items?' + qs.stringify( metaquery ))
.then( res => {
this.loading = false;
this.options = [];
this.options = [];
let result = res.data;
for (let item of result) {
this.options.push({ label: item.title, value: item.id })

View File

@ -1,5 +1,30 @@
<template>
<div>
<p
v-if="value instanceof Array ? value.length > 0 : (value != undefined && value != '')"
class="has-text-gray">
{{ $i18n.get('label_selected_terms') + ' :' }}
</p>
<b-field
v-if="value instanceof Array ? value.length > 0 : (value != undefined && value != '')"
grouped
group-multiline
class="selected-tags">
<div
v-for="(term, index) in (value instanceof Array ? value : [value])"
:key="index">
<b-tag
attached
closable
@close="value.splice(index, 1)">
{{ selectedTagsName[term] }}
</b-tag>
</div>
<div
v-if="isSelectedTermsLoading"
class="control has-icons-right is-loading is-clearfix" />
</b-field>
<div
v-for="(option, index) in options"
:key="index">
@ -20,6 +45,7 @@
</template>
<script>
import { tainacan as axios } from '../../../js/axios/axios';
export default {
created(){
@ -29,11 +55,14 @@
data(){
return {
checked: [],
selectedTagsName: {},
isSelectedTermsLoading: false,
}
},
watch: {
value( val ){
this.checked = val;
this.fetchSelectedLabels();
}
},
props: {
@ -42,6 +71,7 @@
},
value: [ Number, String, Array ],
disabled: false,
taxonomyId: Number
},
methods: {
onChecked() {
@ -51,7 +81,61 @@
onInput($event) {
this.inputValue = $event;
this.$emit('input', this.inputValue);
},
fetchSelectedLabels() {
if (this.value != null && this.value != undefined) {
this.isSelectedTermsLoading = true;
let selected = this.value instanceof Array ? this.value : [this.value];
if (this.taxonomyId && selected.length > 0) {
for (const term of selected) {
if(!this.isSelectedTermsLoading){
this.isSelectedTermsLoading = true;
}
axios.get(`/taxonomy/${this.taxonomyId}/terms/${term}`)
.then((res) => {
this.saveSelectedTagName(res.data.id, res.data.name);
this.isSelectedTermsLoading = false;
})
.catch((error) => {
this.$console.log(error);
this.isSelectedTermsLoading = false;
});
}
} else {
this.isSelectedTermsLoading = false;
}
}
},
saveSelectedTagName(value, label){
if(!this.selectedTagsName[value]) {
this.$set(this.selectedTagsName, `${value}`, label);
}
}
},
mounted() {
this.fetchSelectedLabels();
}
}
</script>
</script>
<style>
.selected-tags {
margin-top: 0.75rem;
font-size: 0.75rem;
position: relative;
}
.selected-tags .is-loading {
margin-left: 2rem;
margin-top: -0.4rem;
}
.selected-tags .is-loading::after {
border: 2px solid #898d8f !important;
border-right-color: #dbdbdb !important;
border-top-color: #dbdbdb !important;
}
</style>

View File

@ -1,5 +1,29 @@
<template>
<div>
<p
v-if="value instanceof Array ? value.length > 0 : (value != undefined && value != '')"
class="has-text-gray">
{{ $i18n.get('label_selected_terms') + ' :' }}
</p>
<b-field
v-if="value instanceof Array ? value.length > 0 : (value != undefined && value != '')"
grouped
group-multiline
class="selected-tags">
<div
v-for="(term, index) in (value instanceof Array ? value : [value])"
:key="index">
<b-tag
attached
closable
@close="value = ''">
{{ selectedTagsName[value] }}
</b-tag>
</div>
<div
v-if="isSelectedTermsLoading"
class="control has-icons-right is-loading is-clearfix" />
</b-field>
<b-radio
:disabled="disabled"
:id="id"
@ -29,16 +53,20 @@
</template>
<script>
import { tainacan as axios } from '../../../js/axios/axios';
export default {
data(){
return {
checked: ( this.value ) ? this.value : ''
checked: ( this.value ) ? this.value : '',
selectedTagsName: {},
isSelectedTermsLoading: false,
}
},
watch: {
value( val ){
this.checked = val;
this.fetchSelectedLabels();
}
},
props: {
@ -47,6 +75,7 @@
},
value: [ Number, String, Array ],
disabled: false,
taxonomyId: Number
},
methods: {
onChecked() {
@ -56,7 +85,44 @@
onInput($event) {
this.inputValue = $event;
this.$emit('input', this.inputValue);
},
fetchSelectedLabels() {
if (this.value != null && this.value != undefined) {
this.isSelectedTermsLoading = true;
let selected = this.value instanceof Array ? this.value : [this.value];
if (this.taxonomyId && selected.length > 0) {
for (const term of selected) {
if(!this.isSelectedTermsLoading){
this.isSelectedTermsLoading = true;
}
axios.get(`/taxonomy/${this.taxonomyId}/terms/${term}`)
.then((res) => {
this.saveSelectedTagName(res.data.id, res.data.name);
this.isSelectedTermsLoading = false;
})
.catch((error) => {
this.$console.log(error);
this.isSelectedTermsLoading = false;
});
}
} else {
this.isSelectedTermsLoading = false;
}
}
},
saveSelectedTagName(value, label){
if(!this.selectedTagsName[value]) {
this.$set(this.selectedTagsName, `${value}`, label);
}
}
},
mounted() {
this.fetchSelectedLabels();
}
}
</script>

View File

@ -187,7 +187,7 @@ class Metadata extends Repository {
'type' => __( 'string' ),
'validation' => v::stringType()->in( [ 'yes', 'no', 'never' ] ),
'description' => __( 'Display by default on listing or do not display or never display.', 'tainacan' ),
'default' => 'yes'
'default' => 'no'
],
'semantic_uri' => [
'map' => 'meta',

View File

@ -9,6 +9,7 @@ const TAINACAN_VENDOR_DIR = __DIR__ . '/../vendor/';
const TAINACAN_TAPI_DIR = __DIR__ . '/../api/';
const TAINACAN_ENDPOINTS_DIR = __DIR__ . '/../api/endpoints/';
const TAINACAN_IMPORTER_DIR = __DIR__ . '/../importer/';
const TAINACAN_EXPORTER_DIR = __DIR__ . '/../exporter/';
const TAINACAN_EXPOSERS_DIR = __DIR__ . '/../exposers/';
const DIRS = [
@ -21,6 +22,7 @@ const DIRS = [
TAINACAN_TAPI_DIR,
TAINACAN_ENDPOINTS_DIR,
TAINACAN_IMPORTER_DIR,
TAINACAN_EXPORTER_DIR,
TAINACAN_EXPOSERS_DIR
];
@ -35,62 +37,65 @@ require_once(TAINACAN_IMPORTER_DIR . 'class-tainacan-importer.php');
require_once(TAINACAN_IMPORTER_DIR . 'class-tainacan-importer-handler.php');
require_once(TAINACAN_EXPOSERS_DIR . 'class-tainacan-exposers.php');
require_once(TAINACAN_EXPORTER_DIR . 'class-tainacan-bg-exporter.php');
require_once(TAINACAN_EXPORTER_DIR . 'class-tainacan-export-handler.php');
spl_autoload_register('tainacan_autoload');
function tainacan_autoload($class_name){
$class_path = explode('\\', $class_name);
$class_name = end($class_path);
$class_path = explode('\\', $class_name);
$class_name = end($class_path);
if(count($class_path) == 1 ) {
foreach(DIRS as $dir) {
$file = $dir . 'class-'. strtolower(str_replace('_', '-' , $class_name)) . '.php';
if(count($class_path) == 1 ) {
foreach(DIRS as $dir) {
$file = $dir . 'class-'. strtolower(str_replace('_', '-' , $class_name)) . '.php';
if(file_exists($file)) {
require_once($file);
}
}
}
elseif ($class_path[0] == 'Tainacan') {
$sliced = array_slice($class_path, 1, count($class_path) -2);
if( isset( $class_path[1] ) && $class_path[1] === 'Importer' ){
if(file_exists($file)) {
require_once($file);
}
}
}
elseif ($class_path[0] == 'Tainacan') {
$sliced = array_slice($class_path, 1, count($class_path) -2);
if( isset( $class_path[1] ) && $class_path[1] === 'Importer' ) {
$dir = TAINACAN_IMPORTER_DIR;
$dir_import = strtolower(str_replace('_', '-' , $class_name));
if (file_exists("$dir$dir_import/")) {
$dir .= "$dir_import/";
}
} else if( isset( $class_path[1] ) && $class_path[1] === 'Exposers' ){
$dir = TAINACAN_EXPOSERS_DIR;
if(count($class_path) > 3) $dir .= strtolower($class_path[2]).DIRECTORY_SEPARATOR;
} else if( isset( $class_path[1] ) && $class_path[1] === 'API' ){
$dir = TAINACAN_TAPI_DIR;
if(count($class_path) > 3) $dir .= strtolower($class_path[2]).DIRECTORY_SEPARATOR;
} else if($sliced) {
$lower = $sliced[0];
$sliced[0] = strtolower( $lower );
} else if( isset( $class_path[1] ) && $class_path[1] === 'Exporter' ) {
$dir = TAINACAN_EXPORTER_DIR;
} else if( isset( $class_path[1] ) && $class_path[1] === 'Exposers' ){
$dir = TAINACAN_EXPOSERS_DIR;
if(count($class_path) > 3) $dir .= strtolower($class_path[2]).DIRECTORY_SEPARATOR;
} else if( isset( $class_path[1] ) && $class_path[1] === 'API' ){
$dir = TAINACAN_TAPI_DIR;
if(count($class_path) > 3) $dir .= strtolower($class_path[2]).DIRECTORY_SEPARATOR;
} else if($sliced) {
$lower = $sliced[0];
$sliced[0] = strtolower( $lower );
$dir = implode( DIRECTORY_SEPARATOR, $sliced ) . DIRECTORY_SEPARATOR;
$dir = TAINACAN_CLASSES_DIR . str_replace( '_', '-', $dir );
} else {
$dir = TAINACAN_CLASSES_DIR;
}
$dir = implode( DIRECTORY_SEPARATOR, $sliced ) . DIRECTORY_SEPARATOR;
$dir = TAINACAN_CLASSES_DIR . str_replace( '_', '-', $dir );
} else {
$dir = TAINACAN_CLASSES_DIR;
}
if( in_array('Metadata_Types', $class_path) || in_array('Filter_Types', $class_path) ){
$exceptions = ['taxonomytaginput','taxonomycheckbox'];
if( in_array( strtolower( $class_name ), $exceptions) ){
$dir.= 'taxonomy/';
}else{
$dir.= strtolower(str_replace('_', '-' , $class_name)).'/';
}
}
if( in_array('Metadata_Types', $class_path) || in_array('Filter_Types', $class_path) ){
$exceptions = ['taxonomytaginput','taxonomycheckbox'];
if( in_array( strtolower( $class_name ), $exceptions) ){
$dir.= 'taxonomy/';
}else{
$dir.= strtolower(str_replace('_', '-' , $class_name)).'/';
}
}
$file = $dir . 'class-tainacan-'. strtolower(str_replace('_', '-' , $class_name)) . '.php';
if(file_exists($file)) {
require_once($file);
}
}
$file = $dir . 'class-tainacan-'. strtolower(str_replace('_', '-' , $class_name)) . '.php';
if(file_exists($file)) {
require_once($file);
}
}
}
$Tainacan_Collections = \Tainacan\Repositories\Collections::get_instance();
@ -149,4 +154,6 @@ $Tainacan_Gutenberg_Block = \Tainacan\GutenbergBlock::get_instance();
$Tainacan_Search_Engine = new \Tainacan\Search_Engine();
$Tainacan_Elastic_press = new \Tainacan\Elastic_Press();
$Tainacan_Capabilities = \Tainacan\Capabilities::get_instance();
?>

View File

@ -0,0 +1,48 @@
<?php
namespace Tainacan;
class Background_Exporter extends Background_Process {
/**
* @var string
*/
protected $action = 'exporter';
public function __construct() {
parent::__construct();
$this->set_name( __('Exporter', 'tainacan') );
}
function task($batch) {
$data = $batch->data;
$key = $batch->key;
$className = $data['class_name'];
if (class_exists($className)) {
$object = new $className($data);
$runned = $object->run();
$this->write_log($key, $object->get_log());
$this->write_error_log($key, $object->get_error_log());
if (true === $object->get_abort()) {
throw new \Exception('Process aborted by Importer');
}
if (false === $runned) {
return false;
}
$batch->progress_label = $object->get_progress_label();
$batch->progress_value = $object->get_progress_value();
$batch->data = $object->_to_Array(true);
return $batch;
}
return false;
}
}
?>

View File

@ -0,0 +1,45 @@
<?php
namespace Tainacan\Exporter;
use Tainacan;
use Tainacan\Entities;
class CSV extends Exporter {
public function __construct($attributes = array()) {
parent::__construct($attributes);
$this->set_mapping_method('any');
}
public function process_item( $index, $collection_definition ) {
$collection_id = $collection_definition['id'];
$tainacan_items = \Tainacan\Repositories\Items::get_instance();
$filters = [
'posts_per_page' => 12,
'paged' => $index+1,
'order' => 'DESC'
];
$this->add_log('Proccessing item index ' . $index . ' in collection ' . $collection_definition['id'] );
$items = $tainacan_items->fetch($filters, $collection_id, 'WP_Query');
$export_items = "";
while ($items->have_posts()) {
$items->the_post();
$item = new Entities\Item($items->post);
$export_items .= json_encode($item);
}
wp_reset_postdata();
return $export_items;
}
public function options_form() {
ob_start();
?>
<div class="field">
<p>Priemiro teste da construção de um Exporter! </p>
</div>
<?php
return ob_get_clean();
}
}

View File

@ -0,0 +1,101 @@
<?php
namespace Tainacan;
class Export_Handler {
private $registered_exporters = [];
function __construct() {
$this->bg_exporter = new Background_Exporter();
add_action('init', array(&$this, 'init'));
}
public function init() {
$this->register_exporter([
'name' => 'CSV collections',
'description' => __('Exporter', 'tainacan'),
'slug' => 'csv',
'class_name' => '\Tainacan\Exporter\CSV'
]);
do_action('tainacan_register_exporters');
add_action( 'tainacan-enqueue-admin-scripts', array($this, 'enqueue_scripts') );
}
function enqueue_scripts() {
return null;
}
public function register_exporter(array $exporter) {
$defaults = [ //isso aqui vai mudar de acordo com a opção de exportação a se utilizada!
'manual_mapping' => false,
'manual_collection' => true
];
$attrs = wp_parse_args($exporter, $defaults);
if (!isset($attrs['slug']) || !isset($attrs['class_name']) || !isset($attrs['name'])) {
return false;
}
$this->registered_exporters[$exporter['slug']] = $attrs;
return true;
}
function add_to_queue(\Tainacan\Exporter\Exporter $exporter_object) {
$data = $exporter_object->_to_Array(true);
$exporter = $this->get_exporter_by_object($exporter_object);
$exporter_name = sprintf( __('%s Exporter', 'tainacan'), $exporter['name'] );
$bg_process = $this->bg_exporter->data($data)->set_name($exporter_name)->save();
if ( is_wp_error($bg_process->dispatch()) ) {
return false;
}
return $bg_process;
}
public function unregister_exporter($slug) {
unset($this->registered_exporters[$slug]);
}
public function get_registered_exporters() {
return $this->registered_exporters;
}
public function get_exporter($slug) {
$exporters = $this->get_registered_exporters();
if (isset($exporters[$slug])) {
return $exporters[$slug];
}
return null;
}
function get_exporter_by_object(\Tainacan\exporter\Exporter $exporter_object) {
$class_name = get_class($exporter_object);
$class_name = '\\' . $class_name;
$exporters = $this->get_registered_exporters();
foreach ($exporters as $exporter) {
if ($exporter['class_name'] == $class_name)
return $exporter;
}
return null;
}
public function initialize_exporter($slug) {
$exporter = $this->get_exporter($slug);
if ( is_array($exporter) && isset($exporter['class_name']) && class_exists($exporter['class_name']) ) {
return new $exporter['class_name']();
}
return false;
}
}
global $Tainacan_Exporter_Handler;
$Tainacan_Exporter_Handler = new Export_Handler();
?>

View File

@ -0,0 +1,642 @@
<?php
namespace Tainacan\Exporter;
use Tainacan;
use Tainacan\Entities;
abstract class CommunImportExport {
public function __construct( ) {
if (!session_id()) {
@session_start();
}
$this->id = uniqid();
$author = get_current_user_id();
if($author) {
$this->add_transient('author', $author);
}
}
/**
* The ID for this importer/exporter session
*
* When creating a new importer/exporter session via API, an id is returned and used to access this
* importer/exporter instance in the SESSION array
*
* @var identifier
*/
protected $id;
/**
* Stores the options for the importer/exporter. Each importer/exporter might use this property to save
* their own specific option
* @var array
*/
protected $options = [];
/**
* Stores the default options for the importer/exporter options
* @var array
*/
protected $default_options = [];
/**
* Declares what are the steps the importer/exporter will run, in the right order.
*
* By default, there is only one step, and the callback is the process_collections method
* that process items for the collections in the collections array.
*
* Child classes may declare as many steps as they want and can keep this default step to use
* this method for importer/exporter the items. But it is optional.
*
* @var array
*/
protected $steps = [
[
'name' => 'Process Items',
'progress_label' => 'Process Items',
'callback' => 'process_collections'
]
];
/**
* This array holds the structure that the default step 'process_collections' will handle.
*
* Its an array of the target collections, with their IDs, an identifier from the source, the total number of items to be importer/exporter, 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/exporter finds appropriate to handle
*
* The source_id can be anyhting you like, that helps you relate this collection to your source.
*
* Example of the structure of this propery for one collection:
* 0 => [
* 'id' => 12,
* 'mapping' => [
* 30 => 'column1'
* 31 => 'column2'
* ],
* 'total_items' => 1234,
* 'source_id' => 55
* ],
*
* use add_collection() and remove_collection() to interact with thiis array.
*
*
* @var array
*/
protected $collections = [];
public function add_collection(array $collection) {
if (isset($collection['id'])) {
$this->remove_collection($collection['id']);
$this->collections[] = $collection;
}
}
public function remove_collection($col_id) {
foreach ($this->get_collections() as $index => $col) {
if ($col['id'] == $col_id) {
unset($this->collections[$index]);
break;
}
}
}
public function next_item() {
$current_collection = $this->get_current_collection();
$current_collection_item = $this->get_current_collection_item();
$collections = $this->get_collections();
$collection = $collections[$current_collection];
$current_collection_item ++;
$this->set_current_collection_item($current_collection_item);
if ($current_collection_item >= $collection['total_items']) {
return $this->next_collection();
}
return $current_collection_item;
}
public function next_collection() {
$current_collection = $this->get_current_collection();
$collections = $this->get_collections();
$this->set_current_collection_item(0);
$current_collection ++;
if (isset($collections[$current_collection])) {
$this->set_current_collection($current_collection);
return $current_collection;
}
return false;
}
/**
* Transients is used to store temporary data to be used accross multiple requests
*
* Add and remove transient data using add_transient() and delete_transient() methods
*
* Transitens can be strings, numbers or arrays. Avoid storing objects.
*
* @var array
*/
protected $transients = [];
/**
* Wether to abort importer/exporter execution.
* @var bool
*/
protected $abort = false;
protected $current_step = 0;
protected $in_step_count = 0;
protected $current_collection = 0;
protected $current_collection_item = 0;
protected $log = [];
protected $error_log = [];
/**
* List of attributes that are saved in DB and that are used to
* reconstruct the object, this property need be overwrite in custom import/export.
* @var array
*/
protected $array_attributes = [
'in_step_count',
'current_step',
'transients',
'options',
'collections',
];
/**
* @return string
*/
public function get_id(){
return $this->id;
}
public function get_current_step() {
return $this->current_step;
}
public function set_current_step($value) {
$this->current_step = $value;
}
public function get_in_step_count() {
return $this->in_step_count;
}
public function set_in_step_count($value) {
$this->in_step_count = $value;
}
public function get_current_collection() {
return $this->current_collection;
}
public function set_current_collection($value) {
$this->current_collection = $value;
}
public function get_current_collection_item() {
return $this->current_collection_item;
}
public function set_current_collection_item($value) {
$this->current_collection_item = $value;
}
public function get_collections() {
return $this->collections;
}
public function set_collections($value) {
$this->collections = $value;
}
/**
* Gets the options for this import/export, including default values for options
* that were not set yet.
* @return array import/export options
*/
public function get_options() {
return array_merge($this->default_options, $this->options);
}
/**
* Set the options array
* @param array $options
*/
public function set_options($options) {
$this->options = $options;
}
/**
* Gets one option from the options array.
*
* Checks if option exist or if it have a default value. Otherwise return an empty string
*
* @param string $key the desired option
* @return mixed the option value, the default value or an empty string
*/
public function get_option($key) {
$options = $this->get_options();
return isset($options[$key]) ? $options[$key] : '';
}
/**
* Set the default options values.
*
* Must be called from the __construct method of the child import/export class to set default values.
*
* @param array $options
*/
protected function set_default_options($options) {
$this->default_options = $options;
}
public function set_steps($steps) {
$this->steps = $steps;
}
public function get_steps() {
return $this->steps;
}
protected function get_transients() {
return $this->transients;
}
protected function set_transients(array $data) {
$this->transients = $data;
}
public function add_transient($key, $data) {
$this->transients[$key] = $data;
}
public function delete_transient($key) {
if (isset($this->transients[$key]))
unset($this->transients[$key]);
}
public function get_transient($key) {
if (isset($this->transients[$key]))
return $this->transients[$key];
return null;
}
public function get_log() {
return $this->log;
}
public function get_error_log() {
return $this->error_log;
}
public function add_log($message ) {
$this->log[] = $message;
}
public function add_error_log($message ) {
$this->error_log[] = $message;
}
public function is_finished() {
if($this->current_step >= count($this->steps)) {
return true;
}
return false;
}
/**
* Cancel Scheduled abortion at the end of run()
* @return void
*/
protected function cancel_abort() {
$this->abort = false;
}
/**
* Schedule importer abortion at the end of run()
* @return void
*/
protected function abort() {
$this->abort = true;
}
/**
* Return wether importer should abort execution or not
* @return bool
*/
public function get_abort() {
return $this->abort;
}
/**
* Gets the current label to be displayed below the progress bar to give
* feedback to the user.
*
* It automatically gets the attribute progress_label from the current step running.
*
* Importers/Exporters may change this label whenever they want
*
* @return string
*/
public function get_progress_label() {
$current_step = $this->get_current_step();
$steps = $this->get_steps();
$label = '';
if ( isset($steps[$current_step]) ) {
$step = $steps[$current_step];
if ( isset($step['progress_label']) ) {
$label = $step['progress_label'];
} elseif ( isset($step['name']) ) {
$label = $step['name'];
}
}
if ( sizeof($steps) > 1 ) {
$preLabel = sprintf( __('Step %d of %d', 'tainacan'), $current_step + 1, sizeof($steps) );
$label = $preLabel . ': ' . $label;
}
if ( empty($label) ) {
$label = __('Running Importer', 'tainacan');
}
return $label;
}
/**
* Gets the current value to build the progress bar and give feedback to the user
* on the background process that is running the importer.
*
* It does so by comparing the "size" attribute with the $in_step_count class attribute
* where size indicates the total size of iterations the step will take and $this->in_step_count
* is the current iteration.
*
* For the step with "process_items" as a callback, this method will look for the the $this->collections array
* and sum the value of all "total_items" attributes of each collection. Then it will look for
* $this->get_current_collection and $this->set_current_collection_item to calculate the progress.
*
* The value must be from 0 to 100
*
* If a negative value is passed, it is assumed that the progress is unknown
*/
public function get_progress_value() {
$current_step = $this->get_current_step();
$steps = $this->get_steps();
$value = -1;
if ( isset($steps[$current_step]) ) {
$step = $steps[$current_step];
if ($step['callback'] == 'process_collections') {
$totalItems = 0;
$currentItem = $this->get_current_collection_item();
$current_collection = $this->get_current_collection();
$collections = $this->get_collections();
foreach ($collections as $i => $col) {
if ( isset($col['total_items']) && is_integer($col['total_items']) ) {
$totalItems += $col['total_items'];
if ($i < $current_collection) {
$currentItem += $col['total_items'];
}
}
}
if ($totalItems > 0) {
$value = round( ($currentItem/$totalItems) * 100 );
}
} else {
if ( isset($step['total']) && is_integer($step['total']) && $step['total'] > 0 ) {
$current = $this->get_in_step_count();
$value = round( ($current/$step['total']) * 100 );
}
}
}
return $value;
}
/**
* Sets the total attribute for the current step
*
* The "total" attribute of a step indicates the number of iterations this step will take to complete.
*
* The iteration is counted using $this->in_step_count attribute, and comparing the two values gives us
* the current progress of the process.
*
*/
protected function set_current_step_total($value) {
$this->set_step_total($this->get_current_step(), $value);
}
/**
* Sets the total attribute for a given step
*
* The "total" attribute of a step indicates the number of iterations this step will take to complete.
*
* The iteration is counted using $this->in_step_count attribute, and comparing the two values gives us
* the current progress of the process.
*
*/
protected function set_step_total($step, $value) {
$steps = $this->get_steps();
if (isset($steps[$step]) && is_array($steps[$step])) {
$steps[$step]['total'] = $value;
$this->set_steps($steps);
}
}
protected function next_step() {
$current_step = $this->get_current_step();
$steps = $this->get_steps();
$current_step ++;
$this->set_current_step($current_step);
if (isset($steps[$current_step])) {
return $current_step;
}
return false;
}
// Abstract methods
abstract public function _to_Array($short = false);
}
////////////////////////////////////
class Exporter extends CommunImportExport {
private $output_files = [];
private $mapping_accept = [
'any' => true,
'list' => false,
'none' => false,
];
private $mapping_list = [];
public function __construct($attributess = array()) {
$this->array_attributes = array_merge($this->array_attributes, ['current_collection_item', 'current_collection']);
parent::__construct();
$_SESSION['tainacan_exporter'][$this->get_id()] = $this;
if (!empty($attributess)) {
foreach ($attributess as $attr => $value) {
$method = 'set_' . $attr;
if (method_exists($this, $method)) {
$this->$method($value);
}
}
}
}
public function _to_Array($short = false) {
$return = ['id' => $this->get_id()];
foreach ($this->array_attributes as $attr) {
$method = 'get_' . $attr;
$return[$attr] = $this->$method();
}
$return['class_name'] = get_class($this);
global $Tainacan_Exporter_Handler;
$exporter_definition = $Tainacan_Exporter_Handler->get_exporter_by_object($this);
if ($short === false) {
$return['manual_collection'] = $exporter_definition['manual_collection'];
$return['manual_mapping'] = $exporter_definition['manual_mapping'];
$return['mapping_accept'] = $this->mapping_accept;
$return['mapping_list'] = $this->mapping_list;
$return['output_files'] = $this->output_files;
$return['options_form'] = $this->options_form();
}
return $return;
}
/**
* get the metadata of file/url to allow mapping
* should return an array
*
* Used when $manual_mapping is set to true, to build the mapping interface
*
* @return array $metadata_source the metadata from the source
*/
public function get_source_metadata() {}
/**
* Method implemented by the child importer/exporter class to return the total number of items that will be imported
*
* Used to build the progress bar
*
* @return int
*/
public function get_source_number_of_items() {}
/**
* Method implemented by child importer/exporter to return the HTML of the Options Form to be rendered in the Importer page
*/
public function options_form() {}
/**
* Default methods to Export
* process an item from the collections queue
*/
public function process_collections() {
$current_collection = $this->get_current_collection();
$collections = $this->get_collections();
$collection_definition = isset($collections[$current_collection]) ? $collections[$current_collection] : false;
$current_collection_item = $this->get_current_collection_item();
if ( !$collection_definition || !is_array($collection_definition) || !isset($collection_definition['id']) || !isset($collection_definition['mapping']) ) {
$this->add_error_log('Collection misconfigured');
return false;
}
$this->add_log('Processing item ' . $current_collection_item);
$processed_item = $this->process_item( $current_collection_item, $collection_definition );
if( $processed_item ) {
$this->append_to_file('exporter', $processed_item."\n");
} else {
$this->add_error_log('failed on item '. $current_collection_item );
}
return $this->next_item();
}
/**
* get values for a single item
*
* @param $index
* @return array with metadatum_source's as the index and values for the
* item
*
* Ex: [ 'Metadatum1' => 'value1', 'Metadatum2' => [ 'value2','value3' ]
*/
public function process_item( $index, $collection_id ) { }
public function add_new_file($key) {
$upload_dir = wp_upload_dir();
$upload_dir = trailingslashit( $upload_dir['basedir'] );
$exporter_folder = $upload_dir . 'tainacan/exporter';
if (!is_dir($exporter_folder)) {
if (!mkdir($exporter_folder)) {
return false;
}
}
$file_name = "$exporter_folder/file_".date('m-d-Y_hia');
$this->output_files[$key] = $file_name;
}
public function append_to_file($key, $data) {
if ( array_key_exists ( $key , $this->output_files ) ) {
$fp = fopen($this->output_files[$key], 'a');
fwrite($fp, $data);
fclose($fp);
} else { // será?
$this->add_new_file($key);
$this->append_to_file($key, $data);
}
}
public function set_mapping_method($method, $list = []) {
if ( array_key_exists($method, $this->mapping_accept) ) {
foreach ($this->mapping_accept as &$value) {
$value = false;
}
$this->mapping_accept[$method] = true;
if(!empty($list)) {
$this->mapping_list = $list;
}
return true;
}
return false;
}
/**
* runs one iteration
*/
public function run() {
if ($this->is_finished()) {
return false;
}
$steps = $this->get_steps();
$current_step = $this->get_current_step();
$method_name = $steps[$current_step]['callback'];
if (method_exists($this, $method_name)) {
$author = $this->get_transient('author');
$this->add_log('User in process: ' . $author);
wp_set_current_user($author);
$result = $this->$method_name();
} else {
$this->add_error_log( 'Callback not found for step ' . $steps[$current_step]['name']);
$result = false;
}
if($result === false || (!is_numeric($result) || $result < 0)) {
//Move on to the next step
$this->set_in_step_count(0);
$return = $this->next_step();
} else if(is_numeric($result) && $result > 0) {
$this->set_in_step_count($result);
$return = $result;
}
return $return;
}
}

View File

@ -49,6 +49,10 @@ class CSV extends Importer {
$this->set_option('document_index', $index);
} else if( $rawColumn === 'special_attachments' ){
$this->set_option('attachment_index', $index);
} else if( $rawColumn === 'special_item_status' ){
$this->set_option('item_status_index', $index);
} else if( $rawColumn === 'special_item_id' ){
$this->set_option('item_id_index', $index);
}
} else {
@ -164,8 +168,9 @@ class CSV extends Importer {
public function after_inserted_item( $inserted_item, $collection_index ) {
$column_document = $this->get_option('document_index');
$column_attachment = $this->get_option('attachment_index');
$column_item_status = $this->get_option('item_status_index');
if( !empty($column_document) || !empty( $column_attachment ) ){
if( !empty($column_document) || !empty( $column_attachment ) || !empty( $column_item_status ) ){
if (($handle = fopen($this->tmp_file, "r")) !== false) {
$file = $handle;
@ -191,6 +196,11 @@ class CSV extends Importer {
if( is_array($values) && !empty($column_attachment) ){
$this->handle_attachment( $values[$column_attachment], $inserted_item);
}
if( is_array($values) && !empty($column_item_status) ){
$this->handle_item_status( $values[$column_item_status], $inserted_item);
}
}
}
@ -309,6 +319,33 @@ class CSV extends Importer {
</div>
</div>
</div>
<div class="field">
<label class="label"><?php _e('Repeated Item', 'tainacan'); ?></label>
<span class="help-wrapper">
<a class="help-button has-text-secondary">
<span class="icon is-small">
<i class="mdi mdi-help-circle-outline" ></i>
</span>
</a>
<div class="help-tooltip">
<div class="help-tooltip-header">
<h5><?php _e('Repeated Item', 'tainacan'); ?></h5>
</div>
<div class="help-tooltip-body">
<p><?php _e('Choose the action when a repeated item is found', 'tainacan'); ?></p>
</div>
</div>
</span>
<div class="control is-clearfix">
<div class="select">
<select name="repeated_item">
<option value="update" <?php selected($this->get_option('repeated_item'), 'update'); ?> >Update</option>
<option value="ignore" <?php selected($this->get_option('repeated_item'), 'ignore'); ?> >Ignore</option>
</select>
</div>
</div>
</div>
<div class="field">
<label class="label"><?php _e('Importing attachments', 'tainacan'); ?></label>
@ -509,4 +546,28 @@ class CSV extends Importer {
$values = explode($delimiter, $line);
return $values;
}
/**
* @param $status string the item status
*/
private function handle_item_status( $status, $item_inserted ){
if ( in_array( $status, array( 'auto-draft', 'draft', 'pending', 'future', 'publish', 'trash', 'inherit' ) ) ) {
$item_inserted->set_status($status);
if( $item_inserted->validate() ) {
$item_inserted = $this->items_repo->update($item_inserted);
}
}
}
private function handle_item_id( $values ){
$item_id_index = $this->set_option('item_id_index');
if( $item_id_index && isset($values[$item_id_index]) ){
$this->add_transient( 'item_id',$values[$item_id_index] );
$this->add_transient( 'item_action',$this->get_option('repeated_item') );
}
}
}

View File

@ -383,6 +383,10 @@ class Old_Tainacan extends Importer{
$value = $values;
}
if( is_array($value) ){
$value = array_filter($value);
}
$item_metadata->set_value($value);
} else if( $metadatum->type === 'item' ){ // RELATIONSHIPS

View File

@ -115,6 +115,7 @@ class Term_Importer extends Importer {
if (($handle = fopen($this->tmp_file, "r")) !== false) {
$file = $handle;
$this->set_current_step_total( filesize($this->tmp_file) );
} else {
$this->add_error_log(' Error reading the file ');
return false;

View File

@ -78,9 +78,15 @@ export const fetchItems = ({ rootGetters, dispatch, commit }, { collectionId, is
resolve({'itemsListTemplate': items, 'total': res.headers['x-wp-total'], hasFiltered: hasFiltered, advancedSearchResults: advancedSearchResults});
} else {
commit('setItems', items);
resolve({'items': items, 'total': res.headers['x-wp-total'], hasFiltered: hasFiltered, advancedSearchResults: advancedSearchResults });
resolve({
'items': items,
'total': res.headers['x-wp-total'],
totalPages: res.headers['x-wp-totalpages'],
hasFiltered: hasFiltered,
advancedSearchResults: advancedSearchResults });
}
dispatch('search/setTotalItems', res.headers['x-wp-total'], { root: true } );
dispatch('search/setTotalPages', res.headers['x-wp-totalpages'], { root: true } );
})
.catch(error => reject(error));
@ -108,7 +114,7 @@ export const deleteItem = ({ commit }, { itemId, isPermanently }) => {
});
};
export const fetchCollections = ({commit} , { page, collectionsPerPage, status }) => {
return new Promise((resolve, reject) => {
let endpoint = '/collections?paged='+page+'&perpage='+collectionsPerPage+'&context=edit';
@ -248,7 +254,6 @@ export const updateCollection = ({ commit }, {
export const sendCollection = ( { commit }, collection) => {
return new Promise(( resolve, reject ) => {
let param = collection;
param['mapper'] = null;
param[tainacan_plugin.exposer_mapper_param] = collection.mapper;
axios.tainacan.post('/collections/', param)
.then( res => {

View File

@ -11,7 +11,7 @@ export const fetchAvailableImporters = ({ commit }) => {
resolve (availableImporters);
})
.catch((error) => {
reject(error);
reject(error.response.data);
});
});
};
@ -26,7 +26,7 @@ export const fetchImporter = ( { commit }, importerId ) => {
resolve( importer );
})
.catch(error => {
reject( error );
reject( error.response.data );
});
});
};
@ -43,7 +43,7 @@ export const sendImporter = ( { commit }, importerTypeSlug) => {
resolve( importer );
})
.catch(error => {
reject( error );
reject( error.response.data );
});
});
};
@ -58,7 +58,7 @@ export const updateImporter = ( { commit }, { sessionId, options }) => {
resolve( importer );
})
.catch(error => {
reject(error);
reject(error.response.data);
});
});
};
@ -74,7 +74,7 @@ export const updateImporterCollection = ( { commit }, { sessionId, collection })
resolve( importer );
})
.catch(error => {
reject(error);
reject(error.response.data);
});
});
};
@ -90,7 +90,7 @@ export const updateImporterURL = ( { commit }, { sessionId, url }) => {
resolve( importer );
})
.catch(error => {
reject(error);
reject(error.response.data);
});
});
};
@ -107,7 +107,7 @@ export const updateImporterOptions = ( { commit }, { sessionId, options }) => {
resolve( importer );
})
.catch(error => {
reject(error);
reject(error.response.data);
});
});
};
@ -129,7 +129,7 @@ export const updateImporterFile = ( { commit }, { sessionId, file }) => {
resolve( updatedImporter );
})
.catch(error => {
reject(error);
reject(error.response.data);
});
});
};
@ -143,7 +143,7 @@ export const fetchImporterSourceInfo = ({ commit }, sessionId ) => {
resolve (importerSourceInfo);
})
.catch((error) => {
reject(error);
reject(error.response.data);
});
});
};
@ -158,7 +158,7 @@ export const runImporter = ( { dispatch } , importerId ) => {
resolve( backgroundProcessId );
})
.catch(error => {
reject( error );
reject( error.response.data );
});
});
};

View File

@ -77,6 +77,10 @@ export const fetchItem = ({ commit }, item_id) => {
});
};
export const replaceItem = ({ commit }, item) => {
commit('setItem', item);
};
export const fetchItemTitle = ({ commit }, id) => {
commit('cleanItemTitle');
return new Promise((resolve, reject) =>{

View File

@ -53,6 +53,9 @@ export const remove_metaquery = ( { commit }, filter ) => {
export const setTotalItems = ({ commit }, total ) => {
commit('setTotalItems', total);
};
export const setTotalPages = ({ commit }, totalPages ) => {
commit('setTotalPages', totalPages);
};
export const setPage = ({ commit }, page ) => {
commit('setPostQueryAttribute', { attr: 'paged', value: page } );

View File

@ -18,6 +18,10 @@ export const getTotalItems = state => {
return state.totalItems;
};
export const getTotalPages = state => {
return state.totalPages;
};
export const getPage = state => {
if (state.postquery.paged == undefined)
return 1;

View File

@ -23,7 +23,8 @@ const state = {
admin_view_mode: 'table'
},
filter_tags: [],
totalItems: 0
totalItems: 0,
totalPages: 0
};
export default {

View File

@ -119,6 +119,10 @@ export const setTotalItems = ( state, total ) => {
state.totalItems = total;
};
export const setTotalPages = ( state, totalPages ) => {
state.totalPages = totalPages;
};
export const setSearchQuery = ( state, searchQuery ) => {
if (searchQuery != '')

View File

@ -73,6 +73,11 @@ class Migrations {
}
static function init_capabilites() {
$Tainacan_Capabilities = \Tainacan\Capabilities::get_instance();
$Tainacan_Capabilities->init();
}
static function tainacan_migrate_post_type_field_to_metadatum(){
global $wpdb;
@ -288,6 +293,12 @@ class Migrations {
static function refresh_rewrite_rules() {
// needed after we changed the Collections post type rewrite slug
$option_name = '_migration_refresh_rewrite_rules_items';
if (!get_option($option_name)) {
return; // avoid running twice cause there is the same update right below this one
}
flush_rewrite_rules(false);
}

View File

@ -1,5 +1,5 @@
=== Tainacan ===
Contributors: andrebenedito, fabianobn, jacsonp, leogermani, weryques, wetah
Contributors: andrebenedito, daltonmartins, fabianobn, jacsonp, leogermani, weryques, wetah
Tags: museums, libraries, archives, GLAM, collections, repository
Requires at least: 4.8
Tested up to: 4.9.8

View File

@ -33,14 +33,10 @@ function tainacan_load_plugin_textdomain() {
}
add_action( 'plugins_loaded', 'tainacan_load_plugin_textdomain' );
add_action( 'after_setup_theme', function() {
add_image_size( 'tainacan-small', 40, 40, true );
add_image_size( 'tainacan-medium', 275, 275, true );
add_image_size( 'tainacan-medium-full', 205, 1500 );
} );
$Tainacan_Capabilities = \Tainacan\Capabilities::get_instance();
register_activation_hook( __FILE__, array( $Tainacan_Capabilities, 'init' ) );
add_action('init', ['Tainacan\Migrations', 'run_migrations']);

View File

@ -84,6 +84,15 @@ class Theme_Helper {
'icon' => '<span class="icon"><i class="mdi mdi-view-dashboard mdi-24px"></i></span>',
'type' => 'component'
]);
$this->register_view_mode('slideshow', [
'label' => __('Slideshow', 'tainacan'),
'dynamic_metadata' => false,
'description' => 'A fullscreen slideshow view.',
'icon' => '<span class="icon"><i class="mdi mdi-fullscreen mdi-24px"></i></span>',
'type' => 'component',
'show_pagination' => false,
'full_screen' => true
]);
}
public function enqueue_scripts($force = false) {
@ -406,6 +415,7 @@ class Theme_Helper {
* @type string $thumbnail Full URL to an thumbnail that represents the view mode. Displayed in admin.
* @type string $icon HTML that outputs an icon that represents the view mode. Displayed in front end.
* @type bool $show_pagination Wether to display or not pagination controls. Default true.
* @type bool $full-screen Wether the view mode will display full screen or not. Default false.
* @type bool $dynamic_metadata Wether to display or not (and use or not) the "displayed metadata" selector. Default false.
*
*
@ -424,6 +434,7 @@ class Theme_Helper {
'thumbnail' => '', // get_stylesheet_directory() . '/tainacan/view-mode-' . $slug . '.png',
'icon' => '', //
'show_pagination' => true,
'full_screen' => false,
'dynamic_metadata' => false,
);

View File

@ -0,0 +1,615 @@
<template>
<div :class="{ 'hide-controls': hideControls }">
<!-- CLOSE BUTTON -->
<button
v-tooltip="{
content: $i18n.get('close'),
autoHide: false,
placement: 'auto-start'
}"
id="close-fullscren-button"
:class="{ 'is-hidden-mobile': !isMetadataCompressed }"
@click="closeSlideViewMode()">
<b-icon icon="close" />
</button>
<!-- METADATA LIST -->
<button
v-tooltip="{
content: isMetadataCompressed ? $i18n.get('label_show_metadata') : $i18n.get('label_hide_metadata'),
autoHide: false,
placement: 'auto-start'
}"
id="metadata-compress-button"
@click="isMetadataCompressed = !isMetadataCompressed">
<b-icon :icon="isMetadataCompressed ? 'menu-right' : 'menu-left'" />
</button>
<aside
v-if="!isMetadataCompressed"
class="metadata-menu tainacan-form">
<div class="metadata-menu-header is-hidden-tablet">
<h2>{{ item.title }}</h2>
<button
id="close-metadata-button"
@click="isMetadataCompressed = true">
<b-icon icon="close" />
</button>
<hr>
</div>
<h3 class="has-text-white has-text-weight-semibold">{{ $i18n.get('metadata') }}</h3>
<a
v-if="!isLoadingItem && Object.keys(item.metadata).length > 0"
class="collapse-all is-size-7"
@click="collapseAll = !collapseAll">
{{ collapseAll ? $i18n.get('label_collapse_all') : $i18n.get('label_expand_all') }}
<b-icon
type="is-secondary"
:icon=" collapseAll ? 'menu-down' : 'menu-right'" />
</a>
<span
v-if="isLoadingItem"
style="width: 100%;"
class="icon is-large loading-icon">
<div class="is-large control has-icons-right is-loading is-clearfix" />
</span>
<div
v-for="(metadatum, index) of item.metadata"
:key="index"
class="field">
<b-collapse :open="!collapseAll">
<label
class="label has-text-white"
slot="trigger"
slot-scope="props">
<b-icon
type="is-secondary"
:icon="props.open ? 'menu-down' : 'menu-right'"
/>
{{ metadatum.name }}
</label>
<div class="content">
<p
class="has-text-white"
v-html="metadatum.value_as_html != '' ? metadatum.value_as_html : `<span class='has-text-gray is-italic'>` + $i18n.get('label_value_not_informed') + `</span>`"/>
</div>
</b-collapse>
</div>
<br>
<br>
</aside>
<div
:class="{ 'fullscreen-spaced-to-right': !isMetadataCompressed }"
@keyup.left.prevent="slideIndex > 0 ? prevSlide() : null"
@keyup.right.prevent="slideIndex < slideItems.length - 1 ? nextSlide() : null">
<div class="table-wrapper">
<!-- SLIDE MAIN VIEW-->
<section
@click.prevent.stop="onHideControls()"
class="tainacan-slide-main-view">
<button
@click.stop.prevent="prevSlide()"
:style="{ visibility: (page > 1 && slideIndex <= 0) || slideIndex > 0 ? 'visible' : 'hidden' }"
class="slide-control-arrow arrow-left">
<span class="icon is-large">
<icon class="mdi mdi-48px mdi-chevron-left"/>
</span>
</button>
<div
class="slide-main-content"
v-hammer:swipe.prevent="onSwipeFiltersMenu">
<transition
mode="out-in"
:name="goingRight ? 'slide-right' : 'slide-left'" >
<span
v-if="isLoadingItem"
class="icon is-large loading-icon">
<div class="is-large control has-icons-right is-loading is-clearfix" />
</span>
<!-- Empty result placeholder -->
<section
v-if="!isLoading && !isLoadingItem && items.length <= 0"
class="section">
<div class="content has-text-gray4 has-text-centered">
<p>
<b-icon
icon="file-multiple"
size="is-large"/>
</p>
<p>{{ $i18n.get('info_no_item_found') }}</p>
</div>
</section>
<div
v-if="!isLoadingItem && slideItems.length > 0 && (item.document != undefined && item.document != undefined && item.document != '')"
v-html="item.document_as_html" />
<div v-else>
<div class="empty-document">
<p>{{ $i18n.get('label_document_empty') }}</p>
<img
:alt="$i18n.get('label_document_empty')"
:src="thumbPlaceholderPath">
</div>
</div>
</transition>
</div>
<button
@click.stop.prevent="nextSlide()"
:style="{ visibility: (slideIndex < slideItems.length - 1) || page < totalPages ? 'visible' : 'hidden' }"
class="slide-control-arrow arrow-right">
<span class="icon is-large has-text-turoquoise5">
<icon class="mdi mdi-48px mdi-chevron-right"/>
</span>
</button>
</section>
<!-- SLIDE ITEMS LIST -->
<div class="tainacan-slides-list">
<section
@click.prevent="onHideControls()"
v-if="slideItems[slideIndex] != undefined"
class="slide-title-area">
<h1>{{ slideItems[slideIndex].title }}</h1>
<button
:disabled="(slideIndex == slideItems.length - 1 && page == totalPages)"
class="play-button"
@click.stop.prevent="isPlaying = !isPlaying">
<b-icon
type="is-secondary"
size="is-medium"
:icon="isPlaying ? 'pause-circle' : 'play-circle' "/>
<circular-counter
v-if="isPlaying"
:time="this.slideTimeout/1000" />
</button>
</section>
<swiper
@slideChange="onSlideChange()"
ref="mySwiper"
:options="swiperOption"
id="tainacan-slide-container">
<swiper-slide
:ref="'thumb-' + item.id"
:key="index"
v-for="(item, index) of slideItems"
class="tainacan-slide-item"
:class="{'active-item': slideIndex == index}">
<img
:alt="item.title"
class="thumnail"
:src="item['thumbnail']['tainacan_small'] ? item['thumbnail']['tainacan_small'] : (item['thumbnail'].thumb ? item['thumbnail'].thumb : thumbPlaceholderPath)">
</swiper-slide>
<!-- Swiper buttons are hidden as they actually swipe from slide to slide -->
<div
style="visibility: hidden; display: none;"
class="swiper-button-prev"
slot="button-prev"/>
<div
style="visibility: hidden; display: none;"
class="swiper-button-next"
slot="button-next"/>
</swiper>
<!-- List loading -->
<span
v-if="isLoading"
:style="{ left: !goingRight ? '' : '25%', right: !goingRight ? '25%' : '' }"
class="icon loading-icon">
<div class="control has-icons-right is-loading is-clearfix" />
</span>
<!-- Extra buttons for sliding groups of slides -->
<button
@click.prevent="prevGroupOfSlides()"
:style="{ visibility: (page > 1 && slideIndex <= 0) || slideIndex > 0 ? 'visible' : 'hidden' }"
class="slide-control-arrow slide-group-arrow arrow-left">
<span class="icon is-medium has-text-white">
<icon class="mdi mdi-24px mdi-chevron-left"/>
</span>
</button>
<button
@click.prevent="nextGroupOfSlides()"
:style="{ visibility: (slideIndex < slideItems.length - 1) || page < totalPages ? 'visible' : 'hidden' }"
class="slide-control-arrow slide-group-arrow arrow-right">
<span class="icon is-medium has-text-white">
<icon class="mdi mdi-24px mdi-chevron-right"/>
</span>
</button>
</div>
</div>
</div>
</div>
</template>
<script>
import { mapActions, mapGetters } from 'vuex';
import axios from '../js/axios/axios.js';
import 'swiper/dist/css/swiper.css';
import { swiper, swiperSlide } from 'vue-awesome-swiper';
import CircularCounter from '../admin/components/other/circular-counter.vue';
export default {
name: 'ViewModeSlideshow',
props: {
collectionId: Number,
displayedMetadata: Array,
items: Array,
isLoading: Boolean,
totalItems: Number,
hideControls: true,
isSwiping: false
},
components: {
swiper,
swiperSlide,
CircularCounter
},
data () {
return {
slideItems: [],
goingRight: true,
isPlaying: false,
slideTimeout: 5000,
intervalId: 0,
collapseAll: false,
isLoadingItem: true,
isMetadataCompressed: true,
slideIndex: 0,
minPage: 1,
maxPage: 1,
readjustedSlideIndex: 0,
preloadedItem: {},
swiperOption: {
mousewheel: true,
observer: true,
keyboard: true,
preventInteractionOnTransition: true,
allowClick: true,
allowTouchMove: true,
slidesPerView: 18,
slidesPerGroup: 1,
centeredSlides: true,
spaceBetween: 12,
slideToClickedSlide: true,
navigation: {
nextEl: '.swiper-button-next',
prevEl: '.swiper-button-prev'
},
breakpoints: {
320: { slidesPerView: 4 },
480: { slidesPerView: 5 },
640: { slidesPerView: 6 },
768: { slidesPerView: 8 },
1024: { slidesPerView: 10 },
1366: { slidesPerView: 12 },
1406: { slidesPerView: 14 },
1600: { slidesPerView: 16 }
}
},
thumbPlaceholderPath: tainacan_plugin.base_url + '/admin/images/placeholder_square.png'
}
},
computed: {
item() {
return this.getItem();
},
page() {
return this.getPage();
},
totalPages() {
return this.getTotalPages();
}
},
watch: {
page: {
handler() {
this.minPage = this.page < this.minPage ? this.page : this.minPage;
this.maxPage = this.page > this.maxPage ? this.page : this.maxPage;
},
immediate: true
},
items: {
handler () {
if (this.items.length > 0) {
let updatedSlideIndex = this.slideIndex != undefined ? (this.slideIndex + 0) : 0;
// Loops through new items list. Depending on direction, goes from start or end of list.
for (let newItem of ((this.goingRight) ? this.items : JSON.parse(JSON.stringify(this.items)).reverse())) {
let existingItemIndex = this.slideItems.findIndex(anItem => anItem.id == newItem.id);
if (existingItemIndex < 0) {
if ( this.goingRight || this.slideIndex == undefined) {
this.slideItems.push(newItem);
} else {
this.slideItems.unshift(newItem);
updatedSlideIndex++;
}
} else {
this.$set(this.slideItems, existingItemIndex, newItem);
}
}
// Checks if list got too big. In this case we remove items from a page that is far from index
if (this.slideItems.length > 36) {
if (this.goingRight) {
this.slideItems.splice(0, this.getItemsPerPage());
this.minPage++;
updatedSlideIndex = this.slideItems.length - 1 - this.items.length;
} else {
this.slideItems.splice(-this.getItemsPerPage());
this.maxPage--;
updatedSlideIndex = this.getItemsPerPage();
}
}
if (this.goingRight == undefined && updatedSlideIndex == 0) {
this.slideIndex = -1; // Used to force reload of index when page has not loaded slideItems yet
} else {
if (this.$refs.mySwiper != undefined && this.$refs.mySwiper.swiper != undefined) {
// if (updatedSlideIndex != undefined && this.$refs.mySwiper.swiper.slides[updatedSlideIndex] != undefined)
// this.$refs.mySwiper.swiper.slides[updatedSlideIndex].click();
this.$refs.mySwiper.swiper.activeIndex = this.slideIndex;
this.slideIndex = updatedSlideIndex;
// console.log("Após: " + this.slideIndex + " " + this.$refs.mySwiper.swiper.activeIndex);
}
}
}
},
immediate: true
},
slideIndex:{
handler(val, oldVal) {
if (this.slideIndex < 0) {
this.slideIndex = 0;
} else {
// Handles direction information, used by animations
if (oldVal == undefined)
this.goingRight = undefined;
else if (val < oldVal || (this.slideIndex == 0 && this.page == 1))
this.goingRight = false;
else
this.goingRight = true;
// Handles loading main item info, displayed in the middle
this.loadCurrentItem();
// Handles requesting new page of items, either to left or right
if (this.$refs.mySwiper != undefined && this.$refs.mySwiper.swiper != undefined) {
if (this.slideIndex != this.$refs.mySwiper.swiper.activeIndex) {
if (this.slideIndex != undefined && this.$refs.mySwiper.swiper.slides[this.slideIndex] != undefined)
this.$refs.mySwiper.swiper.slides[this.slideIndex].click();
this.readjustedSlideIndex = this.slideIndex;
this.$refs.mySwiper.swiper.activeIndex = this.slideIndex + 0;
// console.log("Index: " + this.slideIndex, this.$refs.mySwiper.swiper.activeIndex, this.readjustedSlideIndex)
} else if (this.slideItems.length > 0) {
if (this.$refs.mySwiper.swiper.activeIndex == this.slideItems.length - 1 && this.page < this.totalPages)
oldVal == undefined ? this.$eventBusSearch.setPage(this.page + 1) : this.$eventBusSearch.setPage(this.maxPage + 1);
else if (this.$refs.mySwiper.swiper.activeIndex == 0 && this.page > 1 && this.slideItems.length < this.totalItems) {
oldVal == undefined ? this.$eventBusSearch.setPage(this.page - 1) : this.$eventBusSearch.setPage(this.minPage - 1);
}
}
// Handles pausing auto play when reaches the end of the list.
if (this.$refs.mySwiper.swiper.activeIndex == this.slideItems.length - 1 && this.page == this.totalPages)
this.isPlaying = false;
}
}
},
immediate: true
},
isPlaying() {
if (this.isPlaying) {
this.intervalId = setInterval(() => {
if (this.$refs.mySwiper.swiper != undefined)
this.$refs.mySwiper.swiper.navigation.nextEl.click();
}, this.slideTimeout);
} else {
clearInterval(this.intervalId);
}
}
},
methods: {
...mapActions('item', [
'fetchItem',
'replaceItem'
]),
...mapGetters('item', [
'getItem'
]),
...mapGetters('search', [
'getTotalPages',
'getPage',
'getItemsPerPage'
]),
onHideControls() {
if (this.isSwiping == undefined || this.isSwiping == false)
this.hideControls = !this.hideControls;
},
onSwipeFiltersMenu($event) {
this.isSwiping = true;
if ($event.offsetDirection == 2) {
this.nextSlide();
} else if ($event.offsetDirection == 4) {
this.prevSlide();
}
setTimeout(() => {
this.isSwiping = false;
}, 500);
},
onSlideChange() {
// console.log(this.slideIndex, this.$refs.mySwiper.swiper.activeIndex, this.readjustedSlideIndex)
if (this.$refs.mySwiper.swiper != undefined)
this.slideIndex = this.$refs.mySwiper.swiper.activeIndex;
this.$nextTick(() => {
if (this.readjustedSlideIndex != undefined) {
this.slideIndex = this.readjustedSlideIndex;
// if (this.slideIndex != undefined && this.$refs.mySwiper.swiper.slides[this.slideIndex] != undefined)
// this.$refs.mySwiper.swiper.slides[this.slideIndex].click();
this.$refs.mySwiper.swiper.activeIndex = this.slideIndex + 0;
this.readjustedSlideIndex = undefined;
}
});
},
nextSlide() {
if (this.$refs.mySwiper.swiper != undefined)
this.$refs.mySwiper.swiper.slideNext();
},
prevSlide() {
if (this.$refs.mySwiper.swiper != undefined)
this.$refs.mySwiper.swiper.slidePrev();
},
nextGroupOfSlides() {
let screenWidth = (window.innerWidth || document.documentElement.clientWidth || document.body.clientWidth);
let amountToSkip = 0;
if (screenWidth <= 480) amountToSkip = 1;
else if (screenWidth > 480 && screenWidth <= 640) amountToSkip = 2;
else if (screenWidth > 640 && screenWidth <= 768) amountToSkip = 4;
else if (screenWidth > 768 && screenWidth <= 1366) amountToSkip = 5;
else if (screenWidth > 1366 && screenWidth <= 1600) amountToSkip = 6;
else if (screenWidth > 1600) amountToSkip = 7;
if (this.$refs.mySwiper.swiper != undefined) {
if (this.slideIndex != undefined && this.$refs.mySwiper.swiper.slides[this.slideIndex + amountToSkip] != undefined)
this.$refs.mySwiper.swiper.slideTo(this.slideIndex + amountToSkip);
else
this.$refs.mySwiper.swiper.slideTo(this.$refs.mySwiper.swiper.slides.length - 1)
}
},
prevGroupOfSlides() {
let screenWidth = (window.innerWidth || document.documentElement.clientWidth || document.body.clientWidth);
let amountToSkip = 0;
if (screenWidth <= 480) amountToSkip = 1;
else if (screenWidth > 480 && screenWidth <= 640) amountToSkip = 2;
else if (screenWidth > 640 && screenWidth <= 768) amountToSkip = 4;
else if (screenWidth > 768 && screenWidth <= 1366) amountToSkip = 5;
else if (screenWidth > 1366 && screenWidth <= 1600) amountToSkip = 6;
else if (screenWidth > 1600) amountToSkip = 7;
if (this.$refs.mySwiper.swiper != undefined) {
if (this.slideIndex != undefined && this.$refs.mySwiper.swiper.slides[this.slideIndex - amountToSkip] != undefined)
this.$refs.mySwiper.swiper.slideTo(this.slideIndex - amountToSkip);
else
this.$refs.mySwiper.swiper.slideTo(0);
}
},
loadCurrentItem() {
if ((this.slideItems && this.slideItems[this.slideIndex] && this.slideItems[this.slideIndex].id != undefined)) {
this.isLoadingItem = true;
// Checks if item is preloaded
if (this.preloadedItem.id != undefined && this.preloadedItem.id == this.slideItems[this.slideIndex].id) {
this.replaceItem(this.preloadedItem);
this.$nextTick(() => this.isLoadingItem = false);
} else {
// Loads current item
this.fetchItem(this.slideItems[this.slideIndex].id)
.then(() => {
this.isLoadingItem = false;
})
.catch(() => {
this.isLoadingItem = false;
});
}
// Loads next item, just in case
let nextIndex = (this.goingRight || this.goingRight == undefined) ? this.slideIndex + 1 : this.slideIndex - 1;
if (this.slideItems[nextIndex] != undefined && this.slideItems[nextIndex].id != undefined) {
axios.tainacan.get('/items/' + this.slideItems[nextIndex].id)
.then(res => {
this.preloadedItem = res.data;
})
.catch(error => {
this.$console.log( error );
});
}
}
},
renderMetadata(itemMetadata, column) {
let metadata = itemMetadata[column.slug] != undefined ? itemMetadata[column.slug] : false;
if (!metadata) {
return '';
} else if (metadata.date_i18n) {
return metadata.date_i18n;
} else {
return metadata.value_as_html;
}
},
closeSlideViewMode() {
this.$parent.onChangeViewMode(this.$parent.defaultViewMode);
}
},
mounted() {
this.minPage = this.page;
this.maxPage = this.page;
if (this.$refs.mySwiper.swiper != undefined) {
this.$refs.mySwiper.swiper.initialSlide = this.slideIndex;
}
// Adds clipped class to root html
document.documentElement.scrollTo(0,0);
document.documentElement.classList.add('is-clipped');
},
beforeDestroy() {
clearInterval(this.intervalId);
if (this.$refs.mySwiper.swiper)
this.$refs.mySwiper.swiper.destroy();
// Remove clipped class from root html
document.documentElement.classList.remove('is-clipped');
}
}
</script>
<style lang="scss">
$turquoise1: #e6f6f8;
$turquoise2: #d1e6e6;
$turquoise5: #298596;
$tainacan-input-color: #1d1d1d;
$gray1: #f2f2f2;
$gray2: #e5e5e5;
$gray3: #dcdcdc;
$gray4: #898d8f;
$gray5: #454647;
$page-small-side-padding: 25px;
$page-side-padding: 4.166666667%;
@import "../../src/admin/scss/_view-mode-slideshow.scss";
.is-fullscreen .table-wrapper {
overflow: hidden !important;
display: flex;
flex-wrap: nowrap;
flex-direction: column;
justify-content: space-between;
height: 100vh;
width: 100vw;
}
</style>

View File

@ -89,10 +89,12 @@ class BulkEdit extends TAINACAN_UnitApiTestCase {
for ($i = 1; $i<=40; $i++) {
$title = 'testeItem ' . str_pad($i, 2, "0", STR_PAD_LEFT);
$item = $this->tainacan_entity_factory->create_entity(
'item',
array(
'title' => 'testeItem ' . $i,
'title' => $title,
'collection' => $collection,
'status' => 'publish'
),
@ -103,7 +105,7 @@ class BulkEdit extends TAINACAN_UnitApiTestCase {
$this->tainacan_item_metadata_factory->create_item_metadata($item, $metadatum, $i % 2 == 0 ? 'even' : 'odd');
$this->tainacan_item_metadata_factory->create_item_metadata($item, $category, ['good', 'bad']);
$this->tainacan_item_metadata_factory->create_item_metadata($item, $collection->get_core_title_metadatum(), 'testeItem ' . $i);
$this->tainacan_item_metadata_factory->create_item_metadata($item, $collection->get_core_title_metadatum(), $title);
}
@ -912,7 +914,7 @@ class BulkEdit extends TAINACAN_UnitApiTestCase {
$this->assertTrue(is_string($data['id']));
$this->assertEquals(17, $response->headers['X-WP-Total']);
$this->assertEquals(17, $data['items_count']);
}
@ -960,7 +962,7 @@ class BulkEdit extends TAINACAN_UnitApiTestCase {
$this->assertTrue(is_string($data['id']));
$this->assertEquals(20, $response->headers['X-WP-Total']);
$this->assertEquals(20, $data['items_count']);
}
@ -1302,6 +1304,132 @@ class BulkEdit extends TAINACAN_UnitApiTestCase {
}
/**
* @group sequence
*/
function test_get_item_in_sequence() {
$Tainacan_Items = \Tainacan\Repositories\Items::get_instance();
$query = [
'posts_per_page' => 22,
'orderby' => 'title'
];
$bulk = new \Tainacan\Bulk_Edit([
'query' => $query,
'collection_id' => $this->collection->get_id()
]);
$item_id = $bulk->get_item_id_by_index(7);
$item = $Tainacan_Items->fetch($item_id);
$this->assertEquals('testeItem 34', $item->get_title());
$item_id = $bulk->get_item_id_by_index(15);
$item = $Tainacan_Items->fetch($item_id);
$this->assertEquals('testeItem 26', $item->get_title());
$query = [
'posts_per_page' => 22,
'orderby' => 'title',
'order' => 'ASC'
];
$bulk = new \Tainacan\Bulk_Edit([
'query' => $query,
'collection_id' => $this->collection->get_id()
]);
$item_id = $bulk->get_item_id_by_index(19);
$item = $Tainacan_Items->fetch($item_id);
$this->assertEquals('testeItem 19', $item->get_title());
$item_id = $bulk->get_item_id_by_index(30);
$this->assertFalse($item_id);
}
/**
* @group sequence
*/
function test_api_get_item_in_sequence() {
$Tainacan_Items = \Tainacan\Repositories\Items::get_instance();
$query = [
'posts_per_page' => 22,
'orderby' => 'title',
'order' => 'ASC'
];
$bulk = new \Tainacan\Bulk_Edit([
'query' => $query,
'collection_id' => $this->collection->get_id()
]);
$request = new \WP_REST_Request(
'GET', $this->api_baseroute . '/' . $bulk->get_id() . '/sequence/7'
);
$response = $this->server->dispatch($request);
$id = $response->get_data();
$item = $Tainacan_Items->fetch($id);
$this->assertEquals('testeItem 07', $item->get_title());
}
function test_api_get_group() {
$query = [
'posts_per_page' => 22,
'orderby' => 'title',
'order' => 'ASC'
];
$bulk = new \Tainacan\Bulk_Edit([
'query' => $query,
'collection_id' => $this->collection->get_id()
]);
$request = new \WP_REST_Request(
'GET', $this->api_baseroute . '/' . $bulk->get_id()
);
$response = $this->server->dispatch($request);
$data = $response->get_data();
$this->assertEquals($bulk->get_id(), $data['id']);
$this->assertEquals($bulk->get_options()['order'], $data['options']['order']);
$this->assertEquals($bulk->get_options()['orderby'], $data['options']['orderby']);
$this->assertEquals($bulk->count_posts(), $data['items_count']);
$request = new \WP_REST_Request(
'GET', $this->api_baseroute . '/fefefe23232'
);
$response = $this->server->dispatch($request);
$this->assertEquals(404, $response->get_status());
}
}

View File

@ -504,9 +504,6 @@ class ImporterTests extends TAINACAN_UnitTestCase {
if(@file_get_contents ( 'https://www.w3schools.com/w3css/img_lights.jpg' ))
$this->assertEquals( 1, count( $attachments ) );
if(@file_get_contents ( 'https://www.youtube.com/watch?v=V8dpmD4HG5s&start_radio=1&list=RDEMZS6OrHEAut8dOA38mVtVpg' ))
$this->assertTrue( count($items[2]->get_attachments()) > 0 );
$document_id = $items[2]->get_document();
if(@file_get_contents ( 'https://www.codeproject.com/KB/GDI-plus/ImageProcessing2/img.jpg' ))
@ -519,4 +516,120 @@ class ImporterTests extends TAINACAN_UnitTestCase {
$this->assertFalse( is_numeric($document_id) );
}
/**
* @group importer_csv_special_fields
*/
public function test_special_fields_status_and_id(){
$Tainacan_Items = \Tainacan\Repositories\Items::get_instance();
$Tainacan_Metadata = \Tainacan\Repositories\Metadata::get_instance();
$file_name = 'demosaved.csv';
$csv_importer = new Importer\CSV();
$id = $csv_importer->get_id();
// open the file "demosaved.csv" for writing
$file = fopen($file_name, 'w');
// save the column headers
fputcsv($file, array('Column 1', 'special_item_status', 'Unknow Column'));
// Sample data
$data = array(
array('Data 11', 'publish', 'nothing'),
array('Data 21', 'private', 'void'),
array('Data 31', 'trash', 'empty'),
array('Data 41', 'future', 'null'),
array('Data 51', 'trash', 'zero')
);
// save each row of the data
foreach ($data as $row){
fputcsv($file, $row);
}
// Close the file
fclose($file);
$_SESSION['tainacan_importer'][$id]->set_tmp_file( $file_name );
// file isset on importer
$this->assertTrue( !empty( $_SESSION['tainacan_importer'][$id]->get_tmp_file() ) );
// count total items
$this->assertEquals( 5, $_SESSION['tainacan_importer'][$id]->get_source_number_of_items() );
// get metadata to mapping AVOIDING special fields
$headers = $_SESSION['tainacan_importer'][$id]->get_source_metadata();
$this->assertEquals( $headers[1], 'Unknow Column' );
$this->assertEquals( $_SESSION['tainacan_importer'][$id]->get_option('item_status_index'), 1 );
// inserting the collection
$collection = $this->tainacan_entity_factory->create_entity(
'collection',
array(
'name' => 'Other',
'description' => 'adasdasdsa',
'default_order' => 'DESC',
'status' => 'publish'
),
true
);
$metadatum = $this->tainacan_entity_factory->create_entity(
'metadatum',
array(
'name' => 'Data multiplo',
'description' => 'Descreve o dado do campo data.',
'collection' => $collection,
'status' => 'publish',
'metadata_type' => 'Tainacan\Metadata_Types\Text',
'multiple' => 'yes'
),
true
);
$metadatum = $this->tainacan_entity_factory->create_entity(
'metadatum',
array(
'name' => 'Texto simples',
'description' => 'Descreve o dado do campo data.',
'collection' => $collection,
'status' => 'publish',
'metadata_type' => 'Tainacan\Metadata_Types\Text',
'multiple' => 'no'
),
true
);
$collection_definition = [
'id' => $collection->get_id(),
'total_items' => $_SESSION['tainacan_importer'][$id]->get_source_number_of_items(),
];
// get collection metadata to map
$metadata = $Tainacan_Metadata->fetch_by_collection( $collection, [], 'OBJECT' ) ;
//create a random mapping
$map = [];
foreach ( $metadata as $index => $metadatum ){
if(isset($headers[$index]))
$map[$metadatum->get_id()] = $headers[$index];
}
$collection_definition['mapping'] = $map;
// add the collection
$_SESSION['tainacan_importer'][$id]->add_collection( $collection_definition );
while($_SESSION['tainacan_importer'][$id]->run()){
continue;
}
$items = $Tainacan_Items->fetch( ['order'=> 'DESC', 'orderby' => 'ID'], $collection, 'OBJECT' );
// only 3 items should be published
$this->assertEquals( 3, count( $items ) );
}
}