Pull merge.
[yaffs-website] / web / core / profiles / demo_umami / modules / demo_umami_content / src / InstallHelper.php
1 <?php
2
3 namespace Drupal\demo_umami_content;
4
5 use Drupal\Core\DependencyInjection\ContainerInjectionInterface;
6 use Drupal\Core\Entity\EntityTypeManagerInterface;
7 use Drupal\Core\Extension\ModuleHandlerInterface;
8 use Drupal\Core\Path\AliasManagerInterface;
9 use Drupal\Core\State\StateInterface;
10 use Symfony\Component\DependencyInjection\ContainerInterface;
11 use Drupal\Component\Utility\Html;
12
13 /**
14  * Defines a helper class for importing default content.
15  *
16  * @internal
17  *   This code is only for use by the Umami demo: Content module.
18  */
19 class InstallHelper implements ContainerInjectionInterface {
20
21   /**
22    * The path alias manager.
23    *
24    * @var \Drupal\Core\Path\AliasManagerInterface
25    */
26   protected $aliasManager;
27
28   /**
29    * Entity type manager.
30    *
31    * @var \Drupal\Core\Entity\EntityTypeManagerInterface
32    */
33   protected $entityTypeManager;
34
35   /**
36    * Module handler.
37    *
38    * @var \Drupal\Core\Extension\ModuleHandlerInterface
39    */
40   protected $moduleHandler;
41
42   /**
43    * State.
44    *
45    * @var \Drupal\Core\State\StateInterface
46    */
47   protected $state;
48
49   /**
50    * Constructs a new InstallHelper object.
51    *
52    * @param \Drupal\Core\Path\AliasManagerInterface $aliasManager
53    *   The path alias manager.
54    * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entityTypeManager
55    *   Entity type manager.
56    * @param \Drupal\Core\Extension\ModuleHandlerInterface $moduleHandler
57    *   Module handler.
58    * @param \Drupal\Core\State\StateInterface $state
59    *   State service.
60    */
61   public function __construct(AliasManagerInterface $aliasManager, EntityTypeManagerInterface $entityTypeManager, ModuleHandlerInterface $moduleHandler, StateInterface $state) {
62     $this->aliasManager = $aliasManager;
63     $this->entityTypeManager = $entityTypeManager;
64     $this->moduleHandler = $moduleHandler;
65     $this->state = $state;
66   }
67
68   /**
69    * {@inheritdoc}
70    */
71   public static function create(ContainerInterface $container) {
72     return new static(
73       $container->get('path.alias_manager'),
74       $container->get('entity_type.manager'),
75       $container->get('module_handler'),
76       $container->get('state')
77     );
78   }
79
80   /**
81    * Imports default contents.
82    */
83   public function importContent() {
84     $this->importEditors()
85       ->importArticles()
86       ->importRecipes()
87       ->importPages()
88       ->importBlockContent();
89   }
90
91   /**
92    * Imports editors.
93    *
94    * Other users are created as their content is imported. However, editors
95    * don't have their own content so are created here instead.
96    *
97    * @return $this
98    */
99   protected function importEditors() {
100     $user_storage = $this->entityTypeManager->getStorage('user');
101     $editors = [
102       'Margaret Hopper',
103       'Grace Hamilton',
104     ];
105     foreach ($editors as $name) {
106       $user = $user_storage->create([
107         'name' => $name,
108         'status' => 1,
109         'roles' => ['editor'],
110         'mail' => mb_strtolower(str_replace(' ', '.', $name)) . '@example.com',
111       ]);
112       $user->enforceIsNew();
113       $user->save();
114       $this->storeCreatedContentUuids([$user->uuid() => 'user']);
115     }
116     return $this;
117   }
118
119   /**
120    * Imports articles.
121    *
122    * @return $this
123    */
124   protected function importArticles() {
125     $module_path = $this->moduleHandler->getModule('demo_umami_content')
126       ->getPath();
127     if (($handle = fopen($module_path . '/default_content/articles.csv', "r")) !== FALSE) {
128       $uuids = [];
129       $header = fgetcsv($handle);
130       while (($data = fgetcsv($handle)) !== FALSE) {
131         $data = array_combine($header, $data);
132         // Prepare content.
133         $values = [
134           'type' => 'article',
135           'title' => $data['title'],
136           'moderation_state' => 'published',
137         ];
138         // Fields mapping starts.
139         // Set Body Field.
140         if (!empty($data['body'])) {
141           $body_path = $module_path . '/default_content/article_body/' . $data['body'];
142           $body = file_get_contents($body_path);
143           if ($body !== FALSE) {
144             $values['body'] = [['value' => $body, 'format' => 'basic_html']];
145           }
146         }
147         // Set node alias if exists.
148         if (!empty($data['slug'])) {
149           $values['path'] = [['alias' => '/' . $data['slug']]];
150         }
151         // Set field_tags if exists.
152         if (!empty($data['tags'])) {
153           $values['field_tags'] = [];
154           $tags = explode(',', $data['tags']);
155           foreach ($tags as $term) {
156             $values['field_tags'][] = ['target_id' => $this->getTerm($term)];
157           }
158         }
159         // Set article author.
160         if (!empty($data['author'])) {
161           $values['uid'] = $this->getUser($data['author']);
162         }
163         // Set Image field.
164         if (!empty($data['image'])) {
165           $path = $module_path . '/default_content/images/' . $data['image'];
166           $values['field_image'] = [
167             'target_id' => $this->createFileEntity($path),
168             'alt' => $data['alt'],
169           ];
170         }
171
172         // Create Node.
173         $node = $this->entityTypeManager->getStorage('node')->create($values);
174         $node->save();
175         $uuids[$node->uuid()] = 'node';
176       }
177       $this->storeCreatedContentUuids($uuids);
178       fclose($handle);
179     }
180     return $this;
181   }
182
183   /**
184    * Imports recipes.
185    *
186    * @return $this
187    */
188   protected function importRecipes() {
189     $module_path = $this->moduleHandler->getModule('demo_umami_content')->getPath();
190
191     if (($handle = fopen($module_path . '/default_content/recipes.csv', "r")) !== FALSE) {
192       $header = fgetcsv($handle);
193       $uuids = [];
194       while (($data = fgetcsv($handle)) !== FALSE) {
195         $data = array_combine($header, $data);
196         $values = [
197           'type' => 'recipe',
198           // Title field.
199           'title' => $data['title'],
200           'moderation_state' => 'published',
201         ];
202         // Set article author.
203         if (!empty($data['author'])) {
204           $values['uid'] = $this->getUser($data['author']);
205         }
206         // Set node alias if exists.
207         if (!empty($data['slug'])) {
208           $values['path'] = [['alias' => '/' . $data['slug']]];
209         }
210         // Set field_image field.
211         if (!empty($data['image'])) {
212           $image_path = $module_path . '/default_content/images/' . $data['image'];
213           $values['field_image'] = [
214             'target_id' => $this->createFileEntity($image_path),
215             'alt' => $data['alt'],
216           ];
217         }
218         // Set field_summary Field.
219         if (!empty($data['summary'])) {
220           $values['field_summary'] = [['value' => $data['summary'], 'format' => 'basic_html']];
221         }
222         // Set field_recipe_category if exists.
223         if (!empty($data['recipe_category'])) {
224           $values['field_recipe_category'] = [];
225           $tags = array_filter(explode(',', $data['recipe_category']));
226           foreach ($tags as $term) {
227             $values['field_recipe_category'][] = ['target_id' => $this->getTerm($term, 'recipe_category')];
228           }
229         }
230         // Set field_preparation_time Field.
231         if (!empty($data['preparation_time'])) {
232           $values['field_preparation_time'] = [['value' => $data['preparation_time']]];
233         }
234         // Set field_cooking_time Field.
235         if (!empty($data['cooking_time'])) {
236           $values['field_cooking_time'] = [['value' => $data['cooking_time']]];
237         }
238         // Set field_difficulty Field.
239         if (!empty($data['difficulty'])) {
240           $values['field_difficulty'] = $data['difficulty'];
241         }
242         // Set field_number_of_servings Field.
243         if (!empty($data['number_of_servings'])) {
244           $values['field_number_of_servings'] = [['value' => $data['number_of_servings']]];
245         }
246         // Set field_ingredients Field.
247         if (!empty($data['ingredients'])) {
248           $ingredients = explode(',', $data['ingredients']);
249           $values['field_ingredients'] = [];
250           foreach ($ingredients as $ingredient) {
251             $values['field_ingredients'][] = ['value' => $ingredient];
252           }
253         }
254         // Set field_recipe_instruction Field.
255         if (!empty($data['recipe_instruction'])) {
256           $recipe_instruction_path = $module_path . '/default_content/recipe_instructions/' . $data['recipe_instruction'];
257           $recipe_instructions = file_get_contents($recipe_instruction_path);
258           if ($recipe_instructions !== FALSE) {
259             $values['field_recipe_instruction'] = [['value' => $recipe_instructions, 'format' => 'basic_html']];
260           }
261         }
262         // Set field_tags if exists.
263         if (!empty($data['tags'])) {
264           $values['field_tags'] = [];
265           $tags = array_filter(explode(',', $data['tags']));
266           foreach ($tags as $term) {
267             $values['field_tags'][] = ['target_id' => $this->getTerm($term)];
268           }
269         }
270
271         $node = $this->entityTypeManager->getStorage('node')->create($values);
272         $node->save();
273         $uuids[$node->uuid()] = 'node';
274       }
275       $this->storeCreatedContentUuids($uuids);
276       fclose($handle);
277     }
278     return $this;
279   }
280
281   /**
282    * Imports pages.
283    *
284    * @return $this
285    */
286   protected function importPages() {
287     if (($handle = fopen($this->moduleHandler->getModule('demo_umami_content')->getPath() . '/default_content/pages.csv', "r")) !== FALSE) {
288       $headers = fgetcsv($handle);
289       $uuids = [];
290       while (($data = fgetcsv($handle)) !== FALSE) {
291         $data = array_combine($headers, $data);
292
293         // Prepare content.
294         $values = [
295           'type' => 'page',
296           'title' => $data['title'],
297           'moderation_state' => 'published',
298         ];
299         // Fields mapping starts.
300         // Set Body Field.
301         if (!empty($data['body'])) {
302           $values['body'] = [['value' => $data['body'], 'format' => 'basic_html']];
303         }
304         // Set node alias if exists.
305         if (!empty($data['slug'])) {
306           $values['path'] = [['alias' => '/' . $data['slug']]];
307         }
308         // Set article author.
309         if (!empty($data['author'])) {
310           $values['uid'] = $this->getUser($data['author']);
311         }
312
313         // Create Node.
314         $node = $this->entityTypeManager->getStorage('node')->create($values);
315         $node->save();
316         $uuids[$node->uuid()] = 'node';
317       }
318       $this->storeCreatedContentUuids($uuids);
319       fclose($handle);
320     }
321     return $this;
322   }
323
324   /**
325    * Imports block content entities.
326    *
327    * @return $this
328    */
329   protected function importBlockContent() {
330     $module_path = $this->moduleHandler->getModule('demo_umami_content')->getPath();
331     $block_content_entities = [
332       'umami_recipes_banner' => [
333         'uuid' => '4c7d58a3-a45d-412d-9068-259c57e40541',
334         'info' => 'Umami Recipes Banner',
335         'type' => 'banner_block',
336         'field_title' => [
337           'value' => 'Super easy vegetarian pasta bake',
338         ],
339         'field_content_link' => [
340           'uri' => 'internal:' . call_user_func(function () {
341             $nodes = $this->entityTypeManager->getStorage('node')->loadByProperties(['title' => 'Super easy vegetarian pasta bake']);
342             $node = reset($nodes);
343             return $this->aliasManager->getAliasByPath('/node/' . $node->id());
344           }),
345           'title' => 'Super easy vegetarian pasta bake',
346         ],
347         'field_summary' => [
348           'value' => 'A wholesome pasta bake is the ultimate comfort food. This delicious bake is super quick to prepare and an ideal midweek meal for all the family.',
349         ],
350         'field_banner_image' => [
351           'target_id' => $this->createFileEntity($module_path . '/default_content/images/veggie-pasta-bake-hero-umami.jpg'),
352           'alt' => 'Mouth watering vegetarian pasta bake with rich tomato sauce and cheese toppings',
353         ],
354       ],
355       'umami_disclaimer' => [
356         'uuid' => '9b4dcd67-99f3-48d0-93c9-2c46648b29de',
357         'info' => 'Umami disclaimer',
358         'type' => 'disclaimer_block',
359         'field_disclaimer' => [
360           'value' => '<strong>Umami Magazine & Umami Publications</strong> is a fictional magazine and publisher for illustrative purposes only.',
361           'format' => 'basic_html',
362         ],
363         'field_copyright' => [
364           'value' => '&copy; 2018 Terms & Conditions',
365           'format' => 'basic_html',
366         ],
367       ],
368       'umami_footer_promo' => [
369         'uuid' => '924ab293-8f5f-45a1-9c7f-2423ae61a241',
370         'info' => 'Umami footer promo',
371         'type' => 'footer_promo_block',
372         'field_title' => [
373           'value' => 'Umami Food Magazine',
374         ],
375         'field_summary' => [
376           'value' => 'Skills and know-how. Magazine exclusive articles, recipes and plenty of reasons to get your copy today.',
377         ],
378         'field_content_link' => [
379           'uri' => 'internal:' . call_user_func(function () {
380             $nodes = $this->entityTypeManager->getStorage('node')->loadByProperties(['title' => 'About Umami']);
381             $node = reset($nodes);
382             return $this->aliasManager->getAliasByPath('/node/' . $node->id());
383           }),
384           'title' => 'Find out more',
385         ],
386         'field_promo_image' => [
387           'target_id' => $this->createFileEntity($module_path . '/default_content/images/umami-bundle.png'),
388           'alt' => '3 issue bundle of the Umami food magazine',
389         ],
390       ],
391     ];
392
393     // Create block content.
394     foreach ($block_content_entities as $values) {
395       $block_content = $this->entityTypeManager->getStorage('block_content')->create($values);
396       $block_content->save();
397       $this->storeCreatedContentUuids([$block_content->uuid() => 'block_content']);
398     }
399     return $this;
400   }
401
402   /**
403    * Deletes any content imported by this module.
404    *
405    * @return $this
406    */
407   public function deleteImportedContent() {
408     $uuids = $this->state->get('demo_umami_content_uuids', []);
409     $by_entity_type = array_reduce(array_keys($uuids), function ($carry, $uuid) use ($uuids) {
410       $entity_type_id = $uuids[$uuid];
411       $carry[$entity_type_id][] = $uuid;
412       return $carry;
413     }, []);
414     foreach ($by_entity_type as $entity_type_id => $entity_uuids) {
415       $storage = $this->entityTypeManager->getStorage($entity_type_id);
416       $entities = $storage->loadByProperties(['uuid' => $entity_uuids]);
417       $storage->delete($entities);
418     }
419     return $this;
420   }
421
422   /**
423    * Looks up a user by name, if it is missing the user is created.
424    *
425    * @param string $name
426    *   Username.
427    *
428    * @return int
429    *   User ID.
430    */
431   protected function getUser($name) {
432     $user_storage = $this->entityTypeManager->getStorage('user');
433     $users = $user_storage->loadByProperties(['name' => $name]);;
434     if (empty($users)) {
435       // Creating user without any password.
436       $user = $user_storage->create([
437         'name' => $name,
438         'status' => 1,
439         'roles' => ['author'],
440         'mail' => mb_strtolower(str_replace(' ', '.', $name)) . '@example.com',
441       ]);
442       $user->enforceIsNew();
443       $user->save();
444       $this->storeCreatedContentUuids([$user->uuid() => 'user']);
445       return $user->id();
446     }
447     $user = reset($users);
448     return $user->id();
449   }
450
451   /**
452    * Looks up a term by name, if it is missing the term is created.
453    *
454    * @param string $term_name
455    *   Term name.
456    * @param string $vocabulary_id
457    *   Vocabulary ID.
458    *
459    * @return int
460    *   Term ID.
461    */
462   protected function getTerm($term_name, $vocabulary_id = 'tags') {
463     $term_name = trim($term_name);
464     $term_storage = $this->entityTypeManager->getStorage('taxonomy_term');
465     $terms = $term_storage->loadByProperties([
466       'name' => $term_name,
467       'vid' => $vocabulary_id,
468     ]);
469     if (!$terms) {
470       $term = $term_storage->create([
471         'name' => $term_name,
472         'vid' => $vocabulary_id,
473         'path' => ['alias' => '/' . Html::getClass($vocabulary_id) . '/' . Html::getClass($term_name)],
474       ]);
475       $term->save();
476       $this->storeCreatedContentUuids([$term->uuid() => 'taxonomy_term']);
477       return $term->id();
478     }
479     $term = reset($terms);
480     return $term->id();
481   }
482
483   /**
484    * Creates a file entity based on an image path.
485    *
486    * @param string $path
487    *   Image path.
488    *
489    * @return int
490    *   File ID.
491    */
492   protected function createFileEntity($path) {
493     $uri = $this->fileUnmanagedCopy($path);
494     $file = $this->entityTypeManager->getStorage('file')->create([
495       'uri' => $uri,
496       'status' => 1,
497     ]);
498     $file->save();
499     $this->storeCreatedContentUuids([$file->uuid() => 'file']);
500     return $file->id();
501   }
502
503   /**
504    * Stores record of content entities created by this import.
505    *
506    * @param array $uuids
507    *   Array of UUIDs where the key is the UUID and the value is the entity
508    *   type.
509    */
510   protected function storeCreatedContentUuids(array $uuids) {
511     $uuids = $this->state->get('demo_umami_content_uuids', []) + $uuids;
512     $this->state->set('demo_umami_content_uuids', $uuids);
513   }
514
515   /**
516    * Wrapper around file_unmanaged_copy().
517    *
518    * @param string $path
519    *   Path to image.
520    *
521    * @return string|false
522    *   The path to the new file, or FALSE in the event of an error.
523    */
524   protected function fileUnmanagedCopy($path) {
525     $filename = basename($path);
526     return file_unmanaged_copy($path, 'public://' . $filename, FILE_EXISTS_REPLACE);
527   }
528
529 }