* Core functionality for the Panels engine.
*/
-/**
- * Error code bitmask used for identifying argument behavior at runtime.
- */
-define('PANELS_ARG_IS_BAD', 1);
-define('PANELS_ARG_USE_FALLBACK', 1 << 1);
+define('PANELS_REQUIRED_CTOOLS_API', '1.1.1');
+
+define('PANELS_TITLE_FIXED', 0); // Hide title use to be true/false. So false remains old behavior.
+define('PANELS_TITLE_NONE', 1); // And true meant no title.
+define('PANELS_TITLE_PANE', 2); // And this is the new behavior, where the title field will pick from a pane.
/**
* Returns the API version of Panels. This didn't exist in 1.
* @return An array with the major and minor versions
*/
function panels_api_version() {
- return array(2, 0);
+ return array(3, 0);
}
+/**
+ * Implementation of hook_theme()
+ */
function panels_theme() {
+ // Safety: go away if CTools is not at an appropriate version.
+ if (!module_invoke('ctools', 'api_version', PANELS_REQUIRED_CTOOLS_API)) {
+ return array();
+ }
+
$theme = array();
$theme['panels_layout_link'] = array(
'arguments' => array('title', 'id', 'image', 'link'),
$theme['panels_layout_icon'] = array(
'arguments' => array('id', 'image', 'title' => NULL),
);
- $theme['panels_imagebutton'] = array(
- 'arguments' => array('element'),
- );
$theme['panels_edit_display_form'] = array(
'arguments' => array('form'),
'file' => 'includes/display-edit.inc',
'file' => 'includes/display-edit.inc',
);
$theme['panels_pane'] = array(
- 'arguments' => array('content', 'pane', 'display'),
- 'file' => 'includes/display-render.inc',
+ 'arguments' => array('output' => array(), 'pane' => array(), 'display' => array()),
+ 'path' => drupal_get_path('module', 'panels') . '/templates',
+ 'template' => 'panels-pane',
);
$theme['panels_common_content_list'] = array(
'arguments' => array('display'),
'file' => 'includes/common.inc',
);
- $theme['panels_common_context_list'] = array(
- 'arguments' => array('object'),
- 'file' => 'includes/common.inc',
- );
- $theme['panels_common_context_item_form'] = array(
- 'arguments' => array('form'),
- 'file' => 'includes/common.inc',
+ $theme['panels_render_display_form'] = array(
+ 'arguments' => array('form' => NULL),
);
- $theme['panels_common_context_item_row'] = array(
- 'arguments' => array('type', 'form', 'position', 'count', 'with_tr' => TRUE),
- 'file' => 'includes/common.inc',
- );
- $theme['panels_dnd'] = array(
- 'arguments' => array('content'),
- 'file' => 'includes/display-edit.inc',
- 'function' => 'theme_panels_dnd',
- );
- $theme['panels_panel_dnd'] = array(
- 'arguments' => array('content', 'area', 'label', 'footer'),
- 'file' => 'includes/display-edit.inc',
- 'function' => 'theme_panels_panel_dnd',
- );
- $theme['panels_pane_dnd'] = array(
- 'arguments' => array('block', 'id', 'label', 'left_buttons' => NULL, 'buttons' => NULL),
- 'file' => 'includes/display-edit.inc',
- );
- $theme['panels_pane_collapsible'] = array(
- 'arguments' => array('block'),
- 'file' => 'includes/display-edit.inc',
- );
- $theme['profile_fields_pane'] = array(
- 'arguments' => array('category' => NULL, 'vars' => NULL),
- 'template' => 'content_types/profile_fields_pane',
+
+ $theme['panels_dashboard'] = array(
+ 'arguments' => array(),
+ 'path' => drupal_get_path('module', 'panels') . '/templates',
+ 'file' => '../includes/callbacks.inc',
+ 'template' => 'panels-dashboard',
);
// Register layout and style themes on behalf of all of these items.
// to even know what the theme function is, so files will be auto included.
$layouts = panels_get_layouts();
foreach ($layouts as $name => $data) {
- if (!empty($data['theme'])) {
- $theme[$data['theme']] = array(
- 'arguments' => array('css_id' => NULL, 'content' => NULL, 'settings' => NULL),
- 'path' => $data['path'],
- );
-
- // if no theme function exists, assume template.
- if (!function_exists("theme_$data[theme]")) {
- $theme[$data['theme']]['template'] = str_replace('_', '-', $data['theme']);
+ foreach (array('theme', 'admin theme') as $callback) {
+ if (!empty($data[$callback])) {
+ $theme[$data[$callback]] = array(
+ 'arguments' => array('css_id' => NULL, 'content' => NULL, 'settings' => NULL, 'display' => NULL),
+ 'path' => $data['path'],
+ );
+
+ // if no theme function exists, assume template.
+ if (!function_exists("theme_$data[theme]")) {
+ $theme[$data[$callback]]['template'] = str_replace('_', '-', $data[$callback]);
+ $theme[$data[$callback]]['file'] = $data['file']; // for preprocess.
+ }
}
}
}
foreach ($styles as $name => $data) {
if (!empty($data['render pane'])) {
$theme[$data['render pane']] = array(
- 'arguments' => array('content' => NULL, 'pane' => NULL, 'display' => NULL),
+ 'arguments' => array('output' => NULL, 'pane' => NULL, 'display' => NULL),
);
}
if (!empty($data['render panel'])) {
);
}
+ if (!empty($data['hook theme'])) {
+ if (is_array($data['hook theme'])) {
+ $theme += $data['hook theme'];
+ }
+ else if (function_exists($data['hook theme'])) {
+ $data['hook theme']($theme, $data);
+ }
+ }
}
return $theme;
* Implementation of hook_menu
*/
function panels_menu() {
+ // Safety: go away if CTools is not at an appropriate version.
+ if (!module_invoke('ctools', 'api_version', PANELS_REQUIRED_CTOOLS_API)) {
+ return array();
+ }
$items = array();
// Provide some common options to reduce code repetition.
'access arguments' => array('access content'),
'type' => MENU_CALLBACK,
'file' => 'includes/display-edit.inc',
+ 'page arguments' => array(3),
);
- $items['panels/ajax/add-pane'] = array(
+ $items['panels/ajax/add-pane/%panels_edit_cache'] = array(
'page callback' => 'panels_ajax_add_pane_choose',
) + $base;
- $items['panels/ajax/add-pane-config'] = array(
+ $items['panels/ajax/add-pane-config/%panels_edit_cache'] = array(
'page callback' => 'panels_ajax_add_pane_config',
) + $base;
- $items['panels/ajax/configure'] = array(
+ $items['panels/ajax/configure/%panels_edit_cache'] = array(
'page callback' => 'panels_ajax_configure_pane',
) + $base;
- $items['panels/ajax/show'] = array(
+ $items['panels/ajax/show/%panels_edit_cache'] = array(
'page callback' => 'panels_ajax_toggle_shown',
- 'page arguments' => array('show'),
+ 'page arguments' => array('show', 3),
) + $base;
- $items['panels/ajax/hide'] = array(
+ $items['panels/ajax/hide/%panels_edit_cache'] = array(
'page callback' => 'panels_ajax_toggle_shown',
- 'page arguments' => array('hide'),
+ 'page arguments' => array('hide', 3),
) + $base;
- $items['panels/ajax/cache-method'] = array(
+ $items['panels/ajax/cache-method/%panels_edit_cache'] = array(
'page callback' => 'panels_ajax_cache_method',
) + $base;
- $items['panels/ajax/cache-settings'] = array(
+ $items['panels/ajax/cache-settings/%panels_edit_cache'] = array(
'page callback' => 'panels_ajax_cache_settings',
) + $base;
-
- // For panel settings on the edit layout settings page
- $items['panels/ajax/style-settings'] = array(
+ $items['panels/ajax/display-settings/%panels_edit_cache'] = array(
+ 'page callback' => 'panels_ajax_display_settings',
+ ) + $base;
+ $items['panels/ajax/panel-title/%panels_edit_cache'] = array(
+ 'page callback' => 'panels_ajax_set_display_title',
+ ) + $base;
+ $items['panels/ajax/style-type/%/%panels_edit_cache'] = array(
+ 'page callback' => 'panels_ajax_style_type',
+ 'page arguments' => array(3, 4),
+ ) + $base;
+ $items['panels/ajax/style-settings/%/%panels_edit_cache'] = array(
'page callback' => 'panels_ajax_style_settings',
- 'file' => 'includes/display-layout-settings.inc',
+ 'page arguments' => array(3, 4),
) + $base;
-
- // Non-display editor callbacks
- $items['panels/node/autocomplete'] = array(
- 'title' => 'Autocomplete node',
- 'page callback' => 'panels_node_autocomplete',
- 'file' => 'includes/callbacks.inc',
+ $items['panels/ajax/pane-css/%panels_edit_cache'] = array(
+ 'page callback' => 'panels_ajax_configure_pane_css',
) + $base;
-
- // For context add/configure calls in common-context.inc
- $items['panels/ajax/context-add'] = array(
- 'page callback' => 'panels_ajax_context_item_add',
- 'file' => 'includes/common-context.inc',
+ $items['panels/ajax/access-settings/%panels_edit_cache'] = array(
+ 'page callback' => 'panels_ajax_configure_access_settings',
) + $base;
- $items['panels/ajax/context-configure'] = array(
- 'page callback' => 'panels_ajax_context_item_edit',
- 'file' => 'includes/common-context.inc',
+ $items['panels/ajax/access-test/%panels_edit_cache'] = array(
+ 'page callback' => 'panels_ajax_configure_access_test',
) + $base;
- $items['panels/ajax/context-delete'] = array(
- 'page callback' => 'panels_ajax_context_item_delete',
- 'file' => 'includes/common-context.inc',
+ $items['panels/ajax/access-add/%panels_edit_cache'] = array(
+ 'page callback' => 'panels_ajax_add_access_test',
+ ) + $base;
+ $items['panels/ajax/preview/%panels_edit_cache'] = array(
+ 'page callback' => 'panels_ajax_preview',
) + $base;
+ $admin_base = array(
+ 'file' => 'includes/callbacks.inc',
+ 'access arguments' => array('use panels dashboard'),
+ );
// Provide a nice location for a panels admin panel.
- $items['admin/panels'] = array(
+ $items['admin/build/panels'] = array(
'title' => 'Panels',
- 'access arguments' => array('access administration pages'),
'page callback' => 'panels_admin_page',
- 'file' => 'includes/callbacks.inc',
'description' => 'Administer items related to the Panels module.',
- );
+ ) + $admin_base;
+
+ $items['admin/build/panels/dashboard'] = array(
+ 'title' => 'Dashboard',
+ 'page callback' => 'panels_admin_page',
+ 'type' => MENU_DEFAULT_LOCAL_TASK,
+ 'weight' => -10,
+ ) + $admin_base;
+
+ $items['admin/build/panels/settings'] = array(
+ 'title' => 'Settings',
+ 'page callback' => 'drupal_get_form',
+ 'page arguments' => array('panels_admin_settings_page'),
+ 'type' => MENU_LOCAL_TASK,
+ ) + $admin_base;
+
+ $items['admin/build/panels/settings/general'] = array(
+ 'title' => 'General',
+ 'page callback' => 'drupal_get_form',
+ 'page arguments' => array('panels_admin_settings_page'),
+ 'access arguments' => array('administer page manager'),
+ 'type' => MENU_DEFAULT_LOCAL_TASK,
+ 'weight' => -10,
+ ) + $admin_base;
+
+ if (module_exists('page_manager')) {
+ $items['admin/build/panels/settings/panel-page'] = array(
+ 'title' => 'Panel pages',
+ 'page callback' => 'panels_admin_panel_context_page',
+ 'type' => MENU_LOCAL_TASK,
+ 'weight' => -10,
+ ) + $admin_base;
+ }
+
+ panels_load_include('plugins');
+ $layouts = panels_get_layouts();
+ foreach ($layouts as $name => $data) {
+ if (!empty($data['hook menu'])) {
+ if (is_array($data['hook menu'])) {
+ $items += $data['hook menu'];
+ }
+ else if (function_exists($data['hook menu'])) {
+ $data['hook menu']($items, $data);
+ }
+ }
+ }
return $items;
}
/**
+ * Menu loader function to load a cache item for Panels AJAX.
+ *
+ * This load all of the includes needed to perform AJAX, and loads the
+ * cache object and makes sure it is valid.
+ */
+function panels_edit_cache_load($cache_key) {
+ panels_load_include('display-edit');
+ panels_load_include('plugins');
+ ctools_include('ajax');
+ ctools_include('modal');
+ ctools_include('context');
+
+ return panels_edit_cache_get($cache_key);
+}
+
+/**
* Implementation of hook_init()
*/
function panels_init() {
+ // Safety: go away if CTools is not at an appropriate version.
+ if (!module_invoke('ctools', 'api_version', PANELS_REQUIRED_CTOOLS_API)) {
+ return;
+ }
+
drupal_add_css(panels_get_path('css/panels.css'));
drupal_add_js(panels_get_path('js/panels.js'));
}
'administer pane visibility',
'administer pane access',
'administer advanced pane settings',
- 'use panels caching features'
+ 'use panels caching features',
+ 'use panels dashboard',
);
}
* Get an object from cache.
*/
function panels_cache_get($obj, $did, $skip_cache = FALSE) {
- static $cache = array();
- $key = "$obj:$did";
- if ($skip_cache) {
- unset($cache[$key]);
- }
-
- if (!array_key_exists($key, $cache)) {
- $data = db_fetch_object(db_query("SELECT * FROM {panels_object_cache} WHERE sid = '%s' AND obj = '%s' AND did = %d", session_id(), $obj, $did));
- if ($data) {
- $cache[$key] = unserialize($data->data);
- }
- }
- return isset($cache[$key]) ? $cache[$key] : NULL;
+ ctools_include('object-cache');
+ // we often store contexts in cache, so let's just make sure we can load
+ // them.
+ ctools_include('context');
+ return ctools_object_cache_get($obj, 'panels_display:' . $did, $skip_cache);
}
/**
* Save the edited object into the cache.
*/
function panels_cache_set($obj, $did, $cache) {
- panels_cache_clear($obj, $did);
- db_query("INSERT INTO {panels_object_cache} (sid, obj, did, data, timestamp) VALUES ('%s', '%s', %d, '%s', %d)", session_id(), $obj, $did, serialize($cache), time());
+ ctools_include('object-cache');
+ return ctools_object_cache_set($obj, 'panels_display:' . $did, $cache);
}
/**
* Clear a object from the cache; used if the editing is aborted.
*/
function panels_cache_clear($obj, $did) {
- db_query("DELETE FROM {panels_object_cache} WHERE sid = '%s' AND obj = '%s' AND did = %d", session_id(), $obj, $did);
-}
-
-/**
- * Implementation of hook_cron. Clean up old caches.
- */
-function panels_cron() {
- // delete anything 7 days old or more.
- db_query("DELETE FROM {panels_object_cache} WHERE timestamp < %d", time() - (86400 * 7));
+ ctools_include('object-cache');
+ return ctools_object_cache_clear($obj, 'panels_display:' . $did);
}
// ---------------------------------------------------------------------------
* - If empty($destination) == FALSE: the form will redirect to the URL string
* given in $destination and NO value will be returned.
*/
-function panels_edit($display, $destination = NULL, $content_types = NULL) {
+function panels_edit($display, $destination = NULL, $content_types = NULL, $title = FALSE) {
panels_load_include('display-edit');
- panels_load_include('ajax');
+ ctools_include('ajax');
panels_load_include('plugins');
- return _panels_edit($display, $destination, $content_types);
+ return _panels_edit($display, $destination, $content_types, $title);
}
/**
return _panels_edit_layout($display, $finish, $destination, $allowed_layouts);
}
-/**
- * API entry point for configuring the layout settings for a given display.
- *
- * For all layouts except Flexible, the layout settings form allows the user to select styles,
- * as defined by .inc files in the panels/styles subdirectory, for the panels in their display.
- * For the Flexible layout, the layout settings form allows the user to provide dimensions
- * for their flexible layout in addition to applying styles to panels.
- *
- * @param object $display instanceof panels_display \n
- * A fully loaded panels $display object, as returned from panels_load_display().
- * Merely passing a did is NOT sufficient.
- * @param string $finish
- * A string that will be used for the text of (one of) the form submission button(s). Note that
- * panels will NOT wrap $finish in t() for you, so your caller should make sure to do so. \n
- * The submit behavior of the form is primarily governed by the value of $destination (see
- * below), but is secondarily governed by $finish as follows:
- * -# If $finish != t('Save'), then two #submit buttons will be present: one with the button
- * text t('Save'), and the other with the button text $finish. .
- * - Clicking the 'Save' button will save any changes on the form to the $display object and
- * keep the user on the same editing page.
- * - Clicking the $finish button will also save the $display object, but the user will be
- * redirected to the URL specified in $destination.
- * -# If $finish == t('Save'), then there is only one button, still called t('Save'), but it
- * mimics the behavior of the $finish button above by redirecting the user away from the form.
- * @param mixed $destination
- * Basic usage is a string containing the URL that the form should redirect to upon submission.
- * For a discussion of advanced usages that rely on NULL values for $destination, see the
- * panels_edit() documentation.
- * @param mixed $title
- * The $title variable has three modes of operation:
- * -# If $title == FALSE (the default), then no widget will appear on the panels_edit_layout_settings form
- * allowing the user to select a title, and other means for setting page titles will take precedent. If
- * no other means are used to provide a title, then the title will be hidden when rendering the $display.
- * -# If $title == TRUE, then two widgets will appear on the panels_edit_layout_settings form allowing the
- * user to input a title specific to this $display, as well as a checkbox enabling the user to disable
- * page titles entirely for this $display object.
- * -# If $title == (string), then the behavior is very similar to mode 2, but the widget description
- * on the title textfield will indicate that the $title string will be used as the default page title
- * if none is provided on this form. When utilizing this option, note that the panels API can only
- * provide the data for these values; you must implement the appropriate conditionals to make it true.
- *
- * @return
- * Can return nothing, or a modified $display object, or a redirection string; return values for the
- * panels_edit* family of functions are quite complex. See panels_edit() for detailed discussion.
- * @see panels_edit()
- */
-function panels_edit_layout_settings($display, $finish, $destination = NULL, $title = FALSE) {
- panels_load_include('display-layout-settings');
- panels_load_include('ajax');
- panels_load_include('plugins');
- return _panels_edit_layout_settings($display, $finish, $destination, $title);
-}
-
-
// ---------------------------------------------------------------------------
// panels database functions
var $incoming_content = NULL;
var $css_id = NULL;
var $context = array();
- var $layout_settings = array();
- var $panel_settings = array();
- var $cache = array();
- var $title = '';
- var $hide_title = 0;
- var $layout = '';
+ var $did = 'new';
function add_pane($pane, $location = FALSE) {
$pane->pid = $this->next_new_pid();
$id[] = substr($pid, 4);
}
}
- $next_id = end($id);
+ $next_id = max($id);
return ++$next_id;
}
}
* }@ End of 'defgroup mainapi', although other functions are specifically added later
*/
-function panels_export_pane_across_displays($source_display, &$target_display, $pid, $location = FALSE) {
- $pane = $source_display->clone_pane($pid);
- $target_display->add_pane($pane, $location);
-}
-
/**
* Clean up a display object and add some required information, if missing.
*
* The sanitized display object.
*/
function panels_sanitize_display(&$display) {
+ return;
if (!isset($display->args)) {
$display->args = array();
}
* Creates a new display, setting the ID to our magic new id.
*/
function panels_new_display() {
- $display = new panels_display();
+ ctools_include('export');
+ $display = ctools_export_new_object('panels_display', FALSE);
$display->did = 'new';
return $display;
}
+/**
+ * Create a new pane.
+ *
+ * @todo -- use schema API for some of this?
+ */
function panels_new_pane($type, $subtype) {
- $pane = new stdClass();
+ ctools_include('export');
+ $pane = ctools_export_new_object('panels_pane', FALSE);
$pane->pid = 'new';
$pane->type = $type;
$pane->subtype = $subtype;
- $pane->configuration = array();
- $pane->access = array();
- $pane->shown = TRUE;
- $pane->visibility = '';
return $pane;
}
*
* @return $displays
* An array of displays, keyed by their display dids.
+ *
+ * @todo schema API can drasticly simplify this code.
*/
function panels_load_displays($dids) {
$displays = array();
return $displays;
}
- $result = db_query("SELECT * FROM {panels_display} WHERE did IN (". db_placeholders($dids) .")", $dids);
-
- while ($obj = db_fetch_array($result)) {
- $display = new panels_display();
-
- foreach ($obj as $key => $value) {
- $display->$key = $value;
- // unserialize important bits
- if (in_array($key, array('layout_settings', 'panel_settings', 'cache'))) {
- $display->$key = empty($display->$key) ? array() : unserialize($display->$key);
- }
- }
+ $result = db_query("SELECT * FROM {panels_display} WHERE did IN (" . db_placeholders($dids) . ")", $dids);
- $display->panels = $display->content = array();
-
- $displays[$display->did] = $display;
+ ctools_include('export');
+ while ($obj = db_fetch_object($result)) {
+ $displays[$obj->did] = ctools_export_unpack_object('panels_display', $obj);
+ // Modify the hide_title field to go from a bool to an int if necessary.
}
- foreach (module_implements('panels_layout_content_alter') as $module) {
- $function = $module . '_panels_layout_content_alter';
- $function($content, $layout, $settings);
- }
+ $result = db_query("SELECT * FROM {panels_pane} WHERE did IN (" . db_placeholders($dids) . ") ORDER BY did, panel, position", $dids);
- $result = db_query("SELECT * FROM {panels_pane} WHERE did IN (". db_placeholders($dids) .") ORDER BY did, panel, position", $dids);
-
- while ($pane = db_fetch_object($result)) {
- $pane->configuration = unserialize($pane->configuration);
- $pane->cache = empty($pane->cache) ? array() : unserialize($pane->cache);
- $pane->access = ($pane->access ? explode(', ', $pane->access) : array());
- // Old panels may not have shown property, so enable by default when loading.
- $pane->shown = isset($pane->shown) ? $pane->shown : TRUE;
+ while ($obj = db_fetch_object($result)) {
+ $pane = ctools_export_unpack_object('panels_pane', $obj);
$displays[$pane->did]->panels[$pane->panel][] = $pane->pid;
$displays[$pane->did]->content[$pane->pid] = $pane;
}
+
return $displays;
}
* - $display->incoming_content
*
* While all of these members are defined, $display->context is NEVER defined in the returned $display;
- * it must be set using one of the panels_context_create() functions.
+ * it must be set using one of the ctools_context_create() functions.
*/
function panels_load_display($did) {
$displays = panels_load_displays(array($did));
* @return object $display instanceof panels_display \n
*/
function panels_save_display(&$display) {
- // @todo -- update all this to just use drupal_write_record or something like it.
- if (!empty($display->did) && $display->did != 'new') {
- db_query("UPDATE {panels_display} SET layout = '%s', layout_settings = '%s', panel_settings = '%s', cache = '%s', title = '%s', hide_title = %d WHERE did = %d", $display->layout, serialize($display->layout_settings), serialize($display->panel_settings), serialize($display->cache), $display->title, $display->hide_title, $display->did);
+ $update = (isset($display->did) && is_numeric($display->did)) ? array('did') : array();
+ drupal_write_record('panels_display', $display, $update);
+
+ $pids = array();
+ if ($update) {
// Get a list of all panes currently in the database for this display so we can know if there
// are panes that need to be deleted. (i.e, aren't currently in our list of panes).
$result = db_query("SELECT pid FROM {panels_pane} WHERE did = %d", $display->did);
$pids[$pane->pid] = $pane->pid;
}
}
- else {
- db_query("INSERT INTO {panels_display} (layout, layout_settings, panel_settings, cache, title, hide_title) VALUES ('%s', '%s', '%s', '%s', '%s', %d)", $display->layout, serialize($display->layout_settings), serialize($display->panel_settings), serialize($display->cache), $display->title, $display->hide_title);
- $display->did = db_last_insert_id('panels_display', 'did');
- $pids = array();
- }
// update all the panes
panels_load_include('plugins');
+ ctools_include('content');
- foreach ((array) $display->panels as $id => $panes) {
+ foreach ($display->panels as $id => $panes) {
$position = 0;
$new_panes = array();
foreach ((array) $panes as $pid) {
+ if (!isset($display->content[$pid])) {
+ continue;
+ }
$pane = $display->content[$pid];
- $pane->position = $position++;
-
- // make variables right.
- $type = panels_get_content_type($pane->type);
- $access = isset($pane->access) ? implode(', ', $pane->access) : '';
- $visibility = !empty($type['visibility serialize']) ? serialize($pane->visibility) : $pane->visibility;
- $pane->shown = isset($pane->shown) ? $pane->shown : TRUE;
+ $type = ctools_get_content_type($pane->type);
- if (empty($pane->cache)) {
- $pane->cache = array();
+ $pane->position = $position++;
+ $pane->did = $display->did;
+
+ $old_pid = $pane->pid;
+ drupal_write_record('panels_pane', $pane, is_numeric($pid) ? array('pid') : array());
+
+ if ($pane->pid != $old_pid) {
+ // and put it back so our pids and positions can be used
+ unset($display->content[$id]);
+ $display->content[$pane->pid] = $pane;
+
+ // If the title pane was one of our panes that just got its ID changed,
+ // we need to change it in the database, too.
+ if (isset($display->title_pane) && $display->title_pane == $old_pid) {
+ $display->title_pane = $pane->pid;
+ // Do a simple update query to write it so we don't have to rewrite
+ // the whole record. We can't just save writing the whole record here
+ // because it was needed to get the did. Chicken, egg, more chicken.
+ db_query("UPDATE {panels_display} SET title_pane = %d WHERE did = %d", $pane->pid, $display->did);
+ }
}
- $v = array($display->did, $pane->panel, $pane->type, $pane->subtype, serialize($pane->configuration), serialize($pane->cache), $pane->shown, $access, $visibility, $pane->position);
-
- if (!is_numeric($pid)) {
- unset($display->content[$pid]);
- // doin it this way for readability
- $f = 'did, panel, type, subtype, configuration, cache, shown, access, visibility, position';
- $q = "%d, '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', %d";
-
- db_query("INSERT INTO {panels_pane} ($f) VALUES ($q)", $v);
- $pane->pid = db_last_insert_id('panels_pane', 'pid');
- }
- else {
- $v[] = $pane->pid;
- $f = "did = %d, panel = '%s', type = '%s', subtype = '%s', configuration = '%s', cache = '%s', shown = '%s', access = '%s', visibility = '%s', position = '%d'";
- db_query("UPDATE {panels_pane} SET $f WHERE pid = %d", $v);
- }
- // and put it back so our pids and positions can be used
- $display->content[$pane->pid] = $pane;
+ // re-add this to the list of content for this panel.
$new_panes[] = $pane->pid;
+
+ // Remove this from the list of panes scheduled for deletion.
if (isset($pids[$pane->pid])) {
unset($pids[$pane->pid]);
}
* the same scope as where you eval(), your existing $display variable WILL be overwritten.
*/
function panels_export_display($display, $prefix = '') {
- $output = '';
- $output .= $prefix . '$display = new panels_display()' . ";\n";
- $output .= $prefix . '$display->did = \'new\'' . ";\n";
- // $fields = array('name', 'layout', 'layout_settings', 'panel_settings');
- // TODO 'name' field was removed several months ago, so temporarily removing
- // it from here whil investigations into exactly why it was removed continue
- $fields = array('layout', 'layout_settings', 'panel_settings');
- foreach ($fields as $field) {
- $output .= $prefix . '$display->' . $field . ' = ' . panels_var_export($display->$field, $prefix) . ";\n";
- }
+ ctools_include('export');
+ $output = ctools_export_object('panels_display', $display, $prefix);
+ // Initialize empty properties.
$output .= $prefix . '$display->content = array()' . ";\n";
$output .= $prefix . '$display->panels = array()' . ";\n";
$panels = array();
+ $title_pid = 0;
if (!empty($display->content)) {
$pid_counter = 0;
$region_counters = array();
foreach ($display->content as $pane) {
- $pane->pid = 'new-' . ++$pid_counter;
- $output .= panels_export_pane($pane, $prefix . ' ');
+ $pid = 'new-' . ++$pid_counter;
+ if ($pane->pid == $display->title_pane) {
+ $title_pid = $pid;
+ }
+ $pane->pid = $pid;
+ $output .= ctools_export_object('panels_pane', $pane, $prefix . ' ');
$output .= "$prefix " . '$display->content[\'' . $pane->pid . '\'] = $pane' . ";\n";
if (!isset($region_counters[$pane->panel])) {
$region_counters[$pane->panel] = 0;
$output .= "$prefix " . '$display->panels[\'' . $pane->panel . '\'][' . $region_counters[$pane->panel]++ .'] = \'' . $pane->pid . "';\n";
}
}
- return $output;
-}
-
-function panels_export_pane($pane, $prefix = '') {
- $output = '';
- $output = $prefix . '$pane = new stdClass()' . ";\n";
- $fields = array('pid', 'panel', 'type', 'shown', 'subtype', 'access', 'configuration', 'cache');
- foreach ($fields as $field) {
- $output .= "$prefix " . '$pane->' . $field . ' = ' . panels_var_export($pane->$field, "$prefix ") . ";\n";
- }
- return $output;
-}
-
-function panels_var_export($object, $prefix = '') {
- if (is_array($object) && empty($object)) {
- $output = 'array()';
- }
- else {
- // Remove extra space to match Drupal coding standards.
- $output = str_replace('array (', 'array(', var_export($object, TRUE));
+ $output .= $prefix . '$display->hide_title = ';
+ switch ($display->hide_title) {
+ case PANELS_TITLE_FIXED:
+ $output .= 'PANELS_TITLE_FIXED';
+ break;
+ case PANELS_TITLE_NONE:
+ $output .= 'PANELS_TITLE_NONE';
+ break;
+ case PANELS_TITLE_PANE:
+ $output .= 'PANELS_TITLE_PANE';
+ break;
}
+ $output .= ";\n";
- if ($prefix) {
- $output = str_replace("\n", "\n$prefix", $output);
- }
+ $output .= $prefix . '$display->title_pane =' . " '$title_pid';\n";
return $output;
}
function panels_render_display(&$display) {
panels_load_include('display-render');
panels_load_include('plugins');
+ ctools_include('context');
+
+ if (!empty($display->context)) {
+ if ($form_context = ctools_context_get_form($display->context)) {
+ $form_context->form['#theme'] = 'panels_render_display_form';
+ $form_context->form['#display'] = &$display;
+ $form_context->form['#form_context_id'] = $form_context->id;
+ return drupal_render_form($form_context->form_id, $form_context->form);
+ }
+ }
return _panels_render_display($display);
}
/**
+ * Theme function to render our panel as a form.
+ *
+ * When rendering a display as a form, the entire display needs to be
+ * inside the <form> tag so that the form can be spread across the
+ * panes. This sets up the form system to be the main caller and we
+ * then operate as a theme function of the form.
+ */
+function theme_panels_render_display_form($form) {
+ $form['#children'] = _panels_render_display($form['#display']);
+ drupal_render($form);
+ return theme('form', $form);
+}
+
+/**
* 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
* Print the layout link. Sends out to a theme function.
* @layout
*/
-function panels_print_layout_link($id, $layout, $link) {
+function panels_print_layout_link($id, $layout, $link, $options = array()) {
+ if (isset($options['query']['q'])) {
+ unset($options['query']['q']);
+ }
+
drupal_add_css(panels_get_path('css/panels_admin.css'));
$file = $layout['path'] . '/' . $layout['icon'];
- $image = l(theme('image', $file), $link, array('html' => true));
- $title = l($layout['title'], $link);
+ $image = l(theme('image', $file), $link, array('html' => true) + $options);
+ $title = l($layout['title'], $link, $options);
return theme('panels_layout_link', $title, $id, $image, $link);
}
* Implementation of hook_ctools_plugin_directory() to let the system know
* we implement task and task_handler plugins.
*/
-function panels_ctools_plugin_directory($plugin) {
- if ($plugin == 'tasks' || $plugin == 'task_handlers') {
+function panels_ctools_plugin_directory($module, $plugin) {
+ // Safety: go away if CTools is not at an appropriate version.
+ if (!module_invoke('ctools', 'api_version', PANELS_REQUIRED_CTOOLS_API)) {
+ return;
+ }
+ if ($module == 'page_manager' || $module == 'panels') {
return 'plugins/' . $plugin;
}
}
+
+/**
+ * Inform CTools that the layout plugin can be loaded from themes.
+ */
+function panels_ctools_plugin_layouts() {
+ return array(
+ 'load themes' => TRUE,
+ );
+}
+
+/**
+ * Inform CTools that the style plugin can be loaded from themes.
+ */
+function panels_ctools_plugin_styles() {
+ return array(
+ 'load themes' => TRUE,
+ );
+}
+
+/**
+ * Get the display that is currently being rendered as a page.
+ *
+ * Unlike in previous versions of this, this only returns the display,
+ * not the page itself, because there are a number of different ways
+ * to get to this point. It is hoped that the page data isn't needed
+ * at this point. If it turns out there is, we will do something else to
+ * get that functionality.
+ */
+function panels_get_current_page_display($change = NULL) {
+ static $display = NULL;
+ if ($change) {
+ $display = $change;
+ }
+
+ return $display;
+}
+
+/**
+ * Get display edit cache on behalf of panel context.
+ *
+ * The key is the second half of the key in this form:
+ * panel_context:TASK_NAME:HANDLER_NAME;
+ */
+function panel_context_panels_cache_get($key) {
+ panels_load_include('common');
+ ctools_include('context');
+ ctools_include('context-task-handler');
+ // this loads the panel context inc even if we don't use the plugin.
+ $plugin = page_manager_get_task_handler('panel_context');
+
+ list($task_name, $handler_name) = explode(':', $key, 2);
+ $page = page_manager_get_page_cache($task_name);
+ if (isset($page->display_cache[$handler_name])) {
+ return $page->display_cache[$handler_name];
+ }
+
+ if ($handler_name) {
+ $handler = &$page->handlers[$handler_name];
+ }
+ else {
+ $handler = &$page->new_handler;
+ }
+ $cache = new stdClass();
+
+ $cache->display = &panels_panel_context_get_display($handler);
+ $cache->display->context = ctools_context_handler_get_all_contexts($page->task, $page->subtask, $handler);
+ $cache->display->cache_key = 'panel_context:' . $key;
+ $cache->content_types = panels_common_get_allowed_types('panels_page', $cache->display->context);
+ $cache->display_title = TRUE;
+
+ return $cache;
+}
+
+/**
+ * Store a display edit in progress in the page cache.
+ */
+function panel_context_panels_cache_set($key, $cache) {
+ list($task_name, $handler_name) = explode(':', $key, 2);
+ $page = page_manager_get_page_cache($task_name);
+ $page->display_cache[$handler_name] = $cache;
+ if ($handler_name) {
+ $page->handlers[$handler_name]->conf['display'] = $cache->display;
+ $page->handler_info[$handler_name]['changed'] |= PAGE_MANAGER_CHANGED_CACHED;
+ }
+ else {
+ $page->new_handler->conf['display'] = $cache->display;
+ }
+ page_manager_set_page_cache($page);
+}
+
+/**
+ * Clean up the panel pane variables for the template.
+ */
+function template_preprocess_panels_pane($vars) {
+ $content = $vars['output'];
+ // basic classes
+ $vars['classes'] = 'panel-pane';
+ $vars['id'] = '';
+
+ // Add some usable classes based on type/subtype
+ ctools_include('cleanstring');
+ $type_class = $content->type ? 'pane-'. ctools_cleanstring($content->type, array('lower case' => TRUE)) : '';
+ $subtype_class = $content->subtype ? 'pane-'. ctools_cleanstring($content->subtype, array('lower case' => TRUE)) : '';
+
+ // Sometimes type and subtype are the same. Avoid redudant classes.
+ if ($type_class != $subtype_class) {
+ $vars['classes'] .= " $type_class $subtype_class";
+ }
+ else {
+ $vars['classes'] .= " $type_class";
+ }
+
+ // Add id and custom class if sent in.
+ if (!empty($content->content)) {
+ if (!empty($content->css_id)) {
+ $vars['id'] = ' id="' . $content->css_id . '"';
+ }
+ if (!empty($content->css_class)) {
+ $vars['classes'] .= ' ' . $content->css_class;
+ }
+ }
+
+ // administrative links, only if there is permission.
+ $vars['admin_links'] = '';
+ if (user_access('view pane admin links') && !empty($content->admin_links)) {
+ $vars['admin_links'] = theme('links', $content->admin_links);
+ }
+
+ $vars['title'] = !empty($content->title) ? $content->title : '';
+
+ $vars['feeds'] = !empty($content->feeds) ? implode(' ', $content->feeds) : '';
+ $vars['content'] = !empty($content->content) ? $content->content : '';
+
+ $vars['links'] = !empty($content->links) ? theme('links', $content->links) : '';
+ $vars['more'] = '';
+ if (!empty($content->more)) {
+ if (empty($content->more['title'])) {
+ $content->more['title'] = t('more');
+ }
+ $vars['more'] = l($content->more['title'], $content->more['href'], $content->more);;
+ }
+}