block.module 17.8 KB
Newer Older
Dries's avatar
   
Dries committed
1
<?php
2
// $Id$
Dries's avatar
 
Dries committed
3

Dries's avatar
   
Dries committed
4
5
6
7
8
/**
 * @file
 * Controls the boxes that are displayed around the main content.
 */

Dries's avatar
   
Dries committed
9
10
11
12
/**
 * Implementation of hook_help().
 */
function block_help($section) {
Dries's avatar
   
Dries committed
13
  switch ($section) {
Dries's avatar
   
Dries committed
14
    case 'admin/help#block':
15
      return t('
16
<p>Blocks are the boxes visible in the sidebar(s) of your web site. These are usually generated automatically by modules (e.g. recent forum topics), but you can also create your own blocks.</p>
17
18
19
20
21
22
23
24
25
26
27
28
29
<p>The sidebar each block appears in depends on both which theme you are using (some are left-only, some right, some both), and on the settings in block management.</p>
<p>The block management screen lets you specify the vertical sort-order of the blocks within a sidebar. You do this by assigning a weight to each block. Lighter blocks (smaller weight) "float up" towards the top of the sidebar. Heavier ones "sink down" towards the bottom of it.</p>
<p>A block\'s visibility depends on:</p>
<ul>
<li>Its enabled checkbox. Disabled blocks are never shown.</li>
<li>Its throttle checkbox. Throttled blocks are hidden during high server loads.</li>
<li>Its path options. Blocks can be configured to only show/hide on certain pages</li>.
<li>User settings. You can choose to let your users decide whether to show/hide certain blocks.</li>
<li>Its function. Dynamic blocks (such as those defined by modules) may be empty on certain pages and will not be shown.</li>
</ul>

<h3>Administrator defined blocks</h3>
<p>An administrator defined block contains content supplied by you (as opposed to being generated automatically by a module). Each admin-defined block consists of a title, a description, and a body which can be as long as you wish. The Drupal engine will render the content of the block.</p>');
Dries's avatar
   
Dries committed
30
    case 'admin/modules#description':
Dries's avatar
   
Dries committed
31
      return t('Controls the boxes that are displayed around the main content.');
Dries's avatar
   
Dries committed
32
    case 'admin/block':
33
34
35
      return t("
<p>Blocks are the boxes in the left and right side bars of the web site. They are made available by modules or created manually.</p>
<p>Only enabled blocks are shown. You can position the blocks by deciding which side of the page they will show up on (region) and in which order they appear (weight).</p>
36
<p>If you want certain blocks to disable themselves temporarily during high server loads, check the 'Throttle' box. You can configure the auto-throttle on the <a href=\"%throttle\">throttle configuration page</a> after having enabled the throttle module.
37
", array('%throttle' => url('admin/settings/throttle')));
Dries's avatar
   
Dries committed
38
    case 'admin/block/add':
39
      return t("Here you can create a new block. Once you have created this block you must make it active and give it a place on the page using <a href=\"%overview\">blocks</a>. The title is used when displaying the block. The description is used in the \"block\" column on the <a href=\"%overview\">blocks</a> page.", array('%overview' => url('admin/block')));
Dries's avatar
   
Dries committed
40
  }
Dries's avatar
   
Dries committed
41
42
}

Dries's avatar
   
Dries committed
43
/**
Dries's avatar
Dries committed
44
 * Menu callback; presents the block-specific information from admin/help.
Dries's avatar
   
Dries committed
45
 */
Dries's avatar
   
Dries committed
46
function block_help_page() {
Dries's avatar
   
Dries committed
47
  print theme('page', block_help('admin/help#block'));
Dries's avatar
   
Dries committed
48
49
}

Dries's avatar
   
Dries committed
50
51
52
/**
 * Implementation of hook_perm().
 */
Dries's avatar
   
Dries committed
53
function block_perm() {
Dries's avatar
   
Dries committed
54
  return array('administer blocks');
Dries's avatar
   
Dries committed
55
56
}

Dries's avatar
   
Dries committed
57
/**
Dries's avatar
   
Dries committed
58
 * Implementation of hook_menu().
Dries's avatar
   
Dries committed
59
 */
Dries's avatar
   
Dries committed
60
function block_menu($may_cache) {
Dries's avatar
   
Dries committed
61
  $items = array();
Dries's avatar
   
Dries committed
62
63
64
65
66
67
68

  if ($may_cache) {
    $items[] = array('path' => 'admin/block', 'title' => t('blocks'),
      'access' => user_access('administer blocks'),
      'callback' => 'block_admin');
    $items[] = array('path' => 'admin/block/list', 'title' => t('list'),
      'type' => MENU_DEFAULT_LOCAL_TASK, 'weight' => -10);
69
    $items[] = array('path' => 'admin/block/configure', 'title' => t('configure block'),
Dries's avatar
   
Dries committed
70
      'access' => user_access('administer blocks'),
71
72
73
74
75
      'callback' => 'block_admin_configure',
      'type' => MENU_CALLBACK);
    $items[] = array('path' => 'admin/block/delete', 'title' => t('delete block'),
      'access' => user_access('administer blocks'),
      'callback' => 'block_box_delete',
Dries's avatar
   
Dries committed
76
      'type' => MENU_CALLBACK);
Dries's avatar
   
Dries committed
77
    $items[] = array('path' => 'admin/block/add', 'title' => t('add block'),
Dries's avatar
   
Dries committed
78
      'access' => user_access('administer blocks'),
79
      'callback' => 'block_box_add',
Dries's avatar
   
Dries committed
80
81
82
      'type' => MENU_LOCAL_TASK);
  }

Dries's avatar
   
Dries committed
83
  return $items;
Dries's avatar
   
Dries committed
84
85
}

Dries's avatar
   
Dries committed
86
87
88
89
90
/**
 * Implementation of hook_block().
 *
 * Generates the administrator-defined blocks for display.
 */
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
function block_block($op = 'list', $delta = 0, $edit = array()) {
  switch ($op) {
    case 'list':
      $result = db_query('SELECT bid, title, info FROM {boxes} ORDER BY title');
      while ($block = db_fetch_object($result)) {
        $blocks[$block->bid]['info'] = $block->info;
      }
      return $blocks;

    case 'configure':
      $box = block_box_get($delta);
      if (filter_access($box['format'])) {
        return block_box_form($box);
      }
      break;

    case 'save':
      block_box_save($edit, $delta);
      break;

    case 'view':
      $block = db_fetch_object(db_query('SELECT * FROM {boxes} WHERE bid = %d', $delta));
      $data['subject'] = $block->title;
      $data['content'] = check_output($block->body, $block->format);
      return $data;
116
117
118
  }
}

Dries's avatar
 
Dries committed
119
function block_admin_save($edit) {
Dries's avatar
   
Dries committed
120
121
  foreach ($edit as $module => $blocks) {
    foreach ($blocks as $delta => $block) {
122
123
      db_query("UPDATE {blocks} SET region = %d, status = %d, weight = %d, throttle = %d WHERE module = '%s' AND delta = '%s'",
                $block['region'], $block['status'], $block['weight'], $block['throttle'], $module, $delta);
Dries's avatar
   
Dries committed
124
    }
Dries's avatar
 
Dries committed
125
  }
Dries's avatar
   
Dries committed
126

127
  return t('The block settings have been updated.');
Dries's avatar
 
Dries committed
128
129
}

Dries's avatar
   
Dries committed
130
/**
Dries's avatar
   
Dries committed
131
 * Update the 'blocks' DB table with the blocks currently exported by modules.
Dries's avatar
   
Dries committed
132
 *
Dries's avatar
   
Dries committed
133
134
135
 * @param $order_by php <a
 *   href="http://www.php.net/manual/en/function.array-multisort.php">array_multisort()</a>
 *   style sort ordering, eg. "weight", SORT_ASC, SORT_STRING.
136
 *
Dries's avatar
   
Dries committed
137
138
 * @return
 *   Blocks currently exported by modules, sorted by $order_by.
Dries's avatar
   
Dries committed
139
 */
Dries's avatar
   
Dries committed
140
141
function _block_rehash($order_by = array('weight')) {
  $result = db_query('SELECT * FROM {blocks} ');
Dries's avatar
   
Dries committed
142
143
144
145
  while ($old_block = db_fetch_object($result)) {
    $old_blocks[$old_block->module][$old_block->delta] = $old_block;
  }

Dries's avatar
   
Dries committed
146
  db_query('DELETE FROM {blocks} ');
Dries's avatar
   
Dries committed
147
148

  foreach (module_list() as $module) {
Dries's avatar
   
Dries committed
149
    $module_blocks = module_invoke($module, 'block', 'list');
Dries's avatar
   
Dries committed
150
151
    if ($module_blocks) {
      foreach ($module_blocks as $delta => $block) {
Dries's avatar
   
Dries committed
152
153
        $block['module'] = $module;
        $block['delta']  = $delta;
Dries's avatar
   
Dries committed
154
        if ($old_blocks[$module][$delta]) {
Dries's avatar
   
Dries committed
155
156
157
          $block['status'] = $old_blocks[$module][$delta]->status;
          $block['weight'] = $old_blocks[$module][$delta]->weight;
          $block['region'] = $old_blocks[$module][$delta]->region;
158
159
          $block['visibility'] = $old_blocks[$module][$delta]->visibility;
          $block['pages'] = $old_blocks[$module][$delta]->pages;
Dries's avatar
   
Dries committed
160
161
          $block['custom'] = $old_blocks[$module][$delta]->custom;
          $block['throttle'] = $old_blocks[$module][$delta]->throttle;
Dries's avatar
   
Dries committed
162
163
        }
        else {
Dries's avatar
   
Dries committed
164
          $block['status'] = $block['weight'] = $block['region'] = $block['custom'] = 0;
165
          $block['pages']   = '';
Dries's avatar
   
Dries committed
166
167
168
        }

        // reinsert blocks into table
169
170
        db_query("INSERT INTO {blocks} (module, delta, status, weight, region, visibility, pages, custom, throttle) VALUES ('%s', '%s', %d, %d, %d, %d, '%s', %d, %d)",
                  $block['module'], $block['delta'], $block['status'], $block['weight'], $block['region'], $block['visibility'], $block['pages'], $block['custom'], $block['throttle']);
Dries's avatar
   
Dries committed
171
172

        $blocks[] = $block;
Dries's avatar
   
Dries committed
173

Dries's avatar
   
Dries committed
174
175
        // build array to sort on
        $order[$order_by[0]][] = $block[$order_by[0]];
Dries's avatar
   
Dries committed
176
177
178
179
      }
    }
  }

Dries's avatar
   
Dries committed
180
181
182
  // sort
  array_multisort($order[$order_by[0]], $order_by[1] ? $order_by[1] : SORT_ASC, $order_by[2] ? $order_by[2] : SORT_REGULAR, $blocks);

Dries's avatar
   
Dries committed
183
184
  return $blocks;
}
Dries's avatar
   
Dries committed
185

Dries's avatar
   
Dries committed
186
187
188
/**
 * Prepare the main block administration form.
 */
Dries's avatar
   
Dries committed
189
function block_admin_display() {
Dries's avatar
   
Dries committed
190
  $blocks = _block_rehash();
Dries's avatar
   
Dries committed
191

Dries's avatar
   
Dries committed
192
193
194
195
196
197
  $header = array(t('Block'), t('Enabled'), t('Weight'), t('Region'));
  if (module_exist('throttle')) {
    $header[] = t('Throttle');
  }
  $header[] = array('data' => t('Operations'), 'colspan' => 2);

Dries's avatar
   
Dries committed
198

Dries's avatar
   
Dries committed
199
  foreach ($blocks as $block) {
200
201
    if ($block['module'] == 'block') {
      $operation = l(t('delete'), 'admin/block/delete/'. $block['delta']);
202
    }
Dries's avatar
   
Dries committed
203
    else {
204
      $operation = '';
Dries's avatar
   
Dries committed
205
    }
Dries's avatar
   
Dries committed
206

Dries's avatar
   
Dries committed
207
208
209
210
211
212
213
214
    $row = array($block['info'], array('data' => form_checkbox(NULL, $block['module'] .']['. $block['delta'] .'][status', 1, $block['status']), 'align' => 'center'), form_weight(NULL, $block['module'] .']['. $block['delta'] .'][weight', $block['weight']), form_radios(NULL, $block['module'] .']['. $block['delta'] .'][region', $block['region'], array(t('left'), t('right'))));

    $row = array($block['info'], array('data' => form_checkbox(NULL, $block['module'] .']['. $block['delta'] .'][status', 1, $block['status']), 'align' => 'center'), form_weight(NULL, $block['module'] .']['. $block['delta'] .'][weight', $block['weight']), form_radios(NULL, $block['module'] .']['. $block['delta'] .'][region', $block['region'], array(t('left'), t('right'))));
    if (module_exist('throttle')) {
      $row[] = array('data' => form_checkbox(NULL, $block['module'] .']['. $block['delta'] .'][throttle', 1, $block['throttle']), 'align' => 'center');
    }
    $row[] = array('data' => l(t('configure'), 'admin/block/configure/'. $block['module'] .'/'. $block['delta']), $operation);
    $rows[] = $row;
Dries's avatar
 
Dries committed
215
  }
Dries's avatar
   
Dries committed
216

Dries's avatar
   
Dries committed
217
218
  $output = theme('table', $header, $rows);
  $output .= form_submit(t('Save blocks'));
Dries's avatar
 
Dries committed
219

Dries's avatar
   
Dries committed
220
  return form($output, 'post', url('admin/block'));
Dries's avatar
 
Dries committed
221
222
}

223
function block_box_get($bid) {
Dries's avatar
   
Dries committed
224
225
226
227
  return db_fetch_array(db_query('SELECT * FROM {boxes} WHERE bid = %d', $bid));
}

/**
228
 * Menu callback; displays the block configuration form.
Dries's avatar
   
Dries committed
229
 */
230
function block_admin_configure($module = NULL, $delta = 0) {
Dries's avatar
   
Dries committed
231
232
233
  $edit = $_POST['edit'];
  $op = $_POST['op'];

234
235
  switch ($op) {
    case t('Save block'):
236
237
238
      db_query("UPDATE {blocks} SET visibility = %d, pages = '%s', custom = %d WHERE module = '%s' AND delta = '%s'", $edit['visibility'], $edit['pages'], $edit['custom'], $module, $delta);
      module_invoke($module, 'block', 'save', $delta, $edit);
      drupal_set_message('The block configuration has been saved.');
239
240
241
      cache_clear_all();
      drupal_goto('admin/block');

242
243
244
245
246
247
248
249
250
251
252
253
254
255
    default:
      // Always evaluates to TRUE, but a validation step may be added later.
      if (!$edit) {
        $edit = db_fetch_array(db_query("SELECT pages, visibility, custom FROM {blocks} WHERE module = '%s' AND delta = '%s'", $module, $delta));
      }

      // Module-specific block configurations.
      if ($settings = module_invoke($module, 'block', 'configure', $delta)) {
        $form = form_group(t('Block-specific settings'), $settings);
      }

      // Get the block subject for the page title.
      $info = module_invoke($module, 'block', 'list');
      drupal_set_title(t("'%name' block", array('%name' => $info[$delta]['info'])));
256

257
258
259
260
      // Standard block configurations.
      $group = form_radios(t('Show on specific pages'), 'visibility', $edit['visibility'], array(t('Show on every page except the listed pages.'), t('Show on only the listed pages.')));
      $group .= form_textarea(t('Pages'), 'pages', $edit['pages'], 40, 5, t("Enter one page per line as Drupal paths. The '*' character is a wildcard. Example paths are '<em>blog</em>' for the blog page and '<em>blog/*</em>' for every personal blog. '<em>&lt;front&gt;</em>' is the front page. "));

261
      $group .= form_radios(t('Custom visibility settings'), 'custom', $edit['custom'], array(t('Users cannot control whether or not they see this block.'), t('Show this block by default, but let individual users hide it.'), t('Hide this block by default but let individual users show it.')), t('Allow individual users to customize the visibility of this block in their account settings.'));
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300

      $form .= form_group(t('Visibility settings'), $group);

      $form .= form_submit(t('Save block'));

      print theme('page', form($form));
  }
}

/**
 * Menu callback; displays the block creation form.
 */
function block_box_add() {
  $edit = $_POST['edit'];
  $op = $_POST['op'];

  switch ($op) {
    case t('Save block'):
      block_box_save($edit);
      drupal_set_message(t('The new block has been added.'));
      drupal_goto('admin/block');

    default:
      $form = block_box_form();
      $form .= form_submit(t('Save block'));
      $output .= form($form);
  }

  print theme('page', $output);
}

/**
 * Menu callback; confirm and delete custom blocks.
 */
function block_box_delete($bid = 0) {
  $op = $_POST['op'];
  $box = block_box_get($bid);

  switch ($op) {
301
302
    case t('Delete'):
      db_query('DELETE FROM {boxes} WHERE bid = %d', $bid);
303
      drupal_set_message(t('The block %name has been deleted.', array('%name' => '<em>'. $box['info'] .'</em>')));
304
305
306
307
      cache_clear_all();
      drupal_goto('admin/block');

    default:
308
309
310
      $form = '<p>'. t('Are you sure you want to delete the block %name?', array('%name' => '<em>'. $box['info'] .'</em>')) ."</p>\n";
      $form .= form_submit(t('Delete'));
      $output = form($form);
Dries's avatar
   
Dries committed
311
312
313
  }

  print theme('page', $output);
314
315
316
}

function block_box_form($edit = array()) {
317
318
319
320
  $output = form_textfield(t('Block title'), 'title', $edit['title'], 50, 64, t('The title of the block as shown to the user.'));
  $output .= filter_form('format', $edit['format']);
  $output .= form_textarea(t('Block body'), 'body', $edit['body'], 70, 10, t('The content of the block as shown to the user.'));
  $output .= form_textfield(t('Block description'), 'info', $edit['info'], 50, 64, t('A brief description of your block.  Used on the <a href="%overview">block overview page</a>.', array('%overview' => url('admin/block'))));
321

322
  return $output;
323
324
}

325
function block_box_save($edit, $delta = NULL) {
326
327
  if (!filter_access($edit['format'])) {
    $edit['format'] = FILTER_FORMAT_DEFAULT;
Dries's avatar
   
Dries committed
328
329
  }

330
331
  if (isset($delta)) {
    db_query("UPDATE {boxes} SET title = '%s', body = '%s', info = '%s', format = %d WHERE bid = %d", $edit['title'], $edit['body'], $edit['info'], $edit['format'], $delta);
332
333
  }
  else {
334
    db_query("INSERT INTO {boxes} (title, body, info, format) VALUES  ('%s', '%s', '%s', %d)", $edit['title'], $edit['body'], $edit['info'], $edit['format']);
335
336
337
  }
}

Dries's avatar
   
Dries committed
338
/**
Dries's avatar
Dries committed
339
 * Menu callback; displays the block overview page.
Dries's avatar
   
Dries committed
340
 */
Dries's avatar
 
Dries committed
341
function block_admin() {
Dries's avatar
   
Dries committed
342
343
  $edit = $_POST['edit'];
  $op = $_POST['op'];
Dries's avatar
   
Dries committed
344

Dries's avatar
   
Dries committed
345
346
347
  if ($op == t('Save blocks')) {
    drupal_set_message(block_admin_save($edit));
    cache_clear_all();
348
    drupal_goto($_GET['q']);
Dries's avatar
   
Dries committed
349
  }
Dries's avatar
   
Dries committed
350
  print theme('page', block_admin_display());
Dries's avatar
 
Dries committed
351
}
Dries's avatar
   
Dries committed
352

Dries's avatar
   
Dries committed
353
354
355
356
357
358
/**
 * Implementation of hook_user().
 *
 * Allow users to decide which custom blocks to display when they visit
 * the site.
 */
359
function block_user($type, $edit, &$user, $category = NULL) {
360
  switch ($type) {
361
    case 'form':
362
      if ($category == 'account') {
363
        $result = db_query('SELECT * FROM {blocks} WHERE status = 1 AND custom != 0 ORDER BY weight, module, delta');
364
365
366
367

        while ($block = db_fetch_object($result)) {
          $data = module_invoke($block->module, 'block', 'list');
          if ($data[$block->delta]['info']) {
368
            $form .= form_checkbox($data[$block->delta]['info'], 'block]['. $block->module .']['. $block->delta, 1, isset($user->block[$block->module][$block->delta]) ? $user->block[$block->module][$block->delta] : ($block->custom == 1));
369
          }
Kjartan's avatar
Kjartan committed
370
371
        }

372
373
374
        if (isset($form)) {
          return array(array('title' => t('Block configuration'), 'data' => $form, 'weight' => 2));
        }
375
      }
Dries's avatar
   
Dries committed
376
377

      break;
Dries's avatar
   
Dries committed
378
379
380
    case 'validate':
      if (!$edit['block']) {
        $edit['block'] = array();
381
382
      }
      return $edit;
383
384
385
  }
}

386
387
388
/**
 * Return blocks available for current $user at $region.
 *
Dries's avatar
   
Dries committed
389
 * @param $region main|left|right
390
 *
Dries's avatar
   
Dries committed
391
 * @return array of block objects, indexed with <i>module</i>_<i>delta</i>
392
 *
Dries's avatar
   
Dries committed
393
 * @see <a href="http://drupal.org/node/1042" target="_top">[feature]
394
 *   Generic template design difficult w/o block region "look-ahead"</a>
Dries's avatar
   
Dries committed
395
 * @todo add a proper primary key (bid) to the blocks table so we don't have
396
397
398
399
 *   to mess around with this <i>module</i>_<i>delta</i> construct. currently,
 *   "blocks" has no primary key defined (bad)!
 */
function block_list($region) {
400
  global $user, $base_url;
401
402
403
404
  static $blocks = array();

  if (!isset($blocks[$region])) {
    $blocks[$region] = array();
405
    $result = db_query('SELECT * FROM {blocks} WHERE status = 1 '. ($region != 'all' ? 'AND region = %d ' : '') .'ORDER BY weight, module', $region == 'left' ? 0 : 1);
406
407

    while ($result && ($block = db_fetch_array($result))) {
408
409
410
411
412
413
414
415
416
417
418
      // Use the user's block visibility setting, if necessary
      if ($block['custom'] != 0) {
        if ($user->uid && isset($user->block[$block['module']][$block['delta']])) {
          $enabled = $user->block[$block['module']][$block['delta']];
        }
        else {
          $enabled = ($block['custom'] == 1);
        }
      }
      else {
        $enabled = TRUE;
419
      }
420
421

      // Match path if necessary
422
      if ($block['pages']) {
423
424
        $base = parse_url($base_url);
        $session = session_name() .'='. session_id();
Steven Wittens's avatar
Steven Wittens committed
425
        $url = str_replace(array($base['path'], '?'. $session), '', request_uri());
Dries's avatar
   
Dries committed
426
        $url = ereg_replace('^/(\?q=)?', '', $url);
427
428
        $regexp = '/^('. preg_replace(array('/(\r\n?|\n)/', '/\\\\\*/', '/(^|\|)\\\\<front\\\\>($|\|)/'), array('|', '.*', '\1\2'), preg_quote($block['pages'], '/')) .')$/';
        $matched = !($block['visibility'] xor preg_match($regexp, $url));
429
430
      }
      else {
431
        $matched = TRUE;
432
433
      }

434
435
436
      if ($enabled && $matched) {
        // Check the current throttle status and see if block should be displayed
        // based on server load.
437
        if (!($block['throttle'] && (module_invoke('throttle', 'status') > 0))) {
438
          $array = module_invoke($block['module'], 'block', 'view', $block['delta']);
Dries's avatar
   
Dries committed
439
          if (is_array($array)) {
440
441
            $block = array_merge($block, $array);
          }
Dries's avatar
   
Dries committed
442
        }
Dries's avatar
   
Dries committed
443
        if (isset($block['content']) && $block['content']) {
444
445
446
447
448
449
450
451
          $blocks[$region]["$block[module]_$block[delta]"] = (object) $block;
        }
      }
    }
  }
  return $blocks[$region];
}

452
?>