Further modules included.
[yaffs-website] / web / modules / contrib / advanced_help / src / Controller / AdvancedHelpController.php
1 <?php
2
3 namespace Drupal\advanced_help\Controller;
4
5 use Drupal\Component\Plugin\PluginManagerInterface;
6 use Drupal\Component\Utility\SafeMarkup;
7 use Drupal\Component\Utility\Xss;
8 use Drupal\Core\Controller\ControllerBase;
9 use Drupal\Core\EventSubscriber\MainContentViewSubscriber;
10 use Drupal\Core\Url;
11 use Michelf\MarkdownExtra;
12 use Symfony\Component\DependencyInjection\ContainerInterface;
13 use Symfony\Component\HttpFoundation\Request;
14 use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
15
16 /**
17  * Class AdvancedHelpController.
18  *
19  * @package Drupal\advanced_help\Controller\AdvancedHelpController.
20  */
21 class AdvancedHelpController extends ControllerBase {
22
23   /**
24    * The advanced help plugin manager.
25    *
26    * @var \Drupal\Component\Plugin\PluginManagerInterface
27    */
28   private $advanced_help;
29
30
31   public function __construct(PluginManagerInterface $advanced_help) {
32     $this->advanced_help = $advanced_help;
33   }
34
35   /**
36    * {@inheritdoc}
37    */
38   public static function create(ContainerInterface $container) {
39     return new static(
40       $container->get('plugin.manager.advanced_help')
41     );
42   }
43
44   /**
45    * Content.
46    *
47    * @todo Implement search integration.
48    * @return array
49    *   Returns module index.
50    */
51   public function main() {
52     $topics = $this->advanced_help->getTopics();
53     $settings = $this->advanced_help->getSettings();
54
55     // Print a module index.
56     $modules = $this->advanced_help->getModuleList();
57     asort($modules);
58
59     $items = [];
60     foreach ($modules as $module => $module_name) {
61       if (!empty($topics[$module]) && empty($settings[$module]['hide'])) {
62         if (isset($settings[$module]['index name'])) {
63           $name = $settings[$module]['index name'];
64         }
65         elseif (isset($settings[$module]['name'])) {
66           $name = $settings[$module]['name'];
67         }
68         else {
69           $name = $this->t($module_name);
70         }
71         $items[] = $this->l($name, new Url('advanced_help.module_index', ['module' => $module]));
72       }
73     }
74
75     return [
76       'help_modules' => [
77         '#theme' => 'item_list',
78         '#items' => $items,
79         '#title' => $this->t('Module help index'),
80       ]
81     ];
82   }
83
84   /**
85    * Build a hierarchy for a single module's topics.
86    *
87    * @param $topics array.
88    * @return array.
89    */
90   private function getTopicHierarchy($topics) {
91     foreach ($topics as $module => $module_topics) {
92       foreach ($module_topics as $topic => $info) {
93         $parent_module = $module;
94         // We have a blank topic that we don't want parented to itself.
95         if (!$topic) {
96           continue;
97         }
98
99         if (empty($info['parent'])) {
100           $parent = '';
101         }
102         elseif (strpos($info['parent'], '%')) {
103           list($parent_module, $parent) = explode('%', $info['parent']);
104           if (empty($topics[$parent_module][$parent])) {
105             // If it doesn't exist, top level.
106             $parent = '';
107           }
108         }
109         else {
110           $parent = $info['parent'];
111           if (empty($module_topics[$parent])) {
112             // If it doesn't exist, top level.
113             $parent = '';
114           }
115         }
116
117         if (!isset($topics[$parent_module][$parent]['children'])) {
118           $topics[$parent_module][$parent]['children'] = [];
119         }
120         $topics[$parent_module][$parent]['children'][] = [$module, $topic];
121         $topics[$module][$topic]['_parent'] = [$parent_module, $parent];
122       }
123     }
124     return $topics;
125   }
126
127   /**
128    * Helper function to sort topics.
129    * @param string $id_a
130    * @param string $id_b
131    * @return mixed
132    */
133   private function helpUasort($id_a, $id_b) {
134     $topics = $this->advanced_help->getTopics();
135     list($module_a, $topic_a) = $id_a;
136     $a = $topics[$module_a][$topic_a];
137     list($module_b, $topic_b) = $id_b;
138     $b = $topics[$module_b][$topic_b];
139
140     $a_weight = isset($a['weight']) ? $a['weight'] : 0;
141     $b_weight = isset($b['weight']) ? $b['weight'] : 0;
142     if ($a_weight != $b_weight) {
143       return ($a_weight < $b_weight) ? -1 : 1;
144     }
145
146     if ($a['title'] != $b['title']) {
147       return ($a['title'] < $b['title']) ? -1 : 1;
148     }
149     return 0;
150   }
151
152   /**
153    * Build a tree of advanced help topics.
154    *
155    * @param array $topics
156    *   Topics.
157    * @param array $topic_ids
158    *   Topic Ids.
159    * @param int $max_depth
160    *   Maximum depth for subtopics.
161    * @param int $depth
162    *   Default depth for subtopics.
163    *
164    * @return array
165    *   Returns list of topics/subtopics.
166    */
167   private function getTree($topics, $topic_ids, $max_depth = -1, $depth = 0) {
168     uasort($topic_ids, [$this, 'helpUasort']);
169     $items = [];
170     foreach ($topic_ids as $info) {
171       list($module, $topic) = $info;
172       $item = $this->l($topics[$module][$topic]['title'], new Url('advanced_help.help', ['module' => $module, 'topic' => $topic]));
173       if (!empty($topics[$module][$topic]['children']) && ($max_depth == -1 || $depth < $max_depth)) {
174         $link = [
175           '#theme' => 'item_list',
176           '#items' => advanced_help_get_tree($topics, $topics[$module][$topic]['children'], $max_depth, $depth + 1),
177         ];
178         $item .= \Drupal::service('renderer')->render($link, FALSE);
179       }
180       $items[] = $item;
181     }
182
183     return $items;
184   }
185
186
187   public function moduleIndex($module) {
188     $topics = $this->advanced_help->getTopics();
189
190     if (empty($topics[$module])) {
191       throw new NotFoundHttpException();
192     }
193
194     $topics = $this->getTopicHierarchy($topics);
195     $items = $this->getTree($topics, $topics[$module]['']['children']);
196
197     return [
198       'index' => [
199         '#theme' => 'item_list',
200         '#items' => $items,
201       ]
202     ];
203   }
204
205   /**
206    * Set the name of the module in the index page.
207    *
208    * @param string $module Module name
209    * @return string
210    */
211   public function moduleIndexTitle($module) {
212     return $this->advanced_help->getModuleName($module) . ' help index';
213   }
214
215   public function topicPage(Request $request, $module, $topic) {
216     $is_modal = ($request->query->get(MainContentViewSubscriber::WRAPPER_FORMAT) === 'drupal_modal');
217     $info = $this->advanced_help->getTopic($module, $topic);
218     if (!$info) {
219       throw new NotFoundHttpException();
220     }
221
222     $parent = $info;
223     $pmodule = $module;
224
225     // Loop checker.
226     $checked = [];
227     while (!empty($parent['parent'])) {
228       if (strpos($parent['parent'], '%')) {
229         list($pmodule, $ptopic) = explode('%', $parent['parent']);
230       }
231       else {
232         $ptopic = $parent['parent'];
233       }
234
235       if (!empty($checked[$pmodule][$ptopic])) {
236         break;
237       }
238
239       $checked[$pmodule][$ptopic] = TRUE;
240
241       $parent = $this->advanced_help->getTopic($pmodule, $ptopic);
242       if (!$parent) {
243         break;
244       }
245
246     }
247
248     $build = $this->viewTopic($module, $topic, $is_modal);
249     if (empty($build['#markup'])) {
250       $build['#markup'] = $this->t('Missing help topic.');
251     }
252     $build['#attached']['library'][] = 'advanced_help/help';
253     return $build;
254   }
255
256     /**
257    * Load and render a help topic.
258    *
259    * @param string $module
260    *   Name of the module.
261    * @param string $topic
262    *   Name of the topic.
263    * @todo port the drupal_alter functionality.
264    *
265    * @return string
266    *   Returns formatted topic.
267    */
268   public function viewTopic($module, $topic, $is_modal = false) {
269     $file_info = $this->advanced_help->getTopicFileInfo($module, $topic);
270     if ($file_info) {
271       $info = $this->advanced_help->getTopic($module, $topic);
272       $file = "{$file_info['path']}/{$file_info['file']}";
273       $build = [
274         '#type' => 'markup',
275       ];
276
277       if (!empty($info['css'])) {
278         $build['#attached']['library'][] = $info['module'] . '/' . $info['css'];
279       }
280
281       $build['#markup'] = file_get_contents($file);
282       if (isset($info['readme file']) && $info['readme file']) {
283         $ext = pathinfo($file, PATHINFO_EXTENSION);
284         if ('md' == $ext) {
285           $build['#markup'] = '<div class="advanced-help-topic">' . Xss::filterAdmin(MarkdownExtra::defaultTransform($build['#markup'])) . '</div>';
286         }
287         return $build;
288       }
289
290       // Change 'topic:' to the URL for another help topic.
291       preg_match('/&topic:([^"]+)&/', $build['#markup'], $matches);
292       if (isset($matches[1]) && preg_match('/[\w\-]\/[\w\-]+/', $matches[1])) {
293         list($umodule, $utopic) = explode('/', $matches[1]);
294         $path = new Url('advanced_help.help', ['module' => $umodule, 'topic' => $utopic]);
295         $build['#markup'] = preg_replace('/&topic:([^"]+)&/', $path->toString(), $build['#markup']);
296       }
297
298       global $base_path;
299
300       // Change 'path:' to the URL to the base help directory.
301       $build['#markup'] = str_replace('&path&', $base_path . $info['path'] . '/', $build['#markup']);
302
303       // Change 'trans_path:' to the URL to the actual help directory.
304       $build['#markup'] = str_replace('&trans_path&', $base_path . $file_info['path'] . '/', $build['#markup']);
305
306       // Change 'base_url:' to the URL to the site.
307       $build['#markup'] = preg_replace('/&base_url&([^"]+)"/', $base_path . '$1' . '"', $build['#markup']);
308
309       // Run the line break filter if requested.
310       if (!empty($info['line break'])) {
311         // Remove the header since it adds an extra <br /> to the filter.
312         $build['#markup'] = preg_replace('/^<!--[^\n]*-->\n/', '', $build['#markup']);
313
314         $build['#markup'] = _filter_autop($build['#markup']);
315       }
316
317       if (!empty($info['navigation']) && !$is_modal) {
318         $topics = $this->advanced_help->getTopics();
319         $topics = $this->getTopicHierarchy($topics);
320         if (!empty($topics[$module][$topic]['children'])) {
321           $items = $this->getTree($topics, $topics[$module][$topic]['children']);
322           $links = [
323             '#theme' => 'item_list',
324             '#items' => $items
325           ];
326           $build['#markup'] .= \Drupal::service('renderer')->render($links, FALSE);
327         }
328
329         list($parent_module, $parent_topic) = $topics[$module][$topic]['_parent'];
330         if ($parent_topic) {
331           $parent = $topics[$module][$topic]['_parent'];
332           $up = new Url('advanced_help.help', ['module' => $parent[0], 'topic' => $parent[1]]);
333         }
334         else {
335           $up = new Url('advanced_help.module_index', ['module' => $module]);
336         }
337
338         $siblings = $topics[$parent_module][$parent_topic]['children'];
339         uasort($siblings, [$this, 'helpUasort']);
340         $prev = $next = NULL;
341         $found = FALSE;
342         foreach ($siblings as $sibling) {
343           list($sibling_module, $sibling_topic) = $sibling;
344           if ($found) {
345             $next = $sibling;
346             break;
347           }
348           if ($sibling_module == $module && $sibling_topic == $topic) {
349             $found = TRUE;
350             continue;
351           }
352           $prev = $sibling;
353         }
354
355         if ($prev || $up || $next) {
356           $navigation = '<div class="help-navigation clear-block">';
357
358           if ($prev) {
359             $navigation .= $this->l('«« ' . $topics[$prev[0]][$prev[1]]['title'], new Url('advanced_help.help', ['module' => $prev[0], 'topic' => $prev[1]], ['attributes' => ['class' => 'help-left']]));
360           }
361           if ($up) {
362             $navigation .= $this->l($this->t('Up'), $up->setOption('attributes', ['class' => ($prev) ? 'help-up' : 'help-up-noleft']));
363           }
364           if ($next) {
365             $navigation .= $this->l($topics[$next[0]][$next[1]]['title'] . ' »»', new Url('advanced_help.help', ['module' => $next[0], 'topic' => $next[1]], ['attributes' => ['class' => 'help-right']]));
366           }
367
368           $navigation .= '</div>';
369           $build['#markup'] .= $navigation;
370         }
371       }
372       $build['#markup'] = '<div class="advanced-help-topic">' . $build['#markup'] . '</div>';
373 //      drupal_alter('advanced_help_topic', $output, $popup);
374       return $build;
375     }
376   }
377
378   /**
379    * Set the title of the topic.
380    * @param $module
381    * @param $topic
382    * @return string
383    */
384   public function topicPageTitle($module, $topic) {
385     $info = $this->advanced_help->getTopic($module, $topic);
386     if (!$info) {
387       throw new NotFoundHttpException();
388     }
389     return $info['title'];
390   }
391 }