Projet

Général

Profil

Paste
Télécharger (34,8 ko) Statistiques
| Branche: | Révision:

root / drupal7 / sites / all / modules / advanced_help / advanced_help.module @ 87dbc3bf

1
<?php
2
/**
3
 * @file
4
 * Pluggable system to provide advanced help facilities for Drupal and modules.
5
 *
6
 * Modules utilizing this help system should create a 'help' directory in their
7
 * module. Inside that directory place MODULENAME.help.ini which will be
8
 * formatted like this:
9
 *
10
 * @code
11
 * [buses]
12
 * title = "How buses are tied into the system"
13
 * file = buses
14
 *
15
 * [TOPIC_ID]
16
 * title = "Title of topic"
17
 * file = filename of topic, without the .html extension
18
 * weight = the importance of the topic on the index page
19
 * parent = the optional topic parent to use in the breadcrumb.
20
 * Can be either topic or module%topic
21
 * @endcode
22
 *
23
 * All topics are addressed by the module that provides the topic, and the topic
24
 * id. Links may be embedded as in the following example:
25
 *
26
 * @code
27
 * $output .= theme('advanced_help_topic', array('module' => $module, 'topic' => $topic));
28
 * @endcode
29
 *
30
 * Link to other topics using <a href="topic:module/topic">. (Using
31
 * this format ensures the popup status remains consistent for all
32
 * links.)
33
 */
34

    
35
/**
36
 * Implements hook_menu().
37
 */
38
function advanced_help_menu() {
39
  // View help topic index.
40
  //
41
  // This is structured little oddly so that POTX can handle the translation.
42
  if (module_exists('help')) {
43
    $items['admin/advanced_help'] = array(
44
      'title' => 'Advanced help',
45
      'page callback' => 'advanced_help_index_page',
46
      'access arguments' => array('view advanced help index'),
47
      'weight' => 9,
48
    );
49
  }
50
  else {
51
    $items['admin/advanced_help'] = array(
52
      'title' => 'Help',
53
      'page callback' => 'advanced_help_index_page',
54
      'access arguments' => array('view advanced help index'),
55
      'weight' => 9,
56
    );
57
  }
58
  $items['advanced_help/search/%'] = array(
59
    'title' => 'Search help',
60
    'page callback' => 'advanced_help_search_view',
61
    'page arguments' => array('advanced_help'),
62
    'access arguments' => array('view advanced help index'),
63
  );
64

    
65
  // View help topic.
66
  $items['help/%/%'] = array(
67
    'page callback' => 'advanced_help_topic_page',
68
    'page arguments' => array(1, 2),
69
    'access arguments' => array('view advanced help topic'),
70
    'type' => MENU_CALLBACK,
71
  );
72

    
73
  return $items;
74
}
75

    
76
/**
77
 * Implements hook_menu_alter().
78
 */
79
function advanced_help_menu_alter(&$callbacks) {
80
  // We need to fix the menu item provided by search module to restrict access.
81
  $callbacks['search/advanced_help/%menu_tail']['access callback'] = 'user_access';
82
  $callbacks['search/advanced_help/%menu_tail']['access arguments'] = array('view advanced help index');
83
}
84

    
85
/**
86
 * Implements hook_theme().
87
 */
88
function advanced_help_theme() {
89
  $hooks['advanced_help_topic'] = array(
90
    'variables' => array(
91
      'module' => NULL,
92
      'topic'  => NULL,
93
      'type'   => 'icon',
94
    ),
95
  );
96

    
97
  $hooks['advanced_help_popup'] = array(
98
    'render element' => 'content',
99
    'template' => 'advanced-help-popup',
100
  );
101

    
102
  return $hooks;
103
}
104

    
105
/**
106
 * Helper function to sort topics.
107
 */
108
function advanced_help_uasort($id_a, $id_b) {
109
  $topics = advanced_help_get_topics();
110
  list($module_a, $topic_a) = $id_a;
111
  $a = $topics[$module_a][$topic_a];
112
  list($module_b, $topic_b) = $id_b;
113
  $b = $topics[$module_b][$topic_b];
114

    
115
  $a_weight = isset($a['weight']) ? $a['weight'] : 0;
116
  $b_weight = isset($b['weight']) ? $b['weight'] : 0;
117
  if ($a_weight != $b_weight) {
118
    return ($a_weight < $b_weight) ? -1 : 1;
119
  }
120

    
121
  if ($a['title'] != $b['title']) {
122
    return ($a['title'] < $b['title']) ? -1 : 1;
123
  }
124
  return 0;
125
}
126

    
127
/**
128
 * Helper function for grabbing search keys. Function is missing in D7.
129
 *
130
 * http://api.drupal.org/api/function/search_get_keys/6
131
 */
132
function advanced_help_search_get_keys() {
133
  static $return;
134
  if (!isset($return)) {
135
    // Extract keys as remainder of path
136
    // Note: support old GET format of searches for existing links.
137
    $path = explode('/', $_GET['q'], 3);
138
    $keys = empty($_REQUEST['keys']) ? '' : $_REQUEST['keys'];
139
    $return = count($path) == 3 ? $path[2] : $keys;
140
  }
141
  return $return;
142
}
143

    
144
/**
145
 * Page callback for advanced help search.
146
 */
147
function advanced_help_search_view() {
148
  if (!module_exists('search')) {
149
    return drupal_not_found();
150
  }
151

    
152
  $breadcrumb[] = advanced_help_l(t('Help'), 'admin/advanced_help');
153

    
154
  if (!isset($_POST['form_id'])) {
155
    $keys = advanced_help_search_get_keys();
156
    // Only perform search if there is non-whitespace search term:
157
    $results = '';
158
    if (trim($keys)) {
159
      $search_results = search_data($keys, 'advanced_help');
160
      $search_results = drupal_render($search_results);
161
      // Collect the search results:
162
      $results = array(
163
        '#type' => 'markup',
164
        '#markup' => $search_results,
165
      );
166
    }
167

    
168
    // Construct the search form.
169
    $output['advanced_help_search_form'] = drupal_get_form('advanced_help_search_form', $keys);
170
    $output['results'] = $results;
171

    
172
  }
173
  else {
174
    $output = drupal_get_form('advanced_help_search_form', empty($keys) ? '' : $keys);
175
  }
176

    
177
  $popup = !empty($_GET['popup']) && user_access('view advanced help popup');
178
  if ($popup) {
179
    // Prevent devel module from spewing.
180
    $GLOBALS['devel_shutdown'] = FALSE;
181
    // Suppress admin_menu.
182
    module_invoke('admin_menu', 'suppress');
183
    drupal_set_breadcrumb(array_reverse($breadcrumb));
184
    print theme('advanced_help_popup', array('content' => $output));
185
    return;
186
  }
187

    
188
  $breadcrumb = array_merge(drupal_get_breadcrumb(), array_reverse($breadcrumb));
189
  drupal_set_breadcrumb($breadcrumb);
190
  return $output;
191
}
192

    
193
/**
194
 * Page callback to view the advanced help topic index.
195
 *
196
 * @param string $module
197
 *   Name of the module.
198
 *
199
 * @return array
200
 *   Returns module index.
201
 */
202
function advanced_help_index_page($module = '') {
203
  $topics = advanced_help_get_topics();
204
  $settings = advanced_help_get_settings();
205

    
206
  $output = array();
207
  // Print a search widget.
208
  $output['advanced_help_search'] = module_exists('search') ? drupal_get_form('advanced_help_search_form') : t('Enable the search module to search help.');
209

    
210
  $breadcrumb = array();
211
  if ($module) {
212
    if (empty($topics[$module])) {
213
      return drupal_not_found();
214
    }
215

    
216
    advanced_help_get_topic_hierarchy($topics);
217
    $items = advanced_help_get_tree($topics, $topics[$module]['']['children']);
218

    
219
    $breadcrumb[] = advanced_help_l(t('Help'), 'admin/advanced_help');
220

    
221
    drupal_set_title(t('@module help index', array('@module' => advanced_help_get_module_name($module))));
222
    $output['items-module'] = array(
223
      '#theme' => 'item_list',
224
      '#items' => $items,
225
    );
226
  }
227
  else {
228
    // Print a module index.
229
    $modules = array();
230
    $result = db_query('SELECT * FROM {system}');
231
    foreach ($result as $info) {
232
      $module_info = unserialize($info->info);
233
      $modules[$info->name] = $module_info['name'];
234
    }
235

    
236
    asort($modules);
237

    
238
    $items = array();
239
    foreach ($modules as $module => $module_name) {
240
      if (!empty($topics[$module]) && empty($settings[$module]['hide'])) {
241
        if (isset($settings[$module]['index name'])) {
242
          $name = $settings[$module]['index name'];
243
        }
244
        elseif (isset($settings[$module]['name'])) {
245
          $name = $settings[$module]['name'];
246
        }
247
        else {
248
          $name = t($module_name);
249
        }
250
        $items[] = advanced_help_l($name, "admin/advanced_help/$module");
251
      }
252
    }
253

    
254
    drupal_set_title(t('Module help index'));
255
    $output['items-nomodule'] = array(
256
      '#theme' => 'item_list',
257
      '#items' => $items,
258
    );
259
  }
260

    
261
  $popup = !empty($_GET['popup']) && user_access('view advanced help popup');
262
  if ($popup) {
263
    // Prevent devel module from spewing.
264
    $GLOBALS['devel_shutdown'] = FALSE;
265
    // Suppress admin_menu.
266
    module_invoke('admin_menu', 'suppress');
267
    drupal_set_breadcrumb(array_reverse($breadcrumb));
268
    print theme('advanced_help_popup', array('content' => $output));
269
    return;
270
  }
271

    
272
  $breadcrumb = array_merge(drupal_get_breadcrumb(), array_reverse($breadcrumb));
273
  drupal_set_breadcrumb($breadcrumb);
274
  return $output;
275
}
276

    
277
/**
278
 * Build a tree of advanced help topics.
279
 *
280
 * @param array $topics
281
 *   Topics.
282
 * @param array $topic_ids
283
 *   Topic Ids.
284
 * @param int $max_depth
285
 *   Maximum depth for subtopics.
286
 * @param int $depth
287
 *   Default depth for subtopics.
288
 *
289
 * @return array
290
 *   Returns list of topics/subtopics.
291
 */
292
function advanced_help_get_tree($topics, $topic_ids, $max_depth = -1, $depth = 0) {
293
  uasort($topic_ids, 'advanced_help_uasort');
294
  $items = array();
295
  foreach ($topic_ids as $info) {
296
    list($module, $topic) = $info;
297
    $item = advanced_help_l($topics[$module][$topic]['title'], "help/$module/$topic");
298
    if (!empty($topics[$module][$topic]['children']) && ($max_depth == -1 || $depth < $max_depth)) {
299
      $item .= theme('item_list', array(
300
        'items' => advanced_help_get_tree($topics, $topics[$module][$topic]['children'], $max_depth, $depth + 1),
301
      ));
302
    }
303

    
304
    $items[] = $item;
305
  }
306

    
307
  return $items;
308
}
309

    
310
/**
311
 * Build a hierarchy for a single module's topics.
312
 */
313
function advanced_help_get_topic_hierarchy(&$topics) {
314
  foreach ($topics as $module => $module_topics) {
315
    foreach ($module_topics as $topic => $info) {
316
      $parent_module = $module;
317
      // We have a blank topic that we don't want parented to itself.
318
      if (!$topic) {
319
        continue;
320
      }
321

    
322
      if (empty($info['parent'])) {
323
        $parent = '';
324
      }
325
      elseif (strpos($info['parent'], '%')) {
326
        list($parent_module, $parent) = explode('%', $info['parent']);
327
        if (empty($topics[$parent_module][$parent])) {
328
          // If it doesn't exist, top level.
329
          $parent = '';
330
        }
331
      }
332
      else {
333
        $parent = $info['parent'];
334
        if (empty($module_topics[$parent])) {
335
          // If it doesn't exist, top level.
336
          $parent = '';
337
        }
338
      }
339

    
340
      if (!isset($topics[$parent_module][$parent]['children'])) {
341
        $topics[$parent_module][$parent]['children'] = array();
342
      }
343
      $topics[$parent_module][$parent]['children'][] = array($module, $topic);
344
      $topics[$module][$topic]['_parent'] = array($parent_module, $parent);
345
    }
346
  }
347
}
348

    
349
/**
350
 * Implements hook_form_system_modules_alter().
351
 *
352
 * Add advanced help links to the modules page.
353
 */
354
function advanced_help_form_system_modules_alter(&$form, &$form_state) {
355
  if (!isset($form['modules'])) {
356
    return;
357
  }
358
  $advanced_help_modules = drupal_map_assoc(array_keys(advanced_help_get_topics()));
359
  foreach (element_children($form['modules']) as $group) {
360
    foreach (element_children($form['modules'][$group]) as $module) {
361
      if (isset($advanced_help_modules[$module])) {
362
        $form['modules'][$group][$module]['links']['help'] = array(
363
          '#type' => 'link',
364
          '#title' => t('Help'),
365
          '#href' => "admin/advanced_help/$module",
366
          '#options' => array(
367
            'attributes' => array(
368
              'class' => array('module-link', 'module-link-help'),
369
              'title' => t('Help'),
370
            ),
371
          ),
372
        );
373
      }
374
    }
375
  }
376
}
377

    
378
/**
379
 * Form builder callback to build the search form.
380
 *
381
 * Load search/search.pages so that its template preprocess functions are
382
 * visible and can be invoked.
383
 */
384
function advanced_help_search_form($form, &$form_state, $keys = '') {
385
  module_load_include('inc', 'search', 'search.pages');
386
  $form = search_form($form, $form_state, 'admin/advanced_help', $keys, 'advanced_help', t('Search help'));
387

    
388
  $form['basic']['inline']['submit']['#validate'] = array('search_form_validate');
389
  $form['basic']['inline']['submit']['#submit'] = array('advanced_help_search_form_submit');
390

    
391
  return $form;
392
}
393

    
394
/**
395
 * Process a search form submission.
396
 */
397
function advanced_help_search_form_submit($form, &$form_state) {
398
  $keys = empty($form_state['values']['processed_keys']) ? $form_state['values']['keys'] : $form_state['values']['processed_keys'];
399
  if ($keys == '') {
400
    form_set_error('keys', t('Please enter some keywords.'));
401
    return;
402
  }
403

    
404
  $popup = !empty($_GET['popup']) && user_access('view advanced help popup');
405

    
406
  if ($popup) {
407
    $form_state['redirect'] = array('advanced_help/search/' . $keys, array('query' => array('popup' => 'true')));
408
  }
409
  else {
410
    $form_state['redirect'] = 'advanced_help/search/' . $keys;
411
  }
412
}
413

    
414
/**
415
 * Small helper function to get a module's proper name.
416
 *
417
 * @param string $module
418
 *   Name of the module.
419
 *
420
 * @return string
421
 *   Returns module's descriptive name.
422
 */
423
function advanced_help_get_module_name($module) {
424
  $settings = advanced_help_get_settings();
425
  if (isset($settings[$module]['name'])) {
426
    $name = $settings[$module]['name'];
427
  }
428
  else {
429
    $info = db_query("SELECT s.info FROM {system} s WHERE s.name = :name",
430
      array(':name' => $module))
431
      ->fetchField();
432
    $info = unserialize($info);
433
    $name = t($info['name']);
434
  }
435
  return $name;
436
}
437

    
438
/**
439
 * Page callback to view a help topic.
440
 */
441
function advanced_help_topic_page($module, $topic) {
442
  $info = advanced_help_get_topic($module, $topic);
443
  if (!$info) {
444
    return drupal_not_found();
445
  }
446

    
447
  $popup = !empty($_GET['popup']) && user_access('view advanced help popup');
448

    
449
  drupal_set_title($info['title']);
450

    
451
  // Set up breadcrumb.
452
  $breadcrumb = array();
453

    
454
  $parent = $info;
455
  $pmodule = $module;
456

    
457
  // Loop checker.
458
  $checked = array();
459
  while (!empty($parent['parent'])) {
460
    if (strpos($parent['parent'], '%')) {
461
      list($pmodule, $ptopic) = explode('%', $parent['parent']);
462
    }
463
    else {
464
      $ptopic = $parent['parent'];
465
    }
466

    
467
    if (!empty($checked[$pmodule][$ptopic])) {
468
      break;
469
    }
470
    $checked[$pmodule][$ptopic] = TRUE;
471

    
472
    $parent = advanced_help_get_topic($pmodule, $ptopic);
473
    if (!$parent) {
474
      break;
475
    }
476

    
477
    $breadcrumb[] = advanced_help_l($parent['title'], "help/$pmodule/$ptopic");
478
  }
479

    
480
  $breadcrumb[] = advanced_help_l(advanced_help_get_module_name($pmodule), "admin/advanced_help/$pmodule");
481
  $breadcrumb[] = advanced_help_l(t('Help'), "admin/advanced_help");
482

    
483
  $output = advanced_help_view_topic($module, $topic, $popup);
484
  if (empty($output)) {
485
    $output = t('Missing help topic.');
486
  }
487

    
488
  if ($popup) {
489
    // Prevent devel module from spewing.
490
    $GLOBALS['devel_shutdown'] = FALSE;
491
    // Suppress admin_menu.
492
    module_invoke('admin_menu', 'suppress');
493
    drupal_set_breadcrumb(array_reverse($breadcrumb));
494
    print theme('advanced_help_popup', array('content' => $output));
495
    return;
496
  }
497

    
498
  drupal_add_css(drupal_get_path('module', 'advanced_help') . '/help.css');
499
  $breadcrumb[] = l(t('Home'), '');
500
  drupal_set_breadcrumb(array_reverse($breadcrumb));
501
  return $output;
502
}
503

    
504
/**
505
 * Implements hook_permission().
506
 */
507
function advanced_help_permission() {
508
  return array(
509
    'view advanced help topic' => array('title' => t('View help topics')),
510
    'view advanced help popup' => array('title' => t('View help popups')),
511
    'view advanced help index' => array('title' => t('View help index')),
512
  );
513
}
514

    
515
/**
516
 * Display a help icon with a link to view the topic in a popup.
517
 *
518
 * @param array $variables
519
 *   An associative array containing:
520
 *   - module: The module that owns this help topic.
521
 *   - topic: The identifier for the topic
522
 *   - type
523
 *     - 'icon' to display the question mark icon
524
 *     - 'title' to display the topic's title
525
 *     - any other text to display the text. Be sure to t() it!
526
 */
527
function theme_advanced_help_topic($variables) {
528
  $module = $variables['module'];
529
  $topic  = $variables['topic'];
530
  $type   = $variables['type'];
531

    
532
  $info = advanced_help_get_topic($module, $topic);
533
  if (!$info) {
534
    return;
535
  }
536

    
537
  switch ($type) {
538
    case 'icon':
539
      $text = '<span>' . t('Help') . '</span>';
540
      $class = 'advanced-help-link';
541
      break;
542

    
543
    case 'title':
544
      $text = $info['title'];
545
      $class = 'advanced-help-title';
546
      break;
547

    
548
    default:
549
      $class = 'advanced-help-title';
550
      $text = $type;
551
      break;
552
  }
553

    
554
  if (user_access('view advanced help popup')) {
555
    drupal_add_css(drupal_get_path('module', 'advanced_help') . '/help-icon.css');
556
    return l($text, "help/$module/$topic", array(
557
      'attributes' => array(
558
        'class' => $class,
559
        'onclick' => "var w=window.open(this.href, 'advanced_help_window', 'width=" . $info['popup width'] . ",height=" . $info['popup height'] . ",scrollbars,resizable'); w.focus(); return false;",
560
        'title' => $info['title'],
561
      ),
562
      'query' => array('popup' => TRUE),
563
      'html' => TRUE)
564
    );
565
  }
566
  else {
567
    return l($text, "help/$module/$topic", array(
568
      'attributes' => array(
569
        'class' => $class,
570
        'title' => $info['title'],
571
      ),
572
      'html' => TRUE)
573
    );
574
  }
575
}
576

    
577
/**
578
 * Load and render a help topic.
579
 */
580
function advanced_help_get_topic_filename($module, $topic) {
581
  $info = advanced_help_get_topic_file_info($module, $topic);
582
  if ($info) {
583
    return "./$info[path]/$info[file]";
584
  }
585
}
586
/**
587
 * Load and render a help topic.
588
 */
589
function advanced_help_get_topic_file_info($module, $topic) {
590
  global $language;
591

    
592
  $info = advanced_help_get_topic($module, $topic);
593
  if (empty($info)) {
594
    return;
595
  }
596

    
597
  // Search paths:
598
  $paths = array(
599
    // Allow theme override.
600
    path_to_theme() . '/help',
601
    // Translations.
602
    drupal_get_path('module', $module) . "/translations/help/$language->language",
603
    // In same directory as .inc file.
604
    $info['path'],
605
  );
606

    
607
  foreach ($paths as $path) {
608
    if (file_exists("./$path/$info[file]")) {
609
      return array('path' => $path, 'file' => $info['file']);
610
    }
611
  }
612
}
613

    
614
/**
615
 * Load and render a help topic.
616
 *
617
 * @param string $module
618
 *   Name of the module.
619
 * @param string $topic
620
 *   Name of the topic.
621
 * @param bool $popup
622
 *   Whether to show in popup or not.
623
 *
624
 * @return string
625
 *   Returns formatted topic.
626
 */
627
function advanced_help_view_topic($module, $topic, $popup = FALSE) {
628
  $file_info = advanced_help_get_topic_file_info($module, $topic);
629
  if ($file_info) {
630
    $info = advanced_help_get_topic($module, $topic);
631
    $file = "./$file_info[path]/$file_info[file]";
632

    
633
    $output = file_get_contents($file);
634
    if (isset($info['readme file']) && $info['readme file']) {
635
      // Readme files are treated as plain text: filter accordingly.
636
      $output = check_plain($output);
637
    }
638

    
639
    // Make some exchanges. The strtr is because url() translates $ into %24
640
    // but we need to change it back for the regex replacement.
641
    //
642
    // Change 'topic:' to the URL for another help topic.
643
    if ($popup) {
644
      $output = preg_replace('/href="topic:([^"]+)"/', 'href="' . strtr(url('help/$1', array('query' => array('popup' => 'true'))), array('%24' => '$')) . '"', $output);
645
      $output = preg_replace('/src="topic:([^"]+)"/', 'src="' . strtr(url('help/$1', array('query' => array('popup' => 'true'))), array('%24' => '$')) . '"', $output);
646
      $output = preg_replace('/&topic:([^"]+)&/', strtr(url('help/$1', array('query' => array('popup' => 'true'))), array('%24' => '$')), $output);
647
    }
648
    else {
649
      $output = preg_replace('/href="topic:([^"]+)"/', 'href="' . strtr(url('help/$1'), array('%24' => '$')) . '"', $output);
650
      $output = preg_replace('/src="topic:([^"]+)"/', 'src="' . strtr(url('help/$1'), array('%24' => '$')) . '"', $output);
651
      $output = preg_replace('/&topic:([^"]+)&/', strtr(url('help/$1'), array('%24' => '$')), $output);
652
    }
653

    
654
    global $base_path;
655

    
656
    // Change 'path:' to the URL to the base help directory.
657
    $output = preg_replace('/href="path:([^"]+)"/', 'href="' . $base_path . $info['path'] . '/$1"', $output);
658
    $output = preg_replace('/src="path:([^"]+)"/', 'src="' . $base_path . $info['path'] . '/$1"', $output);
659
    $output = str_replace('&path&', $base_path . $info['path'] . '/', $output);
660

    
661
    // Change 'trans_path:' to the URL to the actual help directory.
662
    $output = preg_replace('/href="trans_path:([^"]+)"/', 'href="' . $base_path . $file_info['path'] . '/$1"', $output);
663
    $output = preg_replace('/src="trans_path:([^"]+)"/', 'src="' . $base_path . $file_info['path'] . '/$1"', $output);
664
    $output = str_replace('&trans_path&', $base_path . $file_info['path'] . '/', $output);
665

    
666
    // Change 'base_url:' to the URL to the site.
667
    $output = preg_replace('/href="base_url:([^"]+)"/', 'href="' . strtr(url('$1'), array('%24' => '$')) . '"', $output);
668
    $output = preg_replace('/src="base_url:([^"]+)"/', 'src="' . strtr(url('$1'), array('%24' => '$')) . '"', $output);
669
    $output = preg_replace('/&base_url&([^"]+)"/', strtr(url('$1'), array('%24' => '$')) . '"', $output);
670

    
671
    // Run the line break filter if requested.
672
    if (!empty($info['line break'])) {
673
      // Remove the header since it adds an extra <br /> to the filter.
674
      $output = preg_replace('/^<!--[^\n]*-->\n/', '', $output);
675

    
676
      $output = _filter_autop($output);
677
    }
678

    
679
    if (!empty($info['navigation'])) {
680
      $topics = advanced_help_get_topics();
681
      advanced_help_get_topic_hierarchy($topics);
682
      if (!empty($topics[$module][$topic]['children'])) {
683
        $items = advanced_help_get_tree($topics, $topics[$module][$topic]['children']);
684
        $output .= theme('item_list', array('items' => $items));
685
      }
686

    
687
      list($parent_module, $parent_topic) = $topics[$module][$topic]['_parent'];
688
      if ($parent_topic) {
689
        $parent = $topics[$module][$topic]['_parent'];
690
        $up = "help/$parent[0]/$parent[1]";
691
      }
692
      else {
693
        $up = "admin/advanced_help/$module";
694
      }
695

    
696
      $siblings = $topics[$parent_module][$parent_topic]['children'];
697
      uasort($siblings, 'advanced_help_uasort');
698
      $prev = $next = NULL;
699
      $found = FALSE;
700
      foreach ($siblings as $sibling) {
701
        list($sibling_module, $sibling_topic) = $sibling;
702
        if ($found) {
703
          $next = $sibling;
704
          break;
705
        }
706
        if ($sibling_module == $module && $sibling_topic == $topic) {
707
          $found = TRUE;
708
          continue;
709
        }
710
        $prev = $sibling;
711
      }
712

    
713
      if ($prev || $up || $next) {
714
        $navigation = '<div class="help-navigation clear-block">';
715

    
716
        if ($prev) {
717
          $navigation .= advanced_help_l('<< ' . $topics[$prev[0]][$prev[1]]['title'], "help/$prev[0]/$prev[1]", array('attributes' => array('class' => 'help-left')));
718
        }
719
        if ($up) {
720
          $navigation .= advanced_help_l(t('Up'), $up, array('attributes' => array('class' => $prev ? 'help-up' : 'help-up-noleft')));
721
        }
722
        if ($next) {
723
          $navigation .= advanced_help_l($topics[$next[0]][$next[1]]['title'] . ' >>', "help/$next[0]/$next[1]", array('attributes' => array('class' => 'help-right')));
724
        }
725

    
726
        $navigation .= '</div>';
727

    
728
        $output .= $navigation;
729
      }
730
    }
731

    
732
    if (!empty($info['css'])) {
733
      drupal_add_css($info['path'] . '/' . $info['css']);
734
    }
735

    
736
    $output = '<div class="advanced-help-topic">' . $output . '</div>';
737
    drupal_alter('advanced_help_topic', $output, $popup);
738
    return $output;
739
  }
740
}
741

    
742
/**
743
 * Get the information for a single help topic.
744
 */
745
function advanced_help_get_topic($module, $topic) {
746
  $topics = advanced_help_get_topics();
747
  if (!empty($topics[$module][$topic])) {
748
    return $topics[$module][$topic];
749
  }
750
}
751

    
752
/**
753
 * Search the system for all available help topics.
754
 */
755
function advanced_help_get_topics() {
756
  $ini = _advanced_help_parse_ini();
757
  return $ini['topics'];
758
}
759

    
760
/**
761
 * Returns advanced help settings.
762
 */
763
function advanced_help_get_settings() {
764
  $ini = _advanced_help_parse_ini();
765
  return $ini['settings'];
766
}
767

    
768
/**
769
 * Funtion to parse ini / txt files.
770
 */
771
function _advanced_help_parse_ini() {
772
  global $language;
773
  static $ini = NULL;
774

    
775
  if (!isset($ini)) {
776
    $cache = cache_get('advanced_help_ini:' . $language->language);
777
    if ($cache) {
778
      $ini = $cache->data;
779
    }
780
  }
781

    
782
  if (!isset($ini)) {
783
    $ini = array('topics' => array(), 'settings' => array());
784

    
785
    $help_path = drupal_get_path('module', 'advanced_help') . '/modules';
786
    foreach (module_list() as $module) {
787
      $module_path = drupal_get_path('module', $module);
788
      $info = array();
789
      if (file_exists("$module_path/help/$module.help.ini")) {
790
        $path = "$module_path/help";
791
        $info = parse_ini_file("./$module_path/help/$module.help.ini", TRUE);
792
      }
793
      elseif (file_exists("$help_path/$module/$module.help.ini")) {
794
        $path = "$help_path/$module";
795
        $info = parse_ini_file("./$help_path/$module/$module.help.ini", TRUE);
796
      }
797
      elseif (!file_exists("$module_path/help")) {
798
        // Look for one or more README files.
799
        $files = file_scan_directory("./$module_path",
800
          '/^(README|readme).*\.(txt|TXT)$/', array('recurse' => FALSE));
801
        $path = "./$module_path";
802
        foreach ($files as $name => $fileinfo) {
803
          $info[$fileinfo->filename] = array(
804
            'line break' => TRUE,
805
            'readme file' => TRUE,
806
            'file' => $fileinfo->filename,
807
            'title' => $fileinfo->name,
808
          );
809
        }
810
      }
811

    
812
      if (!empty($info)) {
813
        // Get translated titles:
814
        $translation = array();
815
        if (file_exists("$module_path/translations/help/$language->language/$module.help.ini")) {
816
          $translation = parse_ini_file("$module_path/translations/help/$language->language/$module.help.ini", TRUE);
817
        }
818

    
819
        $ini['settings'][$module] = array();
820
        if (!empty($info['advanced help settings'])) {
821
          $ini['settings'][$module] = $info['advanced help settings'];
822
          unset($info['advanced help settings']);
823

    
824
          // Check translated strings for translatable global settings.
825
          if (isset($translation['advanced help settings']['name'])) {
826
            $ini['settings']['name'] = $translation['advanced help settings']['name'];
827
          }
828
          if (isset($translation['advanced help settings']['index name'])) {
829
            $ini['settings']['index name'] = $translation['advanced help settings']['index name'];
830
          }
831

    
832
        }
833

    
834
        foreach ($info as $name => $topic) {
835
          // Each topic should have a name, a title, a file and path.
836
          $file = !empty($topic['file']) ? $topic['file'] : $name;
837
          $ini['topics'][$module][$name] = array(
838
            'name' => $name,
839
            'module' => $module,
840
            'ini' => $topic,
841
            'title' => !empty($translation[$name]['title']) ? $translation[$name]['title'] : $topic['title'],
842
            'weight' => isset($topic['weight']) ? $topic['weight'] : 0,
843
            'parent' => isset($topic['parent']) ? $topic['parent'] : 0,
844
            'popup width' => isset($topic['popup width']) ? $topic['popup width'] : 500,
845
            'popup height' => isset($topic['popup height']) ? $topic['popup height'] : 500,
846
            // Require extension.
847
            'file' => isset($topic['readme file']) ? $file : $file . '.html',
848
            // Not in .ini file.
849
            'path' => $path,
850
            'line break' => isset($topic['line break']) ? $topic['line break'] : (isset($ini['settings'][$module]['line break']) ? $ini['settings'][$module]['line break'] : FALSE),
851
            'navigation' => isset($topic['navigation']) ? $topic['navigation'] : (isset($ini['settings'][$module]['navigation']) ? $ini['settings'][$module]['navigation'] : TRUE),
852
            'css' => isset($topic['css']) ? $topic['css'] : (isset($ini['settings'][$module]['css']) ? $ini['settings'][$module]['css'] : NULL),
853
            'readme file' => isset($topic['readme file']) ? $topic['readme file'] : FALSE,
854
          );
855
        }
856
      }
857
    }
858
    drupal_alter('advanced_help_topic_info', $ini);
859

    
860
    cache_set('advanced_help_ini:' . $language->language, $ini);
861
  }
862
  return $ini;
863
}
864

    
865
/**
866
 * Implements hook_search_info().
867
 *
868
 * Returns title for the tab on search page & path component after 'search/'.
869
 */
870
function advanced_help_search_info() {
871
  return array(
872
    'title' => t('Help'),
873
    'path' => 'advanced_help',
874
  );
875
}
876

    
877
/**
878
 * Implements hook_search_execute().
879
 */
880
function advanced_help_search_execute($keys = NULL) {
881
  $topics = advanced_help_get_topics();
882

    
883
  $query = db_select('search_index', 'i', array('target' => 'slave'))
884
    ->extend('SearchQuery')
885
    ->extend('PagerDefault');
886
  $query->join('advanced_help_index', 'ahi', 'i.sid = ahi.sid');
887
  $query->searchExpression($keys, 'help');
888

    
889
  // Only continue if the first pass query matches.
890
  if (!$query->executeFirstPass()) {
891
    return array();
892
  }
893

    
894
  $results = array();
895

    
896
  $find = $query->execute();
897
  foreach ($find as $item) {
898
    $sids[] = $item->sid;
899
  }
900

    
901
  $query = db_select('advanced_help_index', 'ahi');
902
  $result = $query
903
    ->fields('ahi')
904
    ->condition('sid', $sids, 'IN')
905
    ->execute();
906

    
907
  foreach ($result as $sid) {
908
    // Guard against removed help topics that are still indexed.
909
    if (empty($topics[$sid->module][$sid->topic])) {
910
      continue;
911
    }
912
    $info = $topics[$sid->module][$sid->topic];
913
    $text = advanced_help_view_topic($sid->module, $sid->topic);
914
    $results[] = array(
915
      'link' => advanced_help_url("help/$sid->module/$sid->topic"),
916
      'title' => $info['title'],
917
      'snippet' => search_excerpt($keys, $text),
918
    );
919
  }
920
  return $results;
921
}
922

    
923
/**
924
 * Implements hook_search_reset().
925
 */
926
function advanced_help_search_reset() {
927
  variable_del('advanced_help_last_cron');
928
}
929

    
930
/**
931
 * Implements hook_search_status().
932
 */
933
function advanced_help_search_status() {
934
  $topics = advanced_help_get_topics();
935
  $total = 0;
936
  foreach ($topics as $module => $module_topics) {
937
    foreach ($module_topics as $topic => $info) {
938
      $file = advanced_help_get_topic_filename($module, $topic);
939
      if ($file) {
940
        $total++;
941
      }
942
    }
943
  }
944

    
945
  $last_cron = variable_get('advanced_help_last_cron', array('time' => 0));
946
  $indexed = 0;
947
  if ($last_cron['time'] != 0) {
948
    $indexed = db_query("SELECT COUNT(*) FROM {search_dataset} sd WHERE sd.type = 'help' AND sd.sid IS NOT NULL AND sd.reindex = 0")->fetchField();
949
  }
950
  return array('remaining' => $total - $indexed, 'total' => $total);
951
}
952

    
953
/**
954
 * Gets search id for each topic.
955
 *
956
 * Get or create an sid (search id) that correlates to each topic for
957
 * the search system.
958
 */
959
function advanced_help_get_sids(&$topics) {
960
  global $language;
961
  $result = db_query("SELECT * FROM {advanced_help_index} WHERE language = :language",
962
    array(':language' => $language->language));
963
  foreach ($result as $sid) {
964
    if (empty($topics[$sid->module][$sid->topic])) {
965
      db_query("DELETE FROM {advanced_help_index} WHERE sid = :sid",
966
        array(':sid' => $sid->sid));
967
    }
968
    else {
969
      $topics[$sid->module][$sid->topic]['sid'] = $sid->sid;
970
    }
971
  }
972
}
973

    
974
/**
975
 * Implements hook_update_index().
976
 */
977
function advanced_help_update_index() {
978
  global $language;
979

    
980
  // If we got interrupted by limit, this will contain the last module
981
  // and topic we looked at.
982
  $last = variable_get('advanced_help_last_cron', array('time' => 0));
983
  $limit = intval(variable_get('search_cron_limit', 100));
984
  $topics = advanced_help_get_topics();
985
  advanced_help_get_sids($topics);
986

    
987
  $count = 0;
988

    
989
  foreach ($topics as $module => $module_topics) {
990
    // Fast forward if necessary.
991
    if (!empty($last['module']) && $last['module'] != $module) {
992
      continue;
993
    }
994

    
995
    foreach ($module_topics as $topic => $info) {
996
      // Fast forward if necessary.
997
      if (!empty($last['topic']) && $last['topic'] != $topic) {
998
        continue;
999
      }
1000

    
1001
      // If we've been looking to catch up, and we have, reset so we
1002
      // stop fast forwarding.
1003
      if (!empty($last['module'])) {
1004
        unset($last['topic']);
1005
        unset($last['module']);
1006
      }
1007

    
1008
      $file = advanced_help_get_topic_filename($module, $topic);
1009
      if ($file && (empty($info['sid']) || filemtime($file) > $last['time'])) {
1010
        if (empty($info['sid'])) {
1011
          $info['sid'] = db_insert('advanced_help_index')
1012
            ->fields(array(
1013
              'module' => $module,
1014
              'topic'  => $topic,
1015
              'language' => $language->language,
1016
            ))
1017
            ->execute();
1018
        }
1019

    
1020
        search_index($info['sid'], 'help', '<h1>' . $info['title'] . '</h1>' . file_get_contents($file));
1021
        $count++;
1022
        if ($count >= $limit) {
1023
          $last['topic'] = $topic;
1024
          $last['module'] = $module;
1025
          // Don't change time if we stop.
1026
          variable_set('advanced_help_last_cron', $last);
1027
          return;
1028
        }
1029
      }
1030
    }
1031
  }
1032

    
1033
  variable_set('advanced_help_last_cron', array('time' => time()));
1034
}
1035

    
1036
/**
1037
 * Fill in a bunch of page variables for our specialized popup page.
1038
 */
1039
function template_preprocess_advanced_help_popup(&$variables) {
1040
  // Add favicon.
1041
  if (theme_get_setting('toggle_favicon')) {
1042
    drupal_add_html_head('<link rel="shortcut icon" href="' . check_url(theme_get_setting('favicon')) . '" type="image/x-icon" />');
1043
  }
1044

    
1045
  global $theme;
1046
  // Construct page title.
1047
  if (drupal_get_title()) {
1048
    $head_title = array(
1049
      strip_tags(drupal_get_title()),
1050
      variable_get('site_name', 'Drupal'),
1051
    );
1052
  }
1053
  else {
1054
    $head_title = array(variable_get('site_name', 'Drupal'));
1055
    if (variable_get('site_slogan', '')) {
1056
      $head_title[] = variable_get('site_slogan', '');
1057
    }
1058
  }
1059

    
1060
  drupal_add_css(drupal_get_path('module', 'advanced_help') . '/help-popup.css');
1061
  drupal_add_css(drupal_get_path('module', 'advanced_help') . '/help.css');
1062

    
1063
  $variables['head_title']        = implode(' | ', $head_title);
1064
  $variables['base_path']         = base_path();
1065
  $variables['front_page']        = url();
1066
  $variables['breadcrumb']        = theme('breadcrumb', array('breadcrumb' => drupal_get_breadcrumb()));
1067
  $variables['feed_icons']        = drupal_get_feeds();
1068
  $variables['head']              = drupal_get_html_head();
1069
  $variables['language']          = $GLOBALS['language'];
1070
  $variables['language']->dir     = $GLOBALS['language']->direction ? 'rtl' : 'ltr';
1071
  $variables['logo']              = theme_get_setting('logo');
1072
  $variables['messages']          = theme('status_messages');
1073
  $variables['site_name']         = (theme_get_setting('toggle_name') ? variable_get('site_name', 'Drupal') : '');
1074
  $variables['css']               = drupal_add_css();
1075
  $css = drupal_add_css();
1076

    
1077
  // Remove theme css.
1078
  foreach ($css as $media => $types) {
1079
    if (isset($css[$media]['theme'])) {
1080
      $css[$media]['theme'] = array();
1081
    }
1082
  }
1083

    
1084
  $variables['styles']            = drupal_get_css($css);
1085
  $variables['scripts']           = drupal_get_js();
1086
  $variables['title']             = drupal_get_title();
1087

    
1088
  // This function can be called either with a render array or
1089
  // an already rendered string.
1090
  if (is_array($variables['content'])) {
1091
    $variables['content'] = drupal_render($variables['content']);
1092
  }
1093
  // Closure should be filled last.
1094
  $variables['closure']           = theme('closure');
1095
}
1096

    
1097
/**
1098
 * Format a link but preserve popup identity.
1099
 */
1100
function advanced_help_l($text, $dest, $options = array()) {
1101
  $popup = !empty($_GET['popup']) && user_access('view advanced help popup');
1102
  if ($popup) {
1103
    if (empty($options['query'])) {
1104
      $options['query'] = array();
1105
    }
1106

    
1107
    if (is_array($options['query'])) {
1108
      $options['query'] += array('popup' => TRUE);
1109
    }
1110
    else {
1111
      $options['query'] += '&popup=TRUE';
1112
    }
1113
  }
1114

    
1115
  return l($text, $dest, $options);
1116
}
1117

    
1118
/**
1119
 * Format a URL but preserve popup identity.
1120
 */
1121
function advanced_help_url($dest, $options = array()) {
1122
  $popup = !empty($_GET['popup']) && user_access('view advanced help popup');
1123
  if ($popup) {
1124
    if (empty($options['query'])) {
1125
      $options['query'] = array();
1126
    }
1127

    
1128
    $options['query'] += array('popup' => TRUE);
1129
  }
1130

    
1131
  return url($dest, $options);
1132
}