' . t('Grab the title bar of any pane to drag it to another panel. Click the add pane button in any panel to add more content. Click the configure button on any pane to re-configure that pane.') . '
');
$form['did'] = array(
'#type' => 'value',
'#value' => $display->did,
);
$form['type'] = array(
'#type' => 'value',
'#value' => $content_type_id,
);
$form['panel'] = array(
'#type' => 'value',
'#value' => $panel_id,
);
$form['configuration'] = $conf;
$form['configuration']['#tree'] = TRUE;
if (user_access('administer pane visibility')) {
$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['access'] = array(
'#type' => 'checkboxes',
'#title' => t('Access'),
'#default_value' => array(),
'#options' => $rids,
'#description' => t('Only the checked roles will be able to see this pane; if no roles are checked, access will not be restricted.'),
);
}
else {
$form['access'] = array(
'#type' => 'value',
'#value' => array(),
);
}
$form['end_form'] = array('#value' => '
');
$form['next'] = array(
'#type' => 'submit',
'#value' => t('Add pane'),
);
$return->type = 'display';
$return->title = t('Configure !s', array('!s' => $title));
panels_set('return', $return);
return $form;
}
function panels_add_content_config_form_validate($form_id, $form_values, $form) {
panels_ct_validate_add_form($form_values['type'], $form['configuration'], $form_values['configuration']);
}
function panels_add_content_config_form_submit($form_id, $form) {
$cache = panels_cache_get($form['did']);
// keep an incrementing counter, add the data to the display.
// This counter is so that we can add this pane to the cache but not
// make it real until the final 'Save' is clicked.
$pid = ++$cache->highest;
$form['pid'] = "new-$pid";
$form['access'] = array_keys(array_filter($form['access']));
panels_ct_submit_add_form($form['type'], $form['configuration']);
$cache->display->content[$form['pid']] = (object) $form;
$cache->display->panels[$form['panel']][] = $form['pid'];
// drupal_set_message('
' . var_export($cache->display, true) . '
');
panels_cache_set($form['did'], $cache);
// Set info for the javascript so it knows where to put the new pane.
// FIXME: Note that ->area appears to be a holdover -- it should be ->panel now.
$return->type = 'add';
$return->area = $form['panel'];
$return->id = $form['pid'];
// we need to fake the buttons a little.
$buttons['configure'] = panels_add_button('configure.gif', t('Configure'), t('Configure this pane'), 'pane-configure');
$buttons['configure']['#parents'] = array('button', $form['pid'], 'configure');
$buttons['delete'] = panels_add_button('close.gif', t('Delete'), t('Remove this pane'), 'pane-delete');
$buttons['delete']['#parents'] = array('button', $form['pid'], 'delete');
$buttons = form_builder('dummy', $buttons);
// Render the new pane for the javascript.
$return->output = panels_show_pane($cache->display->content[$form['pid']], NULL, drupal_render($buttons));
panels_set('return', $return);
return FALSE;
}
function panels_ajax_configure($did = NULL, $pid = NULL) {
if ((is_numeric($did) || $did == 'new') && $cache = panels_cache_get($did)) {
$text = drupal_get_form('panels_edit_pane_config_form', $cache->display, $pid);
$output = panels_get('return');
$output->output = $text;
}
panels_ajax_render($output);
}
function panels_edit_pane_config_form($display, $pid = NULL) {
$cache = panels_cache_get($display->did);
if ($pid === NULL) {
if (isset($cache->pid)) {
$pid = $cache->pid;
}
else {
return array();
}
}
else {
$cache->pid = $pid;
panels_cache_set($display->did, $cache);
}
$pane = $display->content[$pid];
$form['start_form'] = array('#value' => '
');
$form['did'] = array(
'#type' => 'value',
'#value' => $display->did,
);
$form['pid'] = array(
'#type' => 'value',
'#value' => $pid,
);
$form['type'] = array(
'#type' => 'value',
'#value' => $content_type_id,
);
$return->title = t('Configure !s', array('!s' => panels_get_pane_title($pane)));
$form['configuration'] = panels_get_pane_edit_form($pane, array('configuration'));
$form['configuration']['#tree'] = true;
if (user_access('administer pane visibility')) {
$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['access'] = array(
'#type' => 'checkboxes',
'#title' => t('Access'),
'#default_value' => $pane->access,
'#options' => $rids,
'#description' => t('Only the checked roles will be able to see this pane; if no roles are checked, access will not be restricted.'),
);
}
else {
$form['access'] = array(
'#type' => 'value',
'#value' => $pane->access,
);
}
$return->type = 'display';
panels_set('return', $return);
$form['end_form'] = array('#value' => '
');
$form['next'] = array(
'#type' => 'submit',
'#value' => t('Save'),
);
return $form;
}
function panels_edit_pane_config_form_validate($form_id, $form_values, $form) {
panels_ct_validate_edit_form($form_values['type'], $form['configuration'], $form_values['configuration']);
}
function panels_edit_pane_config_form_submit($form_id, $form) {
// save the new configuration.
$cache = panels_cache_get($form['did']);
panels_ct_submit_edit_form($form['type'], $form['configuration']);
$cache->display->content[$form['pid']]->access = array_keys(array_filter($form['access']));
$cache->display->content[$form['pid']]->configuration = $form['configuration'];
panels_cache_set($form['did'], $cache);
$return->type = 'replace';
$return->id = $form['pid'];
$block = panels_get_pane_content($cache->display->content[$form['pid']]);
if (!$block->subject) {
$block->subject = t('No title');
}
$return->output = theme('panels_pane_collapsible', $block, $cache->display);
panels_set('return', $return);
return FALSE;
}
function panels_edit_submit_subform($display) {
// Check forms to make sure only valid forms can be rendered this way.
$valid_forms = array('panels_add_content_config_form', 'panels_edit_pane_config_form');
$form_id = $_POST['form_id'];
if (!in_array($form_id, $valid_forms)) {
return panels_ajax_render();
}
$output = drupal_get_form($form_id, $display);
$next = panels_get('next');
if ($next) {
$output = drupal_get_form($next['form'], $display, $next['data']);
$return = panels_get('return');
if (!$return->output) {
$return->output = $output;
}
}
else {
if (!($return = panels_get('return'))) {
$return->type = 'display';
$return->output = $output;
}
else if ($return->type == 'display' && !$return->output) {
$return->output = $output;
}
}
if ($return->type == 'display') {
$return->output = theme('status_messages') . $return->output;
}
return $return;
}
// End of ajax functions
// ---------------------
// ---------------------------------------------------------------------------
// panels database functions
/**
* Forms the basis of a panel display and provides a constructur to ensure
* that the display always has a valid context.
*/
class panels_display {
var $args = array();
var $incoming_content = NULL;
var $context = NULL;
var $css_id = NULL;
function panels_display($context = NULL) {
if (!isset($context)) {
$this->context = new panels_context();
}
}
}
/**
* Creates a new display with the given context, so that users don't
* have to mess with classes if they don't want to.
*/
function panels_new_display($context = NULL) {
$display = new panels_display($context);
$display->did = 'new';
return $display;
}
/**
* Load a display from the database and set a context.
*/
function panels_load_display($did, $context = NULL) {
$display = new panels_display($context);
$obj = db_fetch_array(db_query("SELECT * FROM {panels_display} WHERE did = %d", $did));
if (!$obj) {
return NULL;
}
foreach ($obj as $key => $value) {
$display->$key = $value;
}
if (!$display) {
return NULL;
}
$result = db_query("SELECT * FROM {panels_pane} WHERE did = %d ORDER BY panel, position", $did);
$display->panels = $display->content = array();
while ($pane = db_fetch_object($result)) {
$pane->configuration = unserialize($pane->configuration);
$pane->access = ($pane->access ? explode(', ', $pane->access) : array());
$display->panels[$pane->panel][] = $pane->pid;
$display->content[$pane->pid] = $pane;
}
return $display;
}
/**
* Save a display.
*/
function panels_save_display(&$display) {
if ($display->did && $display->did != 'new') {
db_query("UPDATE {panels_display} SET layout = '%s', layout_settings = '%s' WHERE did = %d", $display->layout, $display->layout_settings, $display->did);
db_query("DELETE FROM {panels_pane} WHERE did = %d", $display->did);
}
else {
$display->did = db_next_id("{panels_display}_id");
db_query("INSERT INTO {panels_display} (did, layout, layout_settings) VALUES (%d, '%s', '%s')", $display->did, $display->layout, $display->layout_settings);
}
// update all the panes
foreach ((array) $display->panels as $id => $panes) {
$position = 0;
$new_panes = array();
foreach ((array) $panes as $pid) {
$pane = $display->content[$pid];
$pane->position = $position++;
if (!is_numeric($pid)) {
unset($display->content[$pid]);
$pane->pid = db_next_id("{panels_pane}_pid");
}
db_query("INSERT INTO {panels_pane} (pid, did, panel, type, configuration, access, position) VALUES (%d, %d, '%s', '%s', '%s', '%s', %d)", $pane->pid, $display->did, $pane->panel, $pane->type, serialize($pane->configuration), !empty($pane->access) ? implode(', ', $pane->access) : '', $pane->position);
// and put it back so our pids and positions can be used
$display->content[$pid] = $pane;
$new_panes[] = $pid;
}
$display->panels[$id] = $panes;
}
return $display; // to be nice, even tho we have a reference.
}
/**
* Delete a display
*/
function panels_delete_display($display) {
db_query("DELETE FROM {panels_display} WHERE did = %d", $display->did);
db_query("DELETE FROM {panels_pane} WHERE did = %d", $display->did);
}
/**
* For external use: Given a layout ID and a $content array, return the
* panel display. The content array is filled in based upon the content
* available in the layout. If it's a two column with a content
* array defined like array('left' => t('Left side'), 'right' =>
* t('Right side')), then the $content array should be array('left' =>
* $output_left, 'right' => $output_right)
*/
function panels_print_layout($id, $content) {
$layout = panels_get_layout($id);
if (!$layout) {
return;
}
return panels_render_layout($layout, $content);
}
/**
* Given a full layout structure and a content array, render a panel display.
*/
function panels_render_layout($layout, $content, $css_id = NULL, $settings = array()) {
$output = theme($layout['theme'], check_plain($css_id), $content, $settings);
if ($output) {
if (file_exists(path_to_theme() . '/' . $layout['css'])) {
drupal_add_css(path_to_theme() . '/' . $layout['css']);
}
else {
drupal_add_css(panels_get_path($layout['css'], false, $layout['module']));
}
}
return $output;
}
class panels_context {
var $type = NULL;
var $data = NULL;
function panels_context($type = 'none', $data = NULL) {
$this->type = $type;
$this->data = $data;
}
}
/**
* Clean up a display and make sure it has some required information if
* it doesn't already exist. Currently we wrequire a context, an incoming
* content and a css_id.
*/
function panels_sanitize_display(&$display) {
if (!isset($display->args)) {
$display->args = array();
}
if (!isset($display->incoming_content)) {
$display->incoming_content = NULL;
}
if (!isset($display->context)) {
$display->context = new panels_context();
}
if (!isset($display->css_id)) {
$display->css_id = NULL;
}
}
/**
* Render a display by loading the content into an appropriate
* array and then passing through to panels_render_layout.
*
* if $incoming_content is NULL, default content will be applied. Use
* an empty string to indicate no content.
*/
function panels_render_display(&$display) {
$layout = panels_get_layout($display->layout);
if (!$layout) {
return NULL;
}
panels_sanitize_display($display);
foreach ($display->content as $pid => $pane) {
if ($result = panels_get_pane_content($pane, $display->args, $display->context, $display->incoming_content)) {
if (!empty($content[$pane->panel])) {
$content[$pane->panel] .= theme('panels_separator', $display);
}
if (panels_pane_access($pane)) {
$content[$pane->panel] .= theme('panels_pane', $result, $display);
}
}
}
$output = panels_render_layout($layout, $content, $display->css_id, unserialize($display->layout_settings));
return $output;
}
// ---------------------------------------------------------------------------
// panels data loading
function panels_load_includes($directory, $callback, $file = NULL) {
// Load all our module 'on behalfs'.
$path = panels_get_path($directory);
$files = drupal_system_listing("$file" . '.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_layout($layout) {
return panels_get_layouts($layout);
}
function panels_get_layouts($layout = NULL) {
static $layouts = array();
static $all_layouts = FALSE;
if (!$all_layouts) {
if (!$layout) {
$layouts = panels_load_includes('layouts', 'panels_layouts');
$all_layouts = TRUE;
}
else if (!array_key_exists($layout, $layouts)) {
$temp_layouts = panels_load_includes('layouts', 'panels_layouts', $layout);
if (isset($temp_layouts[$layout])) {
$layouts[$layout] = $temp_layouts[$layout];
}
}
}
if ($layout) {
return isset($layouts[$layout]) ? $layouts[$layout] : NULL;
}
return $layouts;
}
function panels_get_content_type($content_type) {
return panels_get_content_types($content_type);
}
function panels_get_content_types($content_type = NULL) {
static $content_types = array();
static $all_content_types = FALSE;
if (!$all_content_types) {
if (!$content_type) {
$content_types = panels_load_includes('content_types', 'panels_content_types');
$all_content_types = TRUE;
}
else if (!array_key_exists($content_type, $content_types)) {
$temp_content_types = panels_load_includes('content_types', 'panels_content_types', $content_type);
if (isset($temp_content_types[$content_type])) {
$content_types[$content_type] = $temp_content_types[$content_type];
}
}
}
if ($content_type) {
return isset($content_types[$content_type]) ? $content_types[$content_type] : NULL;
}
return $content_types;
}
/**
* Includes required JavaScript libraries:
* jQuery, iutil, idrag, idrop, isortables
* In addition to panels.js.
*/
function _panels_js_files() {
// while we don't use this directly some of our forms do.
drupal_add_js('misc/collapse.js');
drupal_add_js('misc/autocomplete.js');
drupal_add_js(panels_get_path('js/lib/dimensions.js'));
drupal_add_js(panels_get_path('js/lib/mc.js'));
drupal_add_js(panels_get_path('js/lib/form.js'));
drupal_add_js(array('panelsAjaxURL' => url('panels/ajax', NULL, NULL, TRUE)), 'setting');
drupal_add_js(panels_get_path('js/display_editor.js'));
drupal_add_css(panels_get_path('css/panels_dnd.css'));
drupal_add_css(panels_get_path('css/panels_admin.css'));
}
// ---------------------------------------------------------------------------
// Panels theming functions
function theme_panels_dnd($content) {
$output = "