Panels

sample php-handling implementation of a panels content type plugin

  1. /**
  2.  * Note that there is NO check here to ensure that the user has the
  3.  * proper permissions to execute arbitrary php code. That check
  4.  

Panels content type plugin sample

  1. <?php
  2. // $Id$
  3. /**
  4.  * Callback function to supply a list of content types.
  5.  */
  6.  
  7. /*
  8.  * There are seven types of panels plugins at present: context, relationships,
  9.  * arguments, content_types, styles, layouts, cache; Panels provides an API that
  10.  * allows any module to implement these, but there's a starter set that comes
  11.  * with Panels itself. They're found in the various subdirectories that live under
  12.  * the Panels directory.
  13.  *
  14.  * All of them (but for our purposes more importantly, content types) start with
  15.  * a function that returns a 'plugin declaration array'. The declaration array is
  16.  * an associative array containing a bunch of settings and callbacks that the Panels
  17.  * API uses to determine when and how it utilizes the plugin. The Panels API looks
  18.  * for certain keys in that array - exactly which keys depends on the plugin type -
  19.  * and treats the value stored in that key as a directive governing how the plugin
  20.  * should behave.
  21.  *
  22.  * Remember - although grouping the callbacks defined in these directives into the
  23.  * same .inc file can be handy, you're not bound to doing so. If you have a
  24.  * visibility checker that you want to share across a lot of content types, for
  25.  * example, consider defining the visibility checker in the main .module file while
  26.  * keeping the rest of the plugin in its own .inc file.
  27.  *
  28.  * Yes, some unnecessary duplication with things like the add/edit directives; they
  29.  * exist as they are largely for legacy compatibility reasons.
  30.  *
  31.  * Plugin declaration functions must adhere to a particular naming convention; see
  32.  * panels_get_directories() for details.
  33.  *
  34.  * Note that the plugin declaration array given in this sample does NOT define a
  35.  * coherent, working content type; displaying the full range of the plugin's
  36.  * flexibility makes it impossible to do so. For working examples, see the various
  37.  * plugins defined in the panels/content_types directory.
  38.  */
  39. function panels_SAMPLE_CT_panels_content_types() {
  40.   // The string used for the array key IS significant: it will determine the pane's
  41.   // "type". Namespace collisions are destructive, wherein plugins using a given namespace
  42.   // that are read later by the Panels API will override plugins read earlier. That order is
  43.   // generally determined by the weight of the module (in the system table) defining the
  44.   // plugin.
  45.   // All the plugins that come packaged with Panels obey a strict naming convention out
  46.   // of necessity: this array key is the same as the name of the file itself, as
  47.   // well as being the same as the string prefixed by the module name (here,
  48.   // 'panels'), and affixed by a string that indicates the type of plugin (here,
  49.   // 'panels_content_types'). Your modules need not follow the same conventions,
  50.   // although it is recommended that you do if possible; again, refer to
  51.   // panels_get_directories() for the easiest method of doing so.
  52.   $items['SAMPLE_CT'] = array(
  53.     // title (string): SETTING. The title that will be used for this pane on the 'Add Content' modal
  54.     // form, and in the display content editor in general. This is a purely internal setting;
  55.     // normal users will never see it.
  56.     'title' => t('Sample Content Type'),
  57.     // weight (int): SETTING. Standard drupal weighting concept at work here; all it determines is the
  58.     // position of this content type's icon relative to the other content type icons on the
  59.     // general Panels configuration forms. See panels_common_settings().
  60.     'weight' => -10,
  61.     // single (bool): SETTING. Indicates that this content type plugin provides only a single content
  62.     // type. Currently, this setting is ONLY used in figuring out how to group the content type
  63.     // on the general Panels configuration forms; see panels_common_settings().
  64.     // Check out panels_admin_content_types_block() for an example of how one plugin
  65.     // can used define multiple content types (technically, multiple subtypes).
  66.     // Defaults to FALSE.
  67.     'single' => TRUE,
  68.     // content_types (string): CALLBACK. The Panels API calls this function to get
  69.     // basic information about the content types this plugin provides. See the callback
  70.     // for details.
  71.     'content_types' => 'panels_admin_content_types_SAMPLE_CT',
  72.     // render callback (string): CALLBACK. The Panels API calls this function when it's
  73.     // trying to render the pane for viewing. See the callback for details.
  74.     'render callback' => 'panels_content_SAMPLE_CT',
  75.     // add callback (string): CALLBACK. This function gets called when the user
  76.     // clicks an icon to add a new pane (from the 'Add Content' modal form).
  77.     // See the callback for details; note that it is often possible to use the
  78.     // same, or nearly the same, callback for this as for the 'edit callback.'  
  79.     'add callback' => 'panels_admin_add_SAMPLE_CT',
  80.     // edit callback (string): CALLBACK. This function gets called when the user
  81.     // clicks the 'Configure' button; it partially governs what appears on the
  82.     // configuration modal form that pops up.
  83.     'edit callback' => 'panels_admin_edit_SAMPLE_CT',
  84.     // (add|edit) validate callback (string): CALLBACK. Defines a callback to be
  85.     // used as a FAPI validator, but only for the $form_values set by form items
  86.     // defined in the 'add/edit callback'.
  87.     'add validate callback' => 'panels_admin_validate_SAMPLE_CT',
  88.     'edit validate callback' => 'panels_admin_validate_SAMPLE_CT',
  89.     // (add|edit) submit callback (string): CALLBACK. Defines a callback to be
  90.     // used as a FAPI submit handler, but only for the $form_values set by form
  91.     // items defined in the 'add/edit callback'.
  92.     'add submit callback' => 'panels_admin_submit_SAMPLE_CT',
  93.     'edit submit callback' => 'panels_admin_submit_SAMPLE_CT',  
  94.     // title callback (string): CALLBACK. This function determines the title that
  95.     // the pane will use, both in the admin interface and for general viewing. Note
  96.     // that this can almost always be overridden.
  97.     'title callback' => 'panels_admin_title_SAMPLE_CT',
  98.     // render last (bool): SETTING. If set to TRUE, this pane will be pushed to the
  99.     // back of the line during the render routine. See panels_render_panes().
  100.     'render last' => TRUE,
  101.     // visibility control (string): CALLBACK. This function gets called at the same
  102.     // time as the add/edit callbacks. Use it to create a form widget that allowing the
  103.     // user to select values that will make sense when passed to your 'visibility check'
  104.     // callback. If your plugin declares this option, you'll need to be cognizant
  105.     // of your declarations for several other options: 'visibility submit', 'visibility
  106.     // check' and 'visibility serialize'. 'roles and visibility' is also relevant,
  107.     // but won't be useful to the vast majority of content type plugins.
  108.     'visibility control' => 'panels_admin_visibility_control_SAMPLE_CT',
  109.     // visibility submit (string): CALLBACK. The custom submit handler you define
  110.     // for your content type's visibility settings. This function is passed
  111.     // whatever selection the user makes on the form widget defined by the
  112.     // 'visibility control' callback. Implement this if you need to wrangle that
  113.     // data before the Panels API's data storage routines kick in, or if the API's
  114.     // built-in routines are inadequate and you need to build a custom storage
  115.     // mechanism. See panels_content_config_form() and panels_content_config_form_submit()
  116.     // to grok the logic behind if/when/how this callback is triggered.
  117.     'visibility submit' => 'panels_admin_visibility_submit_SAMPLE_CT',
  118.     // visibility check (string): CALLBACK. The Panels API calls this function during
  119.     // the pane accessibility checking routine - that's done in panels_pane_access().
  120.     // Define the logic governing your content type's visibility here.
  121.     'visibility check' => 'panels_content_visibility_check_SAMPLE_CT',
  122.     // visibility serialize (bool): SETTING. Set this to 'true' if the contents of
  123.     // $pane->visibility need to be serialized before being written to the database.
  124.     // Set this to TRUE if, for example, your visibility form widget uses checkboxes
  125.     // (and therefore generates an array), as opposed to if your widget uses radios
  126.     // (and therefore generates an integer that can be stored directly). See
  127.     // panels_content_config_form_submit() and panels_save_display() to better
  128.     // understand how this works.
  129.     // Defaults to FALSE.
  130.     'visibility serialize' => TRUE,
  131.     // role-based access (bool): SETTING. Boolean setting to indicate whether you
  132.     // want the your content type to utilize the Panels API's built-in access
  133.     // system, which is based on drupal user roles. Set this to FALSE to disable
  134.     // role-based access. Note that this will automatically be set to FALSE if
  135.     // a 'visibility control' callback is defined.
  136.     // Defaults to TRUE.
  137.     'role-based access' => FALSE,
  138.     // roles and visibility (bool): SETTING. If you want your content type to use
  139.     // both your custom visibility logic and Panels' built-in roles-based access
  140.     // system, then set this to TRUE. Setting 'role-based access' to TRUE is not
  141.     // sufficient; see panels_ajax_ct_preconfigure() to understand how this works.
  142.     // If you use both systems, panels_pane_access() will AND the results together
  143.     // when determining pane visibility.
  144.     'roles and visibility' => TRUE,
  145.     // form control (string): CALLBACK. If the other callbacks governing the add/edit
  146.     // form ('add/edit callback', 'visibility control') aren't enough for your needs,
  147.     // then implement this callback. The callback will be passed a fully-built $form object
  148.     // by reference, and you can alter it as you see fit. Use with caution.
  149.     'form control' => 'panels_admin_form_control_SAMPLE_CT',
  150.   );
  151.   return $items;
  152. }
  153.  
  154. /**
  155.  * Callback function set by the 'content_types' option. Returns an array of
  156.  * data used by the Panels API to determine:
  157.  *    - Whether or not the content type can be added to this display, based on
  158.  *      what context(s) are available.
  159.  *    - If context requirements are met, the remainder of the array's data
  160.  *      defines the icon, title, and description that the content type will
  161.  *      be rendered with on the the Add Content modal.
  162.  */
  163. function panels_admin_content_types_SAMPLE_CT() {
  164.   return array(
  165.     // As with the plugin declaration, the value of this array key is significant:
  166.     // it will become the pane's subtype, stored in $pane->subtype.
  167.     'content' => array(
  168.       // The name used for this subtype on the Add Content modal - this is what
  169.       // appears right below the icon.
  170.       'title' => t('SAMPLE CONTENT TYPE'),
  171.       // The name of the icon file to be used for this subtype.
  172.       'icon' => 'icon_node.png',
  173.       // The server path to the directory where the above icon is located.
  174.       'path' => panels_get_path('content_types/node'),
  175.       // The 'description' appears as a tooltip when the user hovers their
  176.       // mouse pointer over the icon.
  177.       'description' => t('Descriptive text for the SAMPLE CONTENT TYPE, to be used in the tooltip.'),
  178.       // This option indicates which contexts are prerequisites for the content
  179.       // type to be used. If a display lacks a context required by this content
  180.       // type, then it simply will not be displayed. Multiple required contexts
  181.       // can be declared by placing each context into an indexed array.
  182.       'required context' => new panels_required_context(t('Sample Required Context'), 'sample_context_required'),
  183.       // This option has the same syntax as 'required context', but if optional
  184.       // context requirements are not met, the content type will still be usable,
  185.       // simply in a reduced form. It's up to the plugin author to define just how
  186.       // different that functionality by writing varying the behavior of this plugin's
  187.       // other callbacks according to the presence/absence of the context.
  188.       'optional context' => new panels_optional_context(t('Sample Optional Context'), 'sample_context_optional'),
  189.       // Category is the group this subtype's icon will be placed in. The first
  190.       // item in the array is the category name, and the second is the subtype's
  191.       // weight in that category (used for ordering the subtypes in the category
  192.       // relative to one another). Omitting a value for weight will cause it to
  193.       // default to 0; if you do omit the weight, you can simply return the
  194.       // t()-wrapped string title of the content type - no need to put it in an array.
  195.       'category' => array(t('Node context'), -9),
  196.     ),
  197.   );
  198. }
  199.  
  200. /**
  201.  * Callback function set by the 'render callback' option. This callback constructs
  202.  * and returns an object for display.
  203.  *
  204.  * The sample function below is a direct copy of the node_content plugin's render
  205.  * callback; abstract example cases are of little use from here on out. Note that
  206.  * this case only implements three parameters, but there is also a fourth. Your
  207.  * content type can use as few/many of these parameters as you want, although
  208.  * you won't be able to much if you don't implement the first parameter, $conf.
  209.  *
  210.  * @param array $conf
  211.  *  The contents of $pane->configuration. This will be an array with the following
  212.  *  keys, by default:
  213.  *    - override_title (int/bool): 0 or 1, reflecting whether the user checked the
  214.  *      'override title' checkbox on the pane configuration form.
  215.  *    - override_title_text (string): a string containing the title override, as
  216.  *      written by the user on the pane configuration form.
  217.  *    - css_id (string): the special css id entered by the user on the pane config
  218.  *      form, if any.
  219.  *    - css_class (string): same idea as the css id.
  220.  *    - module (string): a string containing the name of the module implementing
  221.  *      this content type (or, in some cases, owning/generating the content).\n
  222.  * The above keys reflect the standard set of form items that the Panels API
  223.  * provides to every pane type by default. Any additional configuration items that
  224.  * you add (via the add/edit callbacks) will also appear in $conf by default.
  225.  * @param array $panel_args
  226.  *  An indexed array of all arguments, if any, that have been passed to the display.
  227.  * @param mixed $context
  228.  *  The contents of $context can vary widely. If only one context is being passed
  229.  *  to the pane, $context will simply be that context object. If multiple contexts
  230.  *  are passed, however, then $context will be an indexed array of those contexts.
  231.  *  The sort of data contained in the context is completely dependent on the how
  232.  *  that context has been defined.
  233.  * @param $incoming_content
  234.  *
  235.  * @return object $block
  236.  *  An object, ready to be passed through the styling & theming layers. At minimum,
  237.  *  the object should contain a 'content' element, as well as 'title' and/or
  238.  *  'subject' elements. If a 'title' element is not included, then the 'subject'
  239.  *  is copied into $block->title later on in the render process. You are free to
  240.  *  define as many elements as you want, but those elements will only be used
  241.  *  if you write a panels style plugin specifically designed to take advantage of
  242.  *  of them. Note that the '$block' variable name used here is arbitrary.
  243.  */
  244. function panels_content_SAMPLE_CT($conf, $panel_args, $context) {
  245.   // The node_content content_type plugin has a required context of 'node.'
  246.   // This simply double-checks to make sure that the necessary context is present;
  247.   // in particular, it excludes 'empty' contexts, which are used primarily during
  248.   // the edit process.
  249.   if (!empty($context) && empty($context->data)) {
  250.     return;
  251.   }
  252.  
  253.   // The node context plugin stores an entire, fully-loaded $node object into
  254.   // its $context->data element; this pulls that node data out (via cloning, to
  255.   // ensure the original context data itself remains unchanged) and stores it in
  256.   // a correspondingly-named variable, $node.
  257.   $node = isset($context->data) ? drupal_clone($context->data) : NULL;
  258.   $block->module = 'node';
  259.   // Stores the nid from the context, to ensure it is acecssible later.
  260.   $block->delta = $node->nid;
  261.  
  262.   // Just in case the context didn't load, but managed to get past the initial
  263.   // checks, this adds filler content to the $block.
  264.   if (empty($node)) {
  265.     $block->delta = 'placeholder';
  266.     $block->subject = t('Node title.');
  267.     $block->content = t('Node content goes here.');
  268.   }
  269.   else {
  270.     if (!empty($conf['identifier'])) {
  271.       $node->panel_identifier = $conf['identifier'];
  272.     }
  273.  
  274.     $block->subject = $node->title;
  275.  
  276.     unset($node->title);
  277.     // The pane's content is a complex enough operation that we delegate creating
  278.     // it to a helper function.
  279.     $block->content = panels_admin_SAMPLE_CT($node, $conf);
  280.   }
  281.  
  282.   // If the user has the necessary permissions, an 'admin link' is generated.
  283.   // Admin links are the special links that appear above the pane's title when
  284.   // you mouse over the pane.
  285.   if (node_access('update', $node)) {
  286.     $block->admin_links['update'] = array(
  287.       'title' => t('Edit node'),
  288.       'alt' => t("Edit this node"),
  289.       'href' => "node/$node->nid/edit",
  290.       'query' => drupal_get_destination(),
  291.     );
  292.   }
  293.  
  294.   if (!empty($conf['link']) && $node) {
  295.     $block->title_link = "node/$node->nid";
  296.   }
  297.  
  298.   return $block;
  299. }
  300.  
  301. /**
  302.  * Probably the most important lesson to be noted about this helper function is
  303.  * just how similar it is to node.module's routine for node rendering.
  304.  * In fact, helper function is little more than a minor rewrite of
  305.  * node_view(); the first lines are lifted directly from node_build_content(),
  306.  * and the latter half from node_view().
  307.  */
  308. function panels_admin_SAMPLE_CT($node, $conf) {
  309.   // Remove the delimiter (if any) that separates the teaser from the body.
  310.   $node->body = str_replace('

panels content type plugin declaration with comments

  1. /*
  2.  * There are seven types of panels plugins at present: context, relationships,
  3.  * arguments, content_types, styles, layouts, cache; Panels provides an API that
  4.  

Panels show/hide panes js

  1. /** Toggle pane show/hide button **/
  2. Drupal.Panels.bindClickToggleHidden = function (o) {
  3.   $('input.pane-toggle-hidden').unbind('click');
  4.   $('input.pane-toggle-hidden').click(function() {

Updates to panels content type definition arrays

  1. // demoing by sticking all the available values onto node_content
  2. $items['content_type'] = array(
  3.     'title' => t('Node content'),
  4.     'weight' => -10,

Fix for panels pane cloning, duplicating, etc

  1. /**
  2.  * Forms the basis of a panel display
  3.  *
  4.  * @ingroup mainapi
  5.  */
  6. class panels_display {
  7.   var $args = array();
  8.   var $content = array();
  9.   var $panels = array();

panels pane cloning, duplicating, etc

  1. /**
  2.  * Forms the basis of a panel display
  3.  *
  4.  * @ingroup mainapi
  5.  */
  6. class panels_display {
  7.   var $args = array();
  8.   var $content = array();
  9.   var $panels = array();

changes to display loading routine to accomodate new show/hide pane feature

  1. /**
  2.  * Load and fill the requested $display object(s).
  3.  *
  4.  * Helper function primarily for for panels_load_display().
  5.  *
  6.  * @param array $dids
  7.  

Fix for display and pane cloning

  1. function panels_clone_display($did, $new = TRUE, $save = FALSE) {
  2.   // since objects are always passed by reference in php5, need to do this to ensure the original object,

Fix for display and pane cloning

  1. function panels_clone_display($did, $new = TRUE, $save = FALSE) {
  2.   // since objects are always passed by reference in php5, need to do this to ensure the original object,
Syndicate content