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; } /** * Implementation of hook_init() */ function panels_init() { drupal_add_css(panels_get_path('css/panels.css')); drupal_add_js(panels_get_path('js/panels.js')); } /** * Load a panels include file. */ 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; } } /** * panels path helper function */ function panels_get_path($file, $base_path = FALSE, $module = 'panels') { $output = $base_path ? base_path() : ''; return $output . drupal_get_path('module', $module) . '/' . $file; } /** * Implementation of hook_perm */ 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' ); } /** * 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; } /** * 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()); } /** * 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)); } // --------------------------------------------------------------------------- // panels display editing /** * @defgroup mainapi Functions comprising the main panels API * @{ */ /** * 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_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); } /** * 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); } /** * 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 /** * Forms the basis of a panel display * */ 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; } } } else { $this->panels[$location][] = $pane->pid; } } function duplicate_pane($pid, $location = FALSE) { $pane = $this->clone_pane($pid); $this->add_pane($pane, $location); } function clone_pane($pid) { $pane = drupal_clone($this->content[$pid]); foreach (array_keys($this->content) as $pidcheck) { // necessary? unset($pane->position); } return $pane; } 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; } } /** * }@ 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. * * 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_sanitize_display(&$display) { if (!isset($display->args)) { $display->args = array(); } if (!isset($display->incoming_content)) { $display->incoming_content = NULL; } if (!isset($display->context)) { $display->context = array(); } if (!isset($display->css_id)) { $display->css_id = NULL; } } /** * 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; } 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; } /** * 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; } $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); } } $display->panels = $display->content = array(); $displays[$display->did] = $display; } 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); 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; } /** * 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 panels_load_display($did) { $displays = panels_load_displays(array($did)); if (!empty($displays)) { return array_shift($displays); } } /** * 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; } } 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'); foreach ((array) $display->panels as $id => $panes) { $position = 0; $new_panes = array(); foreach ((array) $panes as $pid) { $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; if (empty($pane->cache)) { $pane->cache = array(); } $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]); } } $display->panels[$id] = $new_panes; } if (!empty($pids)) { db_query("DELETE FROM {panels_pane} WHERE pid IN (" . db_placeholders($pids) . ")", $pids); } // Clear any cached content for this display. panels_clear_cached_content($display); // to be nice, even tho we have a reference. return $display; } /** * Delete a display. */ 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); } /** * 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_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"; } $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"; } } 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)); } if ($prefix) { $output = str_replace("\n", "\n$prefix", $output); } 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 * 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) { panels_load_include('plugins'); return _panels_print_layout($id, $content); } // @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 = '
'; $output .= $image; if ($title) { $output .= '
' . $title . '
'; } $output .= '
'; return $output; } /** * Theme the layout link image * @layout */ function theme_panels_layout_link($title, $id, $image, $link) { $output = ''; return $output; } /** * 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); } /** * 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') { return 'plugins/' . $plugin; } }