DrupalBin
Submit Code
About
Recent Posts
How do i make this work in D6 (I have to clear the cache everytime to see the tabs)
1 hour 27 min
ago
Code
2 hours 23 min
ago
Code
6 hours 21 min
ago
oopsie in modules/taxonomy/taxonomy.test
8 hours 20 min
ago
more
Tags
CCK
drupal
fapi
jquery
menu
module
Panels
php
simpletest
test
theme
views
more tags
User login
Log in using OpenID:
What is OpenID?
Username:
*
Password:
*
Create new account
Request new password
Log in using OpenID
Cancel OpenID login
Home
Fix for Panels content type plugin sample
View
Download
Fix
This fix will not be saved to the database until you submit.
Summary:
Tags:
Any tags you'd like to associate with your code, delimitered by commas (example: Views, CCK, Module, etc).
Source code:
*
<?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() { return array( // As with the plugin declaration, the value of this array key is significant: // it will become the pane's subtype, stored in $pane->subtype. 'content' => array( // 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. if (!empty($context) && empty($context->data)) { 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. $node = isset($context->data) ? drupal_clone($context->data) : NULL; $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. if (empty($node)) { $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; unset($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. if (node_access('update', $node)) { $block->admin_links['update'] = array( 'title' => t('Edit node'), 'alt' => t("Edit this node"), 'href' => "node/$node->nid/edit", 'query' => drupal_get_destination(), ); } 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. $node->body = str_replace('<!--break-->', '', $node->body); // The 'view' hook can be implemented to overwrite the default function // to display nodes. if (node_hook($node, 'view')) { $node = node_invoke($node, 'view', $conf['teaser'], $conf['page']); } else { $node = node_prepare($node, $conf['teaser']); } if (empty($conf['no_extras'])) { // Allow modules to make their own additions to the node. node_invoke_nodeapi($node, 'view', $conf['teaser'], $conf['page']); } if ($conf['links']) { $node->links = module_invoke_all('link', 'node', $node, $conf['teaser']); foreach (module_implements('link_alter') AS $module) { $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. $content = drupal_render($node->content); if ($conf['teaser']) { $node->teaser = $content; unset($node->body); } else { $node->body = $content; unset($node->teaser); } // Allow modules to modify the fully-built node. node_invoke_nodeapi($node, 'alter', $conf['teaser'], $conf['page']); 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. */ function panels_admin_edit_SAMPLE_CT($id, $parents, $conf) { $form['module'] = array( '#type' => 'value', '#value' => $conf['module'], ); $form['delta'] = array( '#type' => 'value', '#value' => $conf['delta'], ); if (user_access('administer advanced pane settings')) { $form['block_visibility'] = array( '#type' => 'checkbox', '#title' => t('Use block visibility settings (see block config)'), '#default_value' => $conf['block_visibility'], '#description' => t('If checked, the block visibility settings for this block will apply to this block.'), ); // Module-specific block configurations. if ($settings = module_invoke($conf['module'], 'block', 'configure', $conf['delta'])) { // Specifically modify a couple of core block forms. if ($conf['module'] == 'block') { unset($settings['submit']); $settings['info']['#type'] = 'value'; $settings['info']['#value'] = $settings['info']['#default_value']; } panels_admin_fix_block_tree($settings); $form['block_settings'] = array( '#type' => 'fieldset', '#title' => t('Block settings'), '#description' => t('Settings in this section are global and are for all blocks of this type, anywhere in the system.'), '#tree' => FALSE, ); $form['block_settings'] += $settings; } } return $form; } /** * Callback function set by the '[add|edit] submit callback' option in the plugin * declaration array. In simpler plugins, you won't need to implement this callback; * any values set by the widgets you added to the config form will automatically * have their data added to the $pane->configuration array, which is serialized * and stored in the panels_pane table. * * However, in cases where the settings being changed on this form need to be * reflected in some other data structure, this callback can be used to ensure * that the necessary changes are made. In this example (again from the block * content type plugin), hook_block() is invoked with $op = 'save' for the module * that owns the block, thereby allowing the normal block saving routine to * do its thing. */ function panels_admin_submit_SAMPLE_CT(&$form_values) { if (!empty($form_values['block_settings'])) { module_invoke($form_values['module'], 'block', 'save', $form_values['delta'], $form_values['block_settings']); } } function panels_admin_title_SAMPLE_CT($conf, $context) { return t('"@s" content', array('@s' => $context->identifier)); } /** * Callback function set by the 'visibility control' option in the plugin declaration array. * * Operates quite similarly to the add and edit callbacks, with a few exceptions: * - The FAPI parent is $form['visibility'] instead of $form['configuration'] * - Instead of creating separate callbacks for add and edit, the $add parameter * indicates which operation is taking place. * - Whereas a submit handler is often unnecessary for the add/edit callbacks, * implementing 'visibility control' means you need to be cognizant of several * other options in the declaration array. * The visibility widget defined in this function is a reworked version of one originally * created for og_panels (but was not/has not yet been committed); the goal is to control * pane visibility according to the status of the current user relative to the group. * * Remember, this is NOT where you define the logic behind your visibility handling - * all you're doing here is providing a form widget that to get some data. It's up to * your visibility checker, defined in the 'visibility check' callback, to create the * logic that can take the data from here and make the right decision about pane * visibility. * * @param mixed $contexts * As in the render callback, this is either a context object, or an array of context * objects. It's unnecessary and probably unwise to include this context * data directly in the values that get saved in your form visibility function; that * very same data will be available via the $display variable that's passed to the * checker. Rather, $context is provided in the event that your visibility widget * needs to vary depending on some information in $context. * @param string $subtype * The contents of $pane->subtype for the pane currently being edited. * @param array $conf * The contents of $pane->configuration, if any. * @param bool $add * If TRUE, then a new pane is being added. If FALSE, then an existing pane is being edited. * @return array $visibility_widget * A standard FAPI widget, to be added to the form. */ function panels_admin_visibility_control_SAMPLE_CT($contexts, $subtype, $conf, $add) { return $visibility_widget = array( '#type' => 'radios', '#title' => t('Pane Visibility'), '#description' => t('Who should this pane be visible to?'), '#options' => array( 'all' => t('Everyone'), 'member' => t('Only group members'), 'nonmember' => t('Only group non-members'), 'admin' => t('Group administrators'), ), '#default_value' => isset($conf['visibility']) ? $conf['visibility'] : 0, ); } /** * Callback function set by the 'visibility check' option in the plugin declaration array. * * This function takes advantage of cached static variables to increase performance. On any * given page request, we know that only ONE group is going to be accessed, and only ONE user * is going to be doing the accessing. Since the static keyword only lasts through a single page * request, and nid and uid are the two variables that visibility depends upon in this case, * we only have to query the database and build the $visibility array once, no matter how many * panes fire this callback to determine visibility during this page request. * * @param object $pane * The fully-loaded $pane object that we're running the visibility check against. The * value set by the widget defined in the 'visibility control' callback is contained * in $pane->visibility. * @param object $display * The fully-loaded display object that's currently being rendered. If you need $context * to figure out what action to take, you'll find it/them in $display->context. * @param object $user * The current $user - the same as what you'd get from @code global $user @endcode. * Passed for convenience, since visibility is often dependent on the $user. * @return bool * A boolean indicating if the pane should (TRUE) or should not (FALSE) be visible. */ function panels_content_visibility_check_SAMPLE_CT($pane, $display, $user) { // use static variable to somewhat reduce queries for complex og_panels pages static $visible; if (!is_array($visible)) { $visible = array(); $visible['all'] = TRUE; } if (!isset($visible[$pane->visibility])) { $members = array(); $sql = "SELECT u.uid AS uid, ogu.is_admin AS admin FROM {og_uid} ogu INNER JOIN {users} u ON ogu.uid = u.uid WHERE ogu.nid = %d AND ogu.is_active = 1 AND u.status = 1 ORDER BY ogu.created DESC"; // $display holds the context; the $data element of $context holds a node object, // and we want the nid of that node. $result = db_query($sql, $display->context->data->nid); while ($account = db_fetch_array($result)) { $members[$account['uid']] = $account['admin']; } $visible['member'] = in_array($user->uid, array_keys($members)); $visible['nonmember'] = !$visible['member']; $visible['admin'] = $visible['member'] ? $members[$user->uid] : FALSE; } return $visible[$pane->visibility]; } /** * Callback function set by the 'visibility submit' option in the plugin declaration array. * * Note that in this particular sample implementation, the 'visibility serialize' * directive should be set to FALSE, as the value produced by the widget in the * sample 'visibility control' function returns a simple string. * * Note also that the visibility field in the panels_pane table is the standard * 'text' field. The save routines in panels_save_display() will always * convert the value to a string, so even if you send an integer in, you'll get * a string back out on the other end. * * Note finally that this function is COMPLETELY superfluous, and is only included * for the purposes of showing * * See panels_content_config_form_submit() for the context in which this function is called. * * @param string $visibility_value * @return string $visibility_value */ function panels_admin_visibility_submit_SAMPLE_CT($visibility_value) { return $visibility_value; } function panels_admin_form_control_SAMPLE_CT(&$form) { $form['shmear'] = array('#type' => 'value', '#value' => 'SHMEAR ME!'); }
Syntax highlighting mode:
ActionScript
ColdFusion
Diff
Drupal
Drupal 5
Drupal 6
HTML
Javascript
MySQL
PHP
Python
robots.txt
SQL
Text
Select the syntax highlighting mode to use.