| 1 |
<?php
|
| 2 |
// $Id: helptip.module,v 1.12 2007/01/05 03:28:50 yogadex Exp $
|
| 3 |
|
| 4 |
/**
|
| 5 |
* @file
|
| 6 |
* Creates and displays context sensitive help in Drupal blocks.
|
| 7 |
*
|
| 8 |
* Introduces a new node type called "help tip" and blocks for
|
| 9 |
* displaying help tips. Each tip is configured to appear only on one
|
| 10 |
* or more pages, allowing it to be context sensitive.
|
| 11 |
*
|
| 12 |
* Blocks may show one help tip or, if more than one help tip applies
|
| 13 |
* to a given page, a block may show several.
|
| 14 |
*
|
| 15 |
* If so configured, a user may hide a tip they do not wish to see it
|
| 16 |
* again.
|
| 17 |
*/
|
| 18 |
|
| 19 |
/**
|
| 20 |
* Helpful hint: configure your menus to always expand "create content".
|
| 21 |
* Then clicking the add "help tip" link will have the path of the referring
|
| 22 |
* page already filled in.
|
| 23 |
*/
|
| 24 |
|
| 25 |
/**
|
| 26 |
* How many blocks should this module provide?
|
| 27 |
*/
|
| 28 |
function _helptip_num_blocks() {
|
| 29 |
$num = variable_get('helptip_num_blocks', 1);
|
| 30 |
if (!$num > 0) {
|
| 31 |
return 1;
|
| 32 |
}
|
| 33 |
else
|
| 34 |
return $num;
|
| 35 |
}
|
| 36 |
|
| 37 |
/**
|
| 38 |
* Retrieve the settings for a given helptip block. Provide defaults
|
| 39 |
* if necessary.
|
| 40 |
*/
|
| 41 |
function _helptip_get_block_settings($delta) {
|
| 42 |
$var_name = "helptip_block_".$delta."_settings";
|
| 43 |
$defaults = array('how_many' => 1,
|
| 44 |
'title' => '%title',
|
| 45 |
'min_weight' => -10,
|
| 46 |
'max_weight' => 10);
|
| 47 |
|
| 48 |
$settings = variable_get($var_name,
|
| 49 |
array());
|
| 50 |
// array_merge ensures all defaults will be found.
|
| 51 |
return array_merge($defaults, $settings);
|
| 52 |
}
|
| 53 |
|
| 54 |
/**
|
| 55 |
* Implementation of hook_block().
|
| 56 |
*/
|
| 57 |
function helptip_block($op = 'list', $delta = 0, $edit = array()) {
|
| 58 |
$var_name = "helptip_block_".$delta."_settings";
|
| 59 |
if ($op == 'list') {
|
| 60 |
$blocks[0]['info'] = t('Help tips');
|
| 61 |
for ($i = 1; $i < _helptip_num_blocks(); $i++) {
|
| 62 |
$blocks[$i]['info'] = t('Help tips !num', array('!num' => $i+1));
|
| 63 |
}
|
| 64 |
return $blocks;
|
| 65 |
}
|
| 66 |
else if ($op == 'configure') {
|
| 67 |
$defaults = _helptip_get_block_settings($delta);
|
| 68 |
$form[$var_name] = array('#tree' => true);
|
| 69 |
$form[$var_name]['title'] =
|
| 70 |
array('#type' => 'textfield',
|
| 71 |
'#title' => t('Title'),
|
| 72 |
'#default_value' => $defaults['title'],
|
| 73 |
'#size' => 64,
|
| 74 |
'#maxlength' => 128,
|
| 75 |
'#description' => t('Block title. For example "Tip of the Day". Use %title for the title of the first help tip.'),
|
| 76 |
);
|
| 77 |
$form[$var_name]['how_many'] =
|
| 78 |
array('#type' => 'textfield',
|
| 79 |
'#title' => t('Number of help tips'),
|
| 80 |
'#default_value' => $defaults['how_many'],
|
| 81 |
'#size' => 2,
|
| 82 |
'#maxlength' => 2,
|
| 83 |
'#description' => t('How many tips to display? If more than one tip applies to the current page, more than one can be displayed. Tips of equal weight will be displayed in random order.'),
|
| 84 |
);
|
| 85 |
$form[$var_name]['min_weight'] =
|
| 86 |
array('#type' => 'weight',
|
| 87 |
'#title' => t('Minimum Weight'),
|
| 88 |
'#default_value' => $defaults['min_weight'],
|
| 89 |
'#description' => t('Show only helptips with this weight or greater. This allows you to define multiple helptip blocks, and position helptips on the page by their weight.'),
|
| 90 |
);
|
| 91 |
$form[$var_name]['max_weight'] =
|
| 92 |
array('#type' => 'weight',
|
| 93 |
'#title' => t('Maximum Weight'),
|
| 94 |
'#default_value' => $defaults['max_weight'],
|
| 95 |
'#description' => t('Show only helptips with this weight or less. This allows you to define multiple helptip blocks, and position helptips on the page by their weight.'),
|
| 96 |
);
|
| 97 |
return $form;
|
| 98 |
}
|
| 99 |
else if ($op == 'save') {
|
| 100 |
variable_set($var_name, $edit[$var_name]);
|
| 101 |
}
|
| 102 |
else if ($op == 'view') {
|
| 103 |
global $user;
|
| 104 |
if (!user_access(HELPTIP_ACCESS_VIEW))
|
| 105 |
return;
|
| 106 |
|
| 107 |
$settings = _helptip_get_block_settings($delta);
|
| 108 |
$paths = _helptip_get_current_paths();
|
| 109 |
$path_clause = "('" . implode("' LIKE ht.path OR '", $paths) . "' LIKE ht.path)";
|
| 110 |
// use db_rewrite_sql for people with node_access modules
|
| 111 |
$result = db_query(db_rewrite_sql("SELECT n.nid FROM {node} n LEFT JOIN {helptip} ht ON ht.nid = n.nid LEFT JOIN {helptip_user_data} ht_hidden ON ht_hidden.uid=$user->uid AND ht_hidden.nid=n.nid AND ht_hidden.relation='hidden' WHERE n.status = 1 AND ht_hidden.relation IS NULL AND $path_clause AND weight >= $settings[min_weight] AND weight <= $settings[max_weight] ORDER BY ht.weight, RAND() LIMIT %d"),
|
| 112 |
$settings['how_many']);
|
| 113 |
$block = array();
|
| 114 |
while ($data = db_fetch_object($result)) {
|
| 115 |
$node = node_load($data->nid);
|
| 116 |
if ($body = theme('helptip_body', $node)) {
|
| 117 |
// there is a helptip to display
|
| 118 |
if (!$block['subject'])
|
| 119 |
$block['subject'] = t($settings['title'],
|
| 120 |
array('%title' => check_plain($node->title)));
|
| 121 |
$items[] = $body;
|
| 122 |
}
|
| 123 |
}
|
| 124 |
if (count($items)) {
|
| 125 |
$block['content'] = theme('helptip_list', $items);
|
| 126 |
}
|
| 127 |
return $block;
|
| 128 |
}
|
| 129 |
}
|
| 130 |
|
| 131 |
|
| 132 |
/**
|
| 133 |
* Implementation of hook_help().
|
| 134 |
*/
|
| 135 |
function helptip_help($section) {
|
| 136 |
switch ($section) {
|
| 137 |
case 'admin/help#helptip':
|
| 138 |
return t('TODO: Create admin help text.');
|
| 139 |
case 'admin/modules#description':
|
| 140 |
return t('context sensitive help messages (display and edit)');
|
| 141 |
}
|
| 142 |
}
|
| 143 |
|
| 144 |
// some values we'll refer to many times
|
| 145 |
define('HELPTIP_ACCESS_VIEW', 'view help tips');
|
| 146 |
define('HELPTIP_ACCESS_CREATE', 'create help tips');
|
| 147 |
define('HELPTIP_ACCESS_EDIT_OWN', 'edit own help tips');
|
| 148 |
define('HELPTIP_ACCESS_ADMINISTER', 'administer help tips');
|
| 149 |
|
| 150 |
|
| 151 |
/**
|
| 152 |
* Implementation of hook_menu().
|
| 153 |
*/
|
| 154 |
function helptip_menu($may_cache) {
|
| 155 |
global $user;
|
| 156 |
$items = array();
|
| 157 |
|
| 158 |
if ($may_cache) {
|
| 159 |
$items[] = array
|
| 160 |
(
|
| 161 |
'path' => 'node/add/helptip',
|
| 162 |
'title' => t('Help Tip'),
|
| 163 |
'access' => user_access(HELPTIP_ACCESS_CREATE),
|
| 164 |
'type' => MENU_NORMAL_ITEM,
|
| 165 |
);
|
| 166 |
$items[] = array
|
| 167 |
(
|
| 168 |
'path' => 'helptip/hide',
|
| 169 |
'title' => t('Hide Help Tip'),
|
| 170 |
'callback' => 'helptip_hide_cb',
|
| 171 |
'access' => user_access(HELPTIP_ACCESS_VIEW),
|
| 172 |
'type' => MENU_CALLBACK,
|
| 173 |
);
|
| 174 |
$items[] = array
|
| 175 |
('path' => 'admin/settings/helptip',
|
| 176 |
'title' => t('Helptip Settings'),
|
| 177 |
'description' => t('Configure the Helptip module.'),
|
| 178 |
'callback' => 'drupal_get_form',
|
| 179 |
'callback arguments' => 'helptip_settings_form',
|
| 180 |
'access' => user_access(HELPTIP_ACCESS_ADMINISTER),
|
| 181 |
'type' => MENU_NORMAL_ITEM,
|
| 182 |
);
|
| 183 |
}
|
| 184 |
|
| 185 |
return $items;
|
| 186 |
}
|
| 187 |
|
| 188 |
/**
|
| 189 |
* User has requested to hide the given helptip
|
| 190 |
*/
|
| 191 |
function helptip_hide_cb($nid) {
|
| 192 |
global $user;
|
| 193 |
if ($user->uid && $nid > 0) {
|
| 194 |
db_query("DELETE FROM {helptip_user_data} WHERE nid=%d AND uid=%d AND relation='hidden'", $nid, $user->uid);
|
| 195 |
db_query("INSERT INTO {helptip_user_data} (nid, uid, relation) VALUES (%d, %d, 'hidden')", $nid, $user->uid);
|
| 196 |
}
|
| 197 |
// copied following from devel module
|
| 198 |
header('Location: '. referer_uri());
|
| 199 |
exit();
|
| 200 |
}
|
| 201 |
|
| 202 |
/**
|
| 203 |
* Implementation of hook_perm().
|
| 204 |
*/
|
| 205 |
function helptip_perm() {
|
| 206 |
return array(HELPTIP_ACCESS_CREATE, HELPTIP_ACCESS_EDIT_OWN,
|
| 207 |
HELPTIP_ACCESS_ADMINISTER, HELPTIP_ACCESS_VIEW);
|
| 208 |
}
|
| 209 |
|
| 210 |
|
| 211 |
/**
|
| 212 |
* Implementation of hook_access().
|
| 213 |
*/
|
| 214 |
function helptip_access($op, $node) {
|
| 215 |
global $user;
|
| 216 |
|
| 217 |
if (!($ret_val = user_access(HELPTIP_ACCESS_ADMINISTER))) {
|
| 218 |
switch ($op) {
|
| 219 |
case 'create':
|
| 220 |
$ret_val = user_access(HELPTIP_ACCESS_CREATE);
|
| 221 |
break;
|
| 222 |
case 'delete':
|
| 223 |
case 'update':
|
| 224 |
$ret_val = user_access(HELPTIP_ACCESS_EDIT_OWN);
|
| 225 |
break;
|
| 226 |
case 'view':
|
| 227 |
$ret_val = user_access(HELPTIP_ACCESS_VIEW);
|
| 228 |
break;
|
| 229 |
}
|
| 230 |
}
|
| 231 |
|
| 232 |
return $ret_val;
|
| 233 |
}
|
| 234 |
|
| 235 |
|
| 236 |
/**
|
| 237 |
* Implementation of hook_delete().
|
| 238 |
*/
|
| 239 |
function helptip_delete(&$node) {
|
| 240 |
db_query("DELETE FROM {helptip} WHERE nid = %d", $node->nid);
|
| 241 |
}
|
| 242 |
|
| 243 |
|
| 244 |
/**
|
| 245 |
* Implementation of hook_form().
|
| 246 |
*/
|
| 247 |
function helptip_form(&$node, &$param) {
|
| 248 |
$form['title'] = array('#type' => 'textfield', '#title' => t('Title'), '#required' => TRUE, '#default_value' => $node->title, '#weight' => -5);
|
| 249 |
|
| 250 |
if (!$node->helptip->path) {
|
| 251 |
// for convenience, default path to the previous page
|
| 252 |
global $base_url;
|
| 253 |
$referer = $_SERVER['HTTP_REFERER'];
|
| 254 |
$pattern = "|$base_url\/(\?q=)?([^\?\&]*)|";
|
| 255 |
$result = preg_match($pattern, $referer, $matches);
|
| 256 |
if ($result && (strpos($matches[2], 'node/add') !== 0))
|
| 257 |
$node->helptip->path = $matches[2];
|
| 258 |
}
|
| 259 |
|
| 260 |
$form['helptip'] = array('#tree' => true);
|
| 261 |
|
| 262 |
$form['helptip']['path'] = array('#type' => 'textfield',
|
| 263 |
'#title' => t('Context Sensitive Path'),
|
| 264 |
'#default_value' => $node->helptip->path,
|
| 265 |
'#description' => t('Where to display this help. Use "%" as wildcard.'),
|
| 266 |
'#required' => true);
|
| 267 |
|
| 268 |
$form['helptip']['weight'] = array('#type' => 'weight',
|
| 269 |
'#title' => t('Weight'),
|
| 270 |
'#default_value' => $node->helptip->weight,
|
| 271 |
'#description' => t('When more than one help tip applies to a page, this controls the order in which they are displayed. It may also determine in which block the helptip appears.'),
|
| 272 |
'#required' => false);
|
| 273 |
|
| 274 |
$form['helptip']['settings'] = array('#tree' => true);
|
| 275 |
$form['helptip']['settings']['may_hide'] =
|
| 276 |
array('#type' => 'checkbox',
|
| 277 |
'#title' => t('User May Hide'),
|
| 278 |
'#default_value' => $node->helptip->settings['may_hide'],
|
| 279 |
'#description' => t('Enable "do not show again" link.'));
|
| 280 |
|
| 281 |
$form['body_filter']['body'] = array('#type' => 'textarea', '#title' => t('Body'), '#default_value' => $node->body, '#rows' => 20, '#required' => TRUE);
|
| 282 |
$form['body_filter']['format'] = filter_form($node->format);
|
| 283 |
return $form;
|
| 284 |
|
| 285 |
}
|
| 286 |
|
| 287 |
|
| 288 |
/**
|
| 289 |
* Implementation of hook_insert().
|
| 290 |
*/
|
| 291 |
function helptip_insert($node) {
|
| 292 |
$helptip = (object) $node->helptip;
|
| 293 |
db_query("INSERT INTO {helptip} (nid, path, weight, settings) VALUES (%d, '%s', %d, '%s')",
|
| 294 |
$node->nid,
|
| 295 |
$helptip->path,
|
| 296 |
$helptip->weight,
|
| 297 |
serialize($helptip->settings));
|
| 298 |
}
|
| 299 |
|
| 300 |
|
| 301 |
/**
|
| 302 |
* Implementation of hook_update().
|
| 303 |
*/
|
| 304 |
function helptip_update($node) {
|
| 305 |
$helptip = (object) $node->helptip;
|
| 306 |
db_query("REPLACE INTO {helptip} (nid, path, weight, settings) VALUES (%d, '%s', %d, '%s')",
|
| 307 |
$node->nid,
|
| 308 |
$helptip->path,
|
| 309 |
$helptip->weight,
|
| 310 |
serialize($helptip->settings));
|
| 311 |
}
|
| 312 |
|
| 313 |
/**
|
| 314 |
* Implementation of hook_load().
|
| 315 |
*/
|
| 316 |
function helptip_load($node) {
|
| 317 |
$data = db_fetch_object(db_query('SELECT * FROM {helptip} WHERE nid=%d',
|
| 318 |
$node->nid));
|
| 319 |
$data->settings = unserialize($data->settings);
|
| 320 |
return array('helptip' => $data);
|
| 321 |
}
|
| 322 |
|
| 323 |
|
| 324 |
/**
|
| 325 |
* Implementation of hook_node_info().
|
| 326 |
*/
|
| 327 |
function helptip_node_info() {
|
| 328 |
return array('helptip' => array('name' => t('Help Tip'),
|
| 329 |
'module' => 'helptip',
|
| 330 |
'description' => t('A helptip is text which can appear in a block on context-sensitive pages. For example, to provide detailed instructions to users on a specific page.'),
|
| 331 |
),
|
| 332 |
);
|
| 333 |
}
|
| 334 |
|
| 335 |
/**
|
| 336 |
* Implementation of hook_validate().
|
| 337 |
*/
|
| 338 |
function helptip_validate(&$node) {
|
| 339 |
// TODO
|
| 340 |
}
|
| 341 |
|
| 342 |
|
| 343 |
/**
|
| 344 |
* Implementation of hook_view().
|
| 345 |
*/
|
| 346 |
function helptip_view(&$node, $teaser = FALSE, $page = FALSE) {
|
| 347 |
$node = node_prepare($node, $teaser);
|
| 348 |
return $node;
|
| 349 |
}
|
| 350 |
|
| 351 |
/**
|
| 352 |
* Provide form for module settings.
|
| 353 |
*/
|
| 354 |
function helptip_settings_form() {
|
| 355 |
$form['helptip_num_blocks'] =
|
| 356 |
array('#type' => 'textfield',
|
| 357 |
'#title' => t('Number of blocks'),
|
| 358 |
'#default_value' => variable_get('helptip_num_blocks', 1),
|
| 359 |
'#size' => 1,
|
| 360 |
'#maxlength' => 1,
|
| 361 |
'#description' => t('How many helptip blocks would you like? For example, if you want help tips to appear in the header on some pages, and in a sidebar on others, you need two blocks.'),
|
| 362 |
);
|
| 363 |
return system_settings_form($form);
|
| 364 |
}
|
| 365 |
|
| 366 |
/**
|
| 367 |
* Display the body of the help tip. In this implementation, we show teaser.
|
| 368 |
*/
|
| 369 |
function theme_helptip_body($node) {
|
| 370 |
if ($node->teaser = check_markup($node->teaser, $node->format, FALSE)) {
|
| 371 |
// calculate body, too, as it controls the 'read more' link.
|
| 372 |
$node->body = check_markup($node->body, $node->format, FALSE);
|
| 373 |
return '<div class="helptip">' . $node->teaser .
|
| 374 |
theme('helptip_links', $node) . "</div>\n";
|
| 375 |
}
|
| 376 |
}
|
| 377 |
|
| 378 |
/**
|
| 379 |
* This is like hook_links when displaying a node, but applies to
|
| 380 |
* helptips shown in help blocks.
|
| 381 |
*
|
| 382 |
* Responsible for displaying readmore and don't show anymore links
|
| 383 |
*/
|
| 384 |
function theme_helptip_links($node) {
|
| 385 |
global $user;
|
| 386 |
|
| 387 |
if ($node->body != $node->teaser) {
|
| 388 |
$links['helptip_read_more'] = array('title' => t('read more'),
|
| 389 |
'attributes' => array('class' => 'read-more',
|
| 390 |
'title' => t('Read the rest of this help tip.')),
|
| 391 |
'href' => "node/$node->nid",
|
| 392 |
);
|
| 393 |
}
|
| 394 |
|
| 395 |
// some helptips can be hidden by the user. Ideally some javascript
|
| 396 |
// will come along and replace this link with an ajaxy thing that
|
| 397 |
// does not require a page reload
|
| 398 |
if ($user->uid &&
|
| 399 |
$node->helptip->settings['may_hide'])
|
| 400 |
$links['helptip_hide'] =
|
| 401 |
array('title' => t('do not show again'),
|
| 402 |
'href' => "helptip/hide/$node->nid",
|
| 403 |
'attributes' => array('title' => t('Do not display this message again'),
|
| 404 |
'class' => 'helptip_hide'),
|
| 405 |
);
|
| 406 |
|
| 407 |
// for admin convenience, let them edit this tip
|
| 408 |
if (user_access(HELPTIP_ACCESS_ADMINISTER) ||
|
| 409 |
($user->uid == $node->uid && user_access(HELPTIP_ACCESS_EDIT_OWN)))
|
| 410 |
$links['helptip_edit'] =
|
| 411 |
array('title' => t('edit'),
|
| 412 |
'href' => "node/$node->nid/edit",
|
| 413 |
'attributes' => array('title' => t('edit this help tip')),
|
| 414 |
);
|
| 415 |
|
| 416 |
return '<div class="helptip_links">'.theme('links', $links)."</div>\n";
|
| 417 |
}
|
| 418 |
|
| 419 |
/**
|
| 420 |
* If more than one helptip body is being shown, this is how we do it.
|
| 421 |
*/
|
| 422 |
function theme_helptip_list($items) {
|
| 423 |
if (count($items) == 1)
|
| 424 |
return $items[0];
|
| 425 |
else
|
| 426 |
return theme('item_list', $items);
|
| 427 |
}
|
| 428 |
|
| 429 |
/**
|
| 430 |
* Returns an array of paths which match the current page,
|
| 431 |
* escaped for use in database queries.
|
| 432 |
*/
|
| 433 |
function _helptip_get_current_paths() {
|
| 434 |
$paths = array(db_escape_string($_GET['q']));
|
| 435 |
if ($alias = drupal_lookup_path('alias', $_GET['q']))
|
| 436 |
$paths[] = db_escape_string($alias);
|
| 437 |
return $paths;
|
| 438 |
}
|