type = $type; $this->data = $data; $this->title = t('Unknown context'); } function is_type($type) { if ($type == 'any' || $this->type == 'any') { return TRUE; } $a = is_array($type) ? $type : array($type); $b = is_array($this->type) ? $this->type : array($this->type); return (bool) array_intersect($a, $b); } function get_argument() { return $this->argument; } function get_keyword() { return $this->keyword; } function get_identifier() { return $this->identifier; } function get_title() { return $this->title; } function get_page_title() { return $this->page_title; } } /** * This is used to explain to Panels what context is required. */ class panels_required_context { var $keywords = ''; function panels_required_context() { $args = func_get_args(); $this->title = array_shift($args); if (count($args) == 1) { $args = array_shift($args); } $this->keywords = $args; } function filter($contexts) { $result = array(); // See which of these contexts are valid foreach ($contexts as $cid => $context) { if ($context->is_type($this->keywords)) { $result[$cid] = $context; } } return $result; } function select($contexts, $context) { if (empty($context) || empty($contexts[$context])) { return; } return $contexts[$context]; } } class panels_optional_context extends panels_required_context { function panels_optional_context() { $args = func_get_args(); call_user_func_array(array($this, 'panels_required_context'), $args); } /** * Add the 'empty' context which is possible for optional */ function add_empty(&$contexts) { $context = new panels_context('any'); $context->title = t('No context'); $context->identifier = t('No context'); $contexts = array_merge(array('empty' => $context), $contexts); } function filter($contexts) { $this->add_empty($contexts); return parent::filter($contexts); } function select($contexts, $context) { $this->add_empty($contexts); return parent::select($contexts, $context); } } /** * @defgroup panels_content Panels content and pane helper/info functions * @{ */ /** * Determine visibility of a panel pane * * @param $pane * The pane object to test. * @param $account * The account to test access against. */ function panels_pane_access($pane, $account = NULL) { if (!$account) { global $user; $account = $user; } // Administrator privileges if (user_access('view all pane', $account)) { return TRUE; } // All views with an empty access setting are available to all roles. if (!$pane->access || !is_array($pane->access)) { return TRUE; } // Otherwise, check roles static $roles = array(); if (!isset($roles[$account->uid])) { $roles[$account->uid] = array_keys($account->roles); $roles[$account->uid][] = $account->uid ? DRUPAL_AUTHENTICATED_RID : DRUPAL_ANONYMOUS_RID; } return array_intersect($pane->access, $roles[$account->uid]); } /** * Get the content from a given content type. * * @param $type * The content type. May be the name or an already loaded content type object. * @param $conf * The configuration for the content type. * @param $args * The arguments to the content type. * @param $context * The panels_context object. * @Param $incoming_content * Any incoming content, if this display is a wrapper. */ function panels_ct_get_content($type, $conf, $args, $context, $incoming_content) { if ($function = panels_plugin_get_function('content_types', $type, 'render callback')) { return $function($conf, $args, $context, $incoming_content); } } /** * Get the title from a given content type. * * @param $type * The content type. May be the name or an already loaded content type object. * @param $conf * The configuration for the content type. * @param $context * The panels_context object. * @Param $incoming_content * Any incoming content, if this display is a wrapper. */ function panels_ct_get_title($type, $conf, $context = NULL, $incoming_content = NULL) { if ($function = panels_plugin_get_function('content_types', $type, 'title callback')) { return $function($conf, $context, $incoming_content); } } /** * Create the basic config form * * TODO: This is in the wrong place, it should be part of the master form. */ function panels_ct_conf_form($type, $id, $contexts, $conf) { $content_type = panels_get_content_type($type); $subtype = panels_ct_get_types($type); $form = array(); if (!empty($subtype[$id]['required context']) && is_array($contexts)) { $form['context'] = panels_context_selector($contexts, $subtype[$id]['required context'], $conf['context']); } if (empty($content_type['no title override'])) { $form['aligner_start'] = array( '#value' => '
', ); $form['override_title'] = array( '#type' => 'checkbox', '#default_value' => $conf['override_title'], '#title' => t('Override title'), '#id' => 'override-title-checkbox', ); $form['override_title_text'] = array( '#type' => 'textfield', '#default_value' => $conf['override_title_text'], '#size' => 35, '#id' => 'override-title-textfield', ); $form['aligner_stop'] = array( '#value' => '
', ); $form['override_title_markup'] = array( '#prefix' => '
', '#suffix' => '
', '#value' => t('You may use %keywords from contexts, as well as %title to contain the original title.'), ); } return $form; } /** * Get the form to add a new instance of a content type. * * @param $type * The content type. May be the name or an already loaded content type object. * @param $id * The identifier for the content to add; this is specific to the type of * content. * @param $contexts * A list of possible contexts. * @param $parents * The #parents to be used on the form, because some form gadgets need to * know where they live. */ function panels_ct_get_add_form($type, $id, $contexts, $parents) { $form = panels_ct_conf_form($type, $id, $contexts, array()); if ($function = panels_plugin_get_function('content_types', $type, 'add callback')) { $result = $function($id, $parents); if (is_array($result)) { $form += $result; } } return $form; } /** * Call the validate handler for the add form for a content type. * @param $type * The content type. May be the name or an already loaded content type object. * @param $form * The actual Forms API form that is being validated. * @param $form_values * The actual Forms API values being validated. */ function panels_ct_validate_add_form($type, $form, $form_values) { if ($function = panels_plugin_get_function('content_types', $type, 'add validate callback')) { return $function($form, $form_values); } } /** * Call the submit handler for the add form for a content type. * @param $type * The content type. May be the name or an already loaded content type object. * @param $form_values * The actual Forms API values being validated. */ function panels_ct_submit_add_form($type, $form_values) { if ($function = panels_plugin_get_function('content_types', $type, 'add submit callback')) { return $function($form_values); } } /** * Get the form to edit an instance of a content type. * * @param $type * The content type. May be the name or an already loaded content type object. * @param $id * The identifier for the content to add; this is specific to the type of * content. * @param $parents * The #parents to be used on the form, because some form gadgets need to * know where they live. */ function panels_ct_get_edit_form($type, $subtype, $contexts, $conf, $parents) { $form = panels_ct_conf_form($type, $subtype, $contexts, $conf); if ($function = panels_plugin_get_function('content_types', $type, 'edit callback')) { $result = $function($subtype, $parents, $conf); if (is_array($result)) { $form += $result; } } return $form; } /** * Call the validate handler for the edit form for a content type. * @param $type * The content type. May be the name or an already loaded content type object. * @param $form * The actual Forms API form that is being validated. * @param $form_values * The actual Forms API values being validated. */ function panels_ct_validate_edit_form($type, $form, $form_values) { if ($function = panels_plugin_get_function('content_types', $type, 'edit validate callback')) { return $function($form, $form_values); } } /** * Call the submit handler for the edit form for a content type. * @param $type * The content type. May be the name or an already loaded content type object. * @param $form_values * The actual Forms API values being validated. */ function panels_ct_submit_edit_form($type, $form_values) { if ($function = panels_plugin_get_function('content_types', $type, 'edit submit callback')) { return $function($form_values); } } /** * Get all of the individual types provided by a given content types. This * would be all of the blocks for the block type, or all of the views for * the view type. * * @param $type * The content type to load. */ function panels_ct_get_types($type) { if (is_array($type)) { $content_type = $type; } else { $content_type = panels_get_content_type($type); } $function = $content_type['content_types']; if (is_array($function)) { return $function; } if (function_exists($function)) { return $function($conf); } } /** * Get a list of panels available in the layout. @layout */ function panels_get_panels($layout, $display) { if (!empty($layout['panels function']) && function_exists($layout['panels function'])) { return $layout['panels function']($display, $display->layout_settings); } if (!empty($layout['panels'])) { return $layout['panels']; } return array(); } /** * Get an array of all available content types that can be fed into the * display editor for the add content list. * * @param $context * If a context is provided, content that requires that context can apepar. * @param $has_content * Whether or not the display will have incoming content * @param $allowed_types * An array of content allowed keyed boy content_type . '-' . sub_type * @param $default_types * A default allowed/denied status for content that isn't known about */ function panels_get_available_content_types($contexts = NULL, $has_content = FALSE, $allowed_types = NULL, $default_types = NULL) { $content_types = panels_get_content_types(); $available = array(); foreach ($content_types as $id => $type) { foreach (panels_ct_get_types($type) as $cid => $cinfo) { // exclude items that require content if we're saying we don't // provide it. if (!empty($cinfo['requires content']) && !$has_content) { continue; } // Check to see if the content type can be used in this context. if (!empty($cinfo['required context'])) { if (!panels_context_filter($contexts, $cinfo['required context'])) { continue; } } // Check to see if the passed-in allowed types allows this content. if ($allowed_types) { $key = $id . '-' . $cid; if (!isset($allowed_types[$key])) { $allowed_types[$key] = isset($default_types[$id]) ? $default_types[$id] : $default_types['other']; } if (!$allowed_types[$key]) { continue; } } // If we made it through all the tests, then we can use this content. $available[$id][$cid] = $cinfo; } } return $available; } /** * Get an array of all content types that can be fed into the * display editor for the add content list, regardless of * availability. * */ function panels_get_all_content_types() { $content_types = panels_get_content_types(); $available = array(); foreach ($content_types as $id => $type) { foreach (panels_ct_get_types($type) as $cid => $cinfo) { // If we made it through all the tests, then we can use this content. $available[$id][$cid] = $cinfo; } } return $available; } // ------------------------------------------------------------------ // Functions to provide information about a panel or part of a panel. /** * Get the content from a given pane. * * @param $pane * The pane to retrieve content from. * @param $args * The arguments sent to the display. * @param $context * The panels context. * @param $incoming_content * Any incoming content if this display is a wrapper. */ function panels_get_pane_content($pane, $args = array(), $context = NULL, $incoming_content = '') { if (!$context) { $context = new panels_context; } if (!$incoming_content === '') { $incoming_content = t('Incoming content will be displayed here.'); } return panels_ct_get_content($pane->type, $pane->configuration, $args, $context, $incoming_content); } /** * Get the title of a pane. * * @param $pane * The $pane object. */ function panels_get_pane_title(&$pane, $context = NULL, $incoming_content = NULL) { if (empty($pane->context)) { $pane->context = panels_pane_select_context($pane, $context); if ($pane->context === FALSE) { return FALSE; } } return panels_ct_get_title($pane->type, $pane->configuration, $pane->context, $incoming_content); } /** * @} End of "defgroup panels_content". */ // --------------------------------------------------------------------------- // panels argument helpers /** * Get a context from an argument */ function panels_argument_get_context($argument, $arg, $empty = FALSE) { if ($function = panels_plugin_get_function('arguments', $argument['name'], 'context')) { $context = $function($arg, $argument['argument_settings'], $empty); if ($context) { $context->identifier = $argument['identifier']; $context->page_title = $argument['title']; $context->keyword = $argument['keyword']; return $context; } } } /** * Pick which display an argument wants to use */ function panels_argument_choose_display($type, $conf, $context) { if ($function = panels_plugin_get_function('arguments', $type, 'choose display')) { return $function($conf, $context); } } /** * Determine a unique context ID for an argument */ function panels_argument_context_id($argument) { return "argument_$argument[name]_$argument[id]"; } /** * Retreive a list of empty contexts for all arguments */ function panels_argument_get_contexts($arguments) { $contexts = array(); foreach ($arguments as $argument) { $context = panels_argument_get_context($argument, NULL, true); if ($context) { $contexts[panels_argument_context_id($argument)] = $context; } } return $contexts; } // --------------------------------------------------------------------------- // panels relationship helpers /** * Fetch all relevant relationships * * @param $contexts * An array of contexts used to figure out which relationships are relevant. * * @return * An array of relationship keys that are relevant for the given set of * arguments. */ function panels_get_relevant_relationships($contexts) { $relevant = array(); $relationships = panels_get_relationships(); // Go through each relationship foreach ($relationships as $rid => $relationship) { // For each relationship, see if there is a context that satisfies it. if (panels_context_filter($contexts, $relationship['required context'])) { $relevant[$rid] = $relationship['title']; } } return $relevant; } /** * Fetch all active relationships * * @param $relationships * An keyed array of relationship data including: * - name: name of relationship * - context: context id relationship belongs to. * * @param $contexts * A keyed array of contexts used to figure out which relationships * are relevant. New contexts will be added to this. * */ function panels_relationship_get_contexts($relationships, &$contexts) { $return = array(); foreach ($relationships as $rdata) { if (empty($contexts[$rdata['context']])) { continue; } $relationship = panels_get_relationship($rdata['name']); // If the relationship can't be found or its context can't be found, // ignore. if (!$relationship) { continue; } $cid = panels_relationship_context_id($rdata); if ($context = panels_relationship_get_context($rdata, $contexts[$rdata['context']])) { $contexts[$cid] = $context; } } } /** * Determine a unique context ID for an argument */ function panels_relationship_context_id($relationship) { return "relationship_$relationship[name]_$relationship[id]"; } /** * Fetch a context from a relationship, given the context input. */ function panels_relationship_get_context($relationship, $arg) { if ($function = panels_plugin_get_function('relationships', $relationship['name'], 'context')) { $context = $function($arg, $relationship['relationship_settings']); if ($context) { $context->identifier = $relationship['identifier']; $context->page_title = $relationship['title']; $context->keyword = $relationship['keyword']; return $context; } } } // --------------------------------------------------------------------------- // panels context helpers /** * Return a keyed array of context that match the given 'required context' * filters. * * @param $contexts * A keyed array of all available contexts. * @param $required * The required context string or array. * * @return * A keyed array of contexts. */ function panels_context_filter($contexts, $required) { if (is_array($required)) { $result = array(); foreach ($required as $r) { $result = array_merge($result, _panels_context_filter($contexts, $r)); } return $result; } return _panels_context_filter($contexts, $required); } function _panels_context_filter($contexts, $required) { // TODO: Multiples $result = array(); if (is_object($required)) { $result = $required->filter($contexts); } return $result; } /** * Create a select box to choose possible contexts. This only creates a * selector if there is actually a choice. * * @param $contexts * A keyed array of all available contexts. * @param $required * The required context string or array. * * @return * A form element, or NULL if there are no contexts that satisfy the * requirements. */ function panels_context_selector($contexts, $required, $default) { if (is_array($required)) { $result = array(); $count = 1; foreach ($required as $id => $r) { $result[] = _panels_context_selector($contexts, $r, $default[$id], $count++); } return $result; } return _panels_context_selector($contexts, $required, $default); } function _panels_context_selector($contexts, $required, $default, $num = 0) { $filtered = panels_context_filter($contexts, $required); $count = count($filtered); $form = array(); if ($count == 1) { $keys = array_keys($filtered); return array( '#type' => 'value', '#value' => $keys[0], ); } if ($count > 1) { // If there's more than one to choose from, create a select widget. foreach ($filtered as $cid => $context) { $options[$cid] = $context->get_identifier(); } if (!empty($required->title)) { $title = $required->title; } else { $title = $num ? t('Context %count', array('%count' => $num)) : t('Context'); } return array( '#type' => 'select', '#options' => $options, '#title' => $title, '#description' => t('Multiple contexts are valid for this pane; one must be chosen.'), '#default_value' => $default, ); } } /** * Choose a context based upon the selection made via panels_context_filter * * @param $contexts * A keyed array of all available contexts * @param $required * The required context object provided by the plugin * @param $context * The selection made using panels_context_selector */ function panels_context_select($contexts, $required, $context) { if (is_array($required)) { $result = array(); foreach ($required as $id => $r) { $result[] = _panels_context_select($contexts, $r, $context[$id]); } return $result; } return _panels_context_select($contexts, $required, $context); } function _panels_context_select($contexts, $required, $context) { if (!is_object($required)) { return; } return $required->select($contexts, $context); } /** * Create a new context. * * @param $type * The type of context to create; this loads a plugin. * @param $data * The data to put into the context. * @param $empty * Whether or not this context is specifically empty. * @param $conf * A configuration structure if this context was created via UI. * * @return * A $context or NULL if one could not be created. */ function panels_context_create($type, $data = NULL, $conf = FALSE) { if ($function = panels_plugin_get_function('contexts', $type, 'context')) { return $function(FALSE, $data, $conf); } } function panels_context_create_empty($type) { if ($function = panels_plugin_get_function('contexts', $type, 'context')) { return $function(TRUE); } } /** * When loading a display from cache, we may need to load the plugins * for any cached contexts on it, or the objects won't be available. */ function panels_context_load_plugins($contexts) { $loaded = array(); foreach ($contexts as $context) { if (isset($context->plugin) && empty($loaded[$context->plugin])) { panels_get_context($context->plugin); $loaded[$context->plugin] = TRUE; } } } /** * Fetch keywords for use in string substitutions. * * @param $contexts * An array of contexts. * * @return * An array of keyword substitutions suitable for @code{strtr()} */ function panels_context_get_keywords($contexts) { $keywords = array(); foreach ($contexts as $id => $context) { if ($keyword = $context->get_keyword()) { $keywords["%$keyword"] = $context->get_title(); } } return $keywords; } /** * Get a function from a plugin, if it exists. * * @param $plugin * The type of plugin * @param $which * Either the loaded plugin object, or the specific plugin. * @param $function_name * The identifier of the function. For example, 'settings form'. * * @return * The actual name of the function to call, or NULL if the function * does not exist. */ function panels_plugin_get_function($plugin, $which, $function_name) { if (is_object($which)) { $object = $which; } else { $hook = "panels_$plugin"; $object = panels_get_plugins($plugin, $hook, $which); } $function = $object[$function_name]; if (function_exists($function)) { return $function; } } // --------------------------------------------------------------------------- // panels data loading /** * Load plugins from a directory. * * @param $directory * The directory to choose; also the plugin type. * @param $hook * The name of the hook to be invoked. * @param $file * The file to load if we're looking for just one particular plugin. * * @return * An array of information created for this plugin. */ function panels_load_includes($directory, $hook, $file = NULL) { // Load all our plugins. $path = panels_get_path($directory); $files = drupal_system_listing("$file" . '.inc$', $path, 'name', 0); $info = array(); foreach($files as $file) { require_once('./' . $file->filename); $result = _panels_process_plugin('panels', 'panels_' . $file->name, dirname($file->filename), $hook); if (is_array($result)) { $info = array_merge($info, $result); } } return $info; } /** * Load plugin info for all hooks; this is handled separately from plugins * from files. This is cached so we don't find ourselves building htis * repeatedly. * * @param $hook * The hook being invoked. * * @return * An array of info supplied by any hook implementations. */ function panels_load_hooks($hook) { $info = array(); foreach (module_implements($hook) as $module) { $result = _panels_process_plugin($module, $module, drupal_get_path('module', $module), $hook); if (is_array($result)) { $info = array_merge($info, $result); } } return $info; } /** * Process a single hook implementation of a panels plugin. * * @param $module * The module that owns the hook. * @param $identifier * Either the module or 'panels_' . $file->name * @param $hook * The name of the hook being invoked. */ function _panels_process_plugin($module, $identifier, $path, $hook) { $function = $identifier . '_' . $hook; if (!function_exists($function)) { return NULL; } $result = $function(); if (!isset($result) || !is_array($result)) { return NULL; } // Fill in defaults. foreach ($result as $name => $plugin) { $result[$name]['module'] = $module; $result[$name]['name'] = $name; $result[$name]['path'] = $path; } return $result; } /** * Fetch a group of plugins by name. * * @param $plugin * This is the name of the plugin, and also the name of the directory. * @param $hook * This is the hook to call to get the info for the plugin. * * @return * An array of information arrays about the plugins received. */ function panels_get_plugins($plugin, $hook, $id = NULL) { static $plugins = array(); static $all_hooks = array(); static $all_files = array(); // Always load all hooks if we need them. if (!isset($all_hooks[$plugin])) { $all_hooks[$plugin] = TRUE; $plugins[$plugin] = panels_load_hooks($hook); // TODO: DEPRECATED. Put in to make the transition easier! This WILL // disappear by alpha 11. if ($plugin == 'styles') { $plugins[$plugin] = array_merge($plugins[$plugin], panels_load_hooks('panels_panel_style_info')); } } // First, see if it's in our hooks before we even bother. if ($id && array_key_exists($id, $plugins[$plugin])) { return $plugins[$plugin][$id]; } // Then see if we should load all files. We only do this if we're // want a list of all plugins. if (!$id && empty($all_files[$plugin])) { $all_files[$plugin] = TRUE; $plugins[$plugin] = array_merge($plugins[$plugin], panels_load_includes($plugin, $hook)); } // If no id was requested, we are finished here: if (!$id) { return $plugins[$plugin]; } // Check to see if we need to look for the file if (!array_key_exists($id, $plugins[$plugin])) { $result = panels_load_includes($plugin, $hook, $id); // Set to either what was returned or NULL. $plugins[$plugin][$id] = isset($result[$id]) ? $result[$id] : NULL; } // At this point we should either have the plugin, or a NULL. return $plugins[$plugin][$id]; } /** * Fetch a layout plugin * * @param $layout * Name of a panel layout. * @return * An array with information about the requested panel layout. */ function panels_get_layout($layout) { return panels_get_plugins('layouts', 'panels_layouts', $layout); } /** * Fetch all layout plugins * * @return * An array of arrays with information about all available panel layouts. */ function panels_get_layouts() { return panels_get_plugins('layouts', 'panels_layouts'); } /** * Collate information about a specific panel style. * * @param $style * Name of a panel style. * @return * An array with information about the requested panel style. */ function panels_get_style($style) { return panels_get_plugins('styles', 'panels_styles', $style); } /** * Collate information about all available panel styles. * * @return * An array of arrays with information about all available panel styles. */ function panels_get_styles() { return panels_get_plugins('styles', 'panels_styles'); } /** * Collate information about a specific panel argument. * * @param $argument * Name of a panel argument. * @return * An array with information about the requested panel argument. */ function panels_get_argument($argument) { return panels_get_plugins('arguments', 'panels_arguments', $argument); } /** * Collate information about all available panel arguments. * * @return * An array of arrays with information about all available panel arguments. */ function panels_get_arguments() { return panels_get_plugins('arguments', 'panels_arguments'); } /** * Fetch a content_type plugin * * @param $content type * Name of a panel content type. * @return * An array with information about the requested panel content type. */ function panels_get_content_type($content_type) { return panels_get_plugins('content_types', 'panels_content_types', $content_type); } /** * Fetch all content type plugins * * @return * An array of arrays with information about all available panel content types. */ function panels_get_content_types() { return panels_get_plugins('content_types', 'panels_content_types'); } /** * Fetch a relationship plugin * * @param $content type * Name of a panel content type. * @return * An array with information about the requested relationship */ function panels_get_relationship($relationship) { return panels_get_plugins('relationships', 'panels_relationships', $relationship); } /** * Fetch all relationship plugins * * @return * An array of arrays with information about all available relationships. */ function panels_get_relationships() { return panels_get_plugins('relationships', 'panels_relationships'); } /** * Fetch a context plugin * * @param $context * Name of a panel context. * @return * An array with information about the requested panel context. */ function panels_get_context($context) { return panels_get_plugins('contexts', 'panels_contexts', $context); } /** * Fetch all context plugins * * @return * An array of arrays with information about all available panel contexts. */ function panels_get_contexts() { return panels_get_plugins('contexts', 'panels_contexts'); }