summaryrefslogtreecommitdiffstats
path: root/panels_node/panels_node.module
blob: ffd26939f0189d8a8bb390280518609b58561cde (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
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
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
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
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
<?php


/**
 * @file panels_node.module
 *
 * This module provides the "panel" node type.
 * Panel nodes are useful to add additional content to the content area
 * on a per-node base.
 */

// ---------------------------------------------------------------------------
// General Drupal hooks

/**
 * Implementation of hook_perm().
 */
function panels_node_perm() {
  return array('create panel-nodes', 'edit any panel-nodes', 'edit own panel-nodes', 'administer panel-nodes', 'delete any panel-nodes', 'delete own panel-nodes');
}

/**
 * Implementation of hook_menu().
 */
function panels_node_menu() {
  // Safety: go away if CTools is not at an appropriate version.
  if (!defined('PANELS_REQUIRED_CTOOLS_API') || !module_invoke('ctools', 'api_version', PANELS_REQUIRED_CTOOLS_API)) {
    return array();
  }

  $items['admin/build/panels/settings/panel-node'] = array(
    'title' => 'Panel nodes',
    'description' => 'Configure which content is available to add to panel node displays.',
    'access arguments' => array('administer panel-nodes'),
    'page callback' => 'panels_node_settings',
    'type' => MENU_LOCAL_TASK,
  );

  // Avoid some repetition on these:
  $base = array(
    'access callback' => 'panels_node_edit_node',
    'access arguments' => array(1),
    'page arguments' => array(1),
    'type' => MENU_LOCAL_TASK,
  );

  $items['node/%node/panel_layout'] = array(
    'title' => 'Panel layout',
    'page callback' => 'panels_node_edit_layout',
    'weight' => 2,
  ) + $base;

  $items['node/%node/panel_content'] = array(
    'title' => 'Panel content',
    'page callback' => 'panels_node_edit_content',
    'weight' => 3,
  ) + $base;

  $items['node/add/panel/choose-layout'] = array(
    'title' => 'Choose layout',
    'access arguments' => array('create panel-nodes'),
    'page callback' => 'panels_node_add',
    'type' => MENU_CALLBACK,
  );

  return $items;
}

function panels_node_edit_node($node) {
  if (!isset($node->panels_node)) {
    return FALSE;
  }

  return node_access('update', $node);
}

// ---------------------------------------------------------------------------
// Node hooks

/**
 * Implementation of hook_node_info().
 */
function panels_node_node_info() {
  // Safety: go away if CTools is not at an appropriate version.
  if (!defined('PANELS_REQUIRED_CTOOLS_API') || !module_invoke('ctools', 'api_version', PANELS_REQUIRED_CTOOLS_API)) {
    return array();
  }

  return array(
    'panel' => array(
      'name' => t('Panel'),
      'module' => 'panels_node',
      'body_label' => t('Teaser'),
      'description' => t("A panel layout broken up into rows and columns."),
    ),
  );
}

/**
 * Implementation of hook_access().
 */
function panels_node_access($op, $node, $account) {
  if (user_access('administer panel-nodes', $account)) {
    return TRUE;
  }

  if ($op == 'create' && user_access('create panel-nodes', $account)) {
    return TRUE;
  }

  if ($op == 'update' && (user_access('edit any panel-nodes', $account) || $node->uid == $account->uid && user_access('edit own panel-nodes', $account))) {
    return TRUE;
  }


  if ($op == 'delete' && (user_access('delete any panel-nodes') || $node->uid == $account->uid && user_access('delete own panel-nodes'))) {
    return TRUE;
  }
}

function panels_node_add() {
  $output = '';

  ctools_include('plugins', 'panels');
  ctools_include('common', 'panels');

  $layouts = panels_common_get_allowed_layouts('panels_node');
  return panels_common_print_layout_links($layouts, 'node/add/panel', array('query' => $_GET));
}

/**
 * Implementation of hook_form().
 */
function panels_node_form(&$node, &$param) {
  ctools_include('plugins', 'panels');

  $form['panels_node']['#tree'] = TRUE;
  if (empty($node->nid) && arg(0) == 'node' && arg(1) == 'add') {
    // Grab our selected layout from the $node, If it doesn't exist, try arg(3)
    // and if that doesn't work present them with a list to pick from.
    $panel_layout = isset($node->panel_layout) ? $node->panel_layout : arg(3);
    if (empty($panel_layout)) {
      $opts = $_GET;
      unset($opts['q']);
      return drupal_goto('node/add/panel/choose-layout', $opts);
    }

    $layout = panels_get_layout($panel_layout);
    if (empty($layout)) {
      return drupal_not_found();
    }
    $form['panels_node']['layout'] = array(
      '#type' => 'value',
      '#value' => $panel_layout,
    );
  }

  $type = node_get_types('type', $node);

  $form['title'] = array(
    '#type' => 'textfield',
    '#title' => check_plain($type->title_label),
    '#required' => TRUE,
    '#default_value' => $node->title,
  );

  if (!empty($type->body_label)) {
    $form['body_field']['#prefix'] = '<div class="body-field-wrapper">';
    $form['body_field']['#suffix'] = '</div>';
    $form['body_field']['body'] = array(
      '#type' => 'textarea',
      '#title' => check_plain($type->body_label),
      '#rows' => 10,
      '#required' => TRUE,
      '#description' => t('The teaser is a piece of text to describe when the panel is listed (such as when promoted to front page); the actual content will only be displayed on the full node view.'),
      '#default_value' => $node->body,
    );
    $form['body_field']['format'] = filter_form($node->format); // Now we can set the format!
  }

//  drupal_set_message('<pre>' . check_plain(var_export($node, true)) . '</pre>');
  $css_id = '';
  if (!empty($node->panels_node['css_id'])) {
    $css_id = $node->panels_node['css_id'];
  }

  $form['panels_node']['css_id'] = array(
    '#type' => 'textfield',
    '#title' => t('CSS ID'),
    '#size' => 30,
    '#description' => t('An ID that can be used by CSS to style the panel.'),
    '#default_value' => $css_id,
  );

  // Support for different rendering pipelines
  // Mostly borrowed from panel_context.inc
  $pipelines = panels_get_renderer_pipelines();

  // If there are no pipelines, that probably means we're operating in
  // legacy mode.
  if (empty($pipelines)) {
    // We retain the original pipeline so we don't wreck things by installing
    // old modules.
    $form['panels_node']['pipeline'] = array(
      '#type' => 'value',
      '#value' => $node->panels_node['pipeline'],
    );
  }
  else {
    $options = array();
    foreach ($pipelines as $name => $pipeline) {
      $options[$name] = check_plain($pipeline->admin_title) . '<div class="description">' . check_plain($pipeline->admin_description) . '</div>';
    }

    $form['panels_node']['pipeline'] = array(
      '#type' => 'radios',
      '#options' => $options,
      '#title' => t('Renderer'),
      '#default_value' => !empty($node->panels_node['pipeline']) ? $node->panels_node['pipeline'] : 'standard',
    );
  }

  return $form;
}

/**
 * Implementation of hook_validate().
 */
function panels_node_validate($node) {
  if (!$node->nid && empty($node->panels_node['layout'])) {
    form_set_error('', t('Please select a layout.'));
  }
}

/**
 * Implementation of hook_load().
 *
 * Panels does not use revisions for nodes because that would open us up
 * to have completely separate displays, and we'd have to copy them,
 * and that's going to be a LOT of data.
 */
function panels_node_load($node) {
  // We shortcut this because only in some really drastic corruption circumstance will this
  // not work.
  $additions['panels_node'] = db_fetch_array(db_query("SELECT * FROM {panels_node} WHERE nid = %d", $node->nid));
  return $additions;
}

/**
 * Implementation of hook_insert().
 */
function panels_node_insert(&$node) {
  // Create a new display and record that.
  $display = panels_new_display();
  $display->layout = $node->panels_node['layout'];

  // Special handling for nodes being imported from an export.module data dump.
  if (!empty($node->export_display)) {
    // This works by overriding the $display set above
    eval($node->export_display);
    unset($node->export_display);
  }

  panels_save_display($display);
  $css_id = $node->panels_node['css_id'];

  db_query("INSERT INTO {panels_node} (nid, did, css_id, pipeline) VALUES (%d, %d, '%s', '%s')", $node->nid, $display->did, $node->panels_node['css_id'], $node->panels_node['pipeline']);

  $node->panels_node['did'] = $display->did;
}

/**
 * Implementation of hook_delete().
 */
function panels_node_delete(&$node) {
  db_query("DELETE FROM {panels_node} WHERE nid = %d", $node->nid);
  if (!empty($node->panels_node['did'])) {
    panels_delete_display($node->panels_node['did']);
  }
}

/**
 * Implementation of hook_update().
 */
function panels_node_update($node) {
  db_query("UPDATE {panels_node} SET css_id = '%s', pipeline = '%s' WHERE nid = %d", $node->panels_node['css_id'], $node->panels_node['pipeline'], $node->nid);
}

/**
 * Implementation of hook_view().
 */
function panels_node_view($node, $teaser = FALSE, $page = FALSE) {
  static $rendering = array();

  // Prevent loops if someone foolishly puts the node inside itself:
  if (!empty($rendering[$node->nid])) {
    return $node;
  }

  $rendering[$node->nid] = TRUE;
  ctools_include('plugins', 'panels');
  if ($teaser) {
    // Do the standard view for teaser.
    $node = node_prepare($node, $teaser);
    // Because our teasier is never the same as our content, *always* provide
    // the read more flag.
    $node->readmore = TRUE;
  }
  else {
    if (!empty($node->panels_node['did'])) {
      $display = panels_load_display($node->panels_node['did']);
      $display->css_id = $node->panels_node['css_id'];
      // TODO: Find a way to make sure this can't node_view.
      $display->context = panels_node_get_context($node);
      $renderer = panels_get_renderer($node->panels_node['pipeline'], $display);
      $node->content['body'] = array(
        '#value' => panels_render_display($display, $renderer),
        '#weight' => 0,
      );
    }
  }

  unset($rendering[$node->nid]);
  return $node;
}

// ---------------------------------------------------------------------------
// Administrative pages

/**
 * Settings for panel nodes.
 */
function panels_node_settings() {
  ctools_include('common', 'panels');
  return drupal_get_form('panels_common_settings', 'panels_node');
}

// ---------------------------------------------------------------------------
// Meat of the Panels API; almost completely passing through to panels.module

/**
 * Pass through to the panels layout editor.
 */
function panels_node_edit_layout($node) {
//  ctools_include('plugins', 'panels');
  ctools_include('context');
  $display = panels_load_display($node->panels_node['did']);
  $display->context = panels_node_get_context($node);
  return panels_edit_layout($display, t('Save'), "node/$node->nid/panel_layout", 'panels_node');
}

/**
 * Pass through to the panels content editor.
 */
function panels_node_edit_content($node) {
//  ctools_include('plugins', 'panels');
  ctools_include('context');
  $display = panels_load_display($node->panels_node['did']);
  $display->context = panels_node_get_context($node);
  ctools_include('common', 'panels');
  $content_types = panels_common_get_allowed_types('panels_node', $display->context);

  // Print this with theme('page') so that blocks are disabled while editing a display.
  // This is important because negative margins in common block layouts (i.e, Garland)
  // messes up the drag & drop.
  print theme('page', panels_edit($display, "node/$node->nid/panel_content", $content_types), FALSE);
}

/**
 * Build the context to use for a panel node.
 */
function panels_node_get_context(&$node) {
  ctools_include('context');
  $context = ctools_context_create('node', $node);
  $context->identifier = t('This node');
  $context->keyword = 'node';
  return array('panel-node' => $context);
}

/**
 * Implementation of hook_export_node_alter()
 *
 * Integrate with export.module for saving panel_nodes into code.
 */
function panels_node_export_node_alter(&$node, $original_node, $method) {
  if ($method == 'export') {
    $node_export_omitted = variable_get('node_export_omitted', array());
    if (variable_get('node_export_method', '') != 'save-edit' && (array_key_exists('panel', $node_export_omitted) && !$node_export_omitted['panel'])) {
      drupal_set_message(t("NOTE: in order to import panel_nodes you must first set the export.module settings to \"Save as a new node then edit\", otherwise it won't work."));
    }
    $display = panels_load_display($node->panels_node['did']);
    $export = panels_export_display($display);
    $node->export_display = $export;
  }
}

/**
 * Implementation of hook_panels_dashboard_blocks().
 *
 * Adds panel nodes information to the Panels dashboard.
 */
function panels_node_panels_dashboard_blocks(&$vars) {
  $vars['links']['panels_node'] = array(
    'title' => l(t('Panel node'), 'node/add/panel'),
    'description' => t('Panel nodes are node content and appear in your searches, but are more limited than panel pages.'),
    'weight' => -1,
  );
}