<?php
// $Id$
/**
* Callback function to supply a list of content types.
*/
/*
* There are seven types of panels plugins at present: context, relationships,
* arguments, content_types, styles, layouts, cache; Panels provides an API that
* allows any module to implement these, but there's a starter set that comes
* with Panels itself. They're found in the various subdirectories that live under
* the Panels directory.
*
* All of them (but for our purposes more importantly, content types) start with
* a function that returns a 'plugin declaration array'. The declaration array is
* an associative array containing a bunch of settings and callbacks that the Panels
* API uses to determine when and how it utilizes the plugin. The Panels API looks
* for certain keys in that array - exactly which keys depends on the plugin type -
* and treats the value stored in that key as a directive governing how the plugin
* should behave.
*
* Remember - although grouping the callbacks defined in these directives into the
* same .inc file can be handy, you're not bound to doing so. If you have a
* visibility checker that you want to share across a lot of content types, for
* example, consider defining the visibility checker in the main .module file while
* keeping the rest of the plugin in its own .inc file.
*
* Yes, some unnecessary duplication with things like the add/edit directives; they
* exist as they are largely for legacy compatibility reasons.
*
* Plugin declaration functions must adhere to a particular naming convention; see
* panels_get_directories() for details.
*
* Note that the plugin declaration array given in this sample does NOT define a
* coherent, working content type; displaying the full range of the plugin's
* flexibility makes it impossible to do so. For working examples, see the various
* plugins defined in the panels/content_types directory.
*/
function panels_SAMPLE_CT_panels_content_types() {
// The string used for the array key IS significant: it will determine the pane's
// "type". Namespace collisions are destructive, wherein plugins using a given namespace
// that are read later by the Panels API will override plugins read earlier. That order is
// generally determined by the weight of the module (in the system table) defining the
// plugin.
// All the plugins that come packaged with Panels obey a strict naming convention out
// of necessity: this array key is the same as the name of the file itself, as
// well as being the same as the string prefixed by the module name (here,
// 'panels'), and affixed by a string that indicates the type of plugin (here,
// 'panels_content_types'). Your modules need not follow the same conventions,
// although it is recommended that you do if possible; again, refer to
// panels_get_directories() for the easiest method of doing so.
$items['SAMPLE_CT'] =
array(
// title (string): SETTING. The title that will be used for this pane on the 'Add Content' modal
// form, and in the display content editor in general. This is a purely internal setting;
// normal users will never see it.
'title' =>
t('Sample Content Type'),
// weight (int): SETTING. Standard drupal weighting concept at work here; all it determines is the
// position of this content type's icon relative to the other content type icons on the
// general Panels configuration forms. See panels_common_settings().
'weight' => -10,
// single (bool): SETTING. Indicates that this content type plugin provides only a single content
// type. Currently, this setting is ONLY used in figuring out how to group the content type
// on the general Panels configuration forms; see panels_common_settings().
// Check out panels_admin_content_types_block() for an example of how one plugin
// can used define multiple content types (technically, multiple subtypes).
// Defaults to FALSE.
'single' => TRUE,
// content_types (string): CALLBACK. The Panels API calls this function to get
// basic information about the content types this plugin provides. See the callback
// for details.
'content_types' => 'panels_admin_content_types_SAMPLE_CT',
// render callback (string): CALLBACK. The Panels API calls this function when it's
// trying to render the pane for viewing. See the callback for details.
'render callback' => 'panels_content_SAMPLE_CT',
// add callback (string): CALLBACK. This function gets called when the user
// clicks an icon to add a new pane (from the 'Add Content' modal form).
// See the callback for details; note that it is often possible to use the
// same, or nearly the same, callback for this as for the 'edit callback.'
'add callback' => 'panels_admin_add_SAMPLE_CT',
// edit callback (string): CALLBACK. This function gets called when the user
// clicks the 'Configure' button; it partially governs what appears on the
// configuration modal form that pops up.
'edit callback' => 'panels_admin_edit_SAMPLE_CT',
// (add|edit) validate callback (string): CALLBACK. Defines a callback to be
// used as a FAPI validator, but only for the $form_values set by form items
// defined in the 'add/edit callback'.
'add validate callback' => 'panels_admin_validate_SAMPLE_CT',
'edit validate callback' => 'panels_admin_validate_SAMPLE_CT',
// (add|edit) submit callback (string): CALLBACK. Defines a callback to be
// used as a FAPI submit handler, but only for the $form_values set by form
// items defined in the 'add/edit callback'.
'add submit callback' => 'panels_admin_submit_SAMPLE_CT',
'edit submit callback' => 'panels_admin_submit_SAMPLE_CT',
// title callback (string): CALLBACK. This function determines the title that
// the pane will use, both in the admin interface and for general viewing. Note
// that this can almost always be overridden.
'title callback' => 'panels_admin_title_SAMPLE_CT',
// render last (bool): SETTING. If set to TRUE, this pane will be pushed to the
// back of the line during the render routine. See panels_render_panes().
'render last' => TRUE,
// visibility control (string): CALLBACK. This function gets called at the same
// time as the add/edit callbacks. Use it to create a form widget that allowing the
// user to select values that will make sense when passed to your 'visibility check'
// callback. If your plugin declares this option, you'll need to be cognizant
// of your declarations for several other options: 'visibility submit', 'visibility
// check' and 'visibility serialize'. 'roles and visibility' is also relevant,
// but won't be useful to the vast majority of content type plugins.
'visibility control' => 'panels_admin_visibility_control_SAMPLE_CT',
// visibility submit (string): CALLBACK. The custom submit handler you define
// for your content type's visibility settings. This function is passed
// whatever selection the user makes on the form widget defined by the
// 'visibility control' callback. Implement this if you need to wrangle that
// data before the Panels API's data storage routines kick in, or if the API's
// built-in routines are inadequate and you need to build a custom storage
// mechanism. See panels_content_config_form() and panels_content_config_form_submit()
// to grok the logic behind if/when/how this callback is triggered.
'visibility submit' => 'panels_admin_visibility_submit_SAMPLE_CT',
// visibility check (string): CALLBACK. The Panels API calls this function during
// the pane accessibility checking routine - that's done in panels_pane_access().
// Define the logic governing your content type's visibility here.
'visibility check' => 'panels_content_visibility_check_SAMPLE_CT',
// visibility serialize (bool): SETTING. Set this to 'true' if the contents of
// $pane->visibility need to be serialized before being written to the database.
// Set this to TRUE if, for example, your visibility form widget uses checkboxes
// (and therefore generates an array), as opposed to if your widget uses radios
// (and therefore generates an integer that can be stored directly). See
// panels_content_config_form_submit() and panels_save_display() to better
// understand how this works.
// Defaults to FALSE.
'visibility serialize' => TRUE,
// role-based access (bool): SETTING. Boolean setting to indicate whether you
// want the your content type to utilize the Panels API's built-in access
// system, which is based on drupal user roles. Set this to FALSE to disable
// role-based access. Note that this will automatically be set to FALSE if
// a 'visibility control' callback is defined.
// Defaults to TRUE.
'role-based access' => FALSE,
// roles and visibility (bool): SETTING. If you want your content type to use
// both your custom visibility logic and Panels' built-in roles-based access
// system, then set this to TRUE. Setting 'role-based access' to TRUE is not
// sufficient; see panels_ajax_ct_preconfigure() to understand how this works.
// If you use both systems, panels_pane_access() will AND the results together
// when determining pane visibility.
'roles and visibility' => TRUE,
// form control (string): CALLBACK. If the other callbacks governing the add/edit
// form ('add/edit callback', 'visibility control') aren't enough for your needs,
// then implement this callback. The callback will be passed a fully-built $form object
// by reference, and you can alter it as you see fit. Use with caution.
'form control' => 'panels_admin_form_control_SAMPLE_CT',
);
return $items;
}
/**
* Callback function set by the 'content_types' option. Returns an array of
* data used by the Panels API to determine:
* - Whether or not the content type can be added to this display, based on
* what context(s) are available.
* - If context requirements are met, the remainder of the array's data
* defines the icon, title, and description that the content type will
* be rendered with on the the Add Content modal.
*/
function panels_admin_content_types_SAMPLE_CT() {
// As with the plugin declaration, the value of this array key is significant:
// it will become the pane's subtype, stored in $pane->subtype.
// The name used for this subtype on the Add Content modal - this is what
// appears right below the icon.
'title' =>
t('SAMPLE CONTENT TYPE'),
// The name of the icon file to be used for this subtype.
'icon' => 'icon_node.png',
// The server path to the directory where the above icon is located.
'path' => panels_get_path('content_types/node'),
// The 'description' appears as a tooltip when the user hovers their
// mouse pointer over the icon.
'description' =>
t('Descriptive text for the SAMPLE CONTENT TYPE, to be used in the tooltip.'),
// This option indicates which contexts are prerequisites for the content
// type to be used. If a display lacks a context required by this content
// type, then it simply will not be displayed. Multiple required contexts
// can be declared by placing each context into an indexed array.
'required context' =>
new panels_required_context
(t('Sample Required Context'),
'sample_context_required'),
// This option has the same syntax as 'required context', but if optional
// context requirements are not met, the content type will still be usable,
// simply in a reduced form. It's up to the plugin author to define just how
// different that functionality by writing varying the behavior of this plugin's
// other callbacks according to the presence/absence of the context.
'optional context' =>
new panels_optional_context
(t('Sample Optional Context'),
'sample_context_optional'),
// Category is the group this subtype's icon will be placed in. The first
// item in the array is the category name, and the second is the subtype's
// weight in that category (used for ordering the subtypes in the category
// relative to one another). Omitting a value for weight will cause it to
// default to 0; if you do omit the weight, you can simply return the
// t()-wrapped string title of the content type - no need to put it in an array.
'category' =>
array(t('Node context'),
-9),
),
);
}
/**
* Callback function set by the 'render callback' option. This callback constructs
* and returns an object for display.
*
* The sample function below is a direct copy of the node_content plugin's render
* callback; abstract example cases are of little use from here on out. Note that
* this case only implements three parameters, but there is also a fourth. Your
* content type can use as few/many of these parameters as you want, although
* you won't be able to much if you don't implement the first parameter, $conf.
*
* @param array $conf
* The contents of $pane->configuration. This will be an array with the following
* keys, by default:
* - override_title (int/bool): 0 or 1, reflecting whether the user checked the
* 'override title' checkbox on the pane configuration form.
* - override_title_text (string): a string containing the title override, as
* written by the user on the pane configuration form.
* - css_id (string): the special css id entered by the user on the pane config
* form, if any.
* - css_class (string): same idea as the css id.
* - module (string): a string containing the name of the module implementing
* this content type (or, in some cases, owning/generating the content).\n
* The above keys reflect the standard set of form items that the Panels API
* provides to every pane type by default. Any additional configuration items that
* you add (via the add/edit callbacks) will also appear in $conf by default.
* @param array $panel_args
* An indexed array of all arguments, if any, that have been passed to the display.
* @param mixed $context
* The contents of $context can vary widely. If only one context is being passed
* to the pane, $context will simply be that context object. If multiple contexts
* are passed, however, then $context will be an indexed array of those contexts.
* The sort of data contained in the context is completely dependent on the how
* that context has been defined.
* @param $incoming_content
*
* @return object $block
* An object, ready to be passed through the styling & theming layers. At minimum,
* the object should contain a 'content' element, as well as 'title' and/or
* 'subject' elements. If a 'title' element is not included, then the 'subject'
* is copied into $block->title later on in the render process. You are free to
* define as many elements as you want, but those elements will only be used
* if you write a panels style plugin specifically designed to take advantage of
* of them. Note that the '$block' variable name used here is arbitrary.
*/
function panels_content_SAMPLE_CT($conf, $panel_args, $context) {
// The node_content content_type plugin has a required context of 'node.'
// This simply double-checks to make sure that the necessary context is present;
// in particular, it excludes 'empty' contexts, which are used primarily during
// the edit process.
return;
}
// The node context plugin stores an entire, fully-loaded $node object into
// its $context->data element; this pulls that node data out (via cloning, to
// ensure the original context data itself remains unchanged) and stores it in
// a correspondingly-named variable, $node.
$block->module = 'node';
// Stores the nid from the context, to ensure it is acecssible later.
$block->delta = $node->nid;
// Just in case the context didn't load, but managed to get past the initial
// checks, this adds filler content to the $block.
$block->delta = 'placeholder';
$block->
subject =
t('Node title.');
$block->
content =
t('Node content goes here.');
}
else {
if (!
empty($conf['identifier'])) {
$node->panel_identifier = $conf['identifier'];
}
$block->subject = $node->title;
// The pane's content is a complex enough operation that we delegate creating
// it to a helper function.
$block->content = panels_admin_SAMPLE_CT($node, $conf);
}
// If the user has the necessary permissions, an 'admin link' is generated.
// Admin links are the special links that appear above the pane's title when
// you mouse over the pane.
$block->
admin_links['update'] =
array(
'title' =>
t('Edit node'),
'alt' =>
t("Edit this node"),
'href' => "node/$node->nid/edit",
);
}
if (!
empty($conf['link']) &&
$node) {
$block->title_link = "node/$node->nid";
}
return $block;
}
/**
* Probably the most important lesson to be noted about this helper function is
* just how similar it is to node.module's routine for node rendering.
* In fact, helper function is little more than a minor rewrite of
* node_view(); the first lines are lifted directly from node_build_content(),
* and the latter half from node_view().
*/
function panels_admin_SAMPLE_CT($node, $conf) {
// Remove the delimiter (if any) that separates the teaser from the body.
// The 'view' hook can be implemented to overwrite the default function
// to display nodes.
$node =
node_invoke($node,
'view',
$conf['teaser'],
$conf['page']);
}
else {
}
if (empty($conf['no_extras'])) {
// Allow modules to make their own additions to the node.
}
if ($conf['links']) {
$function = $module .'_link_alter';
$function($node, $node->links);
}
}
// Set the proper node part, then unset unused $node part so that a bad
// theme can not open a security hole.
if ($conf['teaser']) {
$node->teaser = $content;
}
else {
$node->body = $content;
}
// Allow modules to modify the fully-built node.
return theme('node',
$node,
$conf['teaser'],
$conf['page']);
}
/**
* Callback function set by the 'add callback' option. This callback constructs
* the pane configuration form for newly-added panes. This sample is lifted from
* the block content type plugin (block.inc); it is the only built-in Panels content
* type that implements an add callback that is different from the edit callback.
*
* Clearly there's relatively little need to differentiate between the add and edit
* callbacks; the only thing this one does is make sure that $conf has some of the
* right values before heading into the edit form. You still need to define both the
* 'add callback' and 'edit callback' options in the plugin declaration array, but you
* can just make them point to the same function.
*
* See the edit callback for more detailed discussion.
*
*/
function panels_admin_add_SAMPLE_CT
($id,
$parents,
$conf =
array()) {
list($conf['module'],
$conf['delta']) =
explode('-',
$id,
2);
return panels_admin_edit_SAMPLE_CT($id, $parents, $conf);
}
/**
* Callback function set by the 'edit callback' option in the plugin declaration array.
* This callback constructs the configuration form for panes that have already been added;
* the callback is fired when the 'Configure' button is clicked.
*
* This function essentially operates like a limited and targeted implementation of
* hook_form_alter(); the Panels API wrangles FAPI as needed, so all you need to do
* is add the widgets you want for your content type/subtype.
*
* NOTE - in future versions, the 'Block visibility' options are likely to be moved into
* the appropriate visibility callbacks. They're here now because the block content type
* plugin was written long before the visibility system was introduced.
*
* Some of the techniques used in this edit callback are pretty advanced. For a more basic
* but quite thorough implementation of this callback, see panels_admin_edit_node_content().
*
* @param string $id
* The subtype of the pane being edited. The block panels content type plugin calls this
* variable '$id' for legacy reasons; we recommend you call this variable $subtype if you
* want your variable names to be optimally descriptive of their values.
* @param array $parents
* This parameter is largely deprecated, and is included for legacy API compatibility. Its
* intention was to provide information to form widgets about where they live on the $form.
* It is likely to disappear in Panels3. For all add/edit callbacks:
* @code $parents = array('configuration'); @endcode
* This corresponds to the fact that the $form returned from this callback will not be added
* to the root of the overall $form array, but to the $form['configuration'] sub-array.
* See panels_content_config_form().
* @param array $conf
* The contents of $pane->configuration, if any.
* @return array $form
* A standard FAPI form array.
*/