First stub of the asset location hierarchy editor feature.
This commit is contained in:
parent
51562201ee
commit
771c753518
|
@ -32,6 +32,7 @@
|
||||||
"drupal/geofield": "^1.22",
|
"drupal/geofield": "^1.22",
|
||||||
"drupal/gin": "3.0-alpha34",
|
"drupal/gin": "3.0-alpha34",
|
||||||
"drupal/inline_entity_form": "^1.0@RC",
|
"drupal/inline_entity_form": "^1.0@RC",
|
||||||
|
"drupal/inspire_tree": "^1.0",
|
||||||
"drupal/jsonapi_extras": "^3.15",
|
"drupal/jsonapi_extras": "^3.15",
|
||||||
"drupal/jsonapi_schema": "^1.0@beta",
|
"drupal/jsonapi_schema": "^1.0@beta",
|
||||||
"drupal/log": "2.x-dev",
|
"drupal/log": "2.x-dev",
|
||||||
|
|
|
@ -8,4 +8,5 @@ dependencies:
|
||||||
- drupal:user
|
- drupal:user
|
||||||
- drupal:views
|
- drupal:views
|
||||||
- entity:entity
|
- entity:entity
|
||||||
|
- inspire_tree:inspire_tree
|
||||||
- state_machine:state_machine
|
- state_machine:state_machine
|
||||||
|
|
|
@ -0,0 +1,9 @@
|
||||||
|
reorder:
|
||||||
|
version: 1.x
|
||||||
|
js:
|
||||||
|
js/asset_reorder.js: {}
|
||||||
|
dependencies:
|
||||||
|
- core/drupal.message
|
||||||
|
- core/jquery
|
||||||
|
- inspire_tree/inspire_tree
|
||||||
|
- inspire_tree/inspire_tree_dom
|
|
@ -2,3 +2,8 @@ entity.asset.collection:
|
||||||
route_name: entity.asset.collection
|
route_name: entity.asset.collection
|
||||||
title: 'Assets'
|
title: 'Assets'
|
||||||
base_route: system.admin_content
|
base_route: system.admin_content
|
||||||
|
entity.asset.locations:
|
||||||
|
route_name: entity.asset.locations
|
||||||
|
title: 'Locations'
|
||||||
|
base_route: entity.asset.canonical
|
||||||
|
weight: 100
|
||||||
|
|
|
@ -0,0 +1,22 @@
|
||||||
|
asset.locations:
|
||||||
|
path: '/asset-locations'
|
||||||
|
defaults:
|
||||||
|
_title: 'Locations'
|
||||||
|
_controller: '\Drupal\asset\Controller\AssetReorderController::build'
|
||||||
|
requirements:
|
||||||
|
_permission: 'administer assets'
|
||||||
|
|
||||||
|
|
||||||
|
entity.asset.locations:
|
||||||
|
path: '/asset/{asset}/locations'
|
||||||
|
defaults:
|
||||||
|
_title: 'Locations'
|
||||||
|
_controller: '\Drupal\asset\Controller\AssetReorderController::build'
|
||||||
|
requirements:
|
||||||
|
_entity_access: 'asset.edit'
|
||||||
|
_module_dependencies: 'asset'
|
||||||
|
asset: \d+
|
||||||
|
options:
|
||||||
|
parameters:
|
||||||
|
asset:
|
||||||
|
type: entity:asset
|
|
@ -0,0 +1,173 @@
|
||||||
|
/**
|
||||||
|
* @file
|
||||||
|
* Integration with inspire tree.
|
||||||
|
*/
|
||||||
|
|
||||||
|
(function ($, Drupal, settings) {
|
||||||
|
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
// @TODO drag and drop validate if exist.
|
||||||
|
// @TODO validate circular references.
|
||||||
|
|
||||||
|
Drupal.behaviors.asset_reorder = {
|
||||||
|
attach: function (context, settings) {
|
||||||
|
var tree = new InspireTree({
|
||||||
|
data: settings.asset_tree,
|
||||||
|
});
|
||||||
|
new InspireTreeDOM(tree, {
|
||||||
|
target: '.asset-tree',
|
||||||
|
dragAndDrop: true
|
||||||
|
});
|
||||||
|
|
||||||
|
var changes = {};
|
||||||
|
|
||||||
|
tree.on('node.drop', function(event, source, target, index) {
|
||||||
|
var destination = (target === null) ? settings.asset_parent : target.uuid;
|
||||||
|
if (!changes.hasOwnProperty(source.id)) {
|
||||||
|
if (source.original_parent !== destination) {
|
||||||
|
changes[source.id] = {
|
||||||
|
'uuid': source.uuid,
|
||||||
|
'original_parent': source.original_parent,
|
||||||
|
'original_type': source.original_type,
|
||||||
|
'destination': destination,
|
||||||
|
'type': (target === null) ? settings.asset_parent_type : target.type,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if (changes[source.id].original_parent !== destination) {
|
||||||
|
changes[source.id].destination = destination;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
delete changes[source.id];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
$('.asset-tree-reset').on('click', function(event) {
|
||||||
|
event.preventDefault();
|
||||||
|
// Reset the changes so nothing is pushed accidentally.
|
||||||
|
changes = {};
|
||||||
|
// Reset the tree to the original status.
|
||||||
|
tree.reload();
|
||||||
|
});
|
||||||
|
|
||||||
|
$('.asset-tree-save').on('click', function(event) {
|
||||||
|
event.preventDefault();
|
||||||
|
var entries = Object.entries(changes);
|
||||||
|
if (entries.length > 0) {
|
||||||
|
var token = '';
|
||||||
|
$.ajax({
|
||||||
|
async: false,
|
||||||
|
url: Drupal.url('session/token'),
|
||||||
|
success(data) {
|
||||||
|
if (data) {
|
||||||
|
token = data;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
var messages = new Drupal.Message();
|
||||||
|
for (var [treeUuid, item] of entries) {
|
||||||
|
if (item.destination === '' && item.original_parent !== '') {
|
||||||
|
var deleteItem = {
|
||||||
|
'data': [
|
||||||
|
{
|
||||||
|
'type': 'asset--' + item.original_type,
|
||||||
|
'id': item.original_parent,
|
||||||
|
}
|
||||||
|
]
|
||||||
|
};
|
||||||
|
$.ajax({
|
||||||
|
type: 'DELETE',
|
||||||
|
cache: false,
|
||||||
|
headers: {
|
||||||
|
'X-CSRF-Token': token,
|
||||||
|
},
|
||||||
|
url: '/api/asset/' + item.original_type + '/' + item.uuid + '/relationships/parent',
|
||||||
|
data: JSON.stringify(deleteItem),
|
||||||
|
contentType: 'application/vnd.api+json',
|
||||||
|
success: function success(data) {
|
||||||
|
messages.clear();
|
||||||
|
messages.add(Drupal.t('Assets have been saved'), { type: 'status' });
|
||||||
|
delete changes.treeUuid
|
||||||
|
},
|
||||||
|
error: function error(xmlhttp) {
|
||||||
|
var e = new Drupal.AjaxError(xmlhttp);
|
||||||
|
messages.clear();
|
||||||
|
messages.add(e.message, { type: 'error' });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
var patch = {
|
||||||
|
'data': [
|
||||||
|
{
|
||||||
|
'type': 'asset--' + item.type,
|
||||||
|
'id': item.destination,
|
||||||
|
}
|
||||||
|
]
|
||||||
|
};
|
||||||
|
$.ajax({
|
||||||
|
type: 'POST',
|
||||||
|
cache: false,
|
||||||
|
headers: {
|
||||||
|
'X-CSRF-Token': token,
|
||||||
|
},
|
||||||
|
url: '/api/asset/' + item.type + '/' + item.uuid + '/relationships/parent',
|
||||||
|
data: JSON.stringify(patch),
|
||||||
|
contentType: 'application/vnd.api+json',
|
||||||
|
success: function success(data) {
|
||||||
|
if (item.original_parent !== settings.asset_parent) {
|
||||||
|
var deleteItem = {
|
||||||
|
'data': [
|
||||||
|
{
|
||||||
|
'type': 'asset--' + item.original_type,
|
||||||
|
'id': item.original_parent,
|
||||||
|
}
|
||||||
|
]
|
||||||
|
};
|
||||||
|
|
||||||
|
$.ajax({
|
||||||
|
type: 'DELETE',
|
||||||
|
cache: false,
|
||||||
|
headers: {
|
||||||
|
'X-CSRF-Token': token,
|
||||||
|
},
|
||||||
|
url: '/api/asset/' + item.original_type + '/' + item.uuid + '/relationships/parent',
|
||||||
|
data: JSON.stringify(deleteItem),
|
||||||
|
contentType: 'application/vnd.api+json',
|
||||||
|
success: function success(data) {
|
||||||
|
messages.clear();
|
||||||
|
messages.add(Drupal.t('Assets have been saved'), { type: 'status' });
|
||||||
|
delete changes.treeUuid
|
||||||
|
},
|
||||||
|
error: function error(xmlhttp) {
|
||||||
|
var e = new Drupal.AjaxError(xmlhttp);
|
||||||
|
messages.clear();
|
||||||
|
messages.add(e.message, { type: 'error' });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
messages.clear();
|
||||||
|
messages.add(Drupal.t('Assets have been saved'), { type: 'status' });
|
||||||
|
}
|
||||||
|
},
|
||||||
|
error: function error(xmlhttp) {
|
||||||
|
var e = new Drupal.AjaxError(xmlhttp);
|
||||||
|
messages.clear();
|
||||||
|
messages.add(e.message, { type: 'error' });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
})(jQuery, Drupal, drupalSettings);
|
|
@ -0,0 +1,125 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Drupal\asset\Controller;
|
||||||
|
|
||||||
|
use Drupal\asset\Entity\AssetInterface;
|
||||||
|
use Drupal\Core\Controller\ControllerBase;
|
||||||
|
use Drupal\Core\Entity\EntityTypeManagerInterface;
|
||||||
|
use Drupal\Core\Url;
|
||||||
|
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns responses for asset drag and drop routes.
|
||||||
|
*/
|
||||||
|
class AssetReorderController extends ControllerBase {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The entity type manager.
|
||||||
|
*
|
||||||
|
* @var \Drupal\Core\Entity\EntityTypeManagerInterface
|
||||||
|
*/
|
||||||
|
protected $entityTypeManager;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The controller constructor.
|
||||||
|
*
|
||||||
|
* @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager
|
||||||
|
* The entity type manager.
|
||||||
|
*/
|
||||||
|
public function __construct(EntityTypeManagerInterface $entity_type_manager) {
|
||||||
|
$this->entityTypeManager = $entity_type_manager;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public static function create(ContainerInterface $container) {
|
||||||
|
return new static(
|
||||||
|
$container->get('entity_type.manager')
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Builds the response.
|
||||||
|
*/
|
||||||
|
public function build(AssetInterface $asset = NULL) {
|
||||||
|
$build['content'] = [
|
||||||
|
'#type' => 'html_tag',
|
||||||
|
'#tag' => 'div',
|
||||||
|
'#attributes' => [
|
||||||
|
'class' => [
|
||||||
|
'asset-tree',
|
||||||
|
],
|
||||||
|
],
|
||||||
|
];
|
||||||
|
|
||||||
|
$build['save'] = [
|
||||||
|
'#type' => 'link',
|
||||||
|
'#title' => $this->t('Save'),
|
||||||
|
'#url' => Url::fromRoute('<none>'),
|
||||||
|
'#attributes' => [
|
||||||
|
'class' => [
|
||||||
|
'asset-tree-save',
|
||||||
|
'button',
|
||||||
|
'button--primary',
|
||||||
|
],
|
||||||
|
],
|
||||||
|
];
|
||||||
|
$build['reset'] = [
|
||||||
|
'#type' => 'link',
|
||||||
|
'#title' => $this->t('Reset'),
|
||||||
|
'#url' => Url::fromRoute('<none>'),
|
||||||
|
'#attributes' => [
|
||||||
|
'class' => [
|
||||||
|
'asset-tree-reset',
|
||||||
|
'button',
|
||||||
|
'button--danger',
|
||||||
|
],
|
||||||
|
],
|
||||||
|
];
|
||||||
|
|
||||||
|
$build['#attached']['library'][] = 'asset/reorder';
|
||||||
|
$build['#attached']['drupalSettings']['asset_tree'] = $this->buildTree($asset);
|
||||||
|
$build['#attached']['drupalSettings']['asset_parent'] = !empty($asset) ? $asset->uuid() : '';
|
||||||
|
$build['#attached']['drupalSettings']['asset_parent_type'] = !empty($asset) ? $asset->bundle() : '';
|
||||||
|
return $build;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param \Drupal\asset\Entity\AssetInterface|null $asset
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
* @throws \Drupal\Component\Plugin\Exception\InvalidPluginDefinitionException
|
||||||
|
* @throws \Drupal\Component\Plugin\Exception\PluginNotFoundException
|
||||||
|
*/
|
||||||
|
protected function buildTree(AssetInterface $asset = NULL) {
|
||||||
|
$storage = $this->entityTypeManager->getStorage('asset');
|
||||||
|
$query = $storage->getQuery();
|
||||||
|
if ($asset) {
|
||||||
|
$query->condition('parent', $asset->id());
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$query->condition('parent', NULL, 'IS NULL');
|
||||||
|
}
|
||||||
|
$query->sort('name');
|
||||||
|
|
||||||
|
$asset_ids = $query->execute();
|
||||||
|
/** @var \Drupal\asset\Entity\AssetInterface $children */
|
||||||
|
$children = $storage->loadMultiple($asset_ids);
|
||||||
|
$tree = [];
|
||||||
|
foreach ($children as $child) {
|
||||||
|
$element = [
|
||||||
|
'uuid' => $child->uuid(),
|
||||||
|
'text' => $child->label(),
|
||||||
|
'children' => $this->buildTree($child),
|
||||||
|
'type' => $child->bundle(),
|
||||||
|
];
|
||||||
|
$element['original_parent'] = $asset ? $asset->uuid() : '';
|
||||||
|
$element['original_type'] = $asset ? $asset->bundle() : '';
|
||||||
|
$tree[] = $element;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $tree;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -74,6 +74,7 @@ use Drupal\user\EntityOwnerTrait;
|
||||||
* "delete-form" = "/asset/{asset}/delete",
|
* "delete-form" = "/asset/{asset}/delete",
|
||||||
* "delete-multiple-form" = "/asset/delete",
|
* "delete-multiple-form" = "/asset/delete",
|
||||||
* "edit-form" = "/asset/{asset}/edit",
|
* "edit-form" = "/asset/{asset}/edit",
|
||||||
|
* "locations" = "/asset/{asset}/locations",
|
||||||
* "revision" = "/asset/{asset}/revisions/{asset_revision}/view",
|
* "revision" = "/asset/{asset}/revisions/{asset_revision}/view",
|
||||||
* "revision-revert-form" = "/asset/{asset}/revisions/{asset_revision}/revert",
|
* "revision-revert-form" = "/asset/{asset}/revisions/{asset_revision}/revert",
|
||||||
* "version-history" = "/asset/{asset}/revisions",
|
* "version-history" = "/asset/{asset}/revisions",
|
||||||
|
|
Loading…
Reference in New Issue