2 // $Id: og_panels.module,v 1.41 2008/10/31 12:14:04 weitzman Exp $
4 function og_panels_help($section) {
6 case 'admin/help#og_panels':
7 return t('After enabling this module, visit the new "Pages" tab on any group. There, group admins may create as many pages as desired for their group. The pages may contain any layout that the site offers and admins may arrange many different types of content as desired. Site admins can restrict the types of content that may be added on the <a href="!settings">og_panels settings</a> page. You might want to make some Views available using <a href="!apv">admin/panels/views</a>. Group admins may designate one page as their group home page.', array('!settings' => url('admin/og/og_panels'), '!apv' => url('admin/panels/views')));
8 case (arg(0) == 'node' && arg(2) == 'og_panels' && !arg(3)):
9 return '<p>'. t('Create custom pages for your group. Use custom pages to organize your content in a pretty and informative manner. Your group can group to be a whole website within a web site. Each custom page becomes a tab when viewing your group. One of your custom pages should be designated as your <em>group home page</em>. That page will then display when visitors first arrive at your group.') .'</p><p>'. t('Start by clicking the <a href="!url">Add new page</a> tab. Then you will choose a custom layout for your page. Then you will want to <em>edit content</em> for your custom page.', array('!url' => url('node/'. arg(1). '/og_panels/form'))) .'</p>';
13 function og_panels_menu($may_cache) {
16 'path' => 'admin/og/og_panels',
17 'title' => 'Organic groups panels',
18 'description' => 'Configure the content that is available to group admins when creating group pages.',
19 'callback' => 'og_panels_admin_content',
20 'access' => user_access('administer organic groups'),
24 if (arg(0) == 'node' && is_numeric(arg(1))) {
25 $node = node_load(arg(1));
26 if (og_is_group_type($node->type) && node_access('view', $node)) {
29 'path' => "node/$nid/og_panels",
31 'callback' => 'og_panels_overview',
32 'callback arguments' => array($node),
33 'access' => og_is_group_admin($node) && user_access('manage OG panels pages'),
34 'type' => MENU_LOCAL_TASK,
39 'path' => "node/$nid/og_panels/list",
41 'type' => MENU_DEFAULT_LOCAL_TASK,
46 'path' => "node/$nid/og_panels/form",
47 'callback' => 'drupal_get_form',
48 'callback arguments' => array('og_panels_form', $node),
49 'title' => arg(4) ? 'Edit page' : 'Add new page',
50 'type' => MENU_LOCAL_TASK,
54 $displays = og_panels_get_all($node->nid);
55 foreach ($displays as $display) {
56 if ($display->default_page && $display->published && arg(2) != 'feed') {
57 // Hijack the menu handler for this node.
59 'path' => "node/$nid",
60 'title' => $node->title, // doesn't work
61 'callback' => 'og_panels_page',
62 'callback arguments' => array($display->did, $node, $node->title),
66 * Optional. Horrible hack since the tab title is not changeable in D5. Add the following code
67 * to your theme's template.php in the _phptemplate_variables('page') section.
68 * if (isset($GLOBALS['og_panels_view_tab_fix'])) {
69 * $variables['tabs'] = str_replace('>View<', '>'. $GLOBALS['og_panels_view_tab_fix']. '<', $variables['tabs']);
72 $GLOBALS['og_panels_view_tab_fix'] = check_plain($display->page_title);
76 'path' => "node/$nid/$display->path",
77 'title' => $display->page_title,
78 'callback' => 'og_panels_page',
79 // have to pass all these args so the args are known in panels_page()
80 'callback arguments' => array($display->did, $node, $display->page_title),
81 'type' => MENU_LOCAL_TASK,
82 'access' => $display->published || (og_is_group_admin($node) && user_access('manage OG panels pages')),
83 'weight' => $display->weight,
89 'path' => "node/$nid/og_panels/". arg(3). '/view',
90 'callback' => 'og_panels_page',
91 'callback arguments' => array(arg(3), $node),
92 'type' => MENU_CALLBACK,
95 if (is_numeric(arg(3))) {
97 'path' => "node/$nid/og_panels/". arg(3). '/panel_layout',
98 'callback' => 'og_panels_edit_layout',
99 'callback arguments' => array(arg(3), $node),
100 'type' => MENU_CALLBACK,
104 'path' => "node/$nid/og_panels/". arg(3). '/panel_settings',
105 'callback' => 'og_panels_edit_layout_settings',
106 'callback arguments' => array(arg(3), $node),
107 'type' => MENU_CALLBACK,
111 'path' => "node/$nid/og_panels/". arg(3). '/panel_content',
112 'callback' => 'og_panels_edit_content',
113 'callback arguments' => array(arg(3), $node),
114 'type' => MENU_CALLBACK,
118 'path' => "node/$nid/og_panels/". arg(3). '/delete',
120 'callback' => 'drupal_get_form',
121 'callback arguments' => array('og_panels_delete_confirm', arg(3), $node),
122 'type' => MENU_CALLBACK,
131 function og_panels_perm() {
132 return array('manage OG panels pages');
135 function og_panels_delete_confirm($did, $group_node) {
136 $form['did'] = array('#type' => 'value', '#value' => $did);
137 $form['nid'] = array('#type' => 'value', '#value' => $group_node->nid);
138 $sql = "SELECT page_title FROM {og_panels} WHERE did = %d";
139 $page_title = db_result(db_query($sql, $did));
140 $form['page_title'] = array('#type' => 'value', '#value' => $page_title);
142 return confirm_form($form,
143 t('Are you sure you want to delete %title?', array('%title' => $page_title)),
144 isset($_GET['destination']) ? $_GET['destination'] : 'node/'. $node->nid. 'og_panels',
145 t('This action cannot be undone.'),
146 t('Delete'), t('Cancel')
150 function og_panels_delete_confirm_submit($form_id, $form_values) {
151 og_panels_delete($form_values['did']);
152 drupal_set_message(t('%title has been deleted', array('%title' => $form_values['page_title'])));
155 function og_panels_delete($did) {
156 $sql = "DELETE FROM {og_panels} WHERE did = %d";
157 db_query($sql, $did);
158 panels_delete_display($did);
162 * Menu callback. List the pages for this specified group. Provide helpful operations links.
166 function og_panels_overview($group_node) {
167 drupal_set_title(check_plain($group_node->title));
168 $displays = og_panels_get_all($group_node->nid);
169 $output = drupal_get_form('og_panels_table', $displays, $group_node);
174 * A form for setting the group homepage. Includes a helpful table of Pages and their operations links.
176 * @return $form array
178 function og_panels_table($displays, $group_node) {
179 $nid = $group_node->nid;
180 $form['#tree'] = TRUE; // #tree must be true in order to separate out the entries in the weight field
181 foreach ($displays as $display) {
182 $item['page_title'] = array('#value' => l($display->page_title, "node/$nid/$display->path"));
183 $item['weight'] = array('#type' => 'weight', '#default_value' => $display->weight);
184 $item['edit content'] = array('#value' => l(t('Edit content'), "node/$nid/og_panels/$display->did/panel_content", array('query' => drupal_get_destination())));
185 $item['change layout'] = array('#value' => l(t('Change layout'), "node/$nid/og_panels/$display->did/panel_layout"));
186 $item['edit layout settings'] = array('#value' => l(t('Edit layout settings'), "node/$nid/og_panels/$display->did/panel_settings"));
187 $item['edit page'] = array('#value' => l(t('Edit page'), "node/$nid/og_panels/form/$display->did", array('query' => drupal_get_destination())));
188 $item['delete page'] = array('#value' => l(t('Delete page'), "node/$nid/og_panels/$display->did/delete", array('query' => drupal_get_destination())));
190 $form['displays'][$display->did] = $item;
192 // Store the default_page for later.
193 if ($display->default_page == 1) {
194 $default_page = $display->did;
196 // Prepare the options for the radios.
197 $options[$display->did] = '';
200 $form['default_page'] = array(
202 '#options' => $options,
203 '#default_value' => $default_page,
205 $form['submit'] = array(
207 '#value' => t('Save settings'),
210 $form['group_node'] = array('#type' => 'value', '#value' => $group_node);
215 * Wrangle the $form into a tabular listing of pages.
217 * @ingroup Themeable functions
221 function theme_og_panels_table($form) {
222 foreach (element_children($form['displays']) as $did) {
223 if (is_numeric($did)) {
225 drupal_render($form['default_page'][$did]),
226 drupal_render($form['displays'][$did]['page_title']),
227 drupal_render($form['displays'][$did]['weight']),
228 drupal_render($form['displays'][$did]['edit content']),
229 drupal_render($form['displays'][$did]['change layout']),
230 drupal_render($form['displays'][$did]['edit layout settings']),
231 drupal_render($form['displays'][$did]['edit page']),
232 drupal_render($form['displays'][$did]['delete page']),
237 $output = drupal_render($form);
238 $header = array(t('Home page'), t('Title'), t('Weight'), array('align' => 'center', 'colspan' => 5, 'data' => t('Operations')));
239 return theme('table', $header, $rows). $output;
242 function og_panels_table_submit($form_id, $form_values) {
243 db_query("UPDATE {og_panels} SET default_page = 0 WHERE nid = %d", $form_values['group_node']->nid);
244 foreach ($form_values['displays'] as $did => $settings) {
245 $default = $did == $form_values['default_page'] ? TRUE : FALSE;
246 $sql = "UPDATE {og_panels} SET weight = %d, default_page = %d WHERE did = %d";
247 db_query($sql, $settings['weight'], $default, $did);
249 drupal_set_message(t('Updated panels configuration.'));
253 * A menu callback. Renders an og_panel based upon its display ID.
257 function og_panels_page($did, $group_node, $title = NULL) {
258 $og_panel = og_panels_get_one_by_display($did);
259 // Set breadcrumb and title on non group nodes
261 drupal_set_title(check_plain($title));
263 if (!$og_panel->default_page) {
264 $bc[] = l(t('Home'), "");
265 $bc[] = l($group_node->title, "node/$group_node->nid");
266 // I tried MSL but it let me down. again.
267 // menu_set_location($bc);
268 drupal_set_breadcrumb($bc);
270 // Mark node as read and show feed icon for any group panel page (how to do this better?)
271 drupal_add_feed(url("node/$group_node->nid/feed"), t('@name at @site', array('@name' => $group_node->title, '@site' => variable_get('site_name', 'drupal'))));
272 node_tag_new($group_node->nid);
275 $allargs = func_get_args();
276 $args = array_slice($allargs, 3);
277 $display = og_panels_load_display($did, $group_node);
278 $display->args = $args;
279 $output = panels_render_display($display);
280 // We print instead of return in order to allow blocks to be suppressed.
281 print theme('page', $output, $og_panel->show_blocks);
285 * Add/edit an og_panel.
289 function og_panels_form($group_node, $did = NULL) {
290 drupal_set_title(check_plain($group_node->title));
291 if (!is_null($did)) {
292 $display = og_panels_get_one_by_display($did);
295 $display = new stdClass;
298 $form['page_title'] = array(
299 '#title' => t('Page title'),
300 '#type' => 'textfield',
301 '#required' => $display->default_page ? FALSE : TRUE,
302 '#default_value' => $display->page_title,
303 '#description' => t('This is the title of the page and of the tab.'),
307 $field_prefix = "$base_url/node/$group_node->nid/";
308 $form[$display->default_page ? 'path_placeholder' : 'path'] = array(
309 '#title' => t('Path'),
310 '#type' => 'textfield',
311 '#default_value' => $display->default_page ? '' : $display->path,
312 '#field_prefix' => $field_prefix,
313 '#required' => $display->default_page ? FALSE : TRUE,
314 '#description' => $display->default_page ? t('This page is currently your default group home page and has no configurable path.') : '',
315 '#disabled' => $display->default_page,
318 if ($display->default_page) {
319 $form['path'] = array(
321 '#value' => $display->path,
324 $form['show_blocks'] = array(
325 '#title' => t('Show blocks'),
326 '#type' => 'checkbox',
327 '#default_value' => isset($display->show_blocks) ? $display->show_blocks : TRUE,
328 '#description' => t('If unchecked, the standard group blocks will not be shown unless you place them into your page content. This gives admin more control over page presentation.'),
330 $form['published'] = array(
331 '#type' => 'checkbox',
332 '#title' => t('Published'),
333 '#default_value' => $display->published,
334 '#description' => t('If unchecked, this page is only accessible by group or site administrators. Thats useful while you are configuring the page.'),
337 $form['submit'] = array(
339 '#value' => $did ? t('Update page') : t('Create page'),
341 $form['did'] = array('#type' => 'value', '#value' => $did);
342 $form['nid'] = array('#type' => 'value', '#value' => $group_node->nid);
347 * Return an array of all og_panels attached to the given nid.
350 * @return array $rows
351 * An associative array keyed by the $did of the og_panel
353 function og_panels_get_all($nid) {
354 $sql = "SELECT * FROM {og_panels} WHERE nid = %d ORDER BY default_page DESC, weight ASC";
355 $result = db_query($sql, $nid);
356 while ($row = db_fetch_object($result)) {
357 $rows[$row->did] = $row;
366 * Load an og_panels object.
369 * @return object $og_panel
371 function og_panels_get_one_by_display($did) {
372 $sql = "SELECT * FROM {og_panels} WHERE did = %d";
373 $result = db_query($sql, $did);
374 return db_fetch_object($result);
378 * Load the default og_panels object, if any, for the given nid.
381 * @return mixed $og_panel
382 * Either returns the og_panel object, or FALSE if no default panel was found for the nid
384 function og_panels_get_one_by_nid_default($nid) {
385 $sql = "SELECT * FROM {og_panels} WHERE nid = %d AND default_page = 1";
386 $result = db_query($sql, $nid);
387 $og_panel = db_fetch_object($result);
388 return is_object($og_panel) ? $og_panel : FALSE;
392 * Load an og_panel's display information.
394 * Loads a panels display object with various parameters, depending on the information passed into the load function.
397 * The did of the panels display.
398 * @param object $group_node = NULL
399 * The node object for the group to which the panel is attached.
400 * @param bool $ct = FALSE
401 * If TRUE, content_types are also loaded into the $display object
404 * Either returns a display object, or FALSE if no display was found with the parameters provided.
406 function og_panels_load_display($did, $group_node = NULL, $ct = FALSE) {
407 panels_load_include('plugins');
408 $display = panels_load_display($did);
409 if (is_object($group_node)) {
410 $display->context = array('og_panels' => panels_context_create('group', $group_node));
412 panels_load_include('common');
413 $display->content_types = panels_common_get_allowed_types('og_panels', $display->context);
416 return is_object($display) ? $display : FALSE;
419 function og_panels_form_validate($form_id, $form_values, $form) {
420 $pathblacklist = array('view', 'edit', 'delete', 'outline', 'load', 'render', 'clone');
421 if (in_array($form_values['path'], $pathblacklist)) {
422 form_error($form['path'], t('%path is a reserved system path, and cannot be used for a group page. Please enter another path.', array('%path' => $form_values['path'])));
424 else if (preg_match("/[^A-Za-z0-9-]/", $form_values['path'])) {
425 form_error($form['path'], t('Panel paths may only contain alphanumeric characters and dashes.'));
427 else if (db_result(db_query("SELECT path FROM {og_panels} WHERE path = '%s' AND did <> %d AND nid = %d", $form_values['path'], $form_values['did'], $form_values['nid']))) {
428 form_error($form['path'], t("That path is currently in use by another one of your group's pages. Please enter another path."));
433 * INSERT or UPDATE a new og_panel. If insert, redirect to layout form.
437 function og_panels_form_submit($form_id, $form_values) {
438 if ($form_values['did']) {
439 $sql = "UPDATE {og_panels} SET page_title='%s', path='%s', published=%d, show_blocks=%d WHERE did = %d";
440 db_query($sql, $form_values['page_title'], $form_values['path'], $form_values['published'], $form_values['show_blocks'], $form_values['did']);
441 drupal_set_message(t('Group page updated.'));
444 // Create a new display and record that.
445 $display = panels_new_display();
446 panels_save_display($display);
447 $sql = "INSERT INTO {og_panels} (did, nid, page_title, path, published, show_blocks) VALUES (%d, %d, '%s', '%s', %d, %d)";
448 db_query($sql, $display->did, $form_values['nid'], $form_values['page_title'], $form_values['path'], $form_values['published'], $form_values['show_blocks']);
449 drupal_set_message(t('Group page created.'));
450 return 'node/'. $form_values['nid']. "/og_panels/$display->did/panel_layout";
454 function og_panels_set_breadcrumb($section, $group_node = NULL) {
457 $bc[] = l(t('Home'), '');
458 $bc[] = l($group_node->title, "node/$group_node->nid");
459 $bc[] = l(t('Pages'), "node/$group_node->nid/og_panels");
461 drupal_set_breadcrumb($bc);
464 function og_panels_nodeapi($node, $op) {
467 if ($og_panels = og_panels_get_all($node->nid)) {
468 foreach ($og_panels as $og_panel) {
469 panels_delete_display($og_panel->did);
471 $sql = "DELETE FROM {og_panels} WHERE nid = %d";
472 db_query($sql, $node->nid);
473 drupal_set_message(t('Organic groups panel pages deleted.'));
479 // ---------------------------------------------------------------------------
480 // Meat of the Panels API; almost completely passing through to panels.module
483 * Pass through to the panels layout editor.
486 * the $did of the og_panel to be edited.
488 * @param object $group_node
489 * the node object to which the og_panel is attached.
491 function og_panels_edit_layout($did, $group_node) {
492 og_panels_set_breadcrumb('panel_edit', $group_node);
493 $display = og_panels_load_display($did, $group_node); // TODO I don't believe that having the context present is necessary for editing the layout.
494 return panels_edit_layout($display, t('Save'), "node/$group_node->nid/og_panels");
498 * Pass through to the panels layout settings editor.
501 * the $did of the og_panel to be edited.
503 * @param object $group_node
504 * the node object to which the og_panel is attached.
506 function og_panels_edit_layout_settings($did, $group_node) {
507 og_panels_set_breadcrumb('panel_edit', $group_node);
508 $display = og_panels_load_display($did);
509 return panels_edit_layout_settings($display, t('Save'), "node/$group_node->nid/og_panels");
513 * Pass through to the panels content editor.
516 * the $did of the og_panel to be edited.
518 * @param object $group_node
519 * the node object to which the og_panel is attached.
521 function og_panels_edit_content($did, $group_node) {
522 og_panels_set_breadcrumb('panel_edit', $group_node);
523 $display = og_panels_load_display($did, $group_node, TRUE);
524 // Print this with theme('page') so that blocks are disabled while editing a display.
525 // This is important because negative margins in common block layouts (i.e, Garland)
526 // messes up the drag & drop.
527 print theme('page', panels_edit($display, "node/$group_node->nid/og_panels", $display->content_types), FALSE);
530 function og_panels_admin_content() {
531 panels_load_include('common');
532 return drupal_get_form('panels_common_settings', 'og_panels');
536 * Implementation of hook_panels_contexts()
539 function og_panels_panels_contexts() {
540 include_once './'. drupal_get_path('module', 'og_panels') .'/includes/groupcontext.inc';
541 $args['group'] = array(
542 'title' => t('Group'),
543 'description' => t('A node object that is flagged as an OG group type.'),
544 'context' => 'og_panels_context_create_group',
545 'settings form' => 'og_panels_context_group_settings_form',
546 'settings form validate' => 'og_panels_context_group_settings_form_validate',
547 'keyword' => 'group',
548 'context name' => 'group',
554 * Implementation of hook_panels_content_types()
556 function og_panels_panels_content_types() {
557 include_once './'. drupal_get_path('module', 'og_panels') .'/includes/groupcontent.inc';
558 $items['og_mission'] = array(
559 'title' => t('OG mission'),
560 'content_types' => 'og_panels_ct_list_mission',
561 'single' => TRUE, // only provides a single content type
562 'render callback' => 'og_panels_ct_render_callback_mission',
563 // 'add callback' => 'og_panels_content_types_add_callback',
564 // 'edit callback' => 'og_panels_content_types_edit_callback',
565 'title callback' => 'og_panels_ct_title_callback_mission',
566 // 'add submit callback' => 'panels_admin_submit_group',
567 // 'edit submit callback' => 'panels_admin_submit_group',
568 // 'validate callback' => 'panels_admin_validate_group',
570 $items['og_description'] = array(
571 'title' => t('OG description'),
572 'content_types' => 'og_panels_ct_list_description',
573 'single' => TRUE, // only provides a single content type
574 'render callback' => 'og_panels_ct_render_callback_description',
575 'title callback' => 'og_panels_ct_title_callback_description',
578 $items['og_subscribers'] = array(
579 'title' => t('OG members'),
580 'content_types' => 'og_panels_ct_list_subscribers',
581 'single' => TRUE, // only provides a single content type
582 'render callback' => 'og_panels_ct_render_callback_subscribers',
583 'add callback' => 'og_panels_content_types_addedit_callback_subscribers',
584 'edit callback' => 'og_panels_content_types_addedit_callback_subscribers',
585 'title callback' => 'og_panels_ct_title_callback_subscribers',
586 'add validate callback' => 'og_panels_content_types_validate_callback_subscribers',
587 'edit validate callback' => 'og_panels_content_types_validate_callback_subscribers',
590 if (module_exists('search')) {
591 $items['og_search'] = array(
592 'title' => t('Group search'),
593 'content_types' => 'og_panels_ct_list_search',
594 'single' => TRUE, // only provides a single content type
595 'render callback' => 'og_panels_ct_render_callback_search',
596 'title callback' => 'og_panels_ct_title_callback_search',
600 if (og_is_picture()) {
601 $items['og_faces'] = array(
602 'title' => t('OG faces'),
603 'content_types' => 'og_panels_ct_list_faces',
604 'single' => TRUE, // only provides a single content type
605 'render callback' => 'og_panels_ct_render_callback_faces',
606 'add callback' => 'og_panels_content_types_addedit_callback_faces',
607 'edit callback' => 'og_panels_content_types_addedit_callback_faces',
608 'title callback' => 'og_panels_ct_title_callback_faces',
609 'add validate callback' => 'og_panels_content_types_validate_callback_faces',
610 'edit validate callback' => 'og_panels_content_types_validate_callback_faces',
617 // An implementation of hook_panels_relationships.
618 function og_panels_panels_relationships() {
619 include_once './'. drupal_get_path('module', 'og_panels') .'/includes/grouprelationships.inc';
620 $args['group_from_node'] = array(
621 'title' => t('Group from node'),
622 'keyword' => 'group',
623 'description' => t('Adds a group from a node context; if multiple groups are associated with a node, this will get the "first" group only.'),
624 'required context' => new panels_required_context(t('Node'), 'node'),
625 'context' => 'panels_group_from_node_context',
630 // An implementation of hook_panels_block_info. We have to prefix with 'og' and not 'og_panels'.
631 function og_panels_block_info($module, $delta, &$info) {
634 // Provide alternate versions as content items
638 $info['icon'] = 'images/user-multiple.png';
639 $info['required context'] = new panels_required_context(t('Group'), 'group');
640 $info['path'] = drupal_get_path('module', 'og_panels'). '/';
641 $info['category'] = t('Organic groups');
645 // These blocks do not need group context.
646 if ($delta == 3 || $delta == 6) {
647 unset($info['required context']);