<?php
-// $Id$
/**
- * Implementation of hook_help()
+ * @file panels.module
+ *
+ * Core functionality for the Panels engine.
*/
-function panels_help($section = '') {
- switch ($section) {
- case 'admin/modules#description':
- return t('The panels module allows the creation of pages with flexible layouts.');
- case 'admin/panels':
- case 'admin/panels/list':
- return t('<p>You may peruse a list of your current panels layouts and edit them, or click add to create a new page.</p>');
- case 'admin/panels/add':
- return t('<p>Choose a layout for your new page from the list below.</p>');
- }
-}
/**
- * Implementation of hook_perm()
+ * Error code bitmask used for identifying argument behavior at runtime.
*/
-function panels_perm() {
- return array('create panels');
-}
+define('PANELS_ARG_IS_BAD', 1);
+define('PANELS_ARG_USE_FALLBACK', 1 << 1);
/**
- * Implementation of hook_menu()
+ * Returns the API version of Panels. This didn't exist in 1.
+ *
+ * @return An array with the major and minor versions
*/
-function panels_menu($may_cache) {
- if ($may_cache) {
- $access = user_access('create panels');
- $items[] = array(
- 'path' => 'admin/panels',
- 'title' => t('panels'),
- 'access' => $access,
- 'callback' => 'panels_list_page',
- );
- $items[] = array(
- 'path' => 'admin/panels/list',
- 'title' => t('list'),
- 'access' => $access,
- 'callback' => 'panels_list_page',
- 'weight' => -10,
- 'type' => MENU_DEFAULT_LOCAL_TASK,
- );
- $items[] = array(
- 'path' => 'admin/panels/add',
- 'title' => t('add'),
- 'access' => $access,
- 'callback' => 'panels_add_page',
- 'type' => MENU_LOCAL_TASK,
- );
- $items[] = array(
- 'path' => 'admin/panels/add/layout',
- 'title' => t('add'),
- 'access' => $access,
- 'callback' => 'panels_add_layout_page',
- 'type' => MENU_LOCAL_TASK,
- );
- $items[] = array(
- 'path' => 'admin/panels/edit',
- 'title' => t('edit panels'),
- 'access' => $access,
- 'callback' => 'panels_edit_page',
- 'type' => MENU_CALLBACK,
- );
- $items[] = array(
- 'path' => 'admin/panels/delete',
- 'title' => t('delete panels'),
- 'access' => $access,
- 'callback' => 'panels_delete_page',
- '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)) {
- $items[] = array(
- 'path' => $panels->path,
- 'title' => $panels->title,
- 'access' => panels_access(unserialize($panels->access)),
- 'callback' => 'panels_panels_page',
- 'callback arguments' => array($panels->did),
- 'type' => MENU_CALLBACK
+function panels_api_version() {
+ return array(2, 0);
+}
+
+function panels_theme() {
+ $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',
+ );
+ $theme['panels_edit_layout_form_choose'] = array(
+ 'arguments' => array('form'),
+ 'file' => 'includes/display-edit.inc',
+ );
+ $theme['panels_pane'] = array(
+ 'arguments' => array('content', 'pane', 'display'),
+ 'file' => 'includes/display-render.inc',
+ );
+ $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_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',
+ );
+
+ // Register layout and style themes on behalf of all of these items.
+ panels_load_include('plugins');
+
+ // No need to worry about files; the plugin has to already be loaded for us
+ // 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']);
+ }
}
}
+
+ $styles = panels_get_styles();
+ foreach ($styles as $name => $data) {
+ if (!empty($data['render pane'])) {
+ $theme[$data['render pane']] = array(
+ 'arguments' => array('content' => NULL, 'pane' => NULL, 'display' => NULL),
+ );
+ }
+ if (!empty($data['render panel'])) {
+ $theme[$data['render panel']] = array(
+ 'arguments' => array('display' => NULL, 'panel_id' => NULL, 'panes' => NULL, 'settings' => NULL),
+ );
+ }
+
+ }
+
+ return $theme;
+}
+
+/**
+ * Implementation of hook_menu
+ */
+function panels_menu() {
+ $items = array();
+
+ // Provide some common options to reduce code repetition.
+ // By using array addition and making sure these are the rightmost
+ // value, they won't override anything already set.
+ $base = array(
+ 'access arguments' => array('access content'),
+ 'type' => MENU_CALLBACK,
+ 'file' => 'includes/display-edit.inc',
+ );
+
+ $items['panels/ajax/add-pane'] = array(
+ 'page callback' => 'panels_ajax_add_pane_choose',
+ ) + $base;
+ $items['panels/ajax/add-pane-config'] = array(
+ 'page callback' => 'panels_ajax_add_pane_config',
+ ) + $base;
+ $items['panels/ajax/configure'] = array(
+ 'page callback' => 'panels_ajax_configure_pane',
+ ) + $base;
+ $items['panels/ajax/show'] = array(
+ 'page callback' => 'panels_ajax_toggle_shown',
+ 'page arguments' => array('show'),
+ ) + $base;
+ $items['panels/ajax/hide'] = array(
+ 'page callback' => 'panels_ajax_toggle_shown',
+ 'page arguments' => array('hide'),
+ ) + $base;
+ $items['panels/ajax/cache-method'] = array(
+ 'page callback' => 'panels_ajax_cache_method',
+ ) + $base;
+ $items['panels/ajax/cache-settings'] = array(
+ 'page callback' => 'panels_ajax_cache_settings',
+ ) + $base;
+
+ // For panel settings on the edit layout settings page
+ $items['panels/ajax/style-settings'] = array(
+ 'page callback' => 'panels_ajax_style_settings',
+ 'file' => 'includes/display-layout-settings.inc',
+ ) + $base;
+
+ // Non-display editor callbacks
+ $items['panels/node/autocomplete'] = array(
+ 'title' => 'Autocomplete node',
+ 'page callback' => 'panels_node_autocomplete',
+ 'file' => 'includes/callbacks.inc',
+ ) + $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',
+ ) + $base;
+ $items['panels/ajax/context-configure'] = array(
+ 'page callback' => 'panels_ajax_context_item_edit',
+ 'file' => 'includes/common-context.inc',
+ ) + $base;
+ $items['panels/ajax/context-delete'] = array(
+ 'page callback' => 'panels_ajax_context_item_delete',
+ 'file' => 'includes/common-context.inc',
+ ) + $base;
+
+ // Provide a nice location for a panels admin panel.
+ $items['admin/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.',
+ );
+
return $items;
}
/**
- * Determine whether or not the current user has access to this
- * panels.
+ * Implementation of hook_init()
*/
-function panels_access($access) {
- // for now
- return TRUE;
+function panels_init() {
+ drupal_add_css(panels_get_path('css/panels.css'));
+ drupal_add_js(panels_get_path('js/panels.js'));
}
/**
- * panels path helper function
+ * Load a panels include file.
*/
-function panels_get_file_path($module, $file, $base_path = true) {
- if ($base_path) {
- $output = base_path();
+function panels_load_include($include, $path = 'includes/') {
+ static $loaded = array();
+ if (empty($loaded["$path$include.inc"])) {
+ require_once './' . panels_get_path("$path$include.inc");
+ $loaded["$path$include.inc"] = TRUE;
}
- return $output . drupal_get_path('module', $module) . '/' . $file;
}
-// ---------------------------------------------------------------------------
-// panels custom image button
-
/**
- * Custom form element to do our nice images.
+ * panels path helper function
*/
-function panels_elements() {
- $type['panels_imagebutton'] = array('#input' => TRUE, '#button_type' => 'submit',);
- return $type;
+function panels_get_path($file, $base_path = FALSE, $module = 'panels') {
+ $output = $base_path ? base_path() : '';
+ return $output . drupal_get_path('module', $module) . '/' . $file;
}
/**
- * Theme our image button.
+ * Implementation of hook_perm
*/
-function theme_panels_imagebutton($element) {
- return '<input type="image" class="form-'. $element['#button_type'] .'" name="'. $element['#name'] .'" value="'. check_plain($element['#default_value']) .'" '. drupal_attributes($element['#attributes']) . ' src="' . $element['#image'] . '" alt="' . $element['#title'] . '" title="' . $element['#title'] . "\" />\n";
+function panels_perm() {
+ return array(
+ 'view all panes',
+ 'view pane admin links',
+ 'administer pane visibility',
+ 'administer pane access',
+ 'administer advanced pane settings',
+ 'use panels caching features'
+ );
}
-function panels_imagebutton_value() {
- // null function guarantees default_value doesn't get moved to #value.
+/**
+ * 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;
}
/**
- * Add a single button to a form.
+ * Save the edited object into the cache.
*/
-function panels_add_button($image, $name, $text) {
- $module_path = base_path() . drupal_get_path('module', 'panels');
+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());
+}
- return array(
- '#type' => 'panels_imagebutton',
- '#image' => $module_path . '/images/' . $image,
- '#title' => $text,
- '#default_value' => $name,
- );
+/**
+ * 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);
}
/**
- * Set a button to a blank image -- used for placeholders when buttons are
- * not relevant but just removing it would be visually unappealing.
+ * Implementation of hook_cron. Clean up old caches.
*/
-function panels_set_blank(&$form) {
- $form['#type'] = 'markup';
- $form['#value'] = theme('image', drupal_get_path('module', 'panels') . '/images/blank.gif');
+function panels_cron() {
+ // delete anything 7 days old or more.
+ db_query("DELETE FROM {panels_object_cache} WHERE timestamp < %d", time() - (86400 * 7));
}
// ---------------------------------------------------------------------------
-// panels administrative pages
+// panels display editing
/**
- * Provide a list of panels, with links to edit or delete them.
+ * @defgroup mainapi Functions comprising the main panels API
+ * @{
*/
-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('edit', "admin/panels/edit/$panels->did"),
- l('delete', "admin/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.
+/**
+ * Main API entry point to edit a panel display.
+ *
+ * Sample implementations utiltizing the the complex $destination behavior can be found
+ * in panels_page_edit_content() and, in a separate contrib module, OG Blueprints
+ * (http://drupal.org/project/og_blueprints), og_blueprints_blueprint_edit().
+ *
+ * @ingroup mainapi
+ *
+ * @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. \n
+ * Note that 'fully loaded' means the $display must already be loaded with any contexts
+ * the caller wishes to have set for the display.
+ * @param mixed $destination \n
+ * The redirect destination that the user should be taken to on form submission or
+ * cancellation. With panels_edit, $destination has complex effects on the return
+ * values of panels_edit() once the form has been submitted. See the explanation of
+ * the return value below to understand the different types of values returned by panels_edit()
+ * at different stages of FAPI. Under most circumstances, simply passing in
+ * drupal_get_destination() is all that's necessary.
+ * @param array $content_types \n
+ * An associative array of allowed content types, typically as returned from
+ * panels_common_get_allowed_types(). Note that context partially governs available content types,
+ * so you will want to create any relevant contexts using panels_create_context() or
+ * panels_create_context_empty() to make sure all the appropriate content types are available.
+ *
+ * @return
+ * Because the functions called by panels_edit() invoke the form API, this function
+ * returns different values depending on the stage of form submission we're at. In Drupal 5,
+ * the phase of form submission is indicated by the contents of $_POST['op']. Here's what you'll
+ * get at different stages:
+ * -# If !$_POST['op']: then we're on on the initial passthrough and the form is being
+ * rendered, so it's the $form itself that's being returned. Because negative margins,
+ * a common CSS technique, bork the display editor's ajax drag-and-drop, it's important
+ * that the $output be printed, not returned. Use this syntax in the caller function: \n
+ * print theme('page', panels_edit($display, $destination, $content_types), FALSE); \n
+ * -# If $_POST['op'] == t('Cancel'): form submission has been cancelled. If empty($destination) == FALSE,
+ * then there is no return value and the panels API takes care of redirecting to $destination.
+ * If empty($destination) == TRUE, then there's still no return value, but the caller function
+ * has to take care of form redirection.
+ * -# If $_POST['op'] == ('Save'): the form has been submitted successfully and has run through
+ * panels_edit_display_submit(). $output depends on the value of $destination:
+ * - If empty($destination) == TRUE: $output contains the modified $display
+ * object, and no redirection will occur. This option is useful if the caller
+ * needs to perform additional operations on or with the modified $display before
+ * the page request is complete. Using hook_form_alter() to add an additional submit
+ * handler is typically the preferred method for something like this, but there
+ * are certain use cases where that is infeasible and $destination = NULL should
+ * be used instead. If this method is employed, the caller will need to handle form
+ * redirection. Note that having $_REQUEST['destination'] set, whether via
+ * drupal_get_destination() or some other method, will NOT interfere with this
+ * functionality; consequently, you can use drupal_get_destination() to safely store
+ * your desired redirect in the caller function, then simply use drupal_goto() once
+ * panels_edit() has done its business.
+ * - If empty($destination) == FALSE: the form will redirect to the URL string
+ * given in $destination and NO value will be returned.
*/
-function panels_delete_page($did = '') {
- $panels = panels_load_panels($did);
-
- if (!$panels) {
- return 'admin/panels';
- }
+function panels_edit($display, $destination = NULL, $content_types = NULL) {
+ panels_load_include('display-edit');
+ panels_load_include('ajax');
+ panels_load_include('plugins');
+ return _panels_edit($display, $destination, $content_types);
+}
- $form['did'] = array('#type' => 'value', '#value' => $panels->did);
- return confirm_form('panels_delete_confirm', $form,
- t('Are you sure you want to delete %title?', array('%title' => $panels->title)),
- $_GET['destination'] ? $_GET['destination'] : 'admin/panels',
- t('This action cannot be undone.'),
- t('Delete'), t('Cancel')
- );
+/**
+ * API entry point for selecting a layout for a given display.
+ *
+ * Layout selection is nothing more than a list of radio items encompassing the available
+ * layouts for this display, as defined by .inc files in the panels/layouts subdirectory.
+ * The only real complexity occurs when a user attempts to change the layout of a display
+ * that has some content in it.
+ *
+ * @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 the form submission button. If no value is provided,
+ * then the form submission button will default to t('Save').
+ * @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, see panels_edit().
+ * @param mixed $allowed_layouts
+ * Allowed layouts has three different behaviors that depend on which of three value types
+ * are passed in by the caller:
+ * #- if $allowed_layouts instanceof panels_allowed_layouts (includes subclasses): the most
+ * complex use of the API. The caller is passing in a loaded panels_allowed_layouts object
+ * that the client module previously created and stored somewhere using a custom storage
+ * mechanism.
+ * #- if is_string($allowed_layouts): the string will be used in a call to variable_get() which
+ * will call the $allowed_layouts . '_allowed_layouts' var. If the data was stored properly
+ * in the system var, the $allowed_layouts object will be unserialized and recreated.
+ * @see panels_common_set_allowed_layouts()
+ * #- if is_null($allowed_layouts): the default behavior, which also provides backwards
+ * compatibility for implementations of the Panels2 API written before beta4. In this case,
+ * a dummy panels_allowed_layouts object is created which does not restrict any layouts.
+ * Subsequent behavior is indistinguishable from pre-beta4 behavior.
+ *
+ * @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($display, $finish, $destination = NULL, $allowed_layouts = NULL) {
+ panels_load_include('display-layout');
+ panels_load_include('plugins');
+ return _panels_edit_layout($display, $finish, $destination, $allowed_layouts);
}
-/*
- * Handle the submit button to delete a panel page.
+/**
+ * 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_delete_confirm_submit($formid, $form) {
- if ($form['confirm']) {
- panels_delete_panels((object) $form);
- drupal_goto('admin/panels');
- }
+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
+
/**
- * Handle the add panels page
+ * Forms the basis of a panel display
+ *
*/
-function panels_add_page($layout = NULL) {
- $layouts = panels_get_layouts();
- theme_add_style(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;
+class panels_display {
+ var $args = array();
+ var $content = array();
+ var $panels = array();
+ 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 = '';
+
+ function add_pane($pane, $location = FALSE) {
+ $pane->pid = $this->next_new_pid();
+ if (!$location || !isset($this->panels[$location])) {
+ foreach ($this->panels as $panel_name => $panel) {
+ if (array_key_exists($pane->pid, $panel)) {
+ $this->panels[$panel_name][] = $pane->pid;
+ }
}
- $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;
+ else {
+ $this->panels[$location][] = $pane->pid;
+ }
}
- if (!$layouts[$layout]) {
- return drupal_not_found();
+ function duplicate_pane($pid, $location = FALSE) {
+ $pane = $this->clone_pane($pid);
+ $this->add_pane($pane, $location);
}
- $panels->layout = $layout;
- return panels_edit_form($panels);
-}
-
-function theme_panels_add_image($title, $id, $image) {
- $output .= '<div class="layout-link">';
- $output .= $image;
- $output .= '<div>' . l($title, $_GET['q'] . '/' . $id) . '</div>';
- $output .= '</div>';
- return $output;
-}
-// ---------------------------------------------------------------------------
-// panels administrative pages
+ function clone_pane($pid) {
+ $pane = drupal_clone($this->content[$pid]);
+ foreach (array_keys($this->content) as $pidcheck) {
+ // necessary?
+ unset($pane->position);
+ }
+ return $pane;
+ }
-function panels_edit_page($did = NULL) {
- if (!$did || !($panels = panels_load_panels($did))) {
- return drupal_not_found();
+ function next_new_pid() {
+ // necessary if/until we use this method and ONLY this method for adding temporary pids.
+ // then we can do it with a nice static var.
+ $id = array(0);
+ foreach (array_keys($this->content) as $pid) {
+ if (!is_numeric($pid)) {
+ $id[] = substr($pid, 4);
+ }
+ }
+ $next_id = end($id);
+ return ++$next_id;
}
- return panels_edit_form($panels);
}
/**
- * shortcut to ease the syntax of the various form builder tricks we use.
+ * }@ End of 'defgroup mainapi', although other functions are specifically added later
*/
-function panels_form_builder(&$form, $form_id = 'panels_edit_form') {
- $form = form_builder($form_id, $form);
+
+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);
}
/**
- * Edit an already loaded panels.
+ * Clean up a display object and add some required information, if missing.
+ *
+ * Currently a display object needs 'args', 'incoming content', 'context'
+ * and a 'css_id'.
+ *
+ * @param &$display
+ * The display object to be sanitized.
+ * @return
+ * The sanitized display object.
*/
-function panels_edit_form($panels) {
- theme_add_style(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.'),
- );
-
- $default_radio = array_shift(array_keys($layout['content areas']));
- $form['add']['area'] = array(
- '#type' => 'radios',
- '#title' => t('Area'),
- '#options' => $layout['content areas'],
- '#prefix' => '<div class="container-inline">',
- '#suffix' => '</div>',
- '#default_value' => $default_radio,
- );
- foreach ($content_types as $id => $type) {
- $function = $type['admin'];
- if (function_exists($function)) {
- global $form_values;
- $form['add'][$id] = $function('add button', $dummy);
-
- // $dummy needed for cause you can't have default args on a reference.
- $form['add'][$id]['#parents'] = array('add', $id);
- $form['add'][$id]['#tree'] = true;
- panels_form_builder($form['add'][$id]);
-
- if ($conf = $function('add', $form_values['add'][$id])) {
- $add->configuration = $conf;
- $add->type = $id;
- $form['add']['area']['#parents'] = array('area');
- panels_form_builder($form['add']['area']);
- $add->area = $form_values['area'];
- }
- }
+function panels_sanitize_display(&$display) {
+ if (!isset($display->args)) {
+ $display->args = array();
}
- $form['layout'] = array(
- '#type' => 'value',
- '#value' => $panels->layout
- );
+ if (!isset($display->incoming_content)) {
+ $display->incoming_content = NULL;
+ }
- $form['did'] = array(
- '#type' => 'value',
- '#value' => $panels->did,
- );
+ if (!isset($display->context)) {
+ $display->context = array();
+ }
- $form['info'] = array(
- '#type' => 'fieldset',
- '#title' => t('General information'),
- '#collapsible' => false,
- );
+ if (!isset($display->css_id)) {
+ $display->css_id = NULL;
+ }
+}
- $file = panels_get_file_path($layout['module'], $layout['icon'], false);
- $icon .= theme('image', $file);
- $form['info']['layout-icon'] = array(
- '#value' => '<div class="layout-icon">' . $icon . '</div>',
- );
+/**
+ * Creates a new display, setting the ID to our magic new id.
+ */
+function panels_new_display() {
+ $display = new panels_display();
+ $display->did = 'new';
+ return $display;
+}
- $form['info']['layout-display'] = array(
- '#value' => '<strong>Layout</strong>: ' . $layout['title'],
- );
+function panels_new_pane($type, $subtype) {
+ $pane = new stdClass();
+ $pane->pid = 'new';
+ $pane->type = $type;
+ $pane->subtype = $subtype;
+ $pane->configuration = array();
+ $pane->access = array();
+ $pane->shown = TRUE;
+ $pane->visibility = '';
+ return $pane;
+}
- $form['info']['title'] = array(
- '#type' => 'textfield',
- '#default_value' => $panels->title,
- '#title' => t('Page title'),
- '#description' => t('The page title for this panels layout'),
- );
+/**
+ * Load and fill the requested $display object(s).
+ *
+ * Helper function primarily for for panels_load_display().
+ *
+ * @param array $dids
+ * An indexed array of dids to be loaded from the database.
+ *
+ * @return $displays
+ * An array of displays, keyed by their display dids.
+ */
+function panels_load_displays($dids) {
+ $displays = array();
+ if (empty($dids) || !is_array($dids)) {
+ return $displays;
+ }
- $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'),
- );
+ $result = db_query("SELECT * FROM {panels_display} WHERE did IN (". db_placeholders($dids) .")", $dids);
- $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,
- );
+ while ($obj = db_fetch_array($result)) {
+ $display = new panels_display();
- $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]) {
- $position = max(max(array_keys($order)), max(array_keys($panels->content[$area]))) + 1;
- }
- else if ($order) {
- $position = max(array_keys($order)) + 1;
+ 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);
}
- 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']);
- }
- }
- }
+ $display->panels = $display->content = array();
+
+ $displays[$display->did] = $display;
}
- $form['submit'] = array(
- '#type' => 'submit',
- '#value' => t('Save'),
- );
+ 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);
- return drupal_get_form('panels_edit_form', $form);
+ 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;
+
+ $displays[$pane->did]->panels[$pane->panel][] = $pane->pid;
+ $displays[$pane->did]->content[$pane->pid] = $pane;
+ }
+ return $displays;
}
/**
- * Display the form to edit a panels.
+ * Load a single display.
+ *
+ * @ingroup mainapi
+ *
+ * @param int $did
+ * The display id (did) of the display to be loaded.
+ *
+ * @return object $display instanceof panels_display \n
+ * Returns a partially-loaded panels_display object. $display objects returned from
+ * from this function have only the following data:
+ * - $display->did (the display id)
+ * - $display->name (the 'name' of the display, where applicable - it often isn't)
+ * - $display->layout (a string with the system name of the display's layout)
+ * - $display->panel_settings (custom layout style settings contained in an associative array; NULL if none)
+ * - $display->layout_settings (panel size and configuration settings for Flexible layouts; NULL if none)
+ * - $display->css_id (the special css_id that has been assigned to this display, if any; NULL if none)
+ * - $display->content (an array of pane objects, keyed by pane id (pid))
+ * - $display->panels (an associative array of panel regions, each an indexed array of pids in the order they appear in that region)
+ * - $display->cache (any relevant data from panels_simple_cache)
+ * - $display->args
+ * - $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.
*/
-function theme_panels_edit_form($form) {
- $layouts = panels_get_layouts();
- $layout = $layouts[$form['layout']['#value']];
-
- $content_types = panels_get_content_types();
+function panels_load_display($did) {
+ $displays = panels_load_displays(array($did));
+ if (!empty($displays)) {
+ return array_shift($displays);
+ }
+}
- $output .= form_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' => form_render($form['content'][$area][$id]['configuration']),
- '#collapsible' => true,
- '#collapsed' => true
- );
- $buttons = form_render($form['content'][$area][$id]['buttons']);
- $area_content .= $buttons . ' ' . $function('list', $conf) .
- theme('fieldset', $fieldset) /* . '<br />' */;
- }
- }
+/**
+ * Save a display object.
+ *
+ * @ingroup mainapi
+ *
+ * Note a new $display only receives a real did once it is run through this function.
+ * Until then, it uses a string placeholder, 'new', in place of a real did. The same
+ * applies to all new panes (whether on a new $display or not); in addition,
+ * panes have sequential numbers appended, of the form 'new-1', 'new-2', etc.
+ *
+ * @param object $display instanceof panels_display \n
+ * The display object to be saved. Passed by reference so the caller need not use
+ * the return value for any reason except convenience.
+ *
+ * @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);
+ // 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);
+ while ($pane = db_fetch_object($result)) {
+ $pids[$pane->pid] = $pane->pid;
}
- $content[$area] = theme('fieldset', array('#title' => check_plain($title), '#value' => $area_content));
+ }
+ 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();
}
- $output .= panels_get_layout($layout, $content);
+ // update all the panes
+ panels_load_include('plugins');
- $output .= form_render($form);
- return $output;
-}
+ foreach ((array) $display->panels as $id => $panes) {
+ $position = 0;
+ $new_panes = array();
+ foreach ((array) $panes as $pid) {
+ $pane = $display->content[$pid];
+ $pane->position = $position++;
-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']);
- }
+ // 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;
+
+ if (empty($pane->cache)) {
+ $pane->cache = array();
}
- }
- }
-}
-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];
+ $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;
+ $new_panes[] = $pane->pid;
+ if (isset($pids[$pane->pid])) {
+ unset($pids[$pane->pid]);
}
}
- $panels->content[$area] = $array;
+
+ $display->panels[$id] = $new_panes;
+ }
+ if (!empty($pids)) {
+ db_query("DELETE FROM {panels_pane} WHERE pid IN (" . db_placeholders($pids) . ")", $pids);
}
- panels_save_panels($panels);
- drupal_set_message(t('The panels has been saved.'));
- return 'admin/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;
-}
+ // Clear any cached content for this display.
+ panels_clear_cached_content($display);
-/**
- * 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);
+ // to be nice, even tho we have a reference.
+ return $display;
}
/**
- * move an item in an array to the bottom
+ * Delete a display.
*/
-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;
+function panels_delete_display($display) {
+ if (is_object($display)) {
+ $did = $display->did;
+ }
+ else {
+ $did = $display;
+ }
+ db_query("DELETE FROM {panels_display} WHERE did = %d", $did);
+ db_query("DELETE FROM {panels_pane} WHERE did = %d", $did);
}
/**
- * Remove an item from an array
+ * Exports the provided display into portable code.
+ *
+ * This function is primarily intended as a mechanism for cloning displays.
+ * It generates an exact replica (in code) of the provided $display, with
+ * the exception that it replaces all ids (dids and pids) with 'new-*' values.
+ * Only once panels_save_display() is called on the code version of $display will
+ * the exported display written to the database and permanently saved.
+ *
+ * @see panels_page_export() or _panels_page_fetch_display() for sample implementations.
+ *
+ * @ingroup mainapi
+ *
+ * @param object $display instanceof panels_display \n
+ * This export function does no loading of additional data about the provided
+ * display. Consequently, the caller should make sure that all the desired data
+ * has been loaded into the $display before calling this function.
+ * @param string $prefix
+ * A string prefix that is prepended to each line of exported code. This is primarily
+ * used for prepending a double space when exporting so that the code indents and lines up nicely.
+ *
+ * @return string $output
+ * The passed-in $display expressed as code, ready to be imported. Import by running
+ * eval($output) in the caller function; doing so will create a new $display variable
+ * with all the exported values. Note that if you have already defined a $display variable in
+ * the same scope as where you eval(), your existing $display variable WILL be overwritten.
*/
-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;
- }
- $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;
+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";
}
- return $panels;
-}
-function panels_save_panels($panels) {
- 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}");
- 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);
+ $output .= $prefix . '$display->content = array()' . ";\n";
+ $output .= $prefix . '$display->panels = array()' . ";\n";
+ $panels = array();
+
+ 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 . ' ');
+ $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";
}
}
- cache_clear_all('menu:', TRUE);
+ return $output;
}
-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);
- cache_clear_all('menu:', TRUE);
+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;
}
-// ---------------------------------------------------------------------------
-// panels page
-function panels_panels_page($did) {
- $panels = panels_load_panels($did);
- if (!$panels) {
- return drupal_not_found();
+function panels_var_export($object, $prefix = '') {
+ if (is_array($object) && empty($object)) {
+ $output = 'array()';
}
-
- $layouts = panels_get_layouts();
- $layout = $layouts[$panels->layout];
-
- if (!$layout) {
- watchdog('panels', t('Unable to find requested layout %s', array('%s' => check_plain($panels->layout))));
- return drupal_not_found();
+ else {
+ // Remove extra space to match Drupal coding standards.
+ $output = str_replace('array (', 'array(', var_export($object, 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);
- }
- }
+ if ($prefix) {
+ $output = str_replace("\n", "\n$prefix", $output);
}
- $output = panels_get_layout($layout, $content);
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'])) {
- theme_add_style(path_to_theme() . '/' . $layout['css']);
- }
- else {
- theme_add_style(drupal_get_path('module', $layout['module']) . '/' . $layout['css']);
- }
- }
- return $output;
+/**
+ * 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.
+ * @render
+ * @ingroup hook_invocations
+ */
+function panels_render_display(&$display) {
+ panels_load_include('display-render');
+ panels_load_include('plugins');
+ return _panels_render_display($display);
}
/**
* For external use: Given a layout ID and a $content array, return the
- * finished layout.
+ * 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)
+ * @render
*/
function panels_print_layout($id, $content) {
- $layouts = panels_get_layouts();
- $layout = $layouts[$id];
- if (!$layout) {
- return;
- }
-
- return panels_get_layout($layout, $content);
+ panels_load_include('plugins');
+ return _panels_print_layout($id, $content);
}
-// ---------------------------------------------------------------------------
-// panels data loading
-
-function panels_load_includes($directory, $callback) {
- // Load all our module 'on behalfs'.
- $path = drupal_get_path('module', 'panels') . '/' . $directory;
- $files = 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);
- }
- }
+// @layout
+function panels_print_layout_icon($id, $layout, $title = NULL) {
+ drupal_add_css(panels_get_path('css/panels_admin.css'));
+ $file = $layout['path'] . '/' . $layout['icon'];
+ return theme('panels_layout_icon', $id, theme('image', $file), $title);
+}
+
+/**
+ * Theme the layout icon image
+ * @layout
+ * @todo move to theme.inc
+ */
+function theme_panels_layout_icon($id, $image, $title = NULL) {
+ $output = '<div class="layout-icon">';
+ $output .= $image;
+ if ($title) {
+ $output .= '<div class="caption">' . $title . '</div>';
}
+ $output .= '</div>';
return $output;
}
-function panels_get_layouts() {
- static $layout = NULL;
- if (!$layout) {
- $layouts = panels_load_includes('layouts', 'panels_layouts');
- }
- return $layouts;
+/**
+ * Theme the layout link image
+ * @layout
+ */
+function theme_panels_layout_link($title, $id, $image, $link) {
+ $output = '<div class="layout-link">';
+ $output .= $image;
+ $output .= '<div>' . $title . '</div>';
+ $output .= '</div>';
+ return $output;
}
-function panels_get_content_types() {
- static $layout = NULL;
- if (!$layout) {
- $layouts = panels_load_includes('content_types', 'panels_content_types');
- }
- return $layouts;
+/**
+ * Print the layout link. Sends out to a theme function.
+ * @layout
+ */
+function panels_print_layout_link($id, $layout, $link) {
+ 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);
+ return theme('panels_layout_link', $title, $id, $image, $link);
}
/**
- * Helper function for autocompletion of node titles.
- * This is mostly stolen from clipper.
+ * Implementation of hook_ctools_plugin_directory() to let the system know
+ * we implement task and task_handler plugins.
*/
-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] = '<span class="autocomplete_title">'. check_plain($node->title) .'</span> <span class="autocomplete_user">('. t('by %user', array('%user' => check_plain($node->name))) .')</span>';
- }
- print drupal_to_js($matches);
- exit();
+function panels_ctools_plugin_directory($plugin) {
+ if ($plugin == 'tasks' || $plugin == 'task_handlers') {
+ return 'plugins/' . $plugin;
}
}