Commit 76994258 authored by larowlan's avatar larowlan

Initial commit

parent c854d967
name: 'Default content'
type: module
description: 'Imports default content when a module is enabled'
package: Web services
core: 8.x
dependencies:
- hal
<?php
/**
* @file
* Installs default content on the behalf of modules at time of install.
*/
/**
* Implements hook_modules_installed().
*/
function default_content_modules_installed($modules) {
// @todo Move this to an event once we have HookEvent.
foreach ($modules as $module) {
\Drupal::service('default_content.manager')->importContent($module);
}
}
services:
default_content.manager:
class: Drupal\default_content\DefaultContentManager
arguments: ['@serializer', '@plugin.manager.rest', '@current_user', '@entity.manager']
default_content.link_manager.type:
class: Drupal\default_content\LinkManager\TypeLinkManager
arguments: ['@cache.cache']
default_content.link_manager.relation:
class: Drupal\default_content\LinkManager\RelationLinkManager
arguments: ['@cache.cache', '@entity.manager']
<?php
/**
* @file
* Contains \Drupal\default_content\DefaultContentManager.
*/
namespace Drupal\default_content;
use Drupal\Core\Entity\EntityManager;
use Drupal\Core\Session\AccountInterface;
use Drupal\rest\Plugin\Type\ResourcePluginManager;
use Gliph\Graph\DirectedAdjacencyList;
use Gliph\Traversal\DepthFirst;
use Symfony\Component\Serializer\Serializer;
/**
* A service for handling import of default content.
* @todo throw useful exceptions
*/
class DefaultContentManager implements DefaultContentManagerInterface {
/**
* The serializer service.
*
* @var \Symfony\Component\Serializer\Serializer
*/
protected $serializer;
/**
* The rest resource plugin manager.
*
* @var \Drupal\rest\Plugin\Type\ResourcePluginManager
*/
protected $resourcePluginManager;
/**
* The current user.
*
* @var \Drupal\Core\Session\AccountInterface
*/
protected $currentUser;
/**
* The entity manager.
*
* @var \Drupal\Core\Entity\EntityManager
*/
protected $entityManager;
/**
* The file system scanner.
*
* @var \Drupal\default_content\DefaultContentScanner
*/
protected $scanner;
/**
* The tree resolver.
*
* @var \Gliph\Graph\DirectedAdjacencyList
*/
protected $tree = FALSE;
/**
* Constructs the default content manager.
*
* @param \Symfony\Component\Serializer\Serializer $serializer
* The serializer service.
* @param \Drupal\rest\Plugin\Type\ResourcePluginManager $resource_plugin_manager
* The rest resource plugin manager.
* @param \Drupal\Core\Session|AccountInterface $current_user .
* The current user.
* @param \Drupal\Core\Entity\EntityManager $entity_manager
* The entity manager service.
*/
public function __construct(Serializer $serializer, ResourcePluginManager $resource_plugin_manager, AccountInterface $current_user, EntityManager $entity_manager) {
$this->serializer = $serializer;
$this->resourcePluginManager = $resource_plugin_manager;
$this->entityManager = $entity_manager;
}
/**
* {@inheritdoc}
*/
public function importContent($module) {
$created = array();
$folder = drupal_get_path('module', $module) . "/content";
if (file_exists($folder)) {
foreach ($this->entityManager->getDefinitions() as $entity_type => $entity_type_info) {
$reflection = new \ReflectionClass($entity_type_info['class']);
// We are only interested in importing content entities.
if ($reflection->implementsInterface('\Drupal\Core\Config\Entity\ConfigEntityInterface')) {
continue;
}
$files = $this->scanner()->scan('/^(.*)\.json/', $folder . '/' . $entity_type);
foreach ($files as $file) {
$resource = $this->resourcePluginManager->getInstance(array('id' => 'entity:' . $entity_type));
$definition = $resource->getPluginDefinition();
$contents = $this->parseFile($file);
$class = $definition['serialization_class'];
$unserialized = $this->serializer->deserialize($contents, $class, 'hal_json', array('request_method' => 'POST'));
$unserialized->enforceIsNew(TRUE);
$resource->post(NULL, $unserialized);
// Here we need to resolve our dependencies;
//foreach ($unserialized->embedded as $embedded) {
// $this->tree()->addDirectedEdge($unserialized, $embedded);
//}
}
}
//foreach($this->sortTree() as $unserialized) {
//$resource = $this->resourcePluginManager->getInstance(array('id' => 'entity:' . $unserialized->entityType()));
//$resource->post(NULL, $unserialized);
//}
}
// Reset the tree.
$this->resetTree();
return $created;
}
/**
* Utility to get a default content scanner
*
* @return \Drupal\default_content\DefaultContentScanner
* A system listing implementation.
*/
protected function scanner() {
if ($this->scanner) {
return $this->scanner;
}
return new DefaultContentScanner();
}
/**
* {@inheritdoc}
*/
public function setScanner(DefaultContentScanner $scanner) {
$this->scanner = $scanner;
}
/**
* Parses content files
*/
protected function parseFile($file) {
return file_get_contents($file->uri);
}
protected function tree() {
if ($this->tree) {
return $this->tree;
}
return new DirectedAdjacencyList();
}
protected function resetTree() {
$this->tree = FALSE;
}
protected function sortTree() {
return DepthFirst::toposort($this->tree());
}
}
/**
<?php
use \Gliph\Graph\DirectedAdjacencyList;
use \Gliph\Traversal\DepthFirst;
$graph = new DirectedAdjacencyList();
foreach ($entity_list as $entity) {
if ($entity->has('embedded')) {
foreach ($entity->get('embedded') as $embedded) {
$graph->addDirectedEdge($entity, $embedded);
}
}
}
$tsl = DepthFirst::toposort($graph);
foreach($tsl as $entity) {
// do your import thang
}
*/
<?php
/**
* @file
* Contains \Drupal\default_content\DefaultContentManager.
*/
namespace Drupal\default_content;
/**
* An interface defining a default content importer.
*/
interface DefaultContentManagerInterface {
/**
* Set the scanner.
*
* @param \Drupal\default_content\DefaultContentScanner $scanner
* The system scanner.
*/
public function setScanner(DefaultContentScanner $scanner);
/**
* Imports default content for a given module.
*
* @param string $module
* The module to create the default content for.
*
* @return array[\Drupal\Core\Entity\EntityInterface]
* The created entities.
*/
public function importContent($module);
}
<?php
/**
* @file
* Contains \Drupal\default_content\DefaultContentScanner.
*/
namespace Drupal\default_content;
use \Drupal\Core\SystemListing;
/**
* A scanner to find YAML files in a given folder.
*/
class DefaultContentScanner extends SystemListing {
/**
* {@inheritdoc}
*/
public function scan($mask, $directory, $key = 'name') {
if (!in_array($key, array('uri', 'filename', 'name'))) {
$key = 'uri';
}
$directories = array($directory);
$nomask = '/^(CVS|lib|templates|css|js)$/';
$files = array();
// Get current list of items.
foreach ($directories as $dir) {
$files = array_merge($files, $this->process($files, $this->scanDirectory($dir, $key, $mask, $nomask)));
}
return $files;
}
}
<?php
/**
* @file
* Contains \Drupal\default_content\DefaultContentServiceProvider.
*/
namespace Drupal\default_content;
use Drupal\Core\DependencyInjection\ServiceModifierInterface;
use Drupal\Core\DependencyInjection\ContainerBuilder;
use Drupal\Core\DependencyInjection\ServiceProviderInterface;
use Symfony\Component\DependencyInjection\Reference;
/**
* Creates a service modifier to hijack the rest typed link manager service.
*/
class DefaultContentServiceProvider implements ServiceModifierInterface, ServiceProviderInterface {
/**
* {@inheritdoc}
*/
public function register(ContainerBuilder $container) {}
/**
* {@inheritdoc}
*/
public function alter(ContainerBuilder $container) {
if ($link_manager = $container->getDefinition('rest.link_manager')) {
$link_manager->replaceArgument(0, new Reference('default_content.link_manager.type'));
$link_manager->replaceArgument(1, new Reference('default_content.link_manager.relation'));
$container->setDefinition('rest.link_manager', $link_manager);
}
}
}
<?php
/**
* @file
* Contains \Drupal\default_content\LinkManager\RelationLinkManager.
*/
namespace Drupal\default_content\LinkManager;
use Drupal\Core\Cache\CacheBackendInterface;
use Drupal\Core\Entity\EntityManager;
use Drupal\rest\LinkManager\RelationLinkManager as RestRelationLinkManager;
class RelationLinkManager extends RestRelationLinkManager {
/**
* Entity manager service.
*
* @var \Drupal\Core\Entity\EntityManager
*/
protected $entityManager;
/**
* Constructs the relation link manager.
*
* @param \Drupal\Core\Cache\CacheBackendInterface $cache
* The cache of relation URIs and their associated Typed Data IDs.
* @param \Drupal\Core\Entity\EntityManager $entity_manager
* The entity manager.
*/
public function __construct(CacheBackendInterface $cache, EntityManager $entity_manager) {
$this->cache = $cache;
$this->entityManager = $entity_manager;
}
/**
* {@inheritdoc}
*/
public function getRelationUri($entity_type, $bundle, $field_name) {
// Make the base path refer to drupal.org.x`
return "http://drupal.org/rest/relation/$entity_type/$bundle/$field_name";
}
/**
* {@inheritdoc}
*/
protected function writeCache() {
$data = array();
foreach ($this->entityManager->getDefinitions() as $entity_type => $entity_type_info) {
$reflection = new \ReflectionClass($entity_type_info['class']);
// We are only interested in importing content entities.
if ($reflection->implementsInterface('\Drupal\Core\Config\Entity\ConfigEntityInterface') ||
// @todo remove when Menu links are EntityNG.
!$reflection->hasMethod('baseFieldDefinitions')) {
continue;
}
foreach (array_keys($this->entityManager->getBundleInfo($entity_type)) as $bundle) {
foreach ($this->entityManager->getFieldDefinitions($entity_type, $bundle) as $field_name => $field_details) {
$relation_uri = $this->getRelationUri($entity_type, $bundle, $field_name);
$data[$relation_uri] = array(
'entity_type' => $entity_type,
'bundle' => $bundle,
'field_name' => $field_name,
);
}
}
}
// These URIs only change when field info changes, so cache it permanently
// and only clear it when field_info is cleared.
$this->cache->set('rest:links:relations', $data, CacheBackendInterface::CACHE_PERMANENT, array('field_info' => TRUE));
}
}
<?php
/**
* @file
* Contains \Drupal\default_content\LinkManager\TypeLinkManager.
*/
namespace Drupal\default_content\LinkManager;
use Drupal\rest\LinkManager\TypeLinkManager as RestTypeLinkManager;
/**
* Creates a type link manager that references drupal.org as the domain.
*/
class TypeLinkManager extends RestTypeLinkManager {
/**
* {@inheritdoc}
*/
public function getTypeUri($entity_type, $bundle) {
// Make the base path refer to drupal.org.
return "http://drupal.org/rest/type/$entity_type/$bundle";
}
}
<?php
/**
* @file
* Contains \Drupal\default_content\Tests\DefaultContentTest.
*/
namespace Drupal\default_content\Tests;
use Drupal\simpletest\WebTestBase;
class DefaultContentTest extends WebTestBase {
/**
* Modules to enable.
*
* @var array
*/
public static $modules = array('rest', 'taxonomy', 'hal', 'yaml_serialization', 'default_content');
public static function getInfo() {
return array(
'name' => 'Default content test',
'description' => 'Test import of default content.',
'group' => 'Default Content',
);
}
protected function setUp() {
parent::setUp();
// Login as admin.
$this->drupalLogin($this->drupalCreateUser(array_keys(\Drupal::moduleHandler()->invokeAll(('permission')))));
$this->drupalCreateContentType(array('type' => 'page'));
}
/**
* Test importing default content.
*/
public function testImport() {
// Enable the module and import the content.
\Drupal::moduleHandler()->install(array('default_content_test'), TRUE);
$this->rebuildContainer();
$node = $this->drupalGetNodeByTitle('Imported node');
$this->assertEqual($node->body->value, 'Crikey it works!');
// Content is always imported as anonymous.
$this->assertEqual($node->uid->target_id, 0);
$this->drupalGet('node/' . $node->nid->value);
$term_id = $node->field_tags->target_id;
$this->assertTrue(!empty($term_id), 'Term reference populated');
}
}
id: node.field_tags
uuid: A071BBC8-924B-4100-9BF4-3036D9BBDCF3
status: true
langcode: en
name: field_tags
entity_type: node
type: taxonomy_term_reference
settings:
allowed_values:
-
vocabulary: tags
parent: '0'
options_list_callback: null
module: taxonomy
active: true
locked: false
cardinality: '1'
translatable: false
indexes: { }
id: node.page.field_tags
uuid: F6002796-DE9D-4F8B-A8A5-693B7494C125
status: true
langcode: en
field_uuid: A071BBC8-924B-4100-9BF4-3036D9BBDCF3
entity_type: node
bundle: page
label: Tags
description: 'Select the tag.'
required: 1
default_value: { }
default_value_function: ''
settings: { }
field_type: taxonomy_term_reference
vid: tags
uuid: A920ADD7-D66D-4C02-821A-7C8986D38071
name: Tags
description: 'Tags'
hierarchy: 1
weight: -10
status: true
langcode: en
{
"_links": {
"self": {
"href": "http://drupal.org/node/1"
},
"type": {
"href": "http://drupal.org/rest/type/node/page"
},
"http://drupal.org/rest/relation/node/page/uid": [
{
"href": "http://drupal.org/user/1"
}
],
"http://drupal.org/rest/relation/node/page/revision_uid": [
{
"href": "http://drupal.org/user/1"
}
],
"http://drupal.org/rest/relation/node/page/field_tags":[
{
"href":"http://d8.taco/taxonomy/term/1"
}
]
},
"nid": [
{
"value": "1"
}
],
"uuid": [
{
"value": "65c412a3-b83f-4efb-8a05-5a6ecea10ad4"
}
],
"vid": [
{
"value": "1"
}
],
"type": [
{
"value": "page"
}
],
"langcode": [
{
"value": "en"
}
],
"title": [
{
"value": "Imported node"
}
],
"body": [
{
"value": "Crikey it works!"
}
],
"_embedded": {
"http://drupal.org/rest/relation/node/page/uid": [
{
"_links": {
"self": {
"href": "http://drupal.org/user/1"
},
"type": {
"href": "http://drupal.org/rest/type/user/user"
}
}
}
],
"http://drupal.org/rest/relation/node/page/revision_uid": [
{
"_links": {
"self": {
"href": "http://drupal.org/user/1"
},
"type": {
"href": "http://drupal.org/rest/type/user/user"
}
}
}
],
"http://drupal.org/rest/relation/node/release/field_tags": [
{
"_links":{
"self":{
"href":"http://drupal.org/taxonomy/term/1"
},
"type":{
"href":"http://drupal.org/rest/type/taxonomy_term/field_tags"
}
},
"uuid":[
{
"value":"550f86ad-aa11-4047-953f-636d42889f85"
}
]
}
]
},
"status": [
{
"value": "1"
}
],
"created": [
{
"value": "1381645976"
}
],
"changed": [
{
"value": "1381646540"
}
],
"promote": [
{
"value": "1"
}
],
"sticky": [
{
"value": "1"
}
],
"revision_timestamp": [
{
"value": "1381646540"
}
],
"log": [
{
"value": ""
}
]
}
{
"_links":{
"self":{
"href":"http://d8.taco/taxonomy/term/1"
},
"type":{
"href":"http://drupal.org/rest/type/taxonomy_term/tags"
}
},
"tid":[
{
"value":"1"
}
],
"uuid":[
{
"value":"550f86ad-aa11-4047-953f-636d42889f85"
}
],
"vid":[
{
"value":"tags"
}
],
"langcode":[
{
"value":"und"
}
],
"name":[
{
"value":"A tag"
}
],
"description":[
{
"value":""
}
],
"format":[
{
"value":"plain_text"
}