7c961a6c397c864d1c17e7c713f9fd44df9c23ae
[yaffs-website] / web / modules / contrib / devel / devel_generate / src / Plugin / DevelGenerate / MenuDevelGenerate.php
1 <?php
2
3 namespace Drupal\devel_generate\Plugin\DevelGenerate;
4
5 use Drupal\Component\Utility\Unicode;
6 use Drupal\Core\Entity\EntityStorageInterface;
7 use Drupal\Core\Extension\ModuleHandlerInterface;
8 use Drupal\Core\Form\FormStateInterface;
9 use Drupal\Core\Menu\MenuLinkTreeInterface;
10 use Drupal\Core\Menu\MenuTreeParameters;
11 use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
12 use Drupal\devel_generate\DevelGenerateBase;
13 use Symfony\Component\DependencyInjection\ContainerInterface;
14
15 /**
16  * Provides a MenuDevelGenerate plugin.
17  *
18  * @DevelGenerate(
19  *   id = "menu",
20  *   label = @Translation("menus"),
21  *   description = @Translation("Generate a given number of menus and menu links. Optionally delete current menus."),
22  *   url = "menu",
23  *   permission = "administer devel_generate",
24  *   settings = {
25  *     "num_menus" = 2,
26  *     "num_links" = 50,
27  *     "title_length" = 12,
28  *     "max_width" = 6,
29  *     "kill" = FALSE,
30  *   }
31  * )
32  */
33 class MenuDevelGenerate extends DevelGenerateBase implements ContainerFactoryPluginInterface {
34
35   /**
36    * The menu tree service.
37    *
38    * @var \Drupal\Core\Menu\MenuLinkTreeInterface
39    */
40   protected $menuLinkTree;
41
42   /**
43    * The menu storage.
44    *
45    * @var \Drupal\Core\Entity\EntityStorageInterface
46    */
47   protected $menuStorage;
48
49   /**
50    * The menu link storage.
51    *
52    * @var \Drupal\Core\Entity\EntityStorageInterface
53    */
54   protected $menuLinkContentStorage;
55
56   /**
57    * The module handler.
58    *
59    * @var \Drupal\Core\Extension\ModuleHandlerInterface
60    */
61   protected $moduleHandler;
62
63   /**
64    * Constructs a MenuDevelGenerate object.
65    *
66    * @param array $configuration
67    *   A configuration array containing information about the plugin instance.
68    * @param string $plugin_id
69    *   The plugin ID for the plugin instance.
70    * @param mixed $plugin_definition
71    *   The plugin implementation definition.
72    * @param \Drupal\Core\Menu\MenuLinkTreeInterface $menu_tree
73    *   The menu tree service.
74    * @param \Drupal\Core\Entity\EntityStorageInterface $menu_storage
75    *   The menu storage.
76    * @param \Drupal\Core\Entity\EntityStorageInterface $menu_link_storage
77    *   The menu storage.
78    * @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler
79    *   The module handler.
80    */
81   public function __construct(array $configuration, $plugin_id, $plugin_definition, MenuLinkTreeInterface $menu_tree, EntityStorageInterface $menu_storage, EntityStorageInterface $menu_link_storage, ModuleHandlerInterface $module_handler) {
82     parent::__construct($configuration, $plugin_id, $plugin_definition);
83
84     $this->menuLinkTree = $menu_tree;
85     $this->menuStorage = $menu_storage;
86     $this->menuLinkContentStorage = $menu_link_storage;
87     $this->moduleHandler = $module_handler;
88   }
89
90   /**
91    * {@inheritdoc}
92    */
93   public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
94     $entity_manager = $container->get('entity.manager');
95     return new static(
96       $configuration, $plugin_id, $plugin_definition,
97       $container->get('menu.link_tree'),
98       $entity_manager->getStorage('menu'),
99       $entity_manager->getStorage('menu_link_content'),
100       $container->get('module_handler')
101     );
102   }
103
104   /**
105    * {@inheritdoc}
106    */
107   public function settingsForm(array $form, FormStateInterface $form_state) {
108     $menu_enabled = $this->moduleHandler->moduleExists('menu_ui');
109     if ($menu_enabled) {
110       $menus = array('__new-menu__' => $this->t('Create new menu(s)')) + menu_ui_get_menus();
111     }
112     else {
113       $menus = menu_list_system_menus();
114     }
115     $form['existing_menus'] = array(
116       '#type' => 'checkboxes',
117       '#title' => $this->t('Generate links for these menus'),
118       '#options' => $menus,
119       '#default_value' => array('__new-menu__'),
120       '#required' => TRUE,
121     );
122     if ($menu_enabled) {
123       $form['num_menus'] = array(
124         '#type' => 'number',
125         '#title' => $this->t('Number of new menus to create'),
126         '#default_value' => $this->getSetting('num_menus'),
127         '#min' => 0,
128         '#states' => array(
129           'visible' => array(
130             ':input[name="existing_menus[__new-menu__]"]' => array('checked' => TRUE),
131           ),
132         ),
133       );
134     }
135     $form['num_links'] = array(
136       '#type' => 'number',
137       '#title' => $this->t('Number of links to generate'),
138       '#default_value' => $this->getSetting('num_links'),
139       '#required' => TRUE,
140       '#min' => 0,
141     );
142     $form['title_length'] = array(
143       '#type' => 'number',
144       '#title' => $this->t('Maximum number of characters in menu and menu link names'),
145       '#description' => $this->t('The minimum length is 2.'),
146       '#default_value' => $this->getSetting('title_length'),
147       '#required' => TRUE,
148       '#min' => 2,
149       '#max' => 128,
150     );
151     $form['link_types'] = array(
152       '#type' => 'checkboxes',
153       '#title' => $this->t('Types of links to generate'),
154       '#options' => array(
155         'node' => $this->t('Nodes'),
156         'front' => $this->t('Front page'),
157         'external' => $this->t('External'),
158       ),
159       '#default_value' => array('node', 'front', 'external'),
160       '#required' => TRUE,
161     );
162     $form['max_depth'] = array(
163       '#type' => 'select',
164       '#title' => $this->t('Maximum link depth'),
165       '#options' => range(0, $this->menuLinkTree->maxDepth()),
166       '#default_value' => floor($this->menuLinkTree->maxDepth() / 2),
167       '#required' => TRUE,
168     );
169     unset($form['max_depth']['#options'][0]);
170     $form['max_width'] = array(
171       '#type' => 'number',
172       '#title' => $this->t('Maximum menu width'),
173       '#default_value' => $this->getSetting('max_width'),
174       '#description' => $this->t('Limit the width of the generated menu\'s first level of links to a certain number of items.'),
175       '#required' => TRUE,
176       '#min' => 0,
177     );
178     $form['kill'] = array(
179       '#type' => 'checkbox',
180       '#title' => $this->t('Delete existing custom generated menus and menu links before generating new ones.'),
181       '#default_value' => $this->getSetting('kill'),
182     );
183
184     return $form;
185   }
186
187   /**
188    * {@inheritdoc}
189    */
190   public function generateElements(array $values) {
191     // If the create new menus checkbox is off, set the number of new menus to 0.
192     if (!isset($values['existing_menus']['__new-menu__']) || !$values['existing_menus']['__new-menu__']) {
193       $values['num_menus'] = 0;
194     }
195     else {
196       // Unset the aux menu to avoid attach menu new items.
197       unset($values['existing_menus']['__new-menu__']);
198     }
199
200     // Delete custom menus.
201     if ($values['kill']) {
202       $this->deleteMenus();
203       $this->setMessage($this->t('Deleted existing menus and links.'));
204     }
205
206     // Generate new menus.
207     $new_menus = $this->generateMenus($values['num_menus'], $values['title_length']);
208     if (!empty($new_menus)) {
209       $this->setMessage($this->t('Created the following new menus: @menus', array('@menus' => implode(', ', $new_menus))));
210     }
211
212     // Generate new menu links.
213     $menus = $new_menus;
214     if (isset($values['existing_menus'])) {
215       $menus = $menus + $values['existing_menus'];
216     }
217     $new_links = $this->generateLinks($values['num_links'], $menus, $values['title_length'], $values['link_types'], $values['max_depth'], $values['max_width']);
218     $this->setMessage($this->t('Created @count new menu links.', array('@count' => count($new_links))));
219   }
220
221   /**
222    * {@inheritdoc}
223    */
224   public function validateDrushParams($args) {
225
226     $link_types = array('node', 'front', 'external');
227     $values = array(
228       'num_menus' => array_shift($args),
229       'num_links' => array_shift($args),
230       'kill' => drush_get_option('kill'),
231       'pipe' => drush_get_option('pipe'),
232       'link_types' => array_combine($link_types, $link_types),
233     );
234
235     $max_depth = array_shift($args);
236     $max_width = array_shift($args);
237     $values['max_depth'] = $max_depth ? $max_depth : 3;
238     $values['max_width'] = $max_width ? $max_width : 8;
239     $values['title_length'] = $this->getSetting('title_length');
240     $values['existing_menus']['__new-menu__'] = TRUE;
241
242     if ($this->isNumber($values['num_menus']) == FALSE) {
243       return drush_set_error('DEVEL_GENERATE_INVALID_INPUT', dt('Invalid number of menus'));
244     }
245     if ($this->isNumber($values['num_links']) == FALSE) {
246       return drush_set_error('DEVEL_GENERATE_INVALID_INPUT', dt('Invalid number of links'));
247     }
248     if ($this->isNumber($values['max_depth']) == FALSE || $values['max_depth'] > 9 || $values['max_depth'] < 1) {
249       return drush_set_error('DEVEL_GENERATE_INVALID_INPUT', dt('Invalid maximum link depth. Use a value between 1 and 9'));
250     }
251     if ($this->isNumber($values['max_width']) == FALSE || $values['max_width'] < 1) {
252       return drush_set_error('DEVEL_GENERATE_INVALID_INPUT', dt('Invalid maximum menu width. Use a positive numeric value.'));
253     }
254
255     return $values;
256   }
257
258   /**
259    * Deletes custom generated menus.
260    */
261   protected function deleteMenus() {
262     if ($this->moduleHandler->moduleExists('menu_ui')) {
263       $menu_ids = array();
264       foreach (menu_ui_get_menus(FALSE) as $menu => $menu_title) {
265         if (strpos($menu, 'devel-') === 0) {
266           $menu_ids[] = $menu;
267         }
268       }
269
270       if ($menu_ids) {
271         $menus = $this->menuStorage->loadMultiple($menu_ids);
272         $this->menuStorage->delete($menus);
273       }
274     }
275
276     // Delete menu links generated by devel.
277     $link_ids = $this->menuLinkContentStorage->getQuery()
278       ->condition('menu_name', 'devel', '<>')
279       ->condition('link__options', '%' . db_like('s:5:"devel";b:1') . '%', 'LIKE')
280       ->execute();
281
282     if ($link_ids) {
283       $links = $this->menuLinkContentStorage->loadMultiple($link_ids);
284       $this->menuLinkContentStorage->delete($links);
285     }
286
287   }
288
289   /**
290    * Generates new menus.
291    *
292    * @param int $num_menus
293    *   Number of menus to create.
294    * @param int $title_length
295    *   (optional) Maximum length per menu name.
296    *
297    * @return array
298    *   Array containing the generated vocabularies id.
299    */
300   protected function generateMenus($num_menus, $title_length = 12) {
301     $menus = array();
302
303     if ($this->moduleHandler->moduleExists('menu_ui')) {
304       for ($i = 1; $i <= $num_menus; $i++) {
305         $name = $this->getRandom()->word(mt_rand(2, max(2, $title_length)));
306
307         $menu = $this->menuStorage->create(array(
308           'label' => $name,
309           'id' => 'devel-' . Unicode::strtolower($name),
310           'description' => $this->t('Description of @name', array('@name' => $name)),
311         ));
312
313         $menu->save();
314         $menus[$menu->id()] = $menu->label();
315       }
316     }
317
318     return $menus;
319   }
320
321   /**
322    * Generates menu links in a tree structure.
323    */
324   protected function generateLinks($num_links, $menus, $title_length, $link_types, $max_depth, $max_width) {
325     $links = array();
326     $menus = array_keys(array_filter($menus));
327     $link_types = array_keys(array_filter($link_types));
328
329     $nids = array();
330     for ($i = 1; $i <= $num_links; $i++) {
331       // Pick a random menu.
332       $menu_name = $menus[array_rand($menus)];
333       // Build up our link.
334       $link_title = $this->getRandom()->word(mt_rand(2, max(2, $title_length)));
335       $link = $this->menuLinkContentStorage->create(array(
336         'menu_name' => $menu_name,
337         'weight' => mt_rand(-50, 50),
338         'title' => $link_title,
339         'bundle' => 'menu_link_content',
340         'description' => $this->t('Description of @title.', array('@title' => $link_title)),
341       ));
342       $link->link->options = array('devel' => TRUE);
343
344       // For the first $max_width items, make first level links.
345       if ($i <= $max_width) {
346         $depth = 0;
347       }
348       else {
349         // Otherwise, get a random parent menu depth.
350         $depth = mt_rand(1, max(1, $max_depth - 1));
351       }
352       // Get a random parent link from the proper depth.
353       do {
354         $parameters = new MenuTreeParameters();
355         $parameters->setMinDepth($depth);
356         $parameters->setMaxDepth($depth);
357         $tree = $this->menuLinkTree->load($menu_name, $parameters);
358
359         if ($tree) {
360           $link->parent = array_rand($tree);
361         }
362         $depth--;
363       } while (!$link->parent && $depth > 0);
364
365       $link_type = array_rand($link_types);
366       switch ($link_types[$link_type]) {
367         case 'node':
368           // Grab a random node ID.
369           $select = db_select('node_field_data', 'n')
370             ->fields('n', array('nid', 'title'))
371             ->condition('n.status', 1)
372             ->range(0, 1)
373             ->orderRandom();
374           // Don't put a node into the menu twice.
375           if (!empty($nids[$menu_name])) {
376             $select->condition('n.nid', $nids[$menu_name], 'NOT IN');
377           }
378           $node = $select->execute()->fetchAssoc();
379           if (isset($node['nid'])) {
380             $nids[$menu_name][] = $node['nid'];
381             $link->link->uri = 'entity:node/' . $node['nid'];
382             $link->title = $node['title'];
383             break;
384           }
385
386         case 'external':
387           $link->link->uri = 'http://www.example.com/';
388           break;
389
390         case 'front':
391           $link->link->uri = 'internal:/<front>';
392           break;
393
394         default:
395           $link->devel_link_type = $link_type;
396           break;
397       }
398
399       $link->save();
400
401       $links[$link->id()] = $link->link_title;
402     }
403
404     return $links;
405   }
406
407 }