2164 lines
69 KiB
Plaintext
2164 lines
69 KiB
Plaintext
<?php
|
|
|
|
/**
|
|
* @file
|
|
* Core functionality for the Panels engine.
|
|
*/
|
|
|
|
define('PANELS_REQUIRED_CTOOLS_API', '2.0.9');
|
|
|
|
/**
|
|
* The current working panels version.
|
|
*
|
|
* In a release, it should be 7.x-3.x, which should match what drush make will
|
|
* create. In a dev format, it should be 7.x-3.(x+1)-dev, which will allow
|
|
* modules depending on new features in panels to depend on panels > 7.x-3.x.
|
|
*
|
|
* To define a specific version of Panels as a dependency for another module,
|
|
* simply include a dependency line in that module's info file, e.g.:
|
|
* ; Requires Panels v7.x-3.4 or newer.
|
|
* dependencies[] = panels (>=3.4)
|
|
*/
|
|
define('PANELS_VERSION', '7.x-3.8');
|
|
|
|
|
|
// Hide title use to be TRUE/FALSE. So FALSE remains old behavior.
|
|
define('PANELS_TITLE_FIXED', 0);
|
|
// And TRUE meant no title.
|
|
define('PANELS_TITLE_NONE', 1);
|
|
// And this is the new behavior, where the title field will pick from a pane.
|
|
define('PANELS_TITLE_PANE', 2);
|
|
|
|
/**
|
|
* Returns the API version of Panels. This didn't exist in 1.
|
|
*
|
|
* @todo -- this should work more like the CTools API version.
|
|
*
|
|
* @return array
|
|
* An array with the major and minor versions
|
|
*/
|
|
function panels_api_version() {
|
|
return array(3, 1);
|
|
}
|
|
|
|
/**
|
|
* Implements 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(
|
|
'variables' => array(
|
|
'title' => NULL,
|
|
'id' => NULL,
|
|
'image' => NULL,
|
|
'link' => NULL,
|
|
'class' => NULL,
|
|
),
|
|
);
|
|
$theme['panels_layout_icon'] = array(
|
|
'variables' => array(
|
|
'id' => NULL,
|
|
'image' => NULL,
|
|
'title' => NULL,
|
|
),
|
|
);
|
|
$theme['panels_pane'] = array(
|
|
'variables' => array(
|
|
'content' => array(),
|
|
'pane' => array(),
|
|
'display' => array(),
|
|
),
|
|
'path' => drupal_get_path('module', 'panels') . '/templates',
|
|
'template' => 'panels-pane',
|
|
);
|
|
$theme['panels_common_content_list'] = array(
|
|
'variables' => array('display' => NULL),
|
|
'file' => 'includes/common.inc',
|
|
);
|
|
$theme['panels_render_display_form'] = array(
|
|
'render element' => 'element',
|
|
);
|
|
|
|
$theme['panels_dashboard'] = array(
|
|
'variables' => array(),
|
|
'path' => drupal_get_path('module', 'panels') . '/templates',
|
|
'file' => '../includes/callbacks.inc',
|
|
'template' => 'panels-dashboard',
|
|
);
|
|
|
|
$theme['panels_dashboard_link'] = array(
|
|
'variables' => array('link' => array()),
|
|
'path' => drupal_get_path('module', 'panels') . '/templates',
|
|
'file' => '../includes/callbacks.inc',
|
|
'template' => 'panels-dashboard-link',
|
|
);
|
|
|
|
$theme['panels_dashboard_block'] = array(
|
|
'variables' => array('block' => array()),
|
|
'path' => drupal_get_path('module', 'panels') . '/templates',
|
|
'file' => '../includes/callbacks.inc',
|
|
'template' => 'panels-dashboard-block',
|
|
);
|
|
|
|
$theme['panels_add_content_modal'] = array(
|
|
'variables' => array(
|
|
'renderer' => NULL,
|
|
'categories' => array(),
|
|
'region' => NULL,
|
|
'category' => NULL,
|
|
'column_count' => 2,
|
|
),
|
|
'path' => drupal_get_path('module', 'panels') . '/templates',
|
|
'file' => '../includes/add-content.inc',
|
|
'template' => 'panels-add-content-modal',
|
|
);
|
|
|
|
$theme['panels_add_content_link'] = array(
|
|
'variables' => array(
|
|
'renderer' => NULL,
|
|
'region' => NULL,
|
|
'content_type' => NULL,
|
|
),
|
|
'path' => drupal_get_path('module', 'panels') . '/templates',
|
|
'file' => '../includes/add-content.inc',
|
|
'template' => 'panels-add-content-link',
|
|
);
|
|
|
|
// Register layout and style themes on behalf of all of these items.
|
|
ctools_include('plugins', 'panels');
|
|
|
|
// 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) {
|
|
foreach (array('theme', 'admin theme') as $callback) {
|
|
if (!empty($data[$callback])) {
|
|
$theme[$data[$callback]] = array(
|
|
'variables' => array(
|
|
'css_id' => NULL,
|
|
'content' => NULL,
|
|
'settings' => NULL,
|
|
'display' => NULL,
|
|
'layout' => NULL,
|
|
'renderer' => NULL,
|
|
),
|
|
'path' => $data['path'],
|
|
'file' => $data['file'],
|
|
);
|
|
|
|
// If no theme function exists, assume template.
|
|
if (!function_exists("theme_$data[theme]")) {
|
|
$theme[$data[$callback]]['template'] = str_replace('_', '-', $data[$callback]);
|
|
// For preprocess.
|
|
$theme[$data[$callback]]['file'] = $data['file'];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
$styles = panels_get_styles();
|
|
foreach ($styles as $name => $data) {
|
|
if (!empty($data['render pane'])) {
|
|
$theme[$data['render pane']] = array(
|
|
'variables' => array(
|
|
'content' => NULL,
|
|
'pane' => NULL,
|
|
'display' => NULL,
|
|
'style' => NULL,
|
|
'settings' => NULL,
|
|
),
|
|
'path' => $data['path'],
|
|
'file' => $data['file'],
|
|
);
|
|
}
|
|
if (!empty($data['render region'])) {
|
|
$theme[$data['render region']] = array(
|
|
'variables' => array(
|
|
'display' => NULL,
|
|
'owner_id' => NULL,
|
|
'panes' => NULL,
|
|
'settings' => NULL,
|
|
'region_id' => NULL,
|
|
'style' => NULL,
|
|
),
|
|
'path' => $data['path'],
|
|
'file' => $data['file'],
|
|
);
|
|
}
|
|
|
|
if (!empty($data['hook theme'])) {
|
|
if (is_array($data['hook theme'])) {
|
|
$theme += $data['hook theme'];
|
|
}
|
|
elseif (function_exists($data['hook theme'])) {
|
|
$data['hook theme']($theme, $data);
|
|
}
|
|
}
|
|
}
|
|
return $theme;
|
|
}
|
|
|
|
/**
|
|
* Implements 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();
|
|
|
|
// Base AJAX router callback.
|
|
$items['panels/ajax'] = array(
|
|
'access arguments' => array('access content'),
|
|
'page callback' => 'panels_ajax_router',
|
|
'theme callback' => 'ajax_base_page_theme',
|
|
'delivery callback' => 'ajax_deliver',
|
|
'type' => MENU_CALLBACK,
|
|
);
|
|
|
|
$admin_base = array(
|
|
'file' => 'includes/callbacks.inc',
|
|
'access arguments' => array('use panels dashboard'),
|
|
);
|
|
// Provide a nice location for a panels admin panel.
|
|
$items['admin/structure/panels'] = array(
|
|
'title' => 'Panels',
|
|
'page callback' => 'panels_admin_page',
|
|
'description' => 'Get a bird\'s eye view of items related to Panels.',
|
|
) + $admin_base;
|
|
|
|
$items['admin/structure/panels/dashboard'] = array(
|
|
'title' => 'Dashboard',
|
|
'page callback' => 'panels_admin_page',
|
|
'type' => MENU_DEFAULT_LOCAL_TASK,
|
|
'weight' => -10,
|
|
) + $admin_base;
|
|
|
|
$items['admin/structure/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/structure/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/structure/panels/settings/panel-page'] = array(
|
|
'title' => 'Panel pages',
|
|
'page callback' => 'panels_admin_panel_context_page',
|
|
'type' => MENU_LOCAL_TASK,
|
|
'weight' => -10,
|
|
) + $admin_base;
|
|
}
|
|
|
|
ctools_include('plugins', 'panels');
|
|
$layouts = panels_get_layouts();
|
|
foreach ($layouts as $name => $data) {
|
|
if (!empty($data['hook menu'])) {
|
|
if (is_array($data['hook menu'])) {
|
|
$items += $data['hook menu'];
|
|
}
|
|
elseif (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) {
|
|
ctools_include('display-edit', 'panels');
|
|
ctools_include('plugins', 'panels');
|
|
ctools_include('ajax');
|
|
ctools_include('modal');
|
|
ctools_include('context');
|
|
|
|
return panels_edit_cache_get($cache_key);
|
|
}
|
|
|
|
/**
|
|
* Implements 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)) {
|
|
if (user_access('administer site configuration')) {
|
|
drupal_set_message(t('Panels is enabled but CTools is out of date. All Panels modules are disabled until CTools is updated. See the status page for more information.'), 'error');
|
|
}
|
|
return;
|
|
}
|
|
|
|
ctools_add_css('panels', 'panels');
|
|
}
|
|
|
|
/**
|
|
* Implements hook_permission().
|
|
*
|
|
* @todo Almost all of these need to be moved into pipelines.
|
|
*/
|
|
function panels_permission() {
|
|
return array(
|
|
'use panels dashboard' => array(
|
|
'title' => t("Use Panels Dashboard"),
|
|
'description' => t('Allows a user to access the <a href="@url">Panels Dashboard</a>.', array('@url' => url('admin/structure/panels'))),
|
|
),
|
|
// @todo
|
|
'view pane admin links' => array(
|
|
'title' => t("View administrative links on Panel panes"),
|
|
'description' => "",
|
|
),
|
|
// @todo should we really have a global perm for this, or should it be moved into a pipeline question?
|
|
'administer pane access' => array(
|
|
'title' => t("Configure access settings on Panel panes"),
|
|
'description' => t("Access rules (often also called visibility rules) can be configured on a per-pane basis. This permission allows users to configure those settings."),
|
|
),
|
|
'use panels in place editing' => array(
|
|
'title' => t("Use the Panels In-Place Editor"),
|
|
'description' => t("Allows a user to utilize Panels' In-Place Editor."),
|
|
),
|
|
'change layouts in place editing' => array(
|
|
'title' => t("Change layouts with the Panels In-Place Editor"),
|
|
'description' => t("Allows a user to change layouts with the IPE."),
|
|
),
|
|
'bypass access in place editing' => array(
|
|
'title' => t("Bypass access checks when using Panels In-Place Editor"),
|
|
'description' => t("Allows using IPE even if user does not have additional permissions granted by other modules."),
|
|
'restrict access' => TRUE,
|
|
),
|
|
'administer advanced pane settings' => array(
|
|
'title' => t("Configure advanced settings on Panel panes"),
|
|
'description' => "",
|
|
),
|
|
'administer panels layouts' => array(
|
|
'title' => t("Administer Panels layouts"),
|
|
'description' => t("Allows a user to administer exported Panels layout plugins & instances."),
|
|
),
|
|
'administer panels styles' => array(
|
|
'title' => t("Administer Panels styles"),
|
|
'description' => t("DEPRECATED: Modules using this permission should use specific style permissions. See Issue #2329419 for more info."),
|
|
),
|
|
'administer panels display styles' => array(
|
|
'title' => t("Administer Panels display styles"),
|
|
'description' => t("Allows a user to administer the styles of Panel displays."),
|
|
),
|
|
'administer panels pane styles' => array(
|
|
'title' => t("Administer Panels pane styles"),
|
|
'description' => t("Allows a user to administer the styles of Panel panes."),
|
|
),
|
|
'administer panels region styles' => array(
|
|
'title' => t("Administer Panels region styles"),
|
|
'description' => t("Allows a user to administer the styles of Panel regions."),
|
|
),
|
|
'use panels caching features' => array(
|
|
'title' => t("Configure caching settings on Panels"),
|
|
'description' => t("Allows a user to configure caching on Panels displays and panes."),
|
|
),
|
|
'use panels locks' => array(
|
|
'title' => t('Use panel locks'),
|
|
'description' => t('Allows a user to lock and unlock panes in a panel display.'),
|
|
),
|
|
'use ipe with page manager' => array(
|
|
'title' => t("Use the Panels In-Place Editor with Page Manager"),
|
|
'description' => t('Allows users with access to the In-Place editor to administer page manager pages. This permission is only needed for users without "use page manager" access.'),
|
|
),
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Implements hook_flush_caches().
|
|
*/
|
|
function panels_flush_caches() {
|
|
if (db_table_exists('cache_panels')) {
|
|
return array('cache_panels');
|
|
}
|
|
}
|
|
|
|
/**
|
|
* CTools hook implementations.
|
|
*
|
|
* These aren't core Drupal hooks but they are just as important.
|
|
*/
|
|
|
|
/**
|
|
* Implements hook_ctools_plugin_directory().
|
|
*/
|
|
function panels_ctools_plugin_directory($module, $plugin) {
|
|
// To let the system know we implement task and task_handler plugins.
|
|
// Safety: go away if CTools is not at an appropriate version.
|
|
if (!module_invoke('ctools', 'api_version', PANELS_REQUIRED_CTOOLS_API)) {
|
|
return;
|
|
}
|
|
|
|
// We don't support the 'ctools' 'cache' plugin and pretending to causes
|
|
// errors when they're in use.
|
|
if ($module == 'ctools' && $plugin == 'cache') {
|
|
// If we did we'd make a plugin/ctools_cache or something.
|
|
return;
|
|
}
|
|
|
|
if ($module == 'page_manager' || $module == 'panels' || $module == 'ctools' || $module == 'stylizer') {
|
|
// Panels and CTools both implement a 'cache' plugin but we don't implement
|
|
// the CTools version.
|
|
if ($module == 'ctools' && $plugin == 'cache') {
|
|
return;
|
|
}
|
|
return 'plugins/' . $plugin;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Implements hook_ctools_plugin_type().
|
|
*
|
|
* Register layout, style, cache, and display_renderer plugin types, declaring
|
|
* relevant plugin type information as necessary.
|
|
*/
|
|
function panels_ctools_plugin_type() {
|
|
return array(
|
|
'layouts' => array(
|
|
// We can define layouts in themes.
|
|
'load themes' => TRUE,
|
|
'process' => 'panels_layout_process',
|
|
'child plugins' => TRUE,
|
|
),
|
|
'styles' => array(
|
|
'load themes' => TRUE,
|
|
'process' => 'panels_plugin_styles_process',
|
|
'child plugins' => TRUE,
|
|
),
|
|
'cache' => array(),
|
|
'display_renderers' => array(
|
|
'classes' => array('renderer'),
|
|
),
|
|
'panels_storage' => array(),
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Ensure a layout has a minimal set of data.
|
|
*/
|
|
function panels_layout_process(&$plugin) {
|
|
$plugin += array(
|
|
'category' => t('Miscellaneous'),
|
|
'description' => '',
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Implements hook_ctools_plugin_api().
|
|
*/
|
|
function panels_ctools_plugin_api($owner, $api) {
|
|
// Inform CTools about version information for various plugins implemented by
|
|
// panels.
|
|
if ($owner == 'panels' && $api == 'styles') {
|
|
// As of 6.x-3.6, Panels has a slightly new system for style plugins.
|
|
return array('version' => 2.0);
|
|
}
|
|
|
|
if ($owner == 'panels' && $api == 'pipelines') {
|
|
return array(
|
|
'version' => 1,
|
|
'path' => drupal_get_path('module', 'panels') . '/includes',
|
|
);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Implements hook_views_api().
|
|
*/
|
|
function panels_views_api() {
|
|
return array(
|
|
'api' => 2,
|
|
'path' => drupal_get_path('module', 'panels') . '/plugins/views',
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Perform additional processing on a style plugin.
|
|
*
|
|
* Currently this is only being used to apply versioning information to style
|
|
* plugins in order to ensure the legacy renderer passes the right type of
|
|
* parameters to a style plugin in a hybrid environment of both new and old
|
|
* plugins.
|
|
*
|
|
* @param array $plugin
|
|
* The style plugin that is being processed.
|
|
* @param array $info
|
|
* The style plugin type info array.
|
|
*
|
|
* @see _ctools_process_data()
|
|
*/
|
|
function panels_plugin_styles_process(&$plugin, $info) {
|
|
$plugin += array(
|
|
'weight' => 0,
|
|
);
|
|
|
|
$compliant_modules = ctools_plugin_api_info('panels', 'styles', 2.0, 2.0);
|
|
$plugin['version'] = empty($compliant_modules[$plugin['module']]) ? 1.0 : $compliant_modules[$plugin['module']]['version'];
|
|
}
|
|
|
|
/**
|
|
* Declare what style types Panels uses.
|
|
*/
|
|
function panels_ctools_style_base_types() {
|
|
return array(
|
|
'region' => array(
|
|
'title' => t('Panel region'),
|
|
'preview' => 'panels_stylizer_region_preview',
|
|
'theme variables' => array(
|
|
'settings' => NULL,
|
|
'class' => NULL,
|
|
'content' => NULL,
|
|
),
|
|
),
|
|
'pane' => array(
|
|
'title' => t('Panel pane'),
|
|
'preview' => 'panels_stylizer_pane_preview',
|
|
'theme variables' => array(
|
|
'settings' => NULL,
|
|
'content' => NULL,
|
|
'pane' => NULL,
|
|
'display' => NULL,
|
|
),
|
|
),
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Generates Lorem Ipsum.
|
|
*
|
|
* @return string
|
|
* Lorem ipsum string.
|
|
*/
|
|
function panels_stylizer_lipsum() {
|
|
return <<<LIPSUM
|
|
<p>
|
|
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Phasellus at
|
|
velit dolor. Donec egestas tellus sit amet urna rhoncus adipiscing. Proin
|
|
nec porttitor sem. Maecenas aliquam, purus nec tempus dignissim, nulla arcu
|
|
aliquam diam, non tincidunt massa ante vel dolor. Aliquam sapien sapien,
|
|
tincidunt id tristique at, pretium sagittis libero.
|
|
</p>
|
|
<p>
|
|
Nulla facilisi. Curabitur lacinia, tellus sed tristique consequat, diam
|
|
lorem scelerisque felis, at dictum purus augue facilisis lorem. Duis
|
|
pharetra dignissim rutrum. Curabitur ac elit id dui dapibus tincidunt.
|
|
Nulla eget sem quam, non eleifend eros. Cras porttitor tempus lectus ac
|
|
scelerisque. Curabitur vehicula bibendum lorem, vitae ornare ligula
|
|
venenatis ut.
|
|
</p>
|
|
LIPSUM;
|
|
}
|
|
|
|
/**
|
|
* Generate a preview given the current settings.
|
|
*/
|
|
function panels_stylizer_region_preview($plugin, $settings) {
|
|
ctools_stylizer_add_css($plugin, $settings);
|
|
return theme(
|
|
$plugin['theme'],
|
|
array(
|
|
'settings' => $settings,
|
|
'class' => ctools_stylizer_get_css_class($plugin, $settings),
|
|
'content' => panels_stylizer_lipsum(),
|
|
)
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Generate a preview given the current settings.
|
|
*/
|
|
function panels_stylizer_pane_preview($plugin, $settings) {
|
|
ctools_stylizer_add_css($plugin, $settings);
|
|
$pane = new stdClass();
|
|
|
|
$content = new stdClass();
|
|
$content->title = t('Lorem ipsum');
|
|
$content->content = panels_stylizer_lipsum();
|
|
$content->type = 'dummy';
|
|
$content->subtype = 'dummy';
|
|
|
|
$content->css_class = ctools_stylizer_get_css_class($plugin, $settings);
|
|
|
|
$display = new panels_display();
|
|
|
|
if (!empty($plugin['theme'])) {
|
|
return theme(
|
|
$plugin['theme'],
|
|
array(
|
|
'settings' => $settings,
|
|
'content' => $content,
|
|
'pane' => $pane,
|
|
'display' => $display,
|
|
)
|
|
);
|
|
}
|
|
else {
|
|
return theme(
|
|
'panels_pane',
|
|
array(
|
|
'content' => $content,
|
|
'pane' => $pane,
|
|
'display' => $display,
|
|
)
|
|
);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 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().
|
|
*
|
|
* @param object $display
|
|
* Instanceof panels_display.
|
|
*
|
|
* A fully loaded panels $display object, as returned from
|
|
* panels_load_display(). Merely passing a did is NOT sufficient.
|
|
* 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
|
|
* 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
|
|
* 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 mixed
|
|
* 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 is 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:
|
|
* print theme('page', panels_edit($display, $destination, $content_types), FALSE);
|
|
* - 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.
|
|
*
|
|
* @ingroup mainapi
|
|
*/
|
|
function panels_edit($display, $destination = NULL, $content_types = NULL, $title = FALSE) {
|
|
ctools_include('display-edit', 'panels');
|
|
ctools_include('ajax');
|
|
ctools_include('plugins', 'panels');
|
|
return _panels_edit($display, $destination, $content_types, $title);
|
|
}
|
|
|
|
/**
|
|
* 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
|
|
* 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.
|
|
* #- 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 mixed
|
|
* 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()
|
|
* @see panels_common_set_allowed_layouts()
|
|
*/
|
|
function panels_edit_layout($display, $finish, $destination = NULL, $allowed_layouts = NULL) {
|
|
ctools_include('display-layout', 'panels');
|
|
ctools_include('plugins', 'panels');
|
|
return _panels_edit_layout($display, $finish, $destination, $allowed_layouts);
|
|
}
|
|
|
|
/**
|
|
* Panels database functions.
|
|
*/
|
|
|
|
/**
|
|
* Forms the basis of a panel display.
|
|
*/
|
|
class panels_display {
|
|
public $args = array();
|
|
public $content = array();
|
|
public $panels = array();
|
|
public $incoming_content = NULL;
|
|
public $css_id = NULL;
|
|
public $context = array();
|
|
public $did = 'new';
|
|
public $renderer = 'standard';
|
|
|
|
/**
|
|
* Add a pane.
|
|
*/
|
|
public function add_pane(&$pane, $location = NULL) {
|
|
// If no location specified, use what's set in the pane.
|
|
if (empty($location)) {
|
|
$location = $pane->panel;
|
|
}
|
|
else {
|
|
$pane->panel = $location;
|
|
}
|
|
|
|
// Generate a permanent uuid for this pane, and use
|
|
// it as a temporary pid.
|
|
$pane->uuid = ctools_uuid_generate();
|
|
$pane->pid = 'new-' . $pane->uuid;
|
|
|
|
// Add the pane to the appropriate spots.
|
|
$this->content[$pane->pid] = &$pane;
|
|
$this->panels[$location][] = $pane->pid;
|
|
}
|
|
|
|
/**
|
|
* Duplicate a pane.
|
|
*/
|
|
public function duplicate_pane($pid, $location = FALSE) {
|
|
$pane = $this->clone_pane($pid);
|
|
$this->add_pane($pane, $location);
|
|
}
|
|
|
|
/**
|
|
* Get the title from a display.
|
|
*
|
|
* The display must have already been rendered, or the setting to set the
|
|
* display's title from a pane's title will not have worked.
|
|
*
|
|
* @return mixed
|
|
* The title to use. If NULL, this means to let any default title that may
|
|
* be in use pass through. i.e, do not actually set the title.
|
|
*/
|
|
public function get_title() {
|
|
switch ($this->hide_title) {
|
|
case PANELS_TITLE_NONE:
|
|
return '';
|
|
|
|
case PANELS_TITLE_PANE:
|
|
return isset($this->stored_pane_title) ? $this->stored_pane_title : '';
|
|
|
|
case PANELS_TITLE_FIXED:
|
|
case FALSE;
|
|
// For old exported panels that are not in the database.
|
|
if (!empty($this->title)) {
|
|
return filter_xss_admin(ctools_context_keyword_substitute($this->title, array(), $this->context));
|
|
}
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Render this panels display.
|
|
*
|
|
* After checking to ensure the designated layout plugin is valid, a
|
|
* display renderer object is spawned and runs its rendering logic.
|
|
*
|
|
* @param mixed $renderer
|
|
* An instantiated display renderer object, or the name of a display
|
|
* renderer plugin+class to be fetched. Defaults to NULL. When NULL, the
|
|
* predesignated display renderer will be used.
|
|
*
|
|
* @return mixed
|
|
* NULL or output of render function.
|
|
*/
|
|
public function render($renderer = NULL) {
|
|
$layout = panels_get_layout($this->layout);
|
|
if (!$layout) {
|
|
return NULL;
|
|
}
|
|
|
|
// If we were not given a renderer object, load it.
|
|
if (!is_object($renderer)) {
|
|
// If the renderer was not specified, default to $this->renderer
|
|
// which is either standard or was already set for us.
|
|
$renderer = panels_get_renderer_handler(!empty($renderer) ? $renderer : $this->renderer, $this);
|
|
if (!$renderer) {
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
$output = '';
|
|
// Let modules act just prior to render.
|
|
foreach (module_implements('panels_pre_render') as $module) {
|
|
$function = $module . '_panels_pre_render';
|
|
$output .= $function($this, $renderer);
|
|
}
|
|
|
|
$output .= $renderer->render();
|
|
|
|
// Let modules act just after render.
|
|
foreach (module_implements('panels_post_render') as $module) {
|
|
$function = $module . '_panels_post_render';
|
|
$output .= $function($this, $renderer);
|
|
}
|
|
return $output;
|
|
}
|
|
|
|
/**
|
|
* Determine if the given user can perform the requested operation.
|
|
*
|
|
* @param string $op
|
|
* An operation like: create, read, update, or delete.
|
|
* @param object $account
|
|
* (optional) The account to check access for.
|
|
*
|
|
* @return bool
|
|
* TRUE if access is granted; otherwise FALSE.
|
|
*/
|
|
public function access($op, $account = NULL) {
|
|
global $user;
|
|
|
|
if (!$account) {
|
|
$account = $user;
|
|
}
|
|
|
|
// Even administrators need to go through the access system. However, to
|
|
// support legacy plugins, user 1 gets full access no matter what.
|
|
if ($account->uid == 1) {
|
|
return TRUE;
|
|
}
|
|
|
|
if (!in_array($op, array('create', 'read', 'update', 'delete', 'change layout'))) {
|
|
return FALSE;
|
|
}
|
|
|
|
if (empty($this->storage_type) || empty($this->storage_id)) {
|
|
return FALSE;
|
|
}
|
|
|
|
if ($this->storage_type == 'unknown') {
|
|
return FALSE;
|
|
}
|
|
|
|
$storage_plugin = panels_get_panels_storage_plugin($this->storage_type);
|
|
if (!$storage_plugin) {
|
|
return FALSE;
|
|
}
|
|
|
|
$access_callback = panels_plugin_get_function('panels_storage', $storage_plugin, 'access callback');
|
|
if (!$access_callback) {
|
|
return FALSE;
|
|
}
|
|
|
|
return $access_callback($this->storage_type, $this->storage_id, $op, $account);
|
|
}
|
|
|
|
}
|
|
|
|
/**
|
|
* End of 'defgroup mainapi', although other functions are specifically added later.
|
|
*/
|
|
|
|
/**
|
|
* Creates a new display, setting the ID to our magic new id.
|
|
*/
|
|
function panels_new_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, $set_defaults = FALSE) {
|
|
ctools_include('export');
|
|
$pane = ctools_export_new_object('panels_pane', FALSE);
|
|
$pane->pid = 'new';
|
|
$pane->type = $type;
|
|
$pane->subtype = $subtype;
|
|
if ($set_defaults) {
|
|
ctools_include('content');
|
|
$content_type = ctools_get_content_type($type);
|
|
$content_subtype = ctools_content_get_subtype($content_type, $subtype);
|
|
$pane->configuration = ctools_content_get_defaults($content_type, $content_subtype);
|
|
}
|
|
drupal_alter('panels_new_pane', $pane);
|
|
|
|
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 array
|
|
* An array of displays, keyed by their display dids.
|
|
*
|
|
* @todo schema API can drasticly simplify this code.
|
|
*/
|
|
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 (:dids)",
|
|
array(':dids' => $dids)
|
|
);
|
|
|
|
ctools_include('export');
|
|
foreach ($result as $obj) {
|
|
$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.
|
|
}
|
|
|
|
$result = db_query(
|
|
"SELECT * FROM {panels_pane} WHERE did IN (:dids) ORDER BY did, panel, position",
|
|
array(':dids' => $dids)
|
|
);
|
|
foreach ($result as $obj) {
|
|
$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;
|
|
}
|
|
|
|
/**
|
|
* Load a single display.
|
|
*
|
|
* @param int $did
|
|
* The display id (did) of the display to be loaded.
|
|
*
|
|
* @return object $display
|
|
* Returns a partially-loaded panels_display object. $display objects returned
|
|
* 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 ctools_context_create() functions.
|
|
*
|
|
* @ingroup mainapi
|
|
*/
|
|
function panels_load_display($did) {
|
|
$displays = panels_load_displays(array($did));
|
|
if (!empty($displays)) {
|
|
return array_shift($displays);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Save a display object.
|
|
*
|
|
* Note that a new $display only receives a real did once it is run through
|
|
* this function, and likewise for the pid of any new pane.
|
|
*
|
|
* Until then, a new display uses a string placeholder, 'new', in place of
|
|
* a real did, and a new pane (whether on a new $display or not) appends a
|
|
* universally-unique identifier (which is stored permanently in the 'uuid'
|
|
* field). This format is also used in place of the real pid for exports.
|
|
*
|
|
* @param object $display
|
|
* 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
|
|
* This display panel display object to return.
|
|
*
|
|
* @ingroup mainapi
|
|
*/
|
|
function panels_save_display(&$display) {
|
|
$update = (isset($display->did) && is_numeric($display->did)) ? array('did') : array();
|
|
if (empty($display->uuid) || !ctools_uuid_is_valid($display->uuid)) {
|
|
$display->uuid = ctools_uuid_generate();
|
|
}
|
|
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 = :did",
|
|
array(':did' => $display->did)
|
|
);
|
|
foreach ($result as $pane) {
|
|
$pids[$pane->pid] = $pane->pid;
|
|
}
|
|
}
|
|
|
|
// Update all the panes.
|
|
ctools_include('plugins', 'panels');
|
|
ctools_include('content');
|
|
|
|
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];
|
|
$type = ctools_get_content_type($pane->type);
|
|
|
|
$pane->position = $position++;
|
|
$pane->did = $display->did;
|
|
|
|
$old_pid = $pane->pid;
|
|
|
|
if (empty($pane->uuid) || !ctools_uuid_is_valid($pane->uuid)) {
|
|
$pane->uuid = ctools_uuid_generate();
|
|
}
|
|
|
|
drupal_write_record('panels_pane', $pane, is_numeric($pid) ? array('pid') : array());
|
|
|
|
// Allow other modules to take action after a pane is saved.
|
|
if ($pane->pid == $old_pid) {
|
|
module_invoke_all('panels_pane_update', $pane);
|
|
}
|
|
else {
|
|
module_invoke_all('panels_pane_insert', $pane);
|
|
}
|
|
|
|
if ($pane->pid != $old_pid) {
|
|
// Remove the old new-* entry from the displays content.
|
|
unset($display->content[$pid]);
|
|
|
|
// Put it back so our pids and positions can be used.
|
|
$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_update('panels_display')
|
|
->fields(array(
|
|
'title_pane' => $pane->pid,
|
|
))
|
|
->condition('did', $display->did)
|
|
->execute();
|
|
}
|
|
}
|
|
|
|
// 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]);
|
|
}
|
|
}
|
|
|
|
$display->panels[$id] = $new_panes;
|
|
}
|
|
if (!empty($pids)) {
|
|
// Allow other modules to take action before a panes are deleted.
|
|
module_invoke_all('panels_pane_delete', $pids);
|
|
db_delete('panels_pane')->condition('pid', $pids)->execute();
|
|
}
|
|
|
|
// Clear any cached content for this display.
|
|
panels_clear_cached_content($display);
|
|
|
|
// Allow other modules to take action when a display is saved.
|
|
module_invoke_all('panels_display_save', $display);
|
|
|
|
// Log the change to watchdog, using the same style as node.module.
|
|
$watchdog_args = array('%did' => $display->did);
|
|
if (!empty($display->title)) {
|
|
$watchdog_args['%title'] = $display->title;
|
|
watchdog('content', 'Panels: saved display "%title" with display id %did', $watchdog_args, WATCHDOG_NOTICE);
|
|
}
|
|
else {
|
|
watchdog('content', 'Panels: saved display with id %did', $watchdog_args, WATCHDOG_NOTICE);
|
|
}
|
|
|
|
// To be nice, even though we have a reference.
|
|
return $display;
|
|
}
|
|
|
|
/**
|
|
* Delete a display.
|
|
*/
|
|
function panels_delete_display($display) {
|
|
if (is_object($display)) {
|
|
$did = $display->did;
|
|
}
|
|
else {
|
|
$did = $display;
|
|
}
|
|
module_invoke_all('panels_delete_display', $did);
|
|
db_delete('panels_display')->condition('did', $did)->execute();
|
|
db_delete('panels_pane')->condition('did', $did)->execute();
|
|
}
|
|
|
|
/**
|
|
* 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 place-holder
|
|
* values (consisting of the display or pane's uuid, with a 'new-' prefix).
|
|
*
|
|
* Only once panels_save_display() is called on the code version of $display
|
|
* will the exported display be written to the database and permanently saved.
|
|
*
|
|
* @param object $display
|
|
* 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.
|
|
*
|
|
* @see panels_page_export() or _panels_page_fetch_display() for samples.
|
|
*
|
|
* @ingroup mainapi
|
|
*/
|
|
function panels_export_display($display, $prefix = '') {
|
|
ctools_include('export');
|
|
if (empty($display->uuid) || !ctools_uuid_is_valid($display->uuid)) {
|
|
$display->uuid = ctools_uuid_generate();
|
|
}
|
|
$display->did = 'new-' . $display->uuid;
|
|
$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)) {
|
|
$region_counters = array();
|
|
foreach ($display->content as $pane) {
|
|
|
|
if (!isset($pane->uuid) || !ctools_uuid_is_valid($pane->uuid)) {
|
|
$pane->uuid = ctools_uuid_generate();
|
|
}
|
|
$pid = 'new-' . $pane->uuid;
|
|
|
|
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";
|
|
}
|
|
}
|
|
$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";
|
|
|
|
$output .= $prefix . '$display->title_pane =' . " '$title_pid';\n";
|
|
return $output;
|
|
}
|
|
|
|
/**
|
|
* Panels Render Display.
|
|
*
|
|
* 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.
|
|
*
|
|
* @ingroup hook_invocations
|
|
*/
|
|
function panels_render_display(&$display, $renderer = NULL) {
|
|
ctools_include('plugins', 'panels');
|
|
ctools_include('context');
|
|
|
|
if (!empty($display->context)) {
|
|
if ($form_context = ctools_context_get_form($display->context)) {
|
|
$form_context->form['#theme'] = 'panels_render_display_form';
|
|
if (empty($form_context->form['#theme_wrappers']) || !in_array('form', $form_context->form['#theme_wrappers'])) {
|
|
$form_context->form['#theme_wrappers'][] = 'form';
|
|
}
|
|
$form_context->form['#display'] = &$display;
|
|
return $form_context->form;
|
|
}
|
|
}
|
|
return $display->render($renderer);
|
|
}
|
|
|
|
/**
|
|
* 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($vars) {
|
|
return $vars['element']['#display']->render();
|
|
}
|
|
|
|
/**
|
|
* Panels layout icon function.
|
|
*/
|
|
function panels_print_layout_icon($id, $layout, $title = NULL) {
|
|
ctools_add_css('panels_admin', 'panels');
|
|
$file = $layout['path'] . '/' . $layout['icon'];
|
|
return theme(
|
|
'panels_layout_icon',
|
|
array(
|
|
'id' => $id,
|
|
'image' => theme(
|
|
'image',
|
|
array(
|
|
'path' => $file,
|
|
'alt' => strip_tags($layout['title']),
|
|
'title' => strip_tags($layout['description']),
|
|
)
|
|
),
|
|
'title' => $title,
|
|
)
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Theme the layout icon image.
|
|
*
|
|
* @todo move to theme.inc
|
|
*/
|
|
function theme_panels_layout_icon($vars) {
|
|
$id = $vars['id'];
|
|
$image = $vars['image'];
|
|
$title = $vars['title'];
|
|
|
|
$output = '<div class="layout-icon">';
|
|
$output .= $image;
|
|
if ($title) {
|
|
$output .= '<div class="caption">' . $title . '</div>';
|
|
}
|
|
$output .= '</div>';
|
|
return $output;
|
|
}
|
|
|
|
/**
|
|
* Theme the layout link image.
|
|
*
|
|
* @layout
|
|
*
|
|
* @todo Why isn't this a template at this point?
|
|
* @todo Why does this take 4 arguments but only makes use of two?
|
|
*/
|
|
function theme_panels_layout_link($vars) {
|
|
$output = '<div class="' . implode(' ', $vars['class']) . '">';
|
|
$output .= $vars['image'];
|
|
$output .= '<div>' . $vars['title'] . '</div>';
|
|
$output .= '</div>';
|
|
return $output;
|
|
}
|
|
|
|
/**
|
|
* Print the layout link. Sends out to a theme function.
|
|
*
|
|
* @layout
|
|
*/
|
|
function panels_print_layout_link($id, $layout, $link, $options = array(), $current_layout = FALSE) {
|
|
if (isset($options['query']['q'])) {
|
|
unset($options['query']['q']);
|
|
}
|
|
|
|
// Setup classes for layout link, including current-layout information.
|
|
$class = array('layout-link');
|
|
if ($current_layout == $id) {
|
|
$options['attributes']['class'][] = 'current-layout-link';
|
|
$class[] = 'current-layout';
|
|
}
|
|
|
|
ctools_add_css('panels_admin', 'panels');
|
|
$file = $layout['path'] . '/' . $layout['icon'];
|
|
$image = l(
|
|
theme('image', array('path' => $file)),
|
|
$link,
|
|
array('html' => TRUE) + $options
|
|
);
|
|
$title = l($layout['title'], $link, $options);
|
|
return theme(
|
|
'panels_layout_link',
|
|
array(
|
|
'title' => $title,
|
|
'image' => $image,
|
|
'class' => $class,
|
|
)
|
|
);
|
|
}
|
|
|
|
|
|
/**
|
|
* Panels Get legacy state.
|
|
*
|
|
* Gateway to the PanelsLegacyState class/object, which does all legacy state
|
|
* checks and provides information about the cause of legacy states as needed.
|
|
*
|
|
* @return PanelsLegacyState $legacy
|
|
* Returns a legacy panels state.
|
|
*/
|
|
function panels_get_legacy_state() {
|
|
static $legacy = NULL;
|
|
if (!isset($legacy)) {
|
|
ctools_include('legacy', 'panels');
|
|
$legacy = new PanelsLegacyState();
|
|
}
|
|
return $legacy;
|
|
}
|
|
|
|
/**
|
|
* 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;
|
|
}
|
|
|
|
/**
|
|
* Clean up the panel pane variables for the template.
|
|
*/
|
|
function template_preprocess_panels_pane(&$vars) {
|
|
$content = &$vars['content'];
|
|
|
|
$vars['contextual_links'] = array();
|
|
$vars['classes_array'] = array();
|
|
$vars['admin_links'] = '';
|
|
|
|
if (module_exists('contextual') && user_access('access contextual links')) {
|
|
$links = array();
|
|
// These are specified by the content.
|
|
if (!empty($content->admin_links)) {
|
|
$links += $content->admin_links;
|
|
}
|
|
|
|
// Take any that may have been in the render array we were given and
|
|
// move them up so they appear outside the pane properly.
|
|
if (is_array($content->content) && isset($content->content['#contextual_links'])) {
|
|
$element = array(
|
|
'#type' => 'contextual_links',
|
|
'#contextual_links' => $content->content['#contextual_links'],
|
|
);
|
|
unset($content->content['#contextual_links']);
|
|
|
|
// Add content to $element array.
|
|
if (is_array($content->content)) {
|
|
$element['#element'] = $content->content;
|
|
}
|
|
|
|
$element = contextual_pre_render_links($element);
|
|
if (!empty($element['#links'])) {
|
|
$links += $element['#links'];
|
|
}
|
|
}
|
|
|
|
if ($links) {
|
|
$build = array(
|
|
'#prefix' => '<div class="contextual-links-wrapper">',
|
|
'#suffix' => '</div>',
|
|
'#theme' => 'links__contextual',
|
|
'#links' => $links,
|
|
'#attributes' => array('class' => array('contextual-links')),
|
|
'#attached' => array(
|
|
'library' => array(array('contextual', 'contextual-links')),
|
|
),
|
|
);
|
|
$vars['classes_array'][] = 'contextual-links-region';
|
|
$vars['admin_links'] = drupal_render($build);
|
|
}
|
|
}
|
|
|
|
// Basic classes.
|
|
$vars['classes_array'][] = '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 redundant classes.
|
|
$vars['classes_array'][] = $type_class;
|
|
if ($type_class != $subtype_class) {
|
|
$vars['classes_array'][] = $subtype_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_array'][] = $content->css_class;
|
|
}
|
|
}
|
|
|
|
// Set up some placeholders for constructing template file names.
|
|
$base = 'panels_pane';
|
|
$delimiter = '__';
|
|
|
|
// Add template file suggestion for content type and sub-type.
|
|
$vars['theme_hook_suggestions'][] = $base . $delimiter . $content->type;
|
|
$vars['theme_hook_suggestions'][] = $base . $delimiter . strtr(ctools_cleanstring($content->type, array('lower case' => TRUE)), '-', '_') . $delimiter . strtr(ctools_cleanstring($content->subtype, array('lower case' => TRUE)), '-', '_');
|
|
|
|
$vars['pane_prefix'] = !empty($content->pane_prefix) ? $content->pane_prefix : '';
|
|
$vars['pane_suffix'] = !empty($content->pane_suffix) ? $content->pane_suffix : '';
|
|
|
|
$vars['title'] = !empty($content->title) ? $content->title : '';
|
|
$vars['title_heading'] = !empty($content->title_heading) ? $content->title_heading : variable_get('override_title_heading', 'h2');
|
|
$vars['title_attributes_array']['class'][] = 'pane-title';
|
|
|
|
$vars['feeds'] = !empty($content->feeds) ? implode(' ', $content->feeds) : '';
|
|
|
|
$vars['links'] = !empty($content->links) ? theme('links', array('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);
|
|
}
|
|
|
|
if (!empty($content->attributes)) {
|
|
$vars['attributes_array'] = array_merge($vars['attributes_array'], $content->attributes);
|
|
}
|
|
|
|
$vars['content'] = !empty($content->content) ? $content->content : '';
|
|
|
|
}
|
|
|
|
/**
|
|
* Route Panels' AJAX calls to the correct object.
|
|
*
|
|
* Panels' AJAX is controlled mostly by renderer objects. This menu callback
|
|
* accepts the incoming request, figures out which object should handle the
|
|
* request, and attempts to route it. If no object can be found, the default
|
|
* Panels editor object is used.
|
|
*
|
|
* Calls are routed via the ajax_* method space. For example, if visiting
|
|
* panels/ajax/add-pane then $renderer::ajax_add_pane() will be called.
|
|
* This means commands can be added without having to create new callbacks.
|
|
*
|
|
* The first argument *must always* be the cache key so that a cache object
|
|
* can be passed through. Other arguments will be passed through untouched
|
|
* so that the method can do whatever it needs to do.
|
|
*/
|
|
function panels_ajax_router() {
|
|
$args = func_get_args();
|
|
if (count($args) < 3) {
|
|
return MENU_NOT_FOUND;
|
|
}
|
|
|
|
ctools_include('display-edit', 'panels');
|
|
ctools_include('plugins', 'panels');
|
|
ctools_include('ajax');
|
|
ctools_include('modal');
|
|
ctools_include('context');
|
|
ctools_include('content');
|
|
|
|
$plugin_name = array_shift($args);
|
|
$method = array_shift($args);
|
|
$cache_key = array_shift($args);
|
|
|
|
$plugin = panels_get_display_renderer($plugin_name);
|
|
if (!$plugin) {
|
|
// This is the default renderer for handling AJAX commands.
|
|
$plugin = panels_get_display_renderer('editor');
|
|
}
|
|
|
|
$cache = panels_edit_cache_get($cache_key);
|
|
if (empty($cache)) {
|
|
return MENU_ACCESS_DENIED;
|
|
}
|
|
|
|
$renderer = panels_get_renderer_handler($plugin, $cache->display);
|
|
if (!$renderer) {
|
|
return MENU_ACCESS_DENIED;
|
|
}
|
|
|
|
$method = 'ajax_' . str_replace('-', '_', $method);
|
|
if (!method_exists($renderer, $method)) {
|
|
return MENU_NOT_FOUND;
|
|
}
|
|
|
|
$renderer->cache = &$cache;
|
|
ctools_include('cleanstring');
|
|
$renderer->clean_key = ctools_cleanstring($cache_key);
|
|
|
|
$op = $renderer->get_panels_storage_op_for_ajax($method);
|
|
if (!$cache->display->access($op)) {
|
|
return MENU_ACCESS_DENIED;
|
|
}
|
|
|
|
$output = call_user_func_array(array($renderer, $method), $args);
|
|
|
|
if (empty($output) && !empty($renderer->commands)) {
|
|
return array(
|
|
'#type' => 'ajax',
|
|
'#commands' => $renderer->commands,
|
|
);
|
|
}
|
|
else {
|
|
return $output;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Panels caching functions and callbacks.
|
|
*
|
|
* When editing displays and the like, Panels has a caching system that relies
|
|
* on a callback to determine where to get the actual cache.
|
|
*
|
|
* @todo This system needs to be better documented so that it can be better used.
|
|
*/
|
|
|
|
/**
|
|
* Get an object from cache.
|
|
*/
|
|
function panels_cache_get($obj, $did, $skip_cache = FALSE) {
|
|
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) {
|
|
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) {
|
|
ctools_include('object-cache');
|
|
return ctools_object_cache_clear($obj, 'panels_display:' . $did);
|
|
}
|
|
|
|
/**
|
|
* Create the default cache for editing panel displays.
|
|
*
|
|
* If an application is using the Panels display editor without having
|
|
* specified a cache key, this method can be used to create the default
|
|
* cache.
|
|
*/
|
|
function panels_edit_cache_get_default(&$display, $content_types = NULL, $title = FALSE) {
|
|
if (empty($content_types)) {
|
|
$content_types = ctools_content_get_available_types();
|
|
}
|
|
|
|
$display->cache_key = $display->did;
|
|
panels_cache_clear('display', $display->did);
|
|
|
|
$cache = new stdClass();
|
|
$cache->display = &$display;
|
|
$cache->content_types = $content_types;
|
|
$cache->display_title = $title;
|
|
|
|
panels_edit_cache_set($cache);
|
|
return $cache;
|
|
}
|
|
|
|
/**
|
|
* Panels Editor Cache Get.
|
|
*
|
|
* Method to allow modules to provide their own caching mechanism for the
|
|
* display editor.
|
|
*/
|
|
function panels_edit_cache_get($cache_key) {
|
|
if (strpos($cache_key, ':') !== FALSE) {
|
|
list($module, $argument) = explode(':', $cache_key, 2);
|
|
return module_invoke($module, 'panels_cache_get', $argument);
|
|
}
|
|
|
|
// Fall back to our normal method.
|
|
return panels_cache_get('display', $cache_key);
|
|
}
|
|
|
|
/**
|
|
* Panels Editor Cache Set.
|
|
*
|
|
* Method to allow modules to provide their own caching mechanism for the
|
|
* display editor.
|
|
*/
|
|
function panels_edit_cache_set($cache) {
|
|
$cache_key = $cache->display->cache_key;
|
|
if (strpos($cache_key, ':') !== FALSE) {
|
|
list($module, $argument) = explode(':', $cache_key, 2);
|
|
return module_invoke($module, 'panels_cache_set', $argument, $cache);
|
|
}
|
|
|
|
// Fall back to our normal method.
|
|
return panels_cache_set('display', $cache_key, $cache);
|
|
}
|
|
|
|
/**
|
|
* Panels Editor Cache Save.
|
|
*
|
|
* Method to allow modules to provide their own mechanism to write the
|
|
* cache used in the display editor.
|
|
*/
|
|
function panels_edit_cache_save($cache) {
|
|
$cache_key = $cache->display->cache_key;
|
|
if (strpos($cache_key, ':') !== FALSE) {
|
|
list($module, $argument) = explode(':', $cache_key, 2);
|
|
if (function_exists($module . '_panels_cache_save')) {
|
|
return module_invoke($module, 'panels_cache_save', $argument, $cache);
|
|
}
|
|
}
|
|
|
|
// Fall back to our normal method.
|
|
return panels_save_display($cache->display);
|
|
}
|
|
|
|
/**
|
|
* Panels Editor Cache Clear.
|
|
*
|
|
* Method to allow modules to provide their own mechanism to clear the
|
|
* cache used in the display editor.
|
|
*/
|
|
function panels_edit_cache_clear($cache) {
|
|
$cache_key = $cache->display->cache_key;
|
|
if (strpos($cache_key, ':') !== FALSE) {
|
|
list($module, $argument) = explode(':', $cache_key, 2);
|
|
if (function_exists($module . '_panels_cache_clear')) {
|
|
return module_invoke($module, 'panels_cache_clear', $argument, $cache);
|
|
}
|
|
}
|
|
|
|
// Fall back to our normal method.
|
|
return panels_cache_clear('display', $cache_key);
|
|
}
|
|
|
|
/**
|
|
* Method to allow modules to provide a mechanism to break locks.
|
|
*/
|
|
function panels_edit_cache_break_lock($cache) {
|
|
if (empty($cache->locked)) {
|
|
return;
|
|
}
|
|
|
|
$cache_key = $cache->display->cache_key;
|
|
if (strpos($cache_key, ':') !== FALSE) {
|
|
list($module, $argument) = explode(':', $cache_key, 2);
|
|
if (function_exists($module . '_panels_cache_break_lock')) {
|
|
return module_invoke($module, 'panels_cache_break_lock', $argument, $cache);
|
|
}
|
|
}
|
|
|
|
// Normal panel display editing has no locks, so we do nothing if there is
|
|
// no fallback.
|
|
}
|
|
|
|
/**
|
|
* Callbacks on behalf of the panel_context plugin.
|
|
*
|
|
* The panel_context plugin lets Panels be used in page manager. These
|
|
* callbacks allow the display editing system to use the page manager
|
|
* cache rather than the default display cache. They are routed by the cache
|
|
* key via panels_edit_cache_* functions.
|
|
*/
|
|
|
|
/**
|
|
* 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::args::url;
|
|
*/
|
|
function panel_context_panels_cache_get($key) {
|
|
ctools_include('common', 'panels');
|
|
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, $args, $q) = explode('::', $key, 4);
|
|
$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();
|
|
|
|
$task = page_manager_get_task($page->task_id);
|
|
$arguments = array();
|
|
if ($args) {
|
|
$arguments = explode('\\', $args);
|
|
$contexts = ctools_context_handler_get_task_contexts($task, $page->subtask, $arguments);
|
|
$contexts = ctools_context_handler_get_handler_contexts($contexts, $handler);
|
|
}
|
|
else {
|
|
$contexts = ctools_context_handler_get_all_contexts($page->task, $page->subtask, $handler);
|
|
}
|
|
|
|
$cache->display = &panels_panel_context_get_display($handler);
|
|
$cache->display->context = $contexts;
|
|
$cache->display->cache_key = 'panel_context:' . $key;
|
|
$cache->content_types = panels_common_get_allowed_types('panels_page', $cache->display->context);
|
|
$cache->display_title = TRUE;
|
|
$cache->locked = $page->locked;
|
|
|
|
return $cache;
|
|
}
|
|
|
|
/**
|
|
* Get the Page Manager cache for the panel_context plugin.
|
|
*/
|
|
function _panel_context_panels_cache_get_page_cache($key, $cache) {
|
|
list($task_name, $handler_name, $args, $q) = explode('::', $key, 4);
|
|
$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;
|
|
}
|
|
|
|
return $page;
|
|
}
|
|
|
|
/**
|
|
* Store a display edit in progress in the page cache.
|
|
*/
|
|
function panel_context_panels_cache_set($key, $cache) {
|
|
$page = _panel_context_panels_cache_get_page_cache($key, $cache);
|
|
page_manager_set_page_cache($page);
|
|
}
|
|
|
|
/**
|
|
* Save all changes made to a display using the Page Manager page cache.
|
|
*/
|
|
function panel_context_panels_cache_clear($key, $cache) {
|
|
$page = _panel_context_panels_cache_get_page_cache($key, $cache);
|
|
page_manager_clear_page_cache($page->task_name);
|
|
}
|
|
|
|
/**
|
|
* Save all changes made to a display using the Page Manager page cache.
|
|
*/
|
|
function panel_context_panels_cache_save($key, $cache) {
|
|
$page = _panel_context_panels_cache_get_page_cache($key, $cache);
|
|
page_manager_save_page_cache($page);
|
|
}
|
|
|
|
/**
|
|
* Break the lock on a page manager page.
|
|
*/
|
|
function panel_context_panels_cache_break_lock($key, $cache) {
|
|
$page = _panel_context_panels_cache_get_page_cache($key, $cache);
|
|
ctools_object_cache_clear_all('page_manager_page', $page->task_name);
|
|
}
|
|
|
|
/**
|
|
* Callbacks on behalf of the panels page wizards.
|
|
*
|
|
* The page wizards are a pluggable set of 'wizards' to make it easy to create
|
|
* specific types of pages based upon whatever someone felt like putting
|
|
* together. Since they will very often have content editing, we provide
|
|
* a generic mechanism to allow them to store their editing cache in the
|
|
* wizard cache.
|
|
*
|
|
* For them to use this mechanism, they just need to use:
|
|
* $cache = panels_edit_cache_get('panels_page_wizard:' . $plugin['name']);.
|
|
*/
|
|
|
|
/**
|
|
* Get display edit cache for the panels mini export UI.
|
|
*
|
|
* The key is the second half of the key in this form:
|
|
* panels_page_wizard:TASK_NAME:HANDLER_NAME;
|
|
*/
|
|
function panels_page_wizard_panels_cache_get($key) {
|
|
ctools_include('page-wizard');
|
|
ctools_include('context');
|
|
$wizard_cache = page_manager_get_wizard_cache($key);
|
|
if (isset($wizard_cache->display_cache)) {
|
|
return $wizard_cache->display_cache;
|
|
}
|
|
|
|
ctools_include('common', 'panels');
|
|
$cache = new stdClass();
|
|
$cache->display = $wizard_cache->display;
|
|
$cache->display->context = !empty($wizard_cache->context) ? $wizard_cache->context : array();
|
|
$cache->display->cache_key = 'panels_page_wizard:' . $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 panels_page_wizard_panels_cache_set($key, $cache) {
|
|
ctools_include('page-wizard');
|
|
$wizard_cache = page_manager_get_wizard_cache($key);
|
|
$wizard_cache->display_cache = $cache;
|
|
page_manager_set_wizard_cache($wizard_cache);
|
|
}
|
|
|
|
/**
|
|
* Implements hook_default_page_manager_handlers_alter().
|
|
*
|
|
* If a default Panels display has no storage type, set it.
|
|
*/
|
|
function panels_default_page_manager_handlers_alter(&$handlers) {
|
|
foreach ($handlers as &$handler) {
|
|
if ($handler->handler == 'panel_context') {
|
|
$display =& $handler->conf['display'];
|
|
if (empty($display->storage_type)) {
|
|
$display->storage_type = 'page_manager';
|
|
$display->storage_id = $handler->name;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Implements hook_default_page_manager_pages_alter().
|
|
*/
|
|
function panels_default_page_manager_pages_alter(&$pages) {
|
|
foreach ($pages as &$page) {
|
|
panels_default_page_manager_handlers_alter($page->default_handlers);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* General utility functions.
|
|
*/
|
|
|
|
/**
|
|
* Perform a drupal_goto on a destination that may be an array like url().
|
|
*/
|
|
function panels_goto($destination) {
|
|
if (!is_array($destination)) {
|
|
return drupal_goto($destination);
|
|
}
|
|
else {
|
|
// Prevent notices by adding defaults.
|
|
$destination += array(
|
|
'query' => NULL,
|
|
'fragment' => NULL,
|
|
'http_response_code' => NULL,
|
|
);
|
|
|
|
return drupal_goto(
|
|
$destination['path'],
|
|
$destination['query'],
|
|
$destination['fragment'],
|
|
$destination['http_response_code']
|
|
);
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* For external use: Given a layout ID and $content array, return 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.
|
|
* @code
|
|
* array(
|
|
* 'left' => t('Left side'),
|
|
* 'right' => t('Right side')
|
|
* ),
|
|
*
|
|
* Then the $content array should be
|
|
* @code
|
|
* array(
|
|
* 'left' => $output_left,
|
|
* 'right' => $output_right,
|
|
* )
|
|
*
|
|
* The output within each panel region can be either a single rendered
|
|
* HTML string or an array of rendered HTML strings as though they were
|
|
* panes. They will simply be concatenated together without separators.
|
|
*/
|
|
function panels_print_layout($layout, $content, $meta = 'standard') {
|
|
ctools_include('plugins', 'panels');
|
|
|
|
// Create a temporary display for this.
|
|
$display = panels_new_display();
|
|
$display->layout = is_array($layout) ? $layout['name'] : $layout;
|
|
$display->content = $content;
|
|
|
|
// Get our simple renderer.
|
|
$renderer = panels_get_renderer_handler('simple', $display);
|
|
$renderer->meta_location = $meta;
|
|
|
|
return $renderer->render();
|
|
}
|
|
|
|
/**
|
|
* Filter callback for array_filter to remove builders from a list of layouts.
|
|
*/
|
|
function _panels_builder_filter($layout) {
|
|
return empty($layout['builder']);
|
|
}
|
|
|
|
/**
|
|
* Implements hook_get_pane_links_alter().
|
|
*/
|
|
function panels_get_pane_links_alter(&$links, $pane, $content_type) {
|
|
// Add links to the Panels pane dropdown menu.
|
|
if ($pane->type === "block") {
|
|
$prefixed_name = $pane->subtype;
|
|
|
|
// Breakup the subtype string into parts.
|
|
$exploded_subtype = explode('-', $pane->subtype);
|
|
|
|
// Get the first part of the string.
|
|
$subtype_prefix = $exploded_subtype[0];
|
|
|
|
// Get the first part of the string and add a hyphen.
|
|
$subtype_prefix_hyphen = $exploded_subtype[0] . '-';
|
|
|
|
// Remove the prefix block- to get the name.
|
|
$name_of_block = ltrim($prefixed_name, $subtype_prefix_hyphen);
|
|
|
|
// Check for user added menus created at /admin/structure/menu/add
|
|
// menus of that type have a subtype that is prefixed with menu-menu-.
|
|
if (substr($prefixed_name, 0, 10) === "menu-menu-") {
|
|
// Remove the first prefix menu- from menu-menu- to get the name.
|
|
$name_of_block = substr($prefixed_name, 5);
|
|
|
|
$links['top'][] = array(
|
|
'title' => t('Edit block'),
|
|
'href' => url('admin/structure/block/manage/' . $subtype_prefix . '/' . $name_of_block . '/configure', array('absolute' => TRUE)),
|
|
'attributes' => array('target' => array('_blank')),
|
|
);
|
|
|
|
$links['top'][] = array(
|
|
'title' => t('Edit menu links'),
|
|
'href' => url('admin/structure/menu/manage/' . $name_of_block, array('absolute' => TRUE)),
|
|
'attributes' => array('target' => array('_blank')),
|
|
);
|
|
}
|
|
|
|
// Check for module provided menu blocks like Devels or Features
|
|
// menus of that type have a subtype that is prefixed with menu-.
|
|
elseif (substr($prefixed_name, 0, 5) === "menu-") {
|
|
// Remove the first prefix menu- to get the name.
|
|
$name_of_block = substr($prefixed_name, 5);
|
|
|
|
$links['top'][] = array(
|
|
'title' => t('Edit block'),
|
|
'href' => url('admin/structure/block/manage/' . $subtype_prefix . '/' . $name_of_block . '/configure', array('absolute' => TRUE)),
|
|
'attributes' => array('target' => array('_blank')),
|
|
);
|
|
|
|
$links['top'][] = array(
|
|
'title' => t('Edit menu links'),
|
|
'href' => url('admin/structure/menu/manage/' . $name_of_block, array('absolute' => TRUE)),
|
|
'attributes' => array('target' => array('_blank')),
|
|
);
|
|
}
|
|
|
|
// Check for system blocks with menu links.
|
|
elseif (substr($prefixed_name, 0, 7) === "system-") {
|
|
// Remove the first prefix system- to get the name.
|
|
$name_of_block = substr($prefixed_name, 7);
|
|
|
|
$names_of_system_menus = menu_list_system_menus();
|
|
|
|
$links['top'][] = array(
|
|
'title' => t('Edit block'),
|
|
'href' => url('admin/structure/block/manage/' . $subtype_prefix . '/' . $name_of_block . '/configure', array('absolute' => TRUE)),
|
|
'attributes' => array('target' => array('_blank')),
|
|
);
|
|
|
|
if (array_key_exists($name_of_block, $names_of_system_menus)) {
|
|
$links['top'][] = array(
|
|
'title' => t('Edit menu links'),
|
|
'href' => url('admin/structure/menu/manage/' . $name_of_block, array('absolute' => TRUE)),
|
|
'attributes' => array('target' => array('_blank')),
|
|
);
|
|
}
|
|
}
|
|
|
|
// For all other blocks without menus.
|
|
else {
|
|
$links['top'][] = array(
|
|
'title' => t('Edit block'),
|
|
'href' => url('admin/structure/block/manage/' . $subtype_prefix . '/' . $name_of_block . '/configure', array('absolute' => TRUE)),
|
|
'attributes' => array('target' => array('_blank')),
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Deprecated functions.
|
|
*
|
|
* Everything below this line will eventually go away.
|
|
*/
|
|
|
|
/**
|
|
* 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;
|
|
}
|
|
|
|
/**
|
|
* Remove default sidebar related body classes and provide own css classes.
|
|
*/
|
|
function panels_preprocess_html(&$vars) {
|
|
$panel_body_css = &drupal_static('panel_body_css');
|
|
if (!empty($panel_body_css['body_classes_to_remove'])) {
|
|
$classes_to_remove = array_filter(explode(' ', $panel_body_css['body_classes_to_remove']), 'strlen');
|
|
foreach ($vars['classes_array'] as $key => $css_class) {
|
|
if (in_array($css_class, $classes_to_remove)) {
|
|
unset($vars['classes_array'][$key]);
|
|
}
|
|
}
|
|
}
|
|
if (!empty($panel_body_css['body_classes_to_add'])) {
|
|
$vars['classes_array'][] = check_plain($panel_body_css['body_classes_to_add']);
|
|
}
|
|
}
|