clean install

This commit is contained in:
2024-12-10 15:08:16 +01:00
commit e14eb2d8fd
31193 changed files with 3555714 additions and 0 deletions

View File

@@ -0,0 +1,11 @@
name: Asset
description: Provides an asset entity type for real world record keeping.
type: module
package: Asset
core_version_requirement: ^10
dependencies:
- drupal:system (>=8.8.0)
- drupal:user
- drupal:views
- entity:entity
- state_machine:state_machine

View File

@@ -0,0 +1,11 @@
entity.asset.add_page:
route_name: 'entity.asset.add_page'
title: 'Add asset'
appears_on:
- entity.asset.collection
entity.asset_type.add_form:
route_name: 'entity.asset_type.add_form'
title: 'Add asset type'
appears_on:
- entity.asset_type.collection

View File

@@ -0,0 +1,5 @@
entity.asset_type.collection:
title: 'Asset types'
route_name: entity.asset_type.collection
description: 'List of asset types'
parent: system.admin_structure

View File

@@ -0,0 +1,4 @@
entity.asset.collection:
route_name: entity.asset.collection
title: 'Assets'
base_route: system.admin_content

View File

@@ -0,0 +1,123 @@
<?php
/**
* @file
* Hooks and customizations for the asset module.
*/
use Drupal\Core\Render\Element;
use Drupal\Core\Routing\RouteMatchInterface;
use Drupal\asset\Entity\AssetInterface;
use Drupal\asset\Event\AssetEvent;
/**
* Implements hook_help().
*/
function asset_help($route_name, RouteMatchInterface $route_match) {
$output = '';
// Main module help for the asset module.
if ($route_name == 'help.page.asset') {
$output = '';
$output .= '<h3>' . t('About') . '</h3>';
$output .= '<p>' . t('Provides asset entity') . '</p>';
}
return $output;
}
/**
* Implements hook_ENTITY_TYPE_presave().
*/
function asset_asset_presave(AssetInterface $asset) {
// Dispatch an event on asset presave.
// @todo Replace this with core event via https://www.drupal.org/node/2551893.
$event = new AssetEvent($asset);
$event_dispatcher = \Drupal::service('event_dispatcher');
$event_dispatcher->dispatch($event, AssetEvent::PRESAVE);
}
/**
* Implements hook_ENTITY_TYPE_insert().
*/
function asset_asset_insert(AssetInterface $asset) {
// Dispatch an event on asset insert.
// @todo Replace this with core event via https://www.drupal.org/node/2551893.
$event = new AssetEvent($asset);
$event_dispatcher = \Drupal::service('event_dispatcher');
$event_dispatcher->dispatch($event, AssetEvent::INSERT);
}
/**
* Implements hook_ENTITY_TYPE_update().
*/
function asset_asset_update(AssetInterface $asset) {
// Dispatch an event on asset update.
// @todo Replace this with core event via https://www.drupal.org/node/2551893.
$event = new AssetEvent($asset);
$event_dispatcher = \Drupal::service('event_dispatcher');
$event_dispatcher->dispatch($event, AssetEvent::UPDATE);
}
/**
* Implements hook_ENTITY_TYPE_delete().
*/
function asset_asset_delete(AssetInterface $asset) {
// Dispatch an event on asset delete.
// @todo Replace this with core event via https://www.drupal.org/node/2551893.
$event = new AssetEvent($asset);
$event_dispatcher = \Drupal::service('event_dispatcher');
$event_dispatcher->dispatch($event, AssetEvent::DELETE);
}
/**
* Implements hook_theme().
*/
function asset_theme() {
return [
'asset' => [
'render element' => 'elements',
],
];
}
/**
* Implements hook_theme_suggestions_HOOK().
*/
function asset_theme_suggestions_asset(array $variables) {
$suggestions = [];
$asset = $variables['elements']['#asset'];
$sanitized_view_mode = strtr($variables['elements']['#view_mode'], '.', '_');
$suggestions[] = 'asset__' . $sanitized_view_mode;
$suggestions[] = 'asset__' . $asset->bundle();
$suggestions[] = 'asset__' . $asset->bundle() . '__' . $sanitized_view_mode;
$suggestions[] = 'asset__' . $asset->id();
$suggestions[] = 'asset__' . $asset->id() . '__' . $sanitized_view_mode;
return $suggestions;
}
/**
* Prepares variables for asset templates.
*
* Default template: asset.html.twig.
*
* @param array $variables
* An associative array containing:
* - elements: An associative array containing the asset information and any
* fields attached to the asset. Properties used:
* - #asset: A \Drupal\asset\Entity\Asset object. The asset entity.
* - attributes: HTML attributes for the containing element.
*/
function template_preprocess_asset(array &$variables) {
$variables['asset'] = $variables['elements']['#asset'];
// Helpful $content variable for templates.
foreach (Element::children($variables['elements']) as $key) {
$variables['content'][$key] = $variables['elements'][$key];
}
}

View File

@@ -0,0 +1,19 @@
administer assets:
title: 'Administer assets'
description: 'Admin access to all asset entities.'
restrict access: true
administer asset types:
title: 'Administer asset types'
description: 'Maintain the types of content available and the fields that are associated with those types.'
restrict access: true
view all asset revisions:
title: 'View all asset revisions'
description: 'Allow viewing asset entity revisions.'
restrict access: true
revert all asset revisions:
title: 'Revert all asset revisions'
description: 'Allow reverting to a previous asset entity revision.'
restrict access: true

View File

@@ -0,0 +1,3 @@
asset:
label: asset
entity_type: asset

View File

@@ -0,0 +1,18 @@
asset_default:
id: asset_default
group: asset
label: 'Default'
states:
active:
label: Active
archived:
label: Archived
transitions:
archive:
label: 'Archive'
from: [active]
to: archived
to_active:
label: 'Make active'
from: [archived]
to: active

View File

@@ -0,0 +1,10 @@
langcode: en
status: true
dependencies:
module:
- asset
id: asset_activate_action
label: 'Unarchive asset'
type: asset
plugin: 'asset_activate_action'
configuration: { }

View File

@@ -0,0 +1,10 @@
langcode: en
status: true
dependencies:
module:
- asset
id: asset_archive_action
label: 'Archive asset'
type: asset
plugin: 'asset_archive_action'
configuration: { }

View File

@@ -0,0 +1,10 @@
langcode: en
status: true
dependencies:
module:
- asset
id: asset_clone_action
label: 'Clone asset'
type: asset
plugin: 'asset_clone_action'
configuration: { }

View File

@@ -0,0 +1,10 @@
langcode: en
status: true
dependencies:
module:
- asset
id: asset_delete_action
label: 'Delete asset'
type: asset
plugin: entity:delete_action:asset
configuration: { }

View File

@@ -0,0 +1,730 @@
langcode: en
status: true
dependencies:
module:
- asset
- options
- user
id: asset_admin
label: 'Asset admin'
module: views
description: 'Find and manage asset entities.'
tag: default
base_table: asset_field_data
base_field: id
display:
default:
id: default
display_title: Master
display_plugin: default
position: 0
display_options:
title: Assets
fields:
asset_bulk_form:
id: asset_bulk_form
table: asset
field: asset_bulk_form
relationship: none
group_type: group
admin_label: ''
entity_type: asset
plugin_id: bulk_form
label: 'Bulk update'
exclude: false
alter:
alter_text: false
text: ''
make_link: false
path: ''
absolute: false
external: false
replace_spaces: false
path_case: none
trim_whitespace: false
alt: ''
rel: ''
link_class: ''
prefix: ''
suffix: ''
target: ''
nl2br: false
max_length: 0
word_boundary: true
ellipsis: true
more_link: false
more_link_text: ''
more_link_path: ''
strip_tags: false
trim: false
preserve_tags: ''
html: false
element_type: ''
element_class: ''
element_label_type: ''
element_label_class: ''
element_label_colon: true
element_wrapper_type: ''
element_wrapper_class: ''
element_default_classes: true
empty: ''
hide_empty: false
empty_zero: false
hide_alter_empty: true
action_title: 'With selection'
include_exclude: exclude
selected_actions: { }
status:
id: status
table: asset_field_data
field: status
relationship: none
group_type: group
admin_label: ''
entity_type: asset
entity_field: status
plugin_id: field
label: Status
exclude: false
alter:
alter_text: false
text: ''
make_link: false
path: ''
absolute: false
external: false
replace_spaces: false
path_case: none
trim_whitespace: false
alt: ''
rel: ''
link_class: ''
prefix: ''
suffix: ''
target: ''
nl2br: false
max_length: 0
word_boundary: true
ellipsis: true
more_link: false
more_link_text: ''
more_link_path: ''
strip_tags: false
trim: false
preserve_tags: ''
html: false
element_type: ''
element_class: ''
element_label_type: ''
element_label_class: ''
element_label_colon: true
element_wrapper_type: ''
element_wrapper_class: ''
element_default_classes: true
empty: ''
hide_empty: false
empty_zero: false
hide_alter_empty: true
click_sort_column: value
type: list_default
settings: { }
group_column: value
group_columns: { }
group_rows: true
delta_limit: 0
delta_offset: 0
delta_reversed: false
delta_first_last: false
multi_type: separator
separator: ', '
field_api_classes: false
id:
id: id
table: asset_field_data
field: id
relationship: none
group_type: group
admin_label: ''
entity_type: asset
entity_field: id
plugin_id: field
label: ID
exclude: false
alter:
alter_text: false
text: ''
make_link: false
path: ''
absolute: false
external: false
replace_spaces: false
path_case: none
trim_whitespace: false
alt: ''
rel: ''
link_class: ''
prefix: ''
suffix: ''
target: ''
nl2br: false
max_length: 0
word_boundary: true
ellipsis: true
more_link: false
more_link_text: ''
more_link_path: ''
strip_tags: false
trim: false
preserve_tags: ''
html: false
element_type: ''
element_class: ''
element_label_type: ''
element_label_class: ''
element_label_colon: true
element_wrapper_type: ''
element_wrapper_class: ''
element_default_classes: true
empty: ''
hide_empty: false
empty_zero: false
hide_alter_empty: true
click_sort_column: value
type: number_integer
settings:
thousand_separator: ''
prefix_suffix: true
group_column: value
group_columns: { }
group_rows: true
delta_limit: 0
delta_offset: 0
delta_reversed: false
delta_first_last: false
multi_type: separator
separator: ', '
field_api_classes: false
name:
id: name
table: asset_field_data
field: name
relationship: none
group_type: group
admin_label: ''
entity_type: asset
entity_field: name
plugin_id: field
label: Name
exclude: false
alter:
alter_text: false
text: ''
make_link: false
path: ''
absolute: false
external: false
replace_spaces: false
path_case: none
trim_whitespace: false
alt: ''
rel: ''
link_class: ''
prefix: ''
suffix: ''
target: ''
nl2br: false
max_length: 0
word_boundary: true
ellipsis: true
more_link: false
more_link_text: ''
more_link_path: ''
strip_tags: false
trim: false
preserve_tags: ''
html: false
element_type: ''
element_class: ''
element_label_type: ''
element_label_class: ''
element_label_colon: true
element_wrapper_type: ''
element_wrapper_class: ''
element_default_classes: true
empty: ''
hide_empty: false
empty_zero: false
hide_alter_empty: true
click_sort_column: value
type: string
settings:
link_to_entity: true
group_column: value
group_columns: { }
group_rows: true
delta_limit: 0
delta_offset: 0
delta_reversed: false
delta_first_last: false
multi_type: separator
separator: ', '
field_api_classes: false
type:
id: type
table: asset_field_data
field: type
relationship: none
group_type: group
admin_label: ''
entity_type: asset
entity_field: type
plugin_id: field
label: 'Asset type'
exclude: false
alter:
alter_text: false
text: ''
make_link: false
path: ''
absolute: false
external: false
replace_spaces: false
path_case: none
trim_whitespace: false
alt: ''
rel: ''
link_class: ''
prefix: ''
suffix: ''
target: ''
nl2br: false
max_length: 0
word_boundary: true
ellipsis: true
more_link: false
more_link_text: ''
more_link_path: ''
strip_tags: false
trim: false
preserve_tags: ''
html: false
element_type: ''
element_class: ''
element_label_type: ''
element_label_class: ''
element_label_colon: true
element_wrapper_type: ''
element_wrapper_class: ''
element_default_classes: true
empty: ''
hide_empty: false
empty_zero: false
hide_alter_empty: true
click_sort_column: target_id
type: entity_reference_label
settings:
link: true
group_column: target_id
group_columns: { }
group_rows: true
delta_limit: 0
delta_offset: 0
delta_reversed: false
delta_first_last: false
multi_type: separator
separator: ', '
field_api_classes: false
uid:
id: uid
table: asset_field_data
field: uid
relationship: none
group_type: group
admin_label: ''
entity_type: asset
entity_field: uid
plugin_id: field
label: 'Authored by'
exclude: false
alter:
alter_text: false
text: ''
make_link: false
path: ''
absolute: false
external: false
replace_spaces: false
path_case: none
trim_whitespace: false
alt: ''
rel: ''
link_class: ''
prefix: ''
suffix: ''
target: ''
nl2br: false
max_length: 0
word_boundary: true
ellipsis: true
more_link: false
more_link_text: ''
more_link_path: ''
strip_tags: false
trim: false
preserve_tags: ''
html: false
element_type: ''
element_class: ''
element_label_type: ''
element_label_class: ''
element_label_colon: true
element_wrapper_type: ''
element_wrapper_class: ''
element_default_classes: true
empty: ''
hide_empty: false
empty_zero: false
hide_alter_empty: true
click_sort_column: target_id
type: entity_reference_label
settings:
link: true
group_column: target_id
group_columns: { }
group_rows: true
delta_limit: 0
delta_offset: 0
delta_reversed: false
delta_first_last: false
multi_type: separator
separator: ', '
field_api_classes: false
operations:
id: operations
table: asset
field: operations
relationship: none
group_type: group
admin_label: ''
entity_type: null
entity_field: null
plugin_id: entity_operations
label: 'Operations links'
exclude: false
alter:
alter_text: false
text: ''
make_link: false
path: ''
absolute: false
external: false
replace_spaces: false
path_case: none
trim_whitespace: false
alt: ''
rel: ''
link_class: ''
prefix: ''
suffix: ''
target: ''
nl2br: false
max_length: 0
word_boundary: true
ellipsis: true
more_link: false
more_link_text: ''
more_link_path: ''
strip_tags: false
trim: false
preserve_tags: ''
html: false
element_type: ''
element_class: ''
element_label_type: ''
element_label_class: ''
element_label_colon: true
element_wrapper_type: ''
element_wrapper_class: ''
element_default_classes: true
empty: ''
hide_empty: false
empty_zero: false
hide_alter_empty: true
destination: true
pager:
type: full
options:
offset: 0
items_per_page: 25
total_pages: null
id: 0
tags:
next: 'Next '
previous: ' Previous'
first: '« First'
last: 'Last »'
expose:
items_per_page: false
items_per_page_label: 'Items per page'
items_per_page_options: '5, 10, 25, 50'
items_per_page_options_all: false
items_per_page_options_all_label: '- All -'
offset: false
offset_label: Offset
quantity: 9
pagination_heading_level: h4
exposed_form:
type: basic
options:
submit_button: Filter
reset_button: false
reset_button_label: Reset
exposed_sorts_label: 'Sort by'
expose_sort_order: true
sort_asc_label: Asc
sort_desc_label: Desc
access:
type: perm
options:
perm: 'administer assets'
cache:
type: tag
options: { }
empty:
area_text_custom:
id: area_text_custom
table: views
field: area_text_custom
relationship: none
group_type: group
admin_label: ''
plugin_id: text_custom
empty: true
content: 'No assets available.'
tokenize: false
sorts:
created:
id: created
table: asset_field_data
field: created
relationship: none
group_type: group
admin_label: ''
entity_type: asset
entity_field: created
plugin_id: date
order: ASC
expose:
label: ''
field_identifier: created
exposed: false
granularity: second
arguments: { }
filters:
name:
id: name
table: asset_field_data
field: name
relationship: none
group_type: group
admin_label: ''
entity_type: asset
entity_field: name
plugin_id: string
operator: contains
value: ''
group: 1
exposed: true
expose:
operator_id: name_op
label: Name
description: ''
use_operator: false
operator: name_op
operator_limit_selection: false
operator_list: { }
identifier: name
required: false
remember: false
multiple: false
remember_roles:
authenticated: authenticated
placeholder: ''
is_grouped: false
group_info:
label: ''
description: ''
identifier: ''
optional: true
widget: select
multiple: false
remember: false
default_group: All
default_group_multiple: { }
group_items: { }
type:
id: type
table: asset_field_data
field: type
relationship: none
group_type: group
admin_label: ''
entity_type: asset
entity_field: type
plugin_id: bundle
operator: in
value: { }
group: 1
exposed: true
expose:
operator_id: type_op
label: 'Asset type'
description: ''
use_operator: false
operator: type_op
operator_limit_selection: false
operator_list: { }
identifier: type
required: false
remember: false
multiple: false
remember_roles:
authenticated: authenticated
reduce: false
is_grouped: false
group_info:
label: ''
description: ''
identifier: ''
optional: true
widget: select
multiple: false
remember: false
default_group: All
default_group_multiple: { }
group_items: { }
filter_groups:
operator: AND
groups:
1: AND
style:
type: table
options:
grouping: { }
row_class: ''
default_row_class: true
columns:
asset_bulk_form: asset_bulk_form
status: status
id: id
name: name
type: type
uid: uid
operations: operations
default: '-1'
info:
asset_bulk_form:
align: ''
separator: ''
empty_column: false
responsive: ''
status:
sortable: true
default_sort_order: asc
align: ''
separator: ''
empty_column: false
responsive: ''
id:
sortable: true
default_sort_order: asc
align: ''
separator: ''
empty_column: false
responsive: ''
name:
sortable: true
default_sort_order: asc
align: ''
separator: ''
empty_column: false
responsive: ''
type:
sortable: true
default_sort_order: asc
align: ''
separator: ''
empty_column: false
responsive: ''
uid:
sortable: false
default_sort_order: asc
align: ''
separator: ''
empty_column: false
responsive: ''
operations:
align: ''
separator: ''
empty_column: false
responsive: ''
override: true
sticky: false
summary: ''
empty_table: true
caption: ''
description: ''
row:
type: fields
query:
type: views_query
options:
query_comment: ''
disable_sql_rewrite: false
distinct: false
replica: false
query_tags: { }
relationships: { }
header: { }
footer: { }
display_extenders: { }
cache_metadata:
max-age: 0
contexts:
- 'languages:language_content'
- 'languages:language_interface'
- url
- url.query_args
- user.permissions
tags: { }
collection:
id: collection
display_title: Page
display_plugin: page
position: 1
display_options:
display_extenders: { }
path: admin/content/asset
menu:
type: tab
title: Assets
description: ''
weight: 0
expanded: false
menu_name: admin
parent: ''
context: '0'
tab_options:
type: normal
title: asset
description: ''
weight: 0
cache_metadata:
max-age: 0
contexts:
- 'languages:language_content'
- 'languages:language_interface'
- url
- url.query_args
- user.permissions
tags: { }

View File

@@ -0,0 +1,40 @@
# Schema for the configuration files of the asset module.
asset.type.*:
type: config_entity
label: 'Asset type'
mapping:
id:
type: string
label: 'Machine-readable name'
label:
type: label
label: 'Type'
description:
type: text
label: 'Description'
workflow:
type: string
label: 'Workflow'
new_revision:
type: boolean
label: 'Create new revision'
condition.plugin.asset_type:
type: condition.plugin
mapping:
bundles:
type: sequence
sequence:
type: string
action.configuration.asset_activate_action:
type: action_configuration_default
label: 'Configuration for the asset activate action'
action.configuration.asset_archive_action:
type: action_configuration_default
label: 'Configuration for the asset archive action'
action.configuration.asset_clone_action:
type: action_configuration_default
label: 'Configuration for the asset clone action'

View File

@@ -0,0 +1,36 @@
<?php
namespace Drupal\asset;
use Drupal\Core\Entity\EntityInterface;
use Drupal\entity\BulkFormEntityListBuilder;
/**
* Defines a class to build a listing of asset entities.
*
* @ingroup asset
*/
class AssetListBuilder extends BulkFormEntityListBuilder {
/**
* {@inheritdoc}
*/
public function buildHeader() {
$header['id'] = $this->t('Asset ID');
$header['label'] = $this->t('Label');
$header['type'] = $this->t('Type');
return $header + parent::buildHeader();
}
/**
* {@inheritdoc}
*/
public function buildRow(EntityInterface $entity) {
/** @var \Drupal\asset\Entity\AssetInterface $entity */
$row['id'] = ['#markup' => $entity->id()];
$row['name'] = $entity->toLink($entity->label(), 'canonical')->toRenderable();
$row['type'] = ['#markup' => $entity->getBundleLabel()];
return $row + parent::buildRow($entity);
}
}

View File

@@ -0,0 +1,128 @@
<?php
namespace Drupal\asset;
use Drupal\Component\Datetime\TimeInterface;
use Drupal\Core\Cache\CacheBackendInterface;
use Drupal\Core\Cache\MemoryCache\MemoryCacheInterface;
use Drupal\Core\Database\Connection;
use Drupal\Core\Entity\EntityFieldManagerInterface;
use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Entity\EntityTypeBundleInfoInterface;
use Drupal\Core\Entity\EntityTypeInterface;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Core\Entity\Sql\SqlContentEntityStorage;
use Drupal\Core\Language\LanguageManagerInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
/**
* Defines the controller class for assets.
*
* This extends the base storage class, adding required special handling for
* asset entities.
*/
class AssetStorage extends SqlContentEntityStorage {
/**
* The time service.
*
* @var \Drupal\Component\Datetime\TimeInterface
*/
protected $time;
/**
* Constructs an AssetStorage object.
*
* @param \Drupal\Core\Entity\EntityTypeInterface $entity_type
* The entity type definition.
* @param \Drupal\Core\Database\Connection $database
* The database connection to be used.
* @param \Drupal\Core\Entity\EntityFieldManagerInterface $entity_field_manager
* The entity field manager.
* @param \Drupal\Core\Cache\CacheBackendInterface $cache
* The cache backend to be used.
* @param \Drupal\Core\Language\LanguageManagerInterface $language_manager
* The language manager.
* @param \Drupal\Core\Cache\MemoryCache\MemoryCacheInterface $memory_cache
* The memory cache backend to be used.
* @param \Drupal\Core\Entity\EntityTypeBundleInfoInterface $entity_type_bundle_info
* The entity type bundle info.
* @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager
* The entity type manager.
* @param \Drupal\Component\Datetime\TimeInterface $time
* The time service.
*/
public function __construct(EntityTypeInterface $entity_type, Connection $database, EntityFieldManagerInterface $entity_field_manager, CacheBackendInterface $cache, LanguageManagerInterface $language_manager, MemoryCacheInterface $memory_cache, EntityTypeBundleInfoInterface $entity_type_bundle_info, EntityTypeManagerInterface $entity_type_manager, TimeInterface $time) {
parent::__construct($entity_type, $database, $entity_field_manager, $cache, $language_manager, $memory_cache, $entity_type_bundle_info, $entity_type_manager);
$this->time = $time;
}
/**
* {@inheritdoc}
*/
public static function createInstance(ContainerInterface $container, EntityTypeInterface $entity_type) {
return new static(
$entity_type,
$container->get('database'),
$container->get('entity_field.manager'),
$container->get('cache.entity'),
$container->get('language_manager'),
$container->get('entity.memory_cache'),
$container->get('entity_type.bundle.info'),
$container->get('entity_type.manager'),
$container->get('datetime.time'),
);
}
/**
* {@inheritdoc}
*/
protected function doPreSave(EntityInterface $entity) {
/** @var \Drupal\Core\Entity\ContentEntityInterface $entity */
$id = parent::doPreSave($entity);
// If there is no original entity, bail.
if (empty($entity->original)) {
return $id;
}
// Load new and original states.
$new_state = $entity->get('status')->first()->getString();
$old_state = $entity->original->get('status')->first()->getString();
$state_unchanged = $new_state == $old_state;
// If the entity is not archived and this would otherwise not be a state
// transition but the archive timestamp is set, then transition to the
// archived state.
if ($state_unchanged && $old_state != 'archived' && $entity->getArchivedTime() != NULL) {
$entity->get('status')->first()->applyTransitionById('archive');
}
// If the entity is archived and this would otherwise not be a state
// transition but the archive timestemp is NULL, then transition to the
// active state.
if ($state_unchanged && $old_state == 'archived' && $entity->getArchivedTime() == NULL) {
$entity->get('status')->first()->applyTransitionById('to_active');
}
// If the state has not changed, bail.
if ($state_unchanged) {
return $id;
}
// If the state has changed to archived and no archived timestamp was
// specified, set it to the current time.
if ($new_state == 'archived' && $entity->getArchivedTime() == NULL) {
$entity->setArchivedTime($this->time->getRequestTime());
}
// Or, if the state has changed from archived, set a null value.
elseif ($old_state == 'archived') {
$entity->setArchivedTime(NULL);
}
return $id;
}
}

View File

@@ -0,0 +1,57 @@
<?php
namespace Drupal\asset;
use Drupal\Core\Config\Entity\ConfigEntityListBuilder;
use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Url;
/**
* Provides a listing of asset type entities.
*/
class AssetTypeListBuilder extends ConfigEntityListBuilder {
/**
* {@inheritdoc}
*/
public function buildHeader() {
$header['label'] = $this->t('Asset type');
$header['id'] = $this->t('Machine name');
return $header + parent::buildHeader();
}
/**
* {@inheritdoc}
*/
public function buildRow(EntityInterface $entity) {
$row['label'] = $entity->label();
$row['id'] = $entity->id();
// You probably want a few more properties here...
return $row + parent::buildRow($entity);
}
/**
* {@inheritdoc}
*/
public function getDefaultOperations(EntityInterface $entity) {
$operations = parent::getDefaultOperations($entity);
// Place the edit operation after the operations added by field_ui.module
// which have the weights 15, 20, 25.
if (isset($operations['edit'])) {
$operations['edit']['weight'] = 30;
}
return $operations;
}
/**
* {@inheritdoc}
*/
public function render() {
$build = parent::render();
$build['table']['#empty'] = $this->t('No asset types available. <a href=":link">Add asset type</a>.', [
':link' => Url::fromRoute('entity.asset_type.add_form')->toString(),
]);
return $build;
}
}

View File

@@ -0,0 +1,285 @@
<?php
namespace Drupal\asset\Entity;
use Drupal\Core\Entity\EntityChangedTrait;
use Drupal\Core\Entity\EntityTypeInterface;
use Drupal\Core\Entity\RevisionLogEntityTrait;
use Drupal\Core\Field\BaseFieldDefinition;
use Drupal\entity\Revision\RevisionableContentEntityBase;
use Drupal\user\EntityOwnerTrait;
/**
* Defines the asset entity.
*
* @ingroup asset
*
* @ContentEntityType(
* id = "asset",
* label = @Translation("Asset"),
* bundle_label = @Translation("Asset type"),
* label_collection = @Translation("Assets"),
* label_singular = @Translation("asset"),
* label_plural = @Translation("assets"),
* label_count = @PluralTranslation(
* singular = "@count asset",
* plural = "@count assets",
* ),
* handlers = {
* "storage" = "Drupal\asset\AssetStorage",
* "access" = "\Drupal\entity\UncacheableEntityAccessControlHandler",
* "list_builder" = "\Drupal\asset\AssetListBuilder",
* "permission_provider" = "\Drupal\entity\UncacheableEntityPermissionProvider",
* "view_builder" = "Drupal\Core\Entity\EntityViewBuilder",
* "views_data" = "Drupal\views\EntityViewsData",
* "form" = {
* "add" = "Drupal\asset\Form\AssetForm",
* "edit" = "Drupal\asset\Form\AssetForm",
* "delete" = "Drupal\Core\Entity\ContentEntityDeleteForm",
* "delete-multiple-confirm" = "Drupal\Core\Entity\Form\DeleteMultipleForm",
* },
* "route_provider" = {
* "default" = "Drupal\entity\Routing\AdminHtmlRouteProvider",
* "revision" = "\Drupal\entity\Routing\RevisionRouteProvider",
* },
* "local_task_provider" = {
* "default" = "\Drupal\entity\Menu\DefaultEntityLocalTaskProvider",
* },
* },
* base_table = "asset",
* data_table = "asset_field_data",
* revision_table = "asset_revision",
* translatable = TRUE,
* revisionable = TRUE,
* show_revision_ui = TRUE,
* admin_permission = "administer assets",
* entity_keys = {
* "id" = "id",
* "revision" = "revision_id",
* "bundle" = "type",
* "label" = "name",
* "owner" = "uid",
* "uuid" = "uuid",
* "langcode" = "langcode",
* },
* bundle_entity_type = "asset_type",
* field_ui_base_route = "entity.asset_type.edit_form",
* common_reference_target = TRUE,
* permission_granularity = "bundle",
* links = {
* "canonical" = "/asset/{asset}",
* "add-page" = "/asset/add",
* "add-form" = "/asset/add/{asset_type}",
* "collection" = "/admin/content/asset",
* "delete-form" = "/asset/{asset}/delete",
* "delete-multiple-form" = "/asset/delete",
* "edit-form" = "/asset/{asset}/edit",
* "revision" = "/asset/{asset}/revisions/{asset_revision}/view",
* "revision-revert-form" = "/asset/{asset}/revisions/{asset_revision}/revert",
* "version-history" = "/asset/{asset}/revisions",
* },
* revision_metadata_keys = {
* "revision_user" = "revision_user",
* "revision_created" = "revision_created",
* "revision_log_message" = "revision_log_message"
* },
* )
*/
class Asset extends RevisionableContentEntityBase implements AssetInterface {
use EntityChangedTrait;
use EntityOwnerTrait;
use RevisionLogEntityTrait;
/**
* {@inheritdoc}
*/
public function label() {
return $this->getName();
}
/**
* {@inheritdoc}
*/
public function getName() {
return $this->get('name')->value;
}
/**
* {@inheritdoc}
*/
public function setName($name) {
$this->set('name', $name);
return $this;
}
/**
* {@inheritdoc}
*/
public function getCreatedTime() {
return $this->get('created')->value;
}
/**
* {@inheritdoc}
*/
public function setCreatedTime($timestamp) {
$this->set('created', $timestamp);
return $this;
}
/**
* {@inheritdoc}
*/
public function getArchivedTime() {
return $this->get('archived')->value;
}
/**
* {@inheritdoc}
*/
public function setArchivedTime($timestamp) {
$this->set('archived', $timestamp);
return $this;
}
/**
* {@inheritdoc}
*/
public function getBundleLabel() {
/** @var \Drupal\asset\Entity\AssetTypeInterface $type */
$type = $this->entityTypeManager()
->getStorage('asset_type')
->load($this->bundle());
return $type->label();
}
/**
* {@inheritdoc}
*/
public static function getCurrentUserId() {
return [\Drupal::currentUser()->id()];
}
/**
* {@inheritdoc}
*/
public static function getRequestTime() {
return \Drupal::time()->getRequestTime();
}
/**
* {@inheritdoc}
*/
public static function baseFieldDefinitions(EntityTypeInterface $entity_type) {
$fields = parent::baseFieldDefinitions($entity_type);
$fields += static::ownerBaseFieldDefinitions($entity_type);
$fields += static::revisionLogBaseFieldDefinitions($entity_type);
$fields['name'] = BaseFieldDefinition::create('string')
->setLabel(t('Name'))
->setDescription(t('The name of the asset.'))
->setRevisionable(TRUE)
->setTranslatable(TRUE)
->setRequired(TRUE)
->setSetting('max_length', 255)
->setSetting('text_processing', 0)
->setDisplayOptions('view', [
'label' => 'hidden',
'type' => 'string',
'weight' => -5,
])
->setDisplayOptions('form', [
'type' => 'string_textfield',
'weight' => -5,
])
->setDisplayConfigurable('form', TRUE);
$fields['status'] = BaseFieldDefinition::create('state')
->setLabel(t('Status'))
->setDescription(t('Indicates the status of the asset.'))
->setRevisionable(TRUE)
->setRequired(TRUE)
->setSetting('max_length', 255)
->setDisplayOptions('view', [
'label' => 'hidden',
'type' => 'state_transition_form',
'weight' => 10,
])
->setDisplayOptions('form', [
'type' => 'options_select',
'weight' => 11,
])
->setDisplayConfigurable('form', TRUE)
->setDisplayConfigurable('view', TRUE)
->setSetting('workflow_callback', ['\Drupal\asset\Entity\Asset', 'getWorkflowId']);
$fields['uid'] = BaseFieldDefinition::create('entity_reference')
->setLabel(t('Authored by'))
->setDescription(t('The user ID of author of the asset.'))
->setRevisionable(TRUE)
->setSetting('target_type', 'user')
->setSetting('handler', 'default')
->setDefaultValueCallback('Drupal\asset\Entity\Asset::getCurrentUserId')
->setDisplayOptions('view', [
'label' => 'hidden',
'type' => 'author',
'weight' => 0,
])
->setDisplayOptions('form', [
'type' => 'entity_reference_autocomplete',
'weight' => 12,
'settings' => [
'match_operator' => 'CONTAINS',
'size' => '60',
'autocomplete_type' => 'tags',
'placeholder' => '',
],
])
->setDisplayConfigurable('form', TRUE)
->setDisplayConfigurable('view', TRUE);
$fields['created'] = BaseFieldDefinition::create('created')
->setLabel(t('Authored on'))
->setDescription(t('The time that the asset was created.'))
->setRevisionable(TRUE)
->setDefaultValueCallback(static::class . '::getRequestTime')
->setDisplayOptions('view', [
'label' => 'hidden',
'type' => 'timestamp',
'weight' => 0,
])
->setDisplayOptions('form', [
'type' => 'datetime_timestamp',
'weight' => 13,
])
->setDisplayConfigurable('form', TRUE);
$fields['changed'] = BaseFieldDefinition::create('changed')
->setLabel(t('Changed'))
->setDescription(t('The time the asset was last edited.'))
->setRevisionable(TRUE);
$fields['archived'] = BaseFieldDefinition::create('timestamp')
->setLabel(t('Timestamp'))
->setDescription(t('The time the asset was archived.'))
->setRevisionable(TRUE);
return $fields;
}
/**
* Gets the workflow ID for the state field.
*
* @param \Drupal\asset\Entity\AssetInterface $asset
* The asset entity.
*
* @return string
* The workflow ID.
*/
public static function getWorkflowId(AssetInterface $asset) {
$workflow = AssetType::load($asset->bundle())->getWorkflowId();
return $workflow;
}
}

View File

@@ -0,0 +1,82 @@
<?php
namespace Drupal\asset\Entity;
use Drupal\Core\Entity\ContentEntityInterface;
use Drupal\Core\Entity\EntityChangedInterface;
use Drupal\Core\Entity\RevisionLogInterface;
use Drupal\user\EntityOwnerInterface;
/**
* Provides an interface for defining asset entities.
*
* @ingroup asset
*/
interface AssetInterface extends ContentEntityInterface, EntityChangedInterface, RevisionLogInterface, EntityOwnerInterface {
/**
* Gets the asset name.
*
* @return string
* The asset name.
*/
public function getName();
/**
* Sets the asset name.
*
* @param string $name
* The asset name.
*
* @return \Drupal\asset\Entity\AssetInterface
* The asset entity.
*/
public function setName($name);
/**
* Gets the asset creation timestamp.
*
* @return int
* Creation timestamp of the asset.
*/
public function getCreatedTime();
/**
* Sets the asset creation timestamp.
*
* @param int $timestamp
* Creation timestamp of the asset.
*
* @return \Drupal\asset\Entity\AssetInterface
* The asset entity.
*/
public function setCreatedTime($timestamp);
/**
* Gets the asset archived timestamp.
*
* @return int
* Archived timestamp of the asset.
*/
public function getArchivedTime();
/**
* Sets the asset archived timestamp.
*
* @param int $timestamp
* Archived timestamp of the asset.
*
* @return \Drupal\asset\Entity\AssetInterface
* The asset entity.
*/
public function setArchivedTime($timestamp);
/**
* Gets the label of the the asset type.
*
* @return string
* The label of the asset type.
*/
public function getBundleLabel();
}

View File

@@ -0,0 +1,178 @@
<?php
namespace Drupal\asset\Entity;
use Drupal\Core\Config\Entity\ConfigEntityBundleBase;
use Drupal\Core\Entity\EntityStorageInterface;
/**
* Defines the asset type entity.
*
* @ConfigEntityType(
* id = "asset_type",
* label = @Translation("Asset type"),
* label_collection = @Translation("Asset types"),
* label_singular = @Translation("Asset type"),
* label_plural = @Translation("Asset types"),
* label_count = @PluralTranslation(
* singular = "@count asset type",
* plural = "@count asset types",
* ),
* handlers = {
* "list_builder" = "Drupal\asset\AssetTypeListBuilder",
* "view_builder" = "Drupal\Core\Entity\EntityViewBuilder",
* "form" = {
* "add" = "Drupal\asset\Form\AssetTypeForm",
* "edit" = "Drupal\asset\Form\AssetTypeForm",
* "delete" = "\Drupal\Core\Entity\EntityDeleteForm",
* },
* "route_provider" = {
* "default" = "Drupal\entity\Routing\DefaultHtmlRouteProvider",
* },
* },
* admin_permission = "administer asset types",
* config_prefix = "type",
* bundle_of = "asset",
* entity_keys = {
* "id" = "id",
* "label" = "label",
* "uuid" = "uuid"
* },
* links = {
* "canonical" = "/admin/structure/asset-type/{asset_type}",
* "add-form" = "/admin/structure/asset-type/add",
* "edit-form" = "/admin/structure/asset-type/{asset_type}/edit",
* "delete-form" = "/admin/structure/asset-type/{asset_type}/delete",
* "collection" = "/admin/structure/asset-type"
* },
* config_export = {
* "id",
* "label",
* "description",
* "workflow",
* "new_revision",
* }
* )
*/
class AssetType extends ConfigEntityBundleBase implements AssetTypeInterface {
/**
* The asset type ID.
*
* @var string
*/
protected $id;
/**
* The asset type label.
*
* @var string
*/
protected $label;
/**
* A brief description of this asset type.
*
* @var string
*/
protected $description;
/**
* The asset type workflow ID.
*
* @var string
*/
protected $workflow;
/**
* Default value of the 'Create new revision' checkbox of the asset type.
*
* @var bool
*/
protected $new_revision = TRUE;
/**
* {@inheritdoc}
*/
public function getDescription() {
return $this->description;
}
/**
* {@inheritdoc}
*/
public function setDescription($description) {
return $this->set('description', $description);
}
/**
* {@inheritdoc}
*/
public function postSave(EntityStorageInterface $storage, $update = TRUE) {
parent::postSave($storage, $update);
// If the asset type id changed, update all existing assets of that type.
if ($update && $this->getOriginalId() != $this->id()) {
$update_count = $this->entityTypeManager()->getStorage('asset')->updateType($this->getOriginalId(), $this->id());
if ($update_count) {
\Drupal::messenger()->addMessage(\Drupal::translation()->formatPlural($update_count,
'Changed the asset type of 1 post from %old-type to %type.',
'Changed the asset type of @count posts from %old-type to %type.',
[
'%old-type' => $this->getOriginalId(),
'%type' => $this->id(),
]));
}
}
if ($update) {
// Clear the cached field definitions as some settings affect the field
// definitions.
$this->entityTypeManager()->clearCachedDefinitions();
\Drupal::service('entity_field.manager')->clearCachedFieldDefinitions();
}
}
/**
* {@inheritdoc}
*/
public function getWorkflowId() {
return $this->workflow;
}
/**
* {@inheritdoc}
*/
public function setWorkflowId($workflow_id) {
$this->workflow = $workflow_id;
return $this;
}
/**
* {@inheritdoc}
*/
public function calculateDependencies() {
parent::calculateDependencies();
// The asset type must depend on the module that provides the workflow.
$workflow_manager = \Drupal::service('plugin.manager.workflow');
$workflow = $workflow_manager->createInstance($this->getWorkflowId());
$this->calculatePluginDependencies($workflow);
return $this;
}
/**
* {@inheritdoc}
*/
public function shouldCreateNewRevision() {
return $this->new_revision;
}
/**
* {@inheritdoc}
*/
public function setNewRevision($new_revision) {
return $this->set('new_revision', $new_revision);
}
}

View File

@@ -0,0 +1,12 @@
<?php
namespace Drupal\asset\Entity;
use Drupal\Core\Config\Entity\ConfigEntityInterface;
use Drupal\Core\Entity\EntityDescriptionInterface;
use Drupal\Core\Entity\RevisionableEntityBundleInterface;
/**
* Provides an interface for defining asset type entities.
*/
interface AssetTypeInterface extends ConfigEntityInterface, EntityDescriptionInterface, RevisionableEntityBundleInterface {}

View File

@@ -0,0 +1,35 @@
<?php
namespace Drupal\asset\Event;
use Drupal\Component\EventDispatcher\Event;
use Drupal\asset\Entity\AssetInterface;
/**
* Event that is fired by asset save, delete and clone operations.
*/
class AssetEvent extends Event {
const PRESAVE = 'asset_presave';
const INSERT = 'asset_insert';
const UPDATE = 'asset_update';
const DELETE = 'asset_delete';
/**
* The Asset entity.
*
* @var \Drupal\asset\Entity\AssetInterface
*/
public AssetInterface $asset;
/**
* Constructs the object.
*
* @param \Drupal\asset\Entity\AssetInterface $asset
* The Asset entity.
*/
public function __construct(AssetInterface $asset) {
$this->asset = $asset;
}
}

View File

@@ -0,0 +1,26 @@
<?php
namespace Drupal\asset\Form;
use Drupal\Core\Entity\ContentEntityForm;
use Drupal\Core\Form\FormStateInterface;
/**
* Form controller for asset entities.
*
* @ingroup asset
*/
class AssetForm extends ContentEntityForm {
/**
* {@inheritdoc}
*/
public function save(array $form, FormStateInterface $form_state) {
$status = parent::save($form, $form_state);
$entity_url = $this->entity->toUrl()->setAbsolute()->toString();
$this->messenger()->addMessage($this->t('Saved asset: <a href=":url">%label</a>', [':url' => $entity_url, '%label' => $this->entity->label()]));
$form_state->setRedirectUrl($this->entity->toUrl());
return $status;
}
}

View File

@@ -0,0 +1,115 @@
<?php
namespace Drupal\asset\Form;
use Drupal\Core\Entity\EntityForm;
use Drupal\Core\Form\FormStateInterface;
use Drupal\state_machine\WorkflowManagerInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
/**
* Form controller for asset type entities.
*
* @package Drupal\asset\Form
*/
class AssetTypeForm extends EntityForm {
/**
* The workflow manager.
*
* @var \Drupal\state_machine\WorkflowManagerInterface
*/
protected $workflowManager;
/**
* Constructs a new AssetTypeForm object.
*
* @param \Drupal\state_machine\WorkflowManagerInterface $workflow_manager
* The workflow manager.
*/
public function __construct(WorkflowManagerInterface $workflow_manager) {
$this->workflowManager = $workflow_manager;
}
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container) {
return new static(
$container->get('plugin.manager.workflow')
);
}
/**
* {@inheritdoc}
*/
public function form(array $form, FormStateInterface $form_state) {
$form = parent::form($form, $form_state);
$asset_type = $this->entity;
$form['label'] = [
'#type' => 'textfield',
'#title' => $this->t('Label'),
'#maxlength' => 255,
'#default_value' => $asset_type->label(),
'#description' => $this->t('Label for the asset type.'),
'#required' => TRUE,
];
$form['id'] = [
'#type' => 'machine_name',
'#default_value' => $asset_type->id(),
'#machine_name' => [
'exists' => '\Drupal\asset\Entity\AssetType::load',
],
'#disabled' => !$asset_type->isNew(),
];
$form['description'] = [
'#type' => 'textarea',
'#title' => $this->t('Description'),
'#default_value' => $asset_type->getDescription(),
];
$form['workflow'] = [
'#type' => 'select',
'#title' => $this->t('Workflow'),
'#options' => $this->workflowManager->getGroupedLabels('asset'),
'#default_value' => $asset_type->getWorkflowId(),
'#description' => $this->t('Used by all assets of this type.'),
];
$form['new_revision'] = [
'#type' => 'checkbox',
'#title' => $this->t('Create new revision'),
'#default_value' => $asset_type->shouldCreateNewRevision(),
];
return $form;
}
/**
* {@inheritdoc}
*/
public function save(array $form, FormStateInterface $form_state) {
$asset_type = $this->entity;
$status = $asset_type->save();
switch ($status) {
case SAVED_NEW:
$this->messenger()->addMessage($this->t('Created the %label asset type.', [
'%label' => $asset_type->label(),
]));
break;
default:
$this->messenger()->addMessage($this->t('Saved the %label asset type.', [
'%label' => $asset_type->label(),
]));
}
$form_state->setRedirectUrl($asset_type->toUrl('collection'));
return $status;
}
}

View File

@@ -0,0 +1,21 @@
<?php
namespace Drupal\asset\Plugin\Action;
/**
* Action that makes an asset active.
*
* @Action(
* id = "asset_activate_action",
* label = @Translation("Makes an Asset active"),
* type = "asset"
* )
*/
class AssetActivate extends AssetStateChangeBase {
/**
* {@inheritdoc}
*/
protected $targetState = 'active';
}

View File

@@ -0,0 +1,21 @@
<?php
namespace Drupal\asset\Plugin\Action;
/**
* Action that archives an asset.
*
* @Action(
* id = "asset_archive_action",
* label = @Translation("Archive an asset"),
* type = "asset"
* )
*/
class AssetArchive extends AssetStateChangeBase {
/**
* {@inheritdoc}
*/
protected $targetState = 'archived';
}

View File

@@ -0,0 +1,86 @@
<?php
namespace Drupal\asset\Plugin\Action;
use Drupal\Core\Action\Plugin\Action\EntityActionBase;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Core\Session\AccountInterface;
use Drupal\asset\Entity\AssetInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
/**
* Action that clones an asset.
*
* @Action(
* id = "asset_clone_action",
* label = @Translation("Clone an asset"),
* type = "asset"
* )
*/
class AssetClone extends EntityActionBase {
/**
* The current user.
*
* @var \Drupal\Core\Session\AccountInterface
*/
protected $currentUser;
/**
* Constructs an AssetClone object.
*
* @param mixed[] $configuration
* A configuration array containing information about the plugin instance.
* @param string $plugin_id
* The plugin ID for the plugin instance.
* @param mixed $plugin_definition
* The plugin implementation definition.
* @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager
* The entity type manager.
* @param \Drupal\Core\Session\AccountInterface $current_user
* The current user.
*/
public function __construct(array $configuration, $plugin_id, $plugin_definition, EntityTypeManagerInterface $entity_type_manager, AccountInterface $current_user) {
parent::__construct($configuration, $plugin_id, $plugin_definition, $entity_type_manager);
$this->currentUser = $current_user;
}
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
return new static(
$configuration,
$plugin_id,
$plugin_definition,
$container->get('entity_type.manager'),
$container->get('current_user'),
);
}
/**
* {@inheritdoc}
*/
public function execute(?AssetInterface $asset = NULL) {
if ($asset) {
$cloned_asset = $asset->createDuplicate();
$cloned_asset->setOwnerId($this->currentUser->id());
$new_name = $asset->getName() . ' ' . $this->t('(clone of asset #@id)', ['@id' => $asset->id()]);
$cloned_asset->setName($new_name);
$cloned_asset->save();
$this->messenger()->addMessage($this->t('Asset saved: <a href=":uri">%asset_label</a>', [':uri' => $cloned_asset->toUrl()->toString(), '%asset_label' => $cloned_asset->label()]));
}
}
/**
* {@inheritdoc}
*/
public function access($object, ?AccountInterface $account = NULL, $return_as_object = FALSE) {
/** @var \Drupal\asset\Entity\AssetInterface $object */
$result = $object->access('view', $account, TRUE)
->andIf($object->access('create', $account, TRUE));
return $return_as_object ? $result : $result->isAllowed();
}
}

View File

@@ -0,0 +1,99 @@
<?php
namespace Drupal\asset\Plugin\Action;
use Drupal\Core\Access\AccessResult;
use Drupal\Core\Action\Plugin\Action\EntityActionBase;
use Drupal\Core\Session\AccountInterface;
use Drupal\asset\Entity\AssetInterface;
/**
* Base class for actions that change the asset status state.
*/
abstract class AssetStateChangeBase extends EntityActionBase {
/**
* The target state to transition to.
*
* @var string
*/
protected $targetState;
/**
* {@inheritdoc}
*/
public function execute(?AssetInterface $asset = NULL) {
// Bail if there is no asset.
if (empty($asset)) {
return;
}
// Apply the transition to target state if not already the current state.
/** @var \Drupal\state_machine\Plugin\Field\FieldType\StateItemInterface $state_item */
$state_item = $asset->get('status')->first();
if ($state_item->getOriginalId() !== $this->targetState && $transition = $state_item->getWorkflow()->findTransition($state_item->getOriginalId(), $this->targetState)) {
$state_item->applyTransition($transition);
$asset->setNewRevision(TRUE);
// Validate the entity before saving.
$violations = $asset->validate();
if ($violations->count() > 0) {
$this->messenger()->addWarning(
$this->t('Could not change the status of <a href=":entity_link">%entity_label</a>: validation failed.',
[
':entity_link' => $asset->toUrl()->setAbsolute()->toString(),
'%entity_label' => $asset->label(),
],
),
);
return;
}
$asset->save();
}
}
/**
* {@inheritdoc}
*/
public function access($object, ?AccountInterface $account = NULL, $return_as_object = FALSE) {
/** @var \Drupal\asset\Entity\AssetInterface $object */
// First check entity and state field access.
$result = $object->get('status')->access('edit', $account, TRUE)
->andIf($object->access('update', $account, TRUE));
// Save the state field.
/** @var \Drupal\state_machine\Plugin\Field\FieldType\StateItemInterface $state_item */
$state_item = $object->get('status')->first();
// If the state field is already in the target state, return early.
// The workflow will not allow a transition to the same state but the
// action itself does not need to fail.
if ($state_item->getOriginalId() === $this->targetState) {
return $return_as_object ? $result : $result->isAllowed();
}
// Check that the target state exists for the workflow.
$workflow = $state_item->getWorkflow();
$target_state = $workflow->getState($this->targetState);
// Deny access if the workflow does not support the target state.
if (empty($target_state)) {
$result = $result->orIf(AccessResult::forbidden(
$this->t('The %workflow workflow does not support the %target_state state.', ['%workflow' => $workflow->getLabel(), '%target_state' => $this->targetState]),
));
}
// Else check that a transition exists to the desired target state.
else {
$transition = $workflow->findTransition($state_item->getOriginalId(), $this->targetState);
$result = $result->orIf(AccessResult::forbiddenIf(
empty($transition) || !$state_item->isTransitionAllowed($transition->getId()),
$this->t('The state transition from %original_state to %target_state is not allowed.', ['%original' => $state_item->getOriginalLabel(), '%target_state' => $target_state->getLabel()]),
));
}
return $return_as_object ? $result : $result->isAllowed();
}
}

View File

@@ -0,0 +1,78 @@
<?php
namespace Drupal\asset\Plugin\migrate\source\d7;
use Drupal\migrate\Row;
use Drupal\migrate_drupal\Plugin\migrate\source\d7\FieldableEntity;
/**
* Asset source from database.
*
* @MigrateSource(
* id = "d7_asset",
* source_module = "farm_asset"
* )
*
* @deprecated in farm:3.0.0 and is removed from farm:4.0.0. Support for farmOS
* v1 migrations was dropped in farmOS 3.x.
* @see https://www.drupal.org/project/farm/issues/3410701
* @see https://www.drupal.org/project/farm/issues/3382616
*/
class Asset extends FieldableEntity {
/**
* {@inheritdoc}
*/
public function query() {
$query = $this->select('farm_asset', 'fa')
->fields('fa')
->distinct()
->orderBy('id');
if (isset($this->configuration['bundle'])) {
$query->condition('fa.type', (array) $this->configuration['bundle'], 'IN');
}
return $query;
}
/**
* {@inheritdoc}
*/
public function fields() {
$fields = [
'id' => $this->t('The asset ID'),
'name' => $this->t('The asset name'),
'type' => $this->t('The asset type'),
'uid' => $this->t('The asset author ID'),
'created' => $this->t('Timestamp when the asset was created'),
'changed' => $this->t('Timestamp when the asset was last modified'),
'archived' => $this->t('Timestamp when the asset was archived'),
];
return $fields;
}
/**
* {@inheritdoc}
*/
public function prepareRow(Row $row) {
$id = $row->getSourceProperty('id');
$type = $row->getSourceProperty('type');
// Get Field API field values.
foreach ($this->getFields('farm_asset', $type) as $field_name => $field) {
$row->setSourceProperty($field_name, $this->getFieldValues('farm_asset', $field_name, $id));
}
return parent::prepareRow($row);
}
/**
* {@inheritdoc}
*/
public function getIds() {
$ids['id']['type'] = 'integer';
return $ids;
}
}

View File

@@ -0,0 +1,22 @@
{#
/**
* @file asset.html.twig
* Default theme implementation to present asset data.
*
* This template is used when viewing asset pages.
*
*
* Available variables:
* - content: A list of content items. Use 'content' to print all content, or
* - attributes: HTML attributes for the container element.
*
* @see template_preprocess_asset()
*
* @ingroup themeable
*/
#}
<div{{ attributes.addClass('asset') }}>
{% if content %}
{{- content -}}
{% endif %}
</div>

View File

@@ -0,0 +1,7 @@
name: Asset module tests
description: Support module for asset testing.
type: module
package: Testing
core_version_requirement: ^10
dependencies:
- farm:asset

View File

@@ -0,0 +1,6 @@
id: default
label: default
description: 'Test asset type'
langcode: en
workflow: asset_default
new_revision: TRUE

View File

@@ -0,0 +1,46 @@
langcode: en
status: true
dependencies:
module:
- asset
- datetime
- options
- user
id: asset_test_view
label: 'asset test view'
module: views
description: ''
tag: ''
base_table: asset_field_data
base_field: id
display:
default:
display_options:
defaults:
fields: false
pager: false
sorts: false
row:
type: fields
fields:
id:
id: id
table: asset_field_data
field: id
relationship: none
entity_type: asset
entity_field: id
plugin_id: field
name:
id: name
table: asset_field_data
field: name
relationship: none
entity_type: asset
entity_field: name
plugin_id: field
sorts: { }
display_plugin: default
display_title: Master
id: default
position: 0

View File

@@ -0,0 +1,177 @@
<?php
namespace Drupal\Tests\asset\Functional;
use Drupal\Core\StringTranslation\StringTranslationTrait;
use Drupal\asset\Entity\Asset;
/**
* Tests the asset CRUD.
*
* @group farm
*/
class AssetCRUDTest extends AssetTestBase {
use StringTranslationTrait;
/**
* Run all tests.
*/
public function testAll() {
$this->doTestFieldsVisibility();
$this->doTestCreateAsset();
$this->doTestViewAsset();
$this->doTestEditAsset();
$this->doTestDeleteAsset();
$this->doTestArchiveAsset();
$this->doTestArchiveAssetViaTimestamp();
}
/**
* Fields are displayed correctly.
*/
public function doTestFieldsVisibility() {
$this->drupalGet('asset/add/default');
$assert_session = $this->assertSession();
$assert_session->statusCodeEquals(200);
$assert_session->fieldExists('name[0][value]');
$assert_session->fieldExists('status');
$assert_session->fieldExists('revision_log_message[0][value]');
$assert_session->fieldExists('uid[0][target_id]');
$assert_session->fieldExists('created[0][value][date]');
$assert_session->fieldExists('created[0][value][time]');
}
/**
* Create asset entity.
*/
public function doTestCreateAsset() {
$assert_session = $this->assertSession();
$name = $this->randomMachineName();
$edit = [
'name[0][value]' => $name,
];
$this->drupalGet('asset/add/default');
$this->submitForm($edit, 'Save');
$result = \Drupal::entityTypeManager()
->getStorage('asset')
->getQuery()
->accessCheck(TRUE)
->range(0, 1)
->execute();
$asset_id = reset($result);
$asset = Asset::load($asset_id);
$this->assertEquals($asset->get('name')->value, $name, 'asset has been saved.');
$assert_session->pageTextContains("Saved asset: $name");
$assert_session->pageTextContains($name);
}
/**
* Display asset entity.
*/
public function doTestViewAsset() {
$edit = [
'name' => $this->randomMachineName(),
'created' => \Drupal::time()->getRequestTime(),
];
$asset = $this->createAssetEntity($edit);
$asset->save();
$this->drupalGet($asset->toUrl('canonical'));
$this->assertSession()->statusCodeEquals(200);
$this->assertSession()->pageTextContains($edit['name']);
$this->assertSession()->responseContains(\Drupal::service('date.formatter')->format(\Drupal::time()->getRequestTime()));
}
/**
* Edit asset entity.
*/
public function doTestEditAsset() {
$asset = $this->createAssetEntity();
$asset->save();
$edit = [
'name[0][value]' => $this->randomMachineName(),
];
$this->drupalGet($asset->toUrl('edit-form'));
$this->submitForm($edit, 'Save');
$this->assertSession()->pageTextContains($edit['name[0][value]']);
}
/**
* Delete asset entity.
*/
public function doTestDeleteAsset() {
$asset = $this->createAssetEntity();
$asset->save();
$label = $asset->getName();
$asset_id = $asset->id();
$this->drupalGet($asset->toUrl('delete-form'));
$this->submitForm([], 'Delete');
$this->assertSession()->responseContains($this->t('The @entity-type %label has been deleted.', [
'@entity-type' => $asset->getEntityType()->getSingularLabel(),
'%label' => $label,
]));
$this->assertNull(Asset::load($asset_id));
}
/**
* Asset archiving.
*/
public function doTestArchiveAsset() {
$asset = $this->createAssetEntity();
$asset->save();
$this->assertEquals($asset->get('status')->first()->getString(), 'active', 'New assets are active by default');
$this->assertNull($asset->getArchivedTime(), 'Archived timestamp is null by default');
$asset->get('status')->first()->applyTransitionById('archive');
$asset->save();
$this->assertEquals($asset->get('status')->first()->getString(), 'archived', 'Assets can be archived');
$this->assertNotNull($asset->getArchivedTime(), 'Archived timestamp is saved');
$asset->get('status')->first()->applyTransitionById('to_active');
$asset->save();
$this->assertEquals($asset->get('status')->first()->getString(), 'active', 'Assets can be made active');
$this->assertNull($asset->getArchivedTime(), 'Asset made active has a null timestamp');
$asset->get('status')->first()->applyTransitionById('archive');
$asset->setArchivedTime('2021-07-17T19:45:49+00:00');
$asset->save();
$this->assertEquals($asset->get('status')->first()->getString(), 'archived', 'Assets can be archived with explicit timestamp');
$this->assertEquals($asset->getArchivedTime(), '2021-07-17T19:45:49+00:00', 'Explicit archived timestamp is saved');
}
/**
* Asset archiving/unarchiving via timestamp.
*/
public function doTestArchiveAssetViaTimestamp() {
$asset = $this->createAssetEntity();
$asset->save();
$this->assertEquals($asset->get('status')->first()->getString(), 'active', 'New assets are active by default');
$this->assertNull($asset->getArchivedTime(), 'Archived timestamp is null by default');
$asset->setArchivedTime('2021-07-17T19:45:49+00:00');
$asset->save();
$this->assertEquals($asset->get('status')->first()->getString(), 'archived', 'Assets can be archived');
$this->assertEquals($asset->getArchivedTime(), '2021-07-17T19:45:49+00:00', 'Archived timestamp is saved');
$asset->setArchivedTime(NULL);
$asset->save();
$this->assertEquals($asset->get('status')->first()->getString(), 'active', 'Assets can be made active');
$this->assertNull($asset->getArchivedTime(), 'Asset made active has a null timestamp');
}
}

View File

@@ -0,0 +1,82 @@
<?php
namespace Drupal\Tests\asset\Functional;
use Drupal\Tests\farm_test\Functional\FarmBrowserTestBase;
/**
* Tests the asset CRUD.
*/
abstract class AssetTestBase extends FarmBrowserTestBase {
/**
* Modules to install.
*
* @var array
*/
protected static $modules = [
'asset',
'asset_test',
'entity',
'user',
'field',
'text',
];
/**
* A test user with administrative privileges.
*
* @var \Drupal\user\UserInterface
*/
protected $adminUser;
/**
* {@inheritdoc}
*/
protected function setUp(): void {
parent::setUp();
$this->adminUser = $this->drupalCreateUser($this->getAdministratorPermissions());
$this->drupalLogin($this->adminUser);
drupal_flush_all_caches();
}
/**
* Gets the permissions for the admin user.
*
* @return string[]
* The permissions.
*/
protected function getAdministratorPermissions() {
return [
'access administration pages',
'administer assets',
'view any asset',
'create default asset',
'view any default asset',
'update own default asset',
'update any default asset',
'delete own default asset',
'delete any default asset',
];
}
/**
* Creates a asset entity.
*
* @param array $values
* Array of values to feed the entity.
*
* @return \Drupal\asset\Entity\AssetInterface
* The asset entity.
*/
protected function createAssetEntity(array $values = []) {
$storage = \Drupal::service('entity_type.manager')->getStorage('asset');
$entity = $storage->create($values + [
'name' => $this->randomMachineName(),
'created' => \Drupal::time()->getRequestTime(),
'type' => 'default',
]);
return $entity;
}
}

View File

@@ -0,0 +1,35 @@
<?php
namespace Drupal\Tests\asset\Traits;
use Drupal\asset\Entity\Asset;
/**
* Provides methods to create asset entities.
*
* This trait is meant to be used only by test classes.
*/
trait AssetCreationTrait {
/**
* Creates an asset entity.
*
* @param array $values
* Array of values to feed the entity.
*
* @return \Drupal\asset\Entity\AssetInterface
* The asset entity.
*
* @throws \Drupal\Core\Entity\EntityStorageException
*/
protected function createAssetEntity(array $values = []) {
/** @var \Drupal\asset\Entity\AssetInterface $entity */
$entity = Asset::create($values + [
'name' => $this->randomMachineName(),
'type' => 'default',
]);
$entity->save();
return $entity;
}
}