You may peruse a list of your current panels layouts and edit them, or click add to create a new page.
');
case 'admin/build/panels/add':
return t('
Choose a layout for your new page from the list below.
');
}
}
/**
* Implementation of hook_perm()
*/
function panels_perm() {
return array('create panels', 'access all panels');
}
/**
* Determine if the specified user has access to a panel.
*/
function panels_access($panel, $account = NULL) {
if (!$account) {
global $user;
$account = $user;
}
// Administrator privileges
if (user_access('access all panels', $account)) {
return TRUE;
}
// All views with an empty access setting are available to all roles.
if (!$panel->access) {
return TRUE;
}
// Otherwise, check roles
static $roles = array();
if (!isset($roles[$account->uid])) {
$roles[$account->uid] = array_keys($account->roles);
}
return array_intersect($panel->access, $roles[$account->uid]);
}
/**
* Implementation of hook_menu()
*/
function panels_menu($may_cache) {
if ($may_cache) {
$access = user_access('create panels');
$items[] = array(
'path' => 'admin/build/panels',
'title' => t('Panels'),
'access' => $access,
'callback' => 'panels_list_page',
'description' => t('Create pages on your site that are 2 or 3 columns'),
);
$items[] = array(
'path' => 'admin/build/panels/list',
'title' => t('List'),
'access' => $access,
'callback' => 'panels_list_page',
'weight' => -10,
'type' => MENU_DEFAULT_LOCAL_TASK,
);
$items[] = array(
'path' => 'admin/build/panels/add',
'title' => t('Add'),
'access' => $access,
'callback' => 'panels_add_page',
'type' => MENU_LOCAL_TASK,
);
$items[] = array(
'path' => 'admin/build/panels/add/layout',
'title' => t('Add'),
'access' => $access,
'callback' => 'panels_add_layout_page',
'type' => MENU_LOCAL_TASK,
);
$items[] = array(
'path' => 'admin/build/panels/edit',
'title' => t('Edit panels'),
'access' => $access,
'callback' => 'panels_edit_page',
'type' => MENU_CALLBACK,
);
$items[] = array(
'path' => 'admin/build/panels/delete',
'title' => t('Delete panels'),
'access' => $access,
'callback' => 'drupal_get_form',
'callback arguments' => array('panels_delete_confirm'),
'type' => MENU_CALLBACK,
);
$items[] = array(
'path' => 'panels/node/autocomplete',
'title' => t('Autocomplete node'),
'callback' => 'panels_node_autocomplete',
'access' => user_access('access content'),
'type' => MENU_CALLBACK
);
// load panels from database
$result = db_query("SELECT * FROM {panels_info}");
// FIXME: Fow now we're making these all callbacks, but we
// should steal code from Views so they can be normal, tabs,
// etc
while ($panels = db_fetch_object($result)) {
$panels->access = ($panels->access ? explode(', ', $panels->access) : array());
$items[] = array(
'path' => $panels->path,
'title' => filter_xss_admin($panels->title),
'access' => panels_access($panels),
'callback' => 'panels_panels_page',
'callback arguments' => array($panels->did),
'type' => MENU_CALLBACK
);
}
}
return $items;
}
/**
* panels path helper function
*/
function panels_get_file_path($module, $file, $base_path = true) {
if ($base_path) {
$output = base_path();
}
return $output . drupal_get_path('module', $module) . '/' . $file;
}
// ---------------------------------------------------------------------------
// panels custom image button
/**
* Custom form element to do our nice images.
*/
function panels_elements() {
$type['panels_imagebutton'] = array('#input' => TRUE, '#button_type' => 'submit',);
return $type;
}
/**
* Theme our image button.
*/
function theme_panels_imagebutton($element) {
return '\n";
}
function panels_imagebutton_value() {
// null function guarantees default_value doesn't get moved to #value.
}
/**
* Add a single button to a form.
*/
function panels_add_button($image, $name, $text) {
$module_path = base_path() . drupal_get_path('module', 'panels');
return array(
'#type' => 'panels_imagebutton',
'#image' => $module_path . '/images/' . $image,
'#title' => $text,
'#default_value' => $name,
);
}
/**
* Set a button to a blank image -- used for placeholders when buttons are
* not relevant but just removing it would be visually unappealing.
*/
function panels_set_blank(&$form) {
$form['#type'] = 'markup';
$form['#value'] = theme('image', drupal_get_path('module', 'panels') . '/images/blank.gif');
}
// ---------------------------------------------------------------------------
// panels administrative pages
/**
* Provide a list of panels, with links to edit or delete them.
*/
function panels_list_page() {
$result = db_query("SELECT * FROM {panels_info} ORDER BY title");
while ($panels = db_fetch_object($result)) {
$item = array();
$item[] = check_plain($panels->title);
$item[] = l($panels->path, $panels->path);
$item[] = implode(' | ', array(
l(t('Edit'), "admin/build/panels/edit/$panels->did"),
l(t('Delete'), "admin/build/panels/delete/$panels->did"),
));
$items[] = $item;
}
$header = array(
t('Panel title'),
t('URL'),
t('Operations'),
);
$output = theme('table', $header, $items);
return $output;
}
/*
* Provide a form to confirm deletion of a panel page.
*/
function panels_delete_confirm($did = '') {
$panels = panels_load_panels($did);
if (!$panels) {
drupal_goto('admin/build/panels');
}
$form['did'] = array('#type' => 'value', '#value' => $panels->did);
return confirm_form( $form,
t('Are you sure you want to delete %title?', array('%title' => $panels->title)),
$_GET['destination'] ? $_GET['destination'] : 'admin/build/panels',
t('This action cannot be undone.'),
t('Delete'), t('Cancel')
);
}
/*
* Handle the submit button to delete a panel page.
*/
function panels_delete_confirm_submit($formid, $form) {
if ($form['confirm']) {
panels_delete_panels((object) $form);
return 'admin/build/panels';
}
}
/**
* Handle the add panels page
*/
function panels_add_page($layout = NULL) {
$layouts = panels_get_layouts();
drupal_add_css(drupal_get_path('module', 'panels') . '/panels_admin.css');
if (!$layout) {
foreach ($layouts as $id => $layout) {
if (!$default_id) {
// grab the first one for our default.
$default_id = $id;
}
$file = panels_get_file_path($layout['module'], $layout['icon'], false);
$output .= theme('panels_add_image', $layout[title], $id, l(theme('image', $file), $_GET['q'] . '/' . $id, NULL, NULL, NULL, NULL, TRUE));
}
return $output;
}
if (!$layouts[$layout]) {
return drupal_not_found();
}
$panels->layout = $layout;
return drupal_get_form('panels_edit_form', $panels);
}
function theme_panels_add_image($title, $id, $image) {
$output .= '
';
$output .= $image;
$output .= '
' . l($title, $_GET['q'] . '/' . $id) . '
';
$output .= '
';
return $output;
}
// ---------------------------------------------------------------------------
// panels administrative pages
function panels_edit_page($did = NULL) {
if (!$did || !($panels = panels_load_panels($did))) {
return drupal_not_found();
}
return drupal_get_form('panels_edit_form', $panels);
}
/**
* shortcut to ease the syntax of the various form builder tricks we use.
*/
function panels_form_builder(&$form, $form_id = 'panels_edit_form') {
$form['#post'] = $_POST;
$form = form_builder($form_id, $form);
}
/**
* Edit an already loaded panels.
*/
function panels_edit_form($panels) {
drupal_add_css(drupal_get_path('module', 'panels') . '/panels_admin.css');
$layouts = panels_get_layouts();
$layout = $layouts[$panels->layout];
$content_types = panels_get_content_types();
// Process all our add button stuff first so we can add stuff to the
// form semi dynamically.
$form['add'] = array(
'#type' => 'fieldset',
'#title' => t('Add content'),
'#collapsible' => false,
'#description' => t('Select an area to add content to, then select a type of content and click the appropriate button. The content will be added.'),
);
// Drop the array keys into a temporary in order to protect references.
$temp = array_keys($layout['content areas']);
$default_radio = array_shift($temp);
$form['add']['area'] = array(
'#type' => 'radios',
'#title' => t('Area'),
'#options' => $layout['content areas'],
'#prefix' => '
',
);
$form['info']['layout-display'] = array(
'#value' => 'Layout: ' . $layout['title'],
);
$form['info']['title'] = array(
'#type' => 'textfield',
'#default_value' => $panels->title,
'#title' => t('Page title'),
'#description' => t('The page title for this panels layout'),
);
$form['info']['css_id'] = array(
'#type' => 'textfield',
'#default_value' => $panels->css_id,
'#title' => t('CSS ID'),
'#description' => t('The CSS ID to apply to this page'),
);
$rids = array();
$result = db_query("SELECT r.rid, r.name FROM {role} r ORDER BY r.name");
while ($obj = db_fetch_object($result)) {
$rids[$obj->rid] = $obj->name;
}
$form['info']['access'] = array(
'#type' => 'checkboxes',
'#title' => t('Access'),
'#default_value' => $panels->access,
'#options' => $rids,
'#description' => t('Only the checked roles will be able to see this panel in any form; if no roles are checked, access will not be restricted.'),
);
$form['info']['path'] = array(
'#type' => 'textfield',
'#default_value' => $panels->path,
'#title' => t('Path'),
'#description' => t('The URL path to give this page, i.e, path/to/page'),
'#required' => TRUE,
);
$form['content'] = array(
'#tree' => true,
);
// Go through our content areas and display what we have.
foreach ($layout['content areas'] as $area => $title) {
$list = array();
$form['content'][$area] = array(
'#tree' => true,
);
// Construct the order, feeding it the default order for what
// we know about. When we pull it back out, it may well be
// different due to past submits.
$order = array();
if (is_array($panels->content[$area])) {
$order = array_keys($panels->content[$area]);
}
$form['content'][$area]['order'] = array(
'#type' => 'hidden',
'#default_value' => serialize($order),
'#parents' => array('content', $area, 'order'),
);
// If an add button has added an item to the area, put it in and update
// the $order.
panels_form_builder($form['content'][$area]['order']);
$order = unserialize($form['content'][$area]['order']['#value']);
if ($add->area == $area) {
// say THIS 5 times real fast
if ($panels->content[$area] && $order) {
$position = max(max(array_keys($order)), max(array_keys($panels->content[$area]))) + 1;
}
else if ($order) {
$position = max(array_keys($order)) + 1;
}
else {
$position = 0;
}
$panels->content[$area][$position] = $add;
$order[] = $position;
$form['content'][$area]['order']['#value'] = serialize($order);
}
// Go through each item in the area and render it.
$count = count($order);
foreach ($order as $position => $id) {
// place buttons to re-order content.
$form['content'][$area][$id]['buttons'] = array(
'#parents' => array('content', $area, $id, 'buttons'),
'#tree' => TRUE
);
panels_add_buttons($form['content'][$area][$id]['buttons'], $count, $position);
// figure out if one of those buttons was pressed
panels_form_builder($form['content'][$area][$id]['buttons']);
$deleted = false;
foreach ($GLOBALS['form_values']['content'][$area][$id]['buttons'] as $button => $value) {
if ($value) {
$function = 'panels_move_' . $button;
$function($order, $position);
$form['content'][$area]['order']['#value'] = serialize($order);
if ($button == 'delete')
$deleted = true;
}
}
// If a content item was deleted, it still has buttons. Get rid of them.
// It had buttons because we needed to give it buttons to see if its
// buttons were clicked.
if ($deleted) {
unset($form['content'][$area][$id]['buttons']);
}
else {
// we finally get to add the conent item's content to the form.
$area_record = $panels->content[$area][$id];
$form['content'][$area][$id]['type'] = array(
'#type' => 'hidden',
'#default_value' => $area_record->type,
'#parents' => array('content', $area, $id, 'type'),
);
// retrieve what was already there -- so we can retain edits and
// content items that were added.
panels_form_builder($form['content'][$area][$id]['type']);
$type = $form['content'][$area][$id]['type']['#value'];
$function = $content_types[$type]['admin'];
if (function_exists($function)) {
$array = array(
'#tree' => true,
'#parents' => array('content', $area, $id, 'configuration'),
);
$form['content'][$area][$id]['configuration'] = array_merge($array, $function('edit', $area_record->configuration, array('content', $area, $id, 'configuration')));
panels_form_builder($form['content'][$area][$id]['configuration']);
}
}
}
}
$form['submit'] = array(
'#type' => 'submit',
'#value' => t('Save'),
);
return $form;
}
/**
* Display the form to edit a panels.
*/
function theme_panels_edit_form($form) {
$layouts = panels_get_layouts();
$layout = $layouts[$form['layout']['#value']];
$content_types = panels_get_content_types();
$output .= drupal_render($form['info']);
foreach ($layout['content areas'] as $area => $title) {
$order = unserialize($form['content'][$area]['order']['#value']);
if (!$order) {
$area_content = t('This area has no content.');
}
else {
$area_content = '';
$count = count($order);
foreach ($order as $position => $id) {
if ($count > 1) {
if ($position == 0 ) {
panels_set_blank($form['content'][$area][$id]['buttons']['up']);
}
else if ($position == ($count - 1)) {
panels_set_blank($form['content'][$area][$id]['buttons']['down']);
}
}
$type = $form['content'][$area][$id]['type']['#value'];
$function = $content_types[$type]['admin'];
if (function_exists($function)) {
// figure out the actual values; using the global because we need it
// to be in the same format it'll be in 'submit'.
global $form_values;
$conf_form = $form_values['content'][$area][$id]['configuration'];
$conf = $function('save', $conf_form);
$fieldset = array(
'#title' => t('Configure'),
'#children' => drupal_render($form['content'][$area][$id]['configuration']),
'#collapsible' => true,
'#collapsed' => true
);
$buttons = drupal_render($form['content'][$area][$id]['buttons']);
$area_content .= $buttons . ' ' . $function('list', $conf) .
theme('fieldset', $fieldset) /* . ' ' */;
}
}
}
$content[$area] = theme('fieldset', array('#title' => check_plain($title), '#value' => $area_content));
}
$output .= panels_get_layout($layout, $content);
$output .= drupal_render($form);
return $output;
}
function panels_edit_form_validate($form_id, $form_values, $form) {
$content_types = panels_get_content_types();
foreach ($form_values['content'] as $area => $content) {
foreach ($content as $id => $item) {
if (is_numeric($id)) {
$function = $content_types[$item['type']]['admin'];
if (function_exists($function)) {
$function('validate', $item['configuration'], $form['content'][$area][$id]['configuration']);
}
}
}
}
}
function panels_edit_form_submit($form_id, $form_values) {
$panels = (object) $form_values;
// be sure we get the order right.
foreach ($form_values['content'] as $area => $content) {
$array = array();
$order = unserialize($content['order']);
if (is_array($order)) {
foreach($order as $id) {
$array[] = $content[$id];
}
}
$panels->content[$area] = $array;
}
$panels->access = array_keys(array_filter($panels->access));
panels_save_panels($panels);
drupal_set_message(t('The panels has been saved.'));
return 'admin/build/panels';
}
/**
* add the buttons to a content item
*/
function panels_add_buttons(&$form, $count, $position) {
$form['delete'] = panels_add_button('user-trash.png', 'delete', t('Delete this item'));
// Leaving these in but commented out as I'm not convinced we don't want them.
// if ($count > 2) {
// $form['top'] = panels_add_button('go-top.png', 'top', t('Move item to top'));
// }
if ($count > 1) {
$form['up'] = panels_add_button('go-up.png', 'up', t('Move item up'));
$form['down'] = panels_add_button('go-down.png', 'down', t('Move item down'));
}
// if ($count > 2) {
// $form['bottom'] = panels_add_button('go-bottom.png', 'bottom', t('Move item to bottom'));
// }
// if ($count > 1) {
// $form['spacer'] = panels_add_blank();
// }
return $form;
}
/**
* move an item in an array to the top
*/
function panels_move_top(&$array, &$position) {
$value = $array[$position];
unset($array[$position]);
array_unshift($array, $value);
// reindex the array now
$array = array_values($array);
}
/**
* move an item in an array to the bottom
*/
function panels_move_bottom(&$array, &$position) {
$value = $array[$position];
unset($array[$position]);
$array[] = $value;
// reindex the array now
$array = array_values($array);
}
/**
* move an item in an array up one position
*/
function panels_move_up(&$array, &$position) {
$value = $array[$position];
$array[$position] = $array[$position - 1];
$array[$position - 1] = $value;
}
/**
* move an item in an array up one position
*/
function panels_move_down(&$array, &$position) {
$value = $array[$position];
$array[$position] = $array[$position + 1];
$array[$position + 1] = $value;
}
/**
* Remove an item from an array
*/
function panels_move_delete(&$array, &$position) {
unset($array[$position]);
// reindex the array now
$array = array_values($array);
}
// ---------------------------------------------------------------------------
// panels database functions
function panels_load_panels($did) {
$panels = db_fetch_object(db_query("SELECT * FROM {panels_info} WHERE did = %d", $did));
if (!$panels) {
return NULL;
}
$panels->content = array();
$panels->access = ($panels->access ? explode(', ', $panels->access) : array());
$result = db_query("SELECT * FROM {panels_area} WHERE did = %d ORDER BY area, position", $did);
while ($area = db_fetch_object($result)) {
$area->configuration = unserialize($area->configuration);
$panels->content[$area->area][] = $area;
}
return $panels;
}
function panels_save_panels($panels) {
$panels->access = implode(', ', $panels->access);
if ($panels->did) {
db_query("UPDATE {panels_info} SET title = '%s', access = '%s', path = '%s', css_id = '%s', layout = '%s' WHERE did = %d", $panels->title, $panels->access, $panels->path, $panels->css_id, $panels->layout, $panels->did);
db_query("DELETE FROM {panels_area} WHERE did = %d", $panels->did);
}
else {
$panels->did = db_next_id("{panels_info_id}");
// Put this in the form so modules can utilize the did of a new panel.
$GLOBALS['form_values']['did'] = $panels->did;
db_query("INSERT INTO {panels_info} (did, title, access, path, css_id, layout) VALUES (%d, '%s', '%s', '%s', '%s', '%s')", $panels->did, $panels->title, $panels->access, $panels->path, $panels->css_id, $panels->layout);
}
foreach ($panels->content as $area => $info) {
foreach ($info as $position => $block) {
if (is_numeric($position)) { // don't save some random form stuff that may've been here.
$block = (object) $block;
db_query("INSERT INTO {panels_area} (did, area, type, configuration, position) VALUES(%d, '%s', '%s', '%s', %d)", $panels->did, $area, $block->type, serialize($block->configuration), $position);
}
}
}
menu_rebuild();
}
function panels_delete_panels($panels) {
db_query("DELETE FROM {panels_info} WHERE did = %d", $panels->did);
db_query("DELETE FROM {panels_area} WHERE did = %d", $panels->did);
menu_rebuild();
}
// ---------------------------------------------------------------------------
// panels page
/**
* Returns TRUE if the current page contains a panels layout.
* This can be checked in a theme to hide existing sidebars on panel pages, for example.
*
* @param $set (optional) used internally to set the page status
*/
function panels_is_panels_page($set_panels = NULL) {
static $is_panels;
if ($set_panels == TRUE) {
$is_panels = $set_panels;
}
return $is_panels;
}
function panels_panels_page($did) {
$panels = panels_load_panels($did);
if (!$panels) {
return drupal_not_found();
}
$layouts = panels_get_layouts();
$layout = $layouts[$panels->layout];
$layout['css_id'] = $panels->css_id;
if (!$layout) {
watchdog('panels', t('Unable to find requested layout %s', array('%s' => check_plain($panels->layout))));
return drupal_not_found();
}
panels_is_panels_page(TRUE);
$content_types = panels_get_content_types();
foreach ($panels->content as $location => $list) {
foreach ($list as $area) {
$function = $content_types[$area->type]['callback'];
if (function_exists($function)) {
$content[$area->area] .= $function($area->configuration);
}
}
}
$output = panels_get_layout($layout, $content);
drupal_set_title(filter_xss_admin($panels->title));
return $output;
}
function panels_get_layout($layout, $content) {
$output = theme($layout['theme'], check_plain($layout['css_id']), $content);
if ($output) {
if (file_exists(path_to_theme() . '/' . $layout['css'])) {
drupal_add_css(path_to_theme() . '/' . $layout['css']);
}
else {
drupal_add_css(drupal_get_path('module', $layout['module']) . '/' . $layout['css']);
}
}
return $output;
}
/**
* For external use: Given a layout ID and a $content array, return the
* finished layout.
*/
function panels_print_layout($id, $content) {
$layouts = panels_get_layouts();
$layout = $layouts[$id];
if (!$layout) {
return;
}
return panels_get_layout($layout, $content);
}
// ---------------------------------------------------------------------------
// panels data loading
function panels_load_includes($directory, $callback) {
// Load all our module 'on behalfs'.
$path = drupal_get_path('module', 'panels') . '/' . $directory;
$files = drupal_system_listing('.inc$', $path, 'name', 0);
foreach($files as $file) {
require_once('./' . $file->filename);
}
$output = module_invoke_all($callback);
foreach ($files as $file) {
$function = 'panels_' . $file->name . '_' . $callback;
if (function_exists($function)) {
$result = $function();
if (isset($result) && is_array($result)) {
$output = array_merge($output, $result);
}
}
}
return $output;
}
function panels_get_layouts() {
static $layout = NULL;
if (!$layout) {
$layouts = panels_load_includes('layouts', 'panels_layouts');
}
return $layouts;
}
function panels_get_content_types() {
static $layout = NULL;
if (!$layout) {
$layouts = panels_load_includes('content_types', 'panels_content_types');
}
return $layouts;
}
/**
* Helper function for autocompletion of node titles.
* This is mostly stolen from clipper.
*/
function panels_node_autocomplete($string) {
if ($string != '') { // if there are node_types passed, we'll use those in a MySQL IN query.
$result = db_query_range(db_rewrite_sql('SELECT n.title, u.name FROM {node} n INNER JOIN {users} u ON u.uid = n.uid WHERE LOWER(title) LIKE LOWER("%%%s%%")'), $string, 0, 10);
$prefix = count($array) ? implode(', ', $array) .', ' : '';
$matches = array();
while ($node = db_fetch_object($result)) {
$n = $node->title;
// Commas and quotes in terms are special cases, so encode 'em.
if (preg_match('/,/', $node->title) || preg_match('/"/', $node->title)) {
$n = '"'. preg_replace('/"/', '""', $node->title) .'"';
}
$matches[$prefix . $n] = ''. check_plain($node->title) .'('. t('by %user', array('%user' => check_plain($node->name))) .')';
}
print drupal_to_js($matches);
exit();
}
}