Projet

Général

Profil

Paste
Télécharger (36,4 ko) Statistiques
| Branche: | Révision:

root / drupal7 / sites / all / modules / advanced_help / advanced_help.module @ 6e9292aa

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
 * Strings in hook_help() should not be run through t().
39
 */
40
function advanced_help_menu() {
41
  $help_exists = module_exists('help') ? TRUE : FALSE;
42
  if ($help_exists) {
43
    // Add tabs to core Help page to access Advanced Help.
44
    $items['admin/help/tab1'] = array(
45
      'title' => 'Help',
46
      'type' => MENU_DEFAULT_LOCAL_TASK,
47
      'weight' => 0,
48
    );
49

    
50
    $items['admin/help/ah'] = array(
51
      'title' => 'Advanced Help',
52
      'page callback' => 'advanced_help_index_page',
53
      'access arguments' => array('view advanced help index'),
54
      'type' => MENU_LOCAL_TASK,
55
      'weight' => 2,
56
    );
57
  }
58
  else {
59
    // Make Advanced Help the normal help.
60
    $items['admin/help/ah'] = array(
61
      'title' => 'Help',
62
      'page callback' => 'advanced_help_index_page',
63
      'access arguments' => array('view advanced help index'),
64
      'type' => MENU_NORMAL_ITEM,
65
      'weight' => 9,
66
    );
67
  }
68
  $items['help/ah/search/%'] = array(
69
    'title' => 'Search help',
70
    'page callback' => 'advanced_help_search_view',
71
    'page arguments' => array('advanced_help'),
72
    'access arguments' => array('view advanced help index'),
73
  );
74

    
75
  // View help topic.
76
  $items['help/%/%'] = array(
77
    'page callback' => 'advanced_help_topic_page',
78
    'page arguments' => array(1, 2),
79
    'access arguments' => array('view advanced help topic'),
80
    'type' => MENU_CALLBACK,
81
  );
82

    
83
  return $items;
84
}
85

    
86
/**
87
 * Implements hook_menu_alter().
88
 */
89
function advanced_help_menu_alter(&$callbacks) {
90
  // We need to fix the menu item provided by search module to restrict access.
91
  $callbacks['search/advanced_help/%menu_tail']['access callback'] = 'user_access';
92
  $callbacks['search/advanced_help/%menu_tail']['access arguments'] = array('view advanced help index');
93
}
94

    
95
/**
96
 * Implements hook_theme().
97
 */
98
function advanced_help_theme() {
99
  $hooks['advanced_help_topic'] = array(
100
    'variables' => array(
101
      'module' => NULL,
102
      'topic'  => NULL,
103
      'type'   => 'icon',
104
    ),
105
  );
106

    
107
  $hooks['advanced_help_popup'] = array(
108
    'render element' => 'content',
109
    'template' => 'advanced-help-popup',
110
  );
111

    
112
  return $hooks;
113
}
114

    
115
/**
116
 * Helper function to sort topics.
117
 */
118
function advanced_help_uasort($id_a, $id_b) {
119
  $topics = advanced_help_get_topics();
120
  list($module_a, $topic_a) = $id_a;
121
  $a = $topics[$module_a][$topic_a];
122
  list($module_b, $topic_b) = $id_b;
123
  $b = $topics[$module_b][$topic_b];
124

    
125
  $a_weight = isset($a['weight']) ? $a['weight'] : 0;
126
  $b_weight = isset($b['weight']) ? $b['weight'] : 0;
127
  if ($a_weight != $b_weight) {
128
    return ($a_weight < $b_weight) ? -1 : 1;
129
  }
130

    
131
  if ($a['title'] != $b['title']) {
132
    return ($a['title'] < $b['title']) ? -1 : 1;
133
  }
134
  return 0;
135
}
136

    
137
/**
138
 * Helper function for grabbing search keys. Function is missing in D7.
139
 *
140
 * http://api.drupal.org/api/function/search_get_keys/6
141
 */
142
function advanced_help_search_get_keys() {
143
  static $return;
144
  if (!isset($return)) {
145
    // Extract keys as remainder of path
146
    // Note: support old GET format of searches for existing links.
147
    $path = explode('/', $_GET['q'], 4);
148
    $keys = empty($_REQUEST['keys']) ? '' : $_REQUEST['keys'];
149
    $return = count($path) == 4 ? $path[3] : $keys;
150
  }
151
  return $return;
152
}
153

    
154
/**
155
 * Page callback for advanced help search.
156
 */
157
function advanced_help_search_view() {
158
  if (!module_exists('search')) {
159
    return drupal_not_found();
160
  }
161

    
162
  $breadcrumb[] = advanced_help_l(t('Advanced help'), 'admin/help/ah');
163

    
164
  if (!isset($_POST['form_id'])) {
165
    $keys = advanced_help_search_get_keys();
166
    // Only perform search if there is non-whitespace search term:
167
    $results = '';
168
    if (trim($keys)) {
169
      $search_results = search_data($keys, 'advanced_help');
170
      $search_results = drupal_render($search_results);
171
      // Collect the search results:
172
      $results = array(
173
        '#type' => 'markup',
174
        '#markup' => $search_results,
175
      );
176
    }
177

    
178
    // Construct the search form.
179
    $output['advanced_help_search_form'] = drupal_get_form('advanced_help_search_form', $keys);
180
    $output['results'] = $results;
181

    
182
  }
183
  else {
184
    $output = drupal_get_form('advanced_help_search_form', empty($keys) ? '' : $keys);
185
  }
186

    
187
  $popup = !empty($_GET['popup']) && user_access('view advanced help popup');
188
  if ($popup) {
189
    // Prevent devel module from spewing.
190
    $GLOBALS['devel_shutdown'] = FALSE;
191
    // Suppress admin_menu.
192
    module_invoke('admin_menu', 'suppress');
193
    drupal_set_breadcrumb(array_reverse($breadcrumb));
194
    print theme('advanced_help_popup', array('content' => $output));
195
    return;
196
  }
197

    
198
  $breadcrumb = array_merge(drupal_get_breadcrumb(), array_reverse($breadcrumb));
199
  drupal_set_breadcrumb($breadcrumb);
200
  return $output;
201
}
202

    
203
/**
204
 * Page callback to view the advanced help topic index.
205
 *
206
 * @param string $module
207
 *   Name of the module.
208
 *
209
 * @return array
210
 *   Returns module index.
211
 */
212
function advanced_help_index_page($module = '') {
213
  $topics = advanced_help_get_topics();
214
  $settings = advanced_help_get_settings();
215

    
216
  $output = array();
217
  // Print a search widget.
218
  $output['advanced_help_search'] = module_exists('search') ? drupal_get_form('advanced_help_search_form') : array('#markup' => t('Enable the search module to search help.'));
219

    
220
  $breadcrumb = array();
221
  if ($module) {
222
    if (empty($topics[$module])) {
223
      return drupal_not_found();
224
    }
225

    
226
    advanced_help_get_topic_hierarchy($topics);
227
    $items = advanced_help_get_tree($topics, $topics[$module]['']['children']);
228

    
229
    $breadcrumb[] = advanced_help_l(t('Advanced help'), 'admin/help/ah');
230

    
231
    drupal_set_title(t('@module help index', array('@module' => advanced_help_get_module_name($module))));
232
    $output['items-module'] = array(
233
      '#theme' => 'item_list',
234
      '#items' => $items,
235
    );
236
  }
237
  else {
238
    // Print a module index.
239
    $modules = array();
240
    $result = db_query('SELECT * FROM {system}');
241
    foreach ($result as $info) {
242
      $module_info = unserialize($info->info);
243
      $modules[$info->name] = isset($module_info['name']) ? $module_info['name'] : $info->name;
244
    }
245

    
246
    asort($modules);
247

    
248
    $items = array();
249
    foreach ($modules as $module => $module_name) {
250
      if (!empty($topics[$module]) && empty($settings[$module]['hide'])) {
251
        if (isset($settings[$module]['index name'])) {
252
          $name = $settings[$module]['index name'];
253
        }
254
        elseif (isset($settings[$module]['name'])) {
255
          $name = $settings[$module]['name'];
256
        }
257
        else {
258
          $name = t($module_name);
259
        }
260
        $items[] = advanced_help_l($name, "admin/help/ah/$module");
261
      }
262
    }
263

    
264
    drupal_set_title(t('Advanced help'));
265
    $output['items-nomodule'] = array(
266
      '#theme' => 'item_list',
267
      '#items' => $items,
268
    );
269
  }
270

    
271
  $popup = !empty($_GET['popup']) && user_access('view advanced help popup');
272
  if ($popup) {
273
    // Prevent devel module from spewing.
274
    $GLOBALS['devel_shutdown'] = FALSE;
275
    // Suppress admin_menu.
276
    module_invoke('admin_menu', 'suppress');
277
    drupal_set_breadcrumb(array_reverse($breadcrumb));
278
    print theme('advanced_help_popup', array('content' => $output));
279
    return;
280
  }
281

    
282
  $breadcrumb = array_merge(drupal_get_breadcrumb(), array_reverse($breadcrumb));
283
  drupal_set_breadcrumb($breadcrumb);
284
  return $output;
285
}
286

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

    
314
    $items[] = $item;
315
  }
316

    
317
  return $items;
318
}
319

    
320
/**
321
 * Build a hierarchy for a single module's topics.
322
 */
323
function advanced_help_get_topic_hierarchy(&$topics) {
324
  foreach ($topics as $module => $module_topics) {
325
    foreach ($module_topics as $topic => $info) {
326
      $parent_module = $module;
327
      // We have a blank topic that we don't want parented to itself.
328
      if (!$topic) {
329
        continue;
330
      }
331

    
332
      if (empty($info['parent'])) {
333
        $parent = '';
334
      }
335
      elseif (strpos($info['parent'], '%')) {
336
        list($parent_module, $parent) = explode('%', $info['parent']);
337
        if (empty($topics[$parent_module][$parent])) {
338
          // If it doesn't exist, top level.
339
          $parent = '';
340
        }
341
      }
342
      else {
343
        $parent = $info['parent'];
344
        if (empty($module_topics[$parent])) {
345
          // If it doesn't exist, top level.
346
          $parent = '';
347
        }
348
      }
349

    
350
      if (!isset($topics[$parent_module][$parent]['children'])) {
351
        $topics[$parent_module][$parent]['children'] = array();
352
      }
353
      $topics[$parent_module][$parent]['children'][] = array($module, $topic);
354
      $topics[$module][$topic]['_parent'] = array($parent_module, $parent);
355
    }
356
  }
357
}
358

    
359
/**
360
 * Implements hook_form_system_modules_alter().
361
 *
362
 * Add advanced help links to the modules page.
363
 */
364
function advanced_help_form_system_modules_alter(&$form, &$form_state) {
365
  if (!isset($form['modules'])) {
366
    return;
367
  }
368
  $advanced_help_modules = drupal_map_assoc(array_keys(advanced_help_get_topics()));
369
  foreach (element_children($form['modules']) as $group) {
370
    foreach (element_children($form['modules'][$group]) as $module) {
371
      if (isset($advanced_help_modules[$module])) {
372
        $form['modules'][$group][$module]['links']['help'] = array(
373
          '#type' => 'link',
374
          '#title' => t('Help'),
375
          '#href' => "admin/help/ah/$module",
376
          '#options' => array(
377
            'attributes' => array(
378
              'class' => array('module-link', 'module-link-help'),
379
              'title' => t('Help'),
380
            ),
381
          ),
382
        );
383
      }
384
    }
385
  }
386
}
387

    
388
/**
389
 * Form builder callback to build the search form.
390
 *
391
 * Load search/search.pages so that its template preprocess functions are
392
 * visible and can be invoked.
393
 */
394
function advanced_help_search_form($form, &$form_state, $keys = '') {
395
  module_load_include('inc', 'search', 'search.pages');
396
  $form = search_form($form, $form_state, 'admin/help/ah', $keys, 'advanced_help', t('Search help'));
397

    
398
  $form['basic']['inline']['submit']['#validate'] = array('search_form_validate');
399
  $form['basic']['inline']['submit']['#submit'] = array('advanced_help_search_form_submit');
400

    
401
  return $form;
402
}
403

    
404
/**
405
 * Process a search form submission.
406
 */
407
function advanced_help_search_form_submit($form, &$form_state) {
408
  $keys = empty($form_state['values']['processed_keys']) ? $form_state['values']['keys'] : $form_state['values']['processed_keys'];
409
  if ($keys == '') {
410
    form_set_error('keys', t('Please enter some keywords.'));
411
    return;
412
  }
413

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

    
416
  if ($popup) {
417
    $form_state['redirect'] = array('help/ah/search/' . $keys, array('query' => array('popup' => 'true')));
418
  }
419
  else {
420
    $form_state['redirect'] = 'help/ah/search/' . $keys;
421
  }
422
}
423

    
424
/**
425
 * Small helper function to get a module's proper name.
426
 *
427
 * @param string $module
428
 *   Name of the module.
429
 *
430
 * @return string
431
 *   Returns module's descriptive name.
432
 */
433
function advanced_help_get_module_name($module) {
434
  $settings = advanced_help_get_settings();
435
  if (isset($settings[$module]['name'])) {
436
    $name = $settings[$module]['name'];
437
  }
438
  else {
439
    $info = db_query("SELECT s.info FROM {system} s WHERE s.name = :name",
440
      array(':name' => $module))
441
      ->fetchField();
442
    $info = unserialize($info);
443
    $name = t($info['name']);
444
  }
445
  return $name;
446
}
447

    
448
/**
449
 * Page callback to view a help topic.
450
 */
451
function advanced_help_topic_page($module, $topic) {
452
  $info = advanced_help_get_topic($module, $topic);
453
  if (!$info) {
454
    return drupal_not_found();
455
  }
456

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

    
459
  drupal_set_title($info['title']);
460

    
461
  // Set up breadcrumb.
462
  $breadcrumb = array();
463

    
464
  $parent = $info;
465
  $pmodule = $module;
466

    
467
  // Loop checker.
468
  $checked = array();
469
  while (!empty($parent['parent'])) {
470
    if (strpos($parent['parent'], '%')) {
471
      list($pmodule, $ptopic) = explode('%', $parent['parent']);
472
    }
473
    else {
474
      $ptopic = $parent['parent'];
475
    }
476

    
477
    if (!empty($checked[$pmodule][$ptopic])) {
478
      break;
479
    }
480
    $checked[$pmodule][$ptopic] = TRUE;
481

    
482
    $parent = advanced_help_get_topic($pmodule, $ptopic);
483
    if (!$parent) {
484
      break;
485
    }
486

    
487
    $breadcrumb[] = advanced_help_l($parent['title'], "help/$pmodule/$ptopic");
488
  }
489

    
490
  $breadcrumb[] = advanced_help_l(advanced_help_get_module_name($pmodule), "admin/help/ah/$pmodule");
491
  $breadcrumb[] = advanced_help_l(t('Help'), "admin/help/ah");
492

    
493
  $output = advanced_help_view_topic($module, $topic, $popup);
494
  if (empty($output)) {
495
    $output = t('Missing help topic.');
496
  }
497

    
498
  if ($popup) {
499
    // Prevent devel module from spewing.
500
    $GLOBALS['devel_shutdown'] = FALSE;
501
    // Suppress admin_menu.
502
    module_invoke('admin_menu', 'suppress');
503
    drupal_set_breadcrumb(array_reverse($breadcrumb));
504
    print theme('advanced_help_popup', array('content' => $output));
505
    return;
506
  }
507

    
508
  drupal_add_css(drupal_get_path('module', 'advanced_help') . '/help.css');
509
  $breadcrumb[] = l(t('Home'), '');
510
  drupal_set_breadcrumb(array_reverse($breadcrumb));
511
  return $output;
512
}
513

    
514
/**
515
 * Implements hook_permission().
516
 */
517
function advanced_help_permission() {
518
  return array(
519
    'view advanced help topic' => array('title' => t('View help topics')),
520
    'view advanced help popup' => array('title' => t('View help popups')),
521
    'view advanced help index' => array('title' => t('View help index')),
522
  );
523
}
524

    
525
/**
526
 * Display a help icon with a link to view the topic in a popup.
527
 *
528
 * @param array $variables
529
 *   An associative array containing:
530
 *   - module: The module that owns this help topic.
531
 *   - topic: The identifier for the topic
532
 *   - type
533
 *     - 'icon' to display the question mark icon
534
 *     - 'title' to display the topic's title
535
 *     - any other text to display the text. Be sure to t() it!
536
 */
537
function theme_advanced_help_topic($variables) {
538
  $module = $variables['module'];
539
  $topic  = $variables['topic'];
540
  $type   = $variables['type'];
541

    
542
  $info = advanced_help_get_topic($module, $topic);
543
  if (!$info) {
544
    return;
545
  }
546

    
547
  switch ($type) {
548
    case 'icon':
549
      $text = '<span>' . t('Help') . '</span>';
550
      $class = 'advanced-help-link';
551
      break;
552

    
553
    case 'title':
554
      $text = $info['title'];
555
      $class = 'advanced-help-title';
556
      break;
557

    
558
    default:
559
      $class = 'advanced-help-title';
560
      $text = $type;
561
      break;
562
  }
563

    
564
  if (user_access('view advanced help popup')) {
565
    drupal_add_css(drupal_get_path('module', 'advanced_help') . '/help-icon.css');
566
    return l($text, "help/$module/$topic", array(
567
      'attributes' => array(
568
        'class' => $class,
569
        'onclick' => "var w=window.open(this.href, 'advanced_help_window', 'width=" . $info['popup width'] . ",height=" . $info['popup height'] . ",scrollbars,resizable'); w.focus(); return false;",
570
        'title' => $info['title'],
571
      ),
572
      'query' => array('popup' => TRUE),
573
      'html' => TRUE)
574
    );
575
  }
576
  elseif (user_access('view advanced help topic')) {
577
    return l($text, "help/$module/$topic", array(
578
      'attributes' => array(
579
        'class' => $class,
580
        'title' => $info['title'],
581
      ),
582
      'html' => TRUE)
583
    );
584
  }
585
}
586

    
587
/**
588
 * Load and render a help topic.
589
 */
590
function advanced_help_get_topic_filename($module, $topic) {
591
  $info = advanced_help_get_topic_file_info($module, $topic);
592
  if ($info) {
593
    return "./$info[path]/$info[file]";
594
  }
595
}
596
/**
597
 * Load and render a help topic.
598
 */
599
function advanced_help_get_topic_file_info($module, $topic) {
600
  global $language;
601

    
602
  $info = advanced_help_get_topic($module, $topic);
603
  if (empty($info)) {
604
    return;
605
  }
606

    
607
  // Search paths:
608
  $paths = array(
609
    // Allow theme override.
610
    path_to_theme() . '/help',
611
    // Translations.
612
    drupal_get_path('module', $module) . "/translations/help/$language->language",
613
    // In same directory as .inc file.
614
    $info['path'],
615
  );
616

    
617
  foreach ($paths as $path) {
618
    if (file_exists("./$path/$info[file]")) {
619
      return array('path' => $path, 'file' => $info['file']);
620
    }
621
  }
622
}
623

    
624
/**
625
 * Load and render a help topic.
626
 *
627
 * @param string $module
628
 *   Name of the module.
629
 * @param string $topic
630
 *   Name of the topic.
631
 * @param bool $popup
632
 *   Whether to show in popup or not.
633
 *
634
 * @return string
635
 *   Returns formatted topic.
636
 */
637
function advanced_help_view_topic($module, $topic, $popup = FALSE) {
638
  $file_info = advanced_help_get_topic_file_info($module, $topic);
639
  if ($file_info) {
640
    $info = advanced_help_get_topic($module, $topic);
641
    $file = "./$file_info[path]/$file_info[file]";
642

    
643
    $output = file_get_contents($file);
644
    if (isset($info['readme file']) && $info['readme file']) {
645
      $ext = pathinfo($file, PATHINFO_EXTENSION);
646
      if ('md' == $ext && module_exists('markdown')) {
647
        $filters = module_invoke('markdown', 'filter_info');
648
        $md_info = $filters['filter_markdown'];
649
	
650
        if (function_exists($md_info['process callback'])) {
651
          $function = $md_info['process callback'];
652
          $output = '<div class="advanced-help-topic">' . filter_xss_admin($function($output, NULL)) . '</div>';
653
        }
654
        else {
655
          $output = '<div class="advanced-help-topic"><pre class="readme">' . check_plain($output) . '</pre></div>';
656
        }
657
      }
658
      else {
659
        $readme = '';
660
        if ('md' == $ext) {
661
          $readme .=
662
            '<p>' .
663
	    t('If you install the !module module, the text below will be filtered by the module, producing rich text.',
664
	    array('!module' => l(t('Markdown filter'),
665
	    'https://www.drupal.org/project/markdown',
666
	    array('attributes' => array('title' => t('Link to project.')))))) . '</p>';
667
	}
668
        $readme .=
669
          '<div class="advanced-help-topic"><pre class="readme">' . check_plain($output) . '</pre></div>';
670
	$output = $readme;
671
      }
672
      return $output;
673
    }
674

    
675
    // Make some exchanges. The strtr is because url() translates $ into %24
676
    // but we need to change it back for the regex replacement.
677
    //
678
    // Change 'topic:' to the URL for another help topic.
679
    if ($popup) {
680
      $output = preg_replace('/href="topic:([^"]+)"/', 'href="' . strtr(url('help/$1', array('query' => array('popup' => 'true'))), array('%24' => '$')) . '"', $output);
681
      $output = preg_replace('/src="topic:([^"]+)"/', 'src="' . strtr(url('help/$1', array('query' => array('popup' => 'true'))), array('%24' => '$')) . '"', $output);
682
      $output = preg_replace('/&topic:([^"]+)&/', strtr(url('help/$1', array('query' => array('popup' => 'true'))), array('%24' => '$')), $output);
683
    }
684
    else {
685
      $output = preg_replace('/href="topic:([^"]+)"/', 'href="' . strtr(url('help/$1'), array('%24' => '$')) . '"', $output);
686
      $output = preg_replace('/src="topic:([^"]+)"/', 'src="' . strtr(url('help/$1'), array('%24' => '$')) . '"', $output);
687
      $output = preg_replace('/&topic:([^"]+)&/', strtr(url('help/$1'), array('%24' => '$')), $output);
688
    }
689

    
690
    global $base_path;
691

    
692
    // Change 'path:' to the URL to the base help directory.
693
    $output = preg_replace('/href="path:([^"]+)"/', 'href="' . $base_path . $info['path'] . '/$1"', $output);
694
    $output = preg_replace('/src="path:([^"]+)"/', 'src="' . $base_path . $info['path'] . '/$1"', $output);
695
    $output = str_replace('&path&', $base_path . $info['path'] . '/', $output);
696

    
697
    // Change 'trans_path:' to the URL to the actual help directory.
698
    $output = preg_replace('/href="trans_path:([^"]+)"/', 'href="' . $base_path . $file_info['path'] . '/$1"', $output);
699
    $output = preg_replace('/src="trans_path:([^"]+)"/', 'src="' . $base_path . $file_info['path'] . '/$1"', $output);
700
    $output = str_replace('&trans_path&', $base_path . $file_info['path'] . '/', $output);
701

    
702
    // Change 'base_url:' to the URL to the site.
703
    $output = preg_replace('/href="base_url:([^"]+)"/', 'href="' . strtr(url('$1'), array('%24' => '$')) . '"', $output);
704
    $output = preg_replace('/src="base_url:([^"]+)"/', 'src="' . strtr(url('$1'), array('%24' => '$')) . '"', $output);
705
    $output = preg_replace('/&base_url&([^"]+)"/', strtr(url('$1'), array('%24' => '$')) . '"', $output);
706

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

    
712
      $output = _filter_autop($output);
713
    }
714

    
715
    if (!empty($info['navigation'])) {
716
      $topics = advanced_help_get_topics();
717
      advanced_help_get_topic_hierarchy($topics);
718
      if (!empty($topics[$module][$topic]['children'])) {
719
        $items = advanced_help_get_tree($topics, $topics[$module][$topic]['children']);
720
        $output .= theme('item_list', array('items' => $items));
721
      }
722

    
723
      list($parent_module, $parent_topic) = $topics[$module][$topic]['_parent'];
724
      if ($parent_topic) {
725
        $parent = $topics[$module][$topic]['_parent'];
726
        $up = "help/$parent[0]/$parent[1]";
727
      }
728
      else {
729
        $up = "admin/help/ah/$module";
730
      }
731

    
732
      $siblings = $topics[$parent_module][$parent_topic]['children'];
733
      uasort($siblings, 'advanced_help_uasort');
734
      $prev = $next = NULL;
735
      $found = FALSE;
736
      foreach ($siblings as $sibling) {
737
        list($sibling_module, $sibling_topic) = $sibling;
738
        if ($found) {
739
          $next = $sibling;
740
          break;
741
        }
742
        if ($sibling_module == $module && $sibling_topic == $topic) {
743
          $found = TRUE;
744
          continue;
745
        }
746
        $prev = $sibling;
747
      }
748

    
749
      if ($prev || $up || $next) {
750
        $navigation = '<div class="help-navigation clear-block">';
751

    
752
        if ($prev) {
753
          $navigation .= advanced_help_l('«« ' . $topics[$prev[0]][$prev[1]]['title'], "help/$prev[0]/$prev[1]", array('attributes' => array('class' => 'help-left')));
754
        }
755
        if ($up) {
756
          $navigation .= advanced_help_l(t('Up'), $up, array('attributes' => array('class' => $prev ? 'help-up' : 'help-up-noleft')));
757
        }
758
        if ($next) {
759
          $navigation .= advanced_help_l($topics[$next[0]][$next[1]]['title'] . ' »»', "help/$next[0]/$next[1]", array('attributes' => array('class' => 'help-right')));
760
        }
761

    
762
        $navigation .= '</div>';
763

    
764
        $output .= $navigation;
765
      }
766
    }
767

    
768
    if (!empty($info['css'])) {
769
      drupal_add_css($info['path'] . '/' . $info['css']);
770
    }
771

    
772
    $output = '<div class="advanced-help-topic">' . $output . '</div>';
773
    drupal_alter('advanced_help_topic', $output, $popup);
774
    
775
    return $output;
776
  }
777
}
778

    
779
/**
780
 * Get the information for a single help topic.
781
 */
782
function advanced_help_get_topic($module, $topic) {
783
  $topics = advanced_help_get_topics();
784
  if (!empty($topics[$module][$topic])) {
785
    return $topics[$module][$topic];
786
  }
787
}
788

    
789
/**
790
 * Search the system for all available help topics.
791
 */
792
function advanced_help_get_topics() {
793
  $ini = _advanced_help_parse_ini();
794
  return $ini['topics'];
795
}
796

    
797
/**
798
 * Returns advanced help settings.
799
 */
800
function advanced_help_get_settings() {
801
  $ini = _advanced_help_parse_ini();
802
  return $ini['settings'];
803
}
804

    
805
/**
806
 * Funtion to parse ini / txt files.
807
 */
808
function _advanced_help_parse_ini() {
809
  global $language;
810
  static $ini = NULL;
811

    
812
  if (!isset($ini)) {
813
    $cache = cache_get('advanced_help_ini:' . $language->language);
814
    if ($cache) {
815
      $ini = $cache->data;
816
    }
817
  }
818

    
819
  if (!isset($ini)) {
820
    $ini = array('topics' => array(), 'settings' => array());
821

    
822
    $help_path = drupal_get_path('module', 'advanced_help') . '/modules';
823
    foreach (array_merge(module_list(), list_themes()) as $plugin) {
824
      $module = is_string($plugin) ? $plugin : $plugin->name;
825
      $module_path = drupal_get_path(is_string($plugin) ? 'module' : 'theme', $module);
826
      $info = array();
827
      if (file_exists("$module_path/help/$module.help.ini")) {
828
        $path = "$module_path/help";
829
        $info = parse_ini_file("./$module_path/help/$module.help.ini", TRUE);
830
      }
831
      elseif (file_exists("$help_path/$module/$module.help.ini")) {
832
        $path = "$help_path/$module";
833
        $info = parse_ini_file("./$help_path/$module/$module.help.ini", TRUE);
834
      }
835
      elseif (!file_exists("$module_path/help")) {
836
        // Look for one or more README files.
837
        $files = file_scan_directory("./$module_path",
838
          '/^(readme).*\.(txt|md)$/i', array('recurse' => FALSE));
839
        $path = "./$module_path";
840
        foreach ($files as $name => $fileinfo) {
841
          $info[$fileinfo->filename] = array(
842
            'line break' => TRUE,
843
            'readme file' => TRUE,
844
            'file' => $fileinfo->filename,
845
            'title' => $fileinfo->name,
846
          );
847
        }
848
      }
849

    
850
      if (!empty($info)) {
851
        // Get translated titles:
852
        $translation = array();
853
        if (file_exists("$module_path/translations/help/$language->language/$module.help.ini")) {
854
          $translation = parse_ini_file("$module_path/translations/help/$language->language/$module.help.ini", TRUE);
855
        }
856

    
857
        $ini['settings'][$module] = array();
858
        if (!empty($info['advanced help settings'])) {
859
          $ini['settings'][$module] = $info['advanced help settings'];
860
          unset($info['advanced help settings']);
861

    
862
          // Check translated strings for translatable global settings.
863
          if (isset($translation['advanced help settings']['name'])) {
864
            $ini['settings']['name'] = $translation['advanced help settings']['name'];
865
          }
866
          if (isset($translation['advanced help settings']['index name'])) {
867
            $ini['settings']['index name'] = $translation['advanced help settings']['index name'];
868
          }
869

    
870
        }
871

    
872
        foreach ($info as $name => $topic) {
873
          // Each topic should have a name, a title, a file and path.
874
          $file = !empty($topic['file']) ? $topic['file'] : $name;
875
          $ini['topics'][$module][$name] = array(
876
            'name' => $name,
877
            'module' => $module,
878
            'ini' => $topic,
879
            'title' => !empty($translation[$name]['title']) ? $translation[$name]['title'] : $topic['title'],
880
            'weight' => isset($topic['weight']) ? $topic['weight'] : 0,
881
            'parent' => isset($topic['parent']) ? $topic['parent'] : 0,
882
            'popup width' => isset($topic['popup width']) ? $topic['popup width'] : 500,
883
            'popup height' => isset($topic['popup height']) ? $topic['popup height'] : 500,
884
            // Require extension.
885
            'file' => isset($topic['readme file']) ? $file : $file . '.html',
886
            // Not in .ini file.
887
            'path' => $path,
888
            'line break' => isset($topic['line break']) ? $topic['line break'] : (isset($ini['settings'][$module]['line break']) ? $ini['settings'][$module]['line break'] : FALSE),
889
            'navigation' => isset($topic['navigation']) ? $topic['navigation'] : (isset($ini['settings'][$module]['navigation']) ? $ini['settings'][$module]['navigation'] : TRUE),
890
            'css' => isset($topic['css']) ? $topic['css'] : (isset($ini['settings'][$module]['css']) ? $ini['settings'][$module]['css'] : NULL),
891
            'readme file' => isset($topic['readme file']) ? $topic['readme file'] : FALSE,
892
          );
893
        }
894
      }
895
    }
896
    drupal_alter('advanced_help_topic_info', $ini);
897

    
898
    cache_set('advanced_help_ini:' . $language->language, $ini);
899
  }
900
  return $ini;
901
}
902

    
903
/**
904
 * Implements hook_search_info().
905
 *
906
 * Returns title for the tab on search page & path component after 'search/'.
907
 */
908
function advanced_help_search_info() {
909
  return array(
910
    'title' => t('Help'),
911
    'path' => 'advanced_help',
912
  );
913
}
914

    
915
/**
916
 * Implements hook_search_execute().
917
 */
918
function advanced_help_search_execute($keys = NULL) {
919
  $topics = advanced_help_get_topics();
920

    
921
  $query = db_select('search_index', 'i', array('target' => 'slave'))
922
    ->extend('SearchQuery')
923
    ->extend('PagerDefault');
924
  $query->join('advanced_help_index', 'ahi', 'i.sid = ahi.sid');
925
  $query->searchExpression($keys, 'help');
926

    
927
  // Only continue if the first pass query matches.
928
  if (!$query->executeFirstPass()) {
929
    return array();
930
  }
931

    
932
  $results = array();
933

    
934
  $find = $query->execute();
935
  foreach ($find as $item) {
936
    $sids[] = $item->sid;
937
  }
938

    
939
  $query = db_select('advanced_help_index', 'ahi');
940
  $result = $query
941
    ->fields('ahi')
942
    ->condition('sid', $sids, 'IN')
943
    ->execute();
944

    
945
  foreach ($result as $sid) {
946
    // Guard against removed help topics that are still indexed.
947
    if (empty($topics[$sid->module][$sid->topic])) {
948
      continue;
949
    }
950
    $info = $topics[$sid->module][$sid->topic];
951
    $text = advanced_help_view_topic($sid->module, $sid->topic);
952
    $results[] = array(
953
      'link' => advanced_help_url("help/$sid->module/$sid->topic"),
954
      'title' => $info['title'],
955
      'snippet' => search_excerpt($keys, $text),
956
    );
957
  }
958
  return $results;
959
}
960

    
961
/**
962
 * Implements hook_search_reset().
963
 */
964
function advanced_help_search_reset() {
965
  variable_del('advanced_help_last_cron');
966
}
967

    
968
/**
969
 * Implements hook_search_status().
970
 */
971
function advanced_help_search_status() {
972
  $topics = advanced_help_get_topics();
973
  $total = 0;
974
  foreach ($topics as $module => $module_topics) {
975
    foreach ($module_topics as $topic => $info) {
976
      $file = advanced_help_get_topic_filename($module, $topic);
977
      if ($file) {
978
        $total++;
979
      }
980
    }
981
  }
982

    
983
  $last_cron = variable_get('advanced_help_last_cron', array('time' => 0));
984
  $indexed = 0;
985
  if ($last_cron['time'] != 0) {
986
    $indexed = db_query("SELECT COUNT(*) FROM {search_dataset} sd WHERE sd.type = 'help' AND sd.sid IS NOT NULL AND sd.reindex = 0")->fetchField();
987
  }
988
  return array('remaining' => $total - $indexed, 'total' => $total);
989
}
990

    
991
/**
992
 * Gets search id for each topic.
993
 *
994
 * Get or create an sid (search id) that correlates to each topic for
995
 * the search system.
996
 */
997
function advanced_help_get_sids(&$topics) {
998
  global $language;
999
  $result = db_query("SELECT * FROM {advanced_help_index} WHERE language = :language",
1000
    array(':language' => $language->language));
1001
  foreach ($result as $sid) {
1002
    if (empty($topics[$sid->module][$sid->topic])) {
1003
      db_query("DELETE FROM {advanced_help_index} WHERE sid = :sid",
1004
        array(':sid' => $sid->sid));
1005
    }
1006
    else {
1007
      $topics[$sid->module][$sid->topic]['sid'] = $sid->sid;
1008
    }
1009
  }
1010
}
1011

    
1012
/**
1013
 * Implements hook_update_index().
1014
 */
1015
function advanced_help_update_index() {
1016
  global $language;
1017

    
1018
  // If we got interrupted by limit, this will contain the last module
1019
  // and topic we looked at.
1020
  $last = variable_get('advanced_help_last_cron', array('time' => 0));
1021
  $limit = intval(variable_get('search_cron_limit', 100));
1022
  $topics = advanced_help_get_topics();
1023
  advanced_help_get_sids($topics);
1024

    
1025
  $count = 0;
1026

    
1027
  foreach ($topics as $module => $module_topics) {
1028
    // Fast forward if necessary.
1029
    if (!empty($last['module']) && $last['module'] != $module) {
1030
      continue;
1031
    }
1032

    
1033
    foreach ($module_topics as $topic => $info) {
1034
      // Fast forward if necessary.
1035
      if (!empty($last['topic']) && $last['topic'] != $topic) {
1036
        continue;
1037
      }
1038

    
1039
      // If we've been looking to catch up, and we have, reset so we
1040
      // stop fast forwarding.
1041
      if (!empty($last['module'])) {
1042
        unset($last['topic']);
1043
        unset($last['module']);
1044
      }
1045

    
1046
      $file = advanced_help_get_topic_filename($module, $topic);
1047
      if ($file && (empty($info['sid']) || filemtime($file) > $last['time'])) {
1048
        if (empty($info['sid'])) {
1049
          $info['sid'] = db_insert('advanced_help_index')
1050
            ->fields(array(
1051
              'module' => $module,
1052
              'topic'  => $topic,
1053
              'language' => $language->language,
1054
            ))
1055
            ->execute();
1056
        }
1057

    
1058
        search_index($info['sid'], 'help', '<h1>' . $info['title'] . '</h1>' . file_get_contents($file));
1059
        $count++;
1060
        if ($count >= $limit) {
1061
          $last['topic'] = $topic;
1062
          $last['module'] = $module;
1063
          // Don't change time if we stop.
1064
          variable_set('advanced_help_last_cron', $last);
1065
          return;
1066
        }
1067
      }
1068
    }
1069
  }
1070

    
1071
  variable_set('advanced_help_last_cron', array('time' => time()));
1072
}
1073

    
1074
/**
1075
 * Fill in a bunch of page variables for our specialized popup page.
1076
 */
1077
function template_preprocess_advanced_help_popup(&$variables) {
1078
  // Add favicon.
1079
  if (theme_get_setting('toggle_favicon')) {
1080
    drupal_add_html_head('<link rel="shortcut icon" href="' . check_url(theme_get_setting('favicon')) . '" type="image/x-icon" />');
1081
  }
1082

    
1083
  global $theme;
1084
  // Construct page title.
1085
  if (drupal_get_title()) {
1086
    $head_title = array(
1087
      strip_tags(drupal_get_title()),
1088
      variable_get('site_name', 'Drupal'),
1089
    );
1090
  }
1091
  else {
1092
    $head_title = array(variable_get('site_name', 'Drupal'));
1093
    if (variable_get('site_slogan', '')) {
1094
      $head_title[] = variable_get('site_slogan', '');
1095
    }
1096
  }
1097

    
1098
  drupal_add_css(drupal_get_path('module', 'advanced_help') . '/help-popup.css');
1099
  drupal_add_css(drupal_get_path('module', 'advanced_help') . '/help.css');
1100

    
1101
  $variables['head_title']        = implode(' | ', $head_title);
1102
  $variables['base_path']         = base_path();
1103
  $variables['front_page']        = url();
1104
  $variables['breadcrumb']        = theme('breadcrumb', array('breadcrumb' => drupal_get_breadcrumb()));
1105
  $variables['feed_icons']        = drupal_get_feeds();
1106
  $variables['head']              = drupal_get_html_head();
1107
  $variables['language']          = $GLOBALS['language'];
1108
  $variables['language']->dir     = $GLOBALS['language']->direction ? 'rtl' : 'ltr';
1109
  $variables['logo']              = theme_get_setting('logo');
1110
  $variables['messages']          = theme('status_messages');
1111
  $variables['site_name']         = (theme_get_setting('toggle_name') ? variable_get('site_name', 'Drupal') : '');
1112
  $variables['css']               = drupal_add_css();
1113
  $css = drupal_add_css();
1114

    
1115
  // Remove theme css.
1116
  foreach ($css as $key => $value) {
1117
    if ($value['group'] == CSS_THEME) {
1118
      $exclude[$key] = FALSE;
1119
    }
1120
  }
1121
  $css = array_diff_key($css, $exclude);
1122

    
1123
  $variables['styles']            = drupal_get_css($css);
1124
  $variables['scripts']           = drupal_get_js();
1125
  $variables['title']             = drupal_get_title();
1126

    
1127
  // This function can be called either with a render array or
1128
  // an already rendered string.
1129
  if (is_array($variables['content'])) {
1130
    $variables['content'] = drupal_render($variables['content']);
1131
  }
1132
  // Closure should be filled last.
1133
  // There has never been a theme hook for closure (going back to
1134
  // first release 2008-04-17).  Unable to figure out its purpose.
1135
  // $variables['closure']           = theme('closure');
1136
}
1137

    
1138
/**
1139
 * Format a link but preserve popup identity.
1140
 */
1141
function advanced_help_l($text, $dest, $options = array()) {
1142
  $popup = !empty($_GET['popup']) && user_access('view advanced help popup');
1143
  if ($popup) {
1144
    if (empty($options['query'])) {
1145
      $options['query'] = array();
1146
    }
1147

    
1148
    if (is_array($options['query'])) {
1149
      $options['query'] += array('popup' => TRUE);
1150
    }
1151
    else {
1152
      $options['query'] += '&popup=TRUE';
1153
    }
1154
  }
1155

    
1156
  return l($text, $dest, $options);
1157
}
1158

    
1159
/**
1160
 * Format a URL but preserve popup identity.
1161
 */
1162
function advanced_help_url($dest, $options = array()) {
1163
  $popup = !empty($_GET['popup']) && user_access('view advanced help popup');
1164
  if ($popup) {
1165
    if (empty($options['query'])) {
1166
      $options['query'] = array();
1167
    }
1168

    
1169
    $options['query'] += array('popup' => TRUE);
1170
  }
1171

    
1172
  return url($dest, $options);
1173
}