00001 <?php 00002 // $Id: sample_plugin_ct.inc,v 1.1.2.3 2008/07/29 07:14:56 sdboyer Exp $ 00003 00004 /** 00005 @file 00006 Plugin Reference: Content Types 00007 00008 API documentation for Content Type (Soon to be: Pane Type) plugins. 00009 / 00010 00011 /** 00012 @Pplug{content_types,Content Types} 00013 Content type plugins define the content you can put into panes for display; If you want to see a particular piece of content rendered by Panels, you need to write a content type. \n 00014 00015 <p>Remember - although grouping the callbacks defined in these properties 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.</p> 00016 <p>Some unnecessary duplication and inelegancies persist in the logic behind content type plugin properties; this is particularly noteworthy in the case of the add/edit property distinctions. These issues are allowed to persist largely for legacy compatibility reasons.</p> 00017 <p>Plugin declaration functions must adhere to a particular naming convention; see panels_get_include_directories() for details. \n Note that the \pdarray provided by 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.</p> 00018 00019 @SECplug{content_types,pdfunc,Content Type Plugin Declaration} 00020 <p>This sample \pdfunc returns a definition array containing ALL of the \plugprops that Panels currently allows. The string used for the array key is significant: it determines the 'type' that panes which are created by this plugin will be assigned.</p> 00021 <p>Keep in mind when choosing a type name that namespace collisions are both silent and destructive (the Panels engine will not emit any errors, it will simply overwrite data). Such collisions will always be decided in favor of the last plugin processed; processing order order is generally determined by the weight of the module (in the system table) defining the plugin.</p> 00022 <p>All the plugins that come packaged with Panels necessarily obey a strict naming convention: the array key used for $items is the same as the name of the file itself, as well as being the same as the string prefixed by the module name (in this case, 'panels_'), and affixed by a string that indicates the type of plugin (in this case, '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().</p> 00023 @code 00024 function panels_SAMPLE_CT_panels_content_types() { 00025 $items['SAMPLE_CT'] = array( 00026 'title' => t('Sample Content Type'), 00027 'weight' => -10, 00028 'single' => TRUE, 00029 'content_types' => 'panels_admin_content_types_SAMPLE_CT', 00030 'render callback' => 'panels_content_SAMPLE_CT', 00031 'add callback' => 'panels_admin_add_SAMPLE_CT', 00032 'edit callback' => 'panels_admin_edit_SAMPLE_CT', 00033 'add validate callback' => 'panels_admin_validate_SAMPLE_CT', 00034 'edit validate callback' => 'panels_admin_validate_SAMPLE_CT', 00035 'add submit callback' => 'panels_admin_submit_SAMPLE_CT', 00036 'edit submit callback' => 'panels_admin_submit_SAMPLE_CT', 00037 'title callback' => 'panels_admin_title_SAMPLE_CT', 00038 'editor render callback' => 'panels_admin_pane_render_SAMPLE_CT', 00039 'render last' => TRUE, 00040 'visibility control' => 'panels_admin_visibility_control_SAMPLE_CT', 00041 'visibility submit' => 'panels_admin_visibility_submit_SAMPLE_CT', 00042 'visibility check' => 'panels_content_visibility_check_SAMPLE_CT', 00043 'visibility serialize' => TRUE, 00044 'role-based access' => FALSE, 00045 'roles and visibility' => TRUE, 00046 'form control' => 'panels_admin_form_control_SAMPLE_CT', 00047 ); 00048 return $items; 00049 } 00050 @endcode \n\n 00051 00052 @SSECplug{content_types,pdfunc,setprops,Overview of Setting Properties} 00053 <p>Only a few of the twenty properties defined for content type plugins are required, and some of them have default values that Panels will set if you don't assign anything. This first table covers setting properties. Keep in mind that although these tables indicate that there are requirements and interdependencies among the properties, there are no systematic checks to ensure that a plugin meets the requirements. Panels simply assumes that you did it right.</p> 00054 \n 00055 <table> 00056 <tr> 00057 <td>Property Name</td> 00058 <td>Data Type</td> 00059 <td>Required?</td> 00060 <td>Default Value</td> 00061 <td>Dependencies</td> 00062 <td>Notes</td> 00063 </tr> 00064 <tr> 00065 <td>@AAP{content_types,title,title}</td> 00066 <td>string</td> 00067 <td>Yes</td> 00068 <td></td> 00069 <td>None</td> 00070 <td>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.</td> 00071 </tr> 00072 <tr> 00073 <td>@AAP{content_types,weight,weight}</td> 00074 <td>Integer</td> 00075 <td>No</td> 00076 <td>\c 0</td> 00077 <td>None</td> 00078 <td>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().</td> 00079 </tr> 00080 <tr> 00081 <td>@AAP{content_types,single,single}</td> 00082 <td>Boolean</td> 00083 <td>No</td> 00084 <td>\c FALSE</td> 00085 <td>None</td> 00086 <td>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)</td> 00087 </tr> 00088 <tr> 00089 <td>@AAP{content_types,render-last,render last}</td> 00090 <td>Boolean</td> 00091 <td>No</td> 00092 <td><var>FALSE</var><sup>1</sup></td> 00093 <td>none</td> 00094 <td>If set to \c TRUE, this pane will be pushed to the back of the line during the render routine. See panels_render_panes().</td> 00095 </tr> 00096 <tr> 00097 <td>@AAP{content_types,visibility-serialize,visibility serialize}</td> 00098 <td>Boolean</td> 00099 <td>Yes<sup>2,3</sup></td> 00100 <td>FALSE</td> 00101 <td>@LAP{content_types,t-visibility-control,visibility control}</td> 00102 <td>If \c TRUE, then contents of $pane->visibility will be serialized before being saved to the database. This should be set as \c 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.</td> 00103 </tr> 00104 <tr> 00105 <td>@AAP{content_types,role-based-access,role-based access}</td> 00106 <td>Boolean</td> 00107 <td>No</td> 00108 <td><var>TRUE</var><sup>4</sup></td> 00109 <td>none</td> 00110 <td>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.</td> 00111 </tr> 00112 <tr> 00113 <td>@AAP{content_types,roles-and-visibility,roles and visibility}</td> 00114 <td>Boolean</td> 00115 <td>No</td> 00116 <td><var>FALSE</var><sup>1</sup></td> 00117 <td>@LAP{content_types,t-visibility-control,visibility control}</td> 00118 <td>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 <var>TRUE</var>. Setting 'role-based access' to <var>TRUE</var> is not sufficient; see panels_ajax_ct_preconfigure() to understand how this works. If you use both systems, panels_pane_access() will \c AND the results together when determining pane visibility.</td> 00119 </tr> 00120 </table> 00121 \par 00122 <small> 00123 <sup>1</sup> Technically, this property doesn't have a default value. However, because the setting is checked using a call to empty(), it effectively has a default value of <var>FALSE</var>.\n 00124 <sup>2</sup> Only required if properties it depends upon have been set.\n 00125 <sup>3</sup> Note that this will automatically be set to <var>FALSE</var> if the @LAP{content_types,t-visibility-control,visibility control} property is set defined.\n 00126 <sup>4</sup> 'Required' is a little misleading in this case. it means that Panels needs this property to be set, not that it must be set by the plugin. So, if the default value works for your plugin, you don't have to define this property.\n 00127 </small>\n\n 00128 00129 @SSECplug{content_types,pdfunc,callprops,Overview of Callback Properties} 00130 00131 <p>This table provides a basic summary for all thirteen @LGt{p-p-callback,callback properties}. Most of the properties also have detailed documentation, which can be reached by clicking the property name.</p> 00132 <p>Parameters that are passed by reference from the function side (through call_user_func_array()) are marked with the by-reference operator.</p> 00133 \attention 00134 apis may break. here's how they're likely to. be warned. 00135 \attention 00136 form control is the most subject to change. 00137 \note 00138 'required' and 'dependencies' have specific meanings. 00139 00140 <table> 00141 <tr> 00142 <td>Property Name</td> 00143 <td>Return Value Type</td> 00144 <td>Required?</td> 00145 <td>Dependencies</td> 00146 <td>Parameters</td> 00147 <td>Notes</td> 00148 </tr> 00149 <tr> 00150 <td>@LAP{content_types,callbacks_content_types,content_types}@AAP{content_types,t-content_types, }</td> 00151 <td>Array</td> 00152 <td>Yes</td> 00153 <td>None</td> 00154 <td></td> 00155 <td>Panels calls this function to find out how many content types this plugin provides, as well as some basic 'gatekeeper' information about each of those content types. Most importantly, optional and required context(s) are defined in this function.</td> 00156 </tr> 00157 <tr> 00158 <td>@LAP{content_types,callbacks_render-callback,render callback}@AAP{content_types,t-render-callback, }</td> 00159 <td>Object</td> 00160 <td>Yes</td> 00161 <td>None</td> 00162 <td><var>$pane->configuration</var><sup>1</sup>, <var>$panel_args</var>, <var>$context</var></td> 00163 <td>Panels calls this function while preparing a @LGt{display,display object} for viewing. The callback needs to construct and return an object, which is passed along to the @LAP{styles,Style} and @LAP{layouts,Layout} plugins for handling.</td> 00164 </tr> 00165 <tr> 00166 <td>@LAP{content_types,callbacks_add-callback,add callback}@AAP{content_types,t-add-callback, }</td> 00167 <td>FAPI Array</td> 00168 <td>No</td> 00169 <td>None</td> 00170 <td><var>$subtype</var>, <var>$parents</var>, <var>$pane->configuration</var><sup>1,2</sup></td> 00171 <td>This function gets called when the user clicks an icon to add a new pane (from the 'Add Content' @LGt{modal,modal form}). note that it is often possible to use the same, or nearly the same, callback for this as for the @LAP{content_types,callbacks_edit-callback,edit callback}.</td> 00172 </tr> 00173 <tr> 00174 <td>@LAP{content_types,callbacks_edit-callback,edit callback}@AAP{content_types,t-edit-callback, }</td> 00175 <td>FAPI Array</td> 00176 <td>No</td> 00177 <td>None</td> 00178 <td><var>$subtype</var>, <var>$parents</var>, <var>$pane->configuration</var></td> 00179 <td>This function gets called when the user clicks the 'Configure' button on an already-existing pane; it partially governs what appears on the resulting configuration modal.</td> 00180 </tr> 00181 <tr> 00182 <td>@LAP{content_types,callbacks_addedit-validate-callback,add/edit validate callback}@AAP{content_types,t-addedit-validate-callback, }</td> 00183 <td></td> 00184 <td>No</td> 00185 <td>None<sup>3</sup></td> 00186 <td><var>$form</var>, <var>$form_values</var></td> 00187 <td>Defines a callback to be used as a FAPI validator, but only for the $form_values set by form items defined in the @LAP{content_types,callbacks_edit-callback,add/edit callback}.</td> 00188 </tr> 00189 <tr> 00190 <td>@LAP{content_types,callbacks_addedit-submit-callback,add/edit submit callback}@AAP{content_types,t-addedit-submit-callback, }</td> 00191 <td>part of the <var>$pane->configuration</var> Array</td> 00192 <td>No</td> 00193 <td>None<sup>3</sup></td> 00194 <td><var>$form_values</var></td> 00195 <td>Defines a callback to be used as a FAPI submit handler, but only for the $form_values set by form items defined in the @LAP{content_types,callbacks_edit-callback,add/edit callback}.</td> 00196 </tr> 00197 <tr> 00198 <td>@LAP{content_types,callbacks_title-callback,title callback}@AAP{content_types,t-title-callback, }</td> 00199 <td>String</td> 00200 <td>Yes</td> 00201 <td>None</td> 00202 <td><var>$pane->configuration</var>, <var>$context</var></td> 00203 <td>This function determines the title that the pane will use in the display content editor, and ONLY that title.</td> 00204 </tr> 00205 <tr> 00206 <td>@LAP{content_types,callbacks_editor-render-callback,editor render callback}@AAP{content_types,t-editor-render-callback, }</td> 00207 <td>String</td> 00208 <td>No</td> 00209 <td>None</td> 00210 <td><var>$display</var>, <var>$pane</var></td> 00211 <td>This function determines the title that the pane will use in the display content editor, and ONLY that title.</td> 00212 </tr> 00213 <tr> 00214 <td>@LAP{content_types,callbacks_visibility-control,visibility control}@AAP{content_types,t-visibility-control, }</td> 00215 <td>FAPI Array</td> 00216 <td>No</td> 00217 <td>None</td> 00218 <td><var>$contexts</var>, <var>$subtype</var>, <var>$pane->configuration</var>, <var>$add</var></td> 00219 <td>This callback is fired shortly after the add/edit callbacks. Use it to create a form widget form widget from which the user can select values that will make sense when passed to your @LAP{content_types,callbacks_visibility-check,visibility check callback}.</td> 00220 </tr> 00221 <tr> 00222 <td>@LAP{content_types,callbacks_visibility-submit,visibility submit}@AAP{content_types,t-visibility-submit, }</td> 00223 <td>Mixed</td> 00224 <td>No</td> 00225 <td>@LAP{content_types,callbacks_visibility-control,visibility control}</td> 00226 <td><var>$contexts</var>, <var>$subtype</var>, <var>$pane->configuration</var>, <var>$add</var></td> 00227 <td>The custom submit handler for your content type's visibility settings. This function is passed the portion of the $form_values array that was generated from the widgets created by the @LAP{content_types,callbacks_visibility-control,visibility control} callback. Most plugins won't need to define this property, even if they define custom visibility control.</td> 00228 </tr> 00229 <tr> 00230 <td>@LAP{content_types,callbacks_visibility-check,visibility check}@AAP{content_types,t-visibility-check, }</td> 00231 <td>Boolean</td> 00232 <td>Yes<sup>4</sup></td> 00233 <td>@LAP{content_types,callbacks_visibility-control,visibility control}</td> 00234 <td><var>$contexts</var>, <var>$subtype</var>, <var>$pane->configuration</var>, <var>$add</var></td> 00235 <td>Panels calls this function during the pane accessibility checking routine, which is handled by primarily by panels_pane_access(). Define the logic governing your content type's visibility here.</td> 00236 </tr> 00237 <tr> 00238 <td>@LAP{content_types,callbacks_form-control,form control}@AAP{content_types,t-form-control, }</td> 00239 <td>FAPI <var>$form</var> Array</td> 00240 <td>No</td> 00241 <td>None</td> 00242 <td><var>&$form</var>, <var>&$pane</var>, <var>&$display</var></td> 00243 <td>If the other callbacks governing the add/edit form (i.e., the @LAP{content_types,callbacks_edit-callback,add/edit callback} properties or the @LAP{content_types,callbacks_visibility-control,visibility control} property) aren't enough for your needs, then implement this callback. This function is passed virtually all of the Panels editing data by reference. Use with caution.</td> 00244 </tr> 00245 </table> 00246 \par 00247 <small> 00248 <sup>1</sup> In the sample functions, the variable <var>$conf</var> is used. <var>$pane->configuration</var> is used here instead because that's the value being passed in, and it has meaning out of context, unlike <var>$conf</var>.\n 00249 <sup>2</sup> Because this is a new pane, its configuration is always going to be empty.\n 00250 <sup>3</sup> Even if you haven't defined the add/edit callback property, you can still define the validate/submit properties - Panels doesn't check. Of course, if you do so, your callback will be passed a great big <var>NULL</var>...\n 00251 <sup>4</sup> Only required if properties it depends upon have been set.\n 00252 </small>\n 00253 00254 <hr> 00255 \n 00256 00257 00258 @SECplug{content_types,callbacks,Discussion and Samples of Callbacks} 00259 00260 @SSECplug{content_types,callbacks,content_types,Callback Property: 'content_types'} 00261 Callback function set by the @LAP{content_types,t-content_types,content_types} property. Returns an array of data that Panels uses to determine: 00262 - Whether or not the content type can be added to this display, based on 00263 what context(s) are available. 00264 - If context requirements are met, the remainder of the array's data 00265 defines the icon, title, and description that the content type will 00266 be rendered with on the the Add Content @LGt{modal,modal}.\n\n 00267 00268 @code 00269 function panels_admin_content_types_SAMPLE_CT() { 00270 return array( 00271 // As with the plugin declaration, the value of this array key is significant: 00272 // it will become the pane's subtype, stored in $pane->subtype. 00273 'content' => array( 00274 // The name used for this subtype on the Add Content modal - this is what 00275 // appears right below the icon. 00276 'title' => t('SAMPLE CONTENT TYPE'), 00277 // The name of the icon file to be used for this subtype. 00278 'icon' => 'icon_node.png', 00279 // The server path to the directory where the above icon is located. 00280 'path' => panels_get_path('content_types/node'), 00281 // The 'description' appears as a tooltip when the user hovers their 00282 // mouse pointer over the icon. 00283 'description' => t('Descriptive text for the SAMPLE CONTENT TYPE, to be used in the tooltip.'), 00284 // This property indicates which contexts are prerequisites for the content 00285 // type to be used. If a display lacks a context required by this content 00286 // type, then it simply will not be displayed. Multiple required contexts 00287 // can be declared by placing each context into an indexed array. 00288 'required context' => new panels_required_context(t('Sample Required Context'), 'sample_context_required'), 00289 // This property has the same syntax as 'required context', but if optional 00290 // context requirements are not met, the content type will still be usable, 00291 // simply in a reduced form. It's up to the plugin author to define just how 00292 // different that functionality by writing varying the behavior of this plugin's 00293 // other callbacks according to the presence/absence of the context. 00294 'optional context' => new panels_optional_context(t('Sample Optional Context'), 'sample_context_optional'), 00295 // Category is the group this subtype's icon will be placed in. The first 00296 // item in the array is the category name, and the second is the subtype's 00297 // weight in that category (used for ordering the subtypes in the category 00298 // relative to one another). Omitting a value for weight will cause it to 00299 // default to 0; if you do omit the weight, you can simply return the 00300 // t()-wrapped string title of the content type - no need to put it in an array. 00301 'category' => array(t('Node context'), -9), 00302 ), 00303 ); 00304 } 00305 @endcode 00306 00307 00308 <hr> 00309 00310 00311 @SSECplug{content_types,callbacks,render-callback,Callback Property: 'render callback'} 00312 Callback function set by the @LAP{content_types,t-render-callback,render callback} property. This callback constructs and returns an object for display. 00313 00314 The sample function below is a direct copy of the node_content plugin's render 00315 callback; abstract example cases are of little use from here on out. Note that 00316 this case only implements three parameters, but there is also a fourth. Your 00317 content type can use as few/many of these parameters as you want, although 00318 you won't be able to much if you don't implement the first parameter, $conf. 00319 00320 @param array $conf 00321 The contents of $pane->configuration. This will be an array with the following 00322 keys, by default: 00323 - override_title (int/bool): 0 or 1, reflecting whether the user checked the 00324 'override title' checkbox on the pane configuration form. 00325 - override_title_text (string): a string containing the title override, as 00326 written by the user on the pane configuration form. 00327 - css_id (string): the special css id entered by the user on the pane config 00328 form, if any. 00329 - css_class (string): same idea as the css id. 00330 - module (string): a string containing the name of the module implementing 00331 this content type (or, in some cases, owning/generating the content).\n 00332 The above keys reflect the standard set of form items that the Panels API 00333 provides to every pane type by default. Any additional configuration items that 00334 you add (via the add/edit callbacks) will also appear in $conf by default. 00335 @param array $panel_args 00336 An indexed array of all arguments, if any, that have been passed to the display. 00337 @param mixed $context 00338 The contents of $context can vary widely. If only one context is being passed 00339 to the pane, $context will simply be that context object. If multiple contexts 00340 are passed, however, then $context will be an indexed array of those contexts. 00341 The sort of data contained in the context is completely dependent on the how 00342 that context has been defined. 00343 @param $incoming_content 00344 00345 @return object $block 00346 An object, ready to be passed through the styling & theming layers. At minimum, 00347 the object should contain a 'content' element, as well as 'title' and/or 00348 'subject' elements. If a 'title' element is not included, then the 'subject' 00349 is copied into $block->title later on in the render process. You are free to 00350 define as many elements as you want, but those elements will only be used 00351 if you write a panels style plugin specifically designed to take advantage of 00352 of them. Note that the '$block' variable name used here is arbitrary. 00353 00354 @code 00355 function panels_content_SAMPLE_CT($conf, $panel_args, $context) { 00356 // The node_content content_type plugin has a required context of 'node.' 00357 // This simply double-checks to make sure that the necessary context is present; 00358 // in particular, it excludes 'empty' contexts, which are used primarily during 00359 // the edit process. 00360 if (!empty($context) && empty($context->data)) { 00361 return; 00362 } 00363 00364 // The node context plugin stores an entire, fully-loaded $node object into 00365 // its $context->data element; this pulls that node data out (via cloning, to 00366 // ensure the original context data itself remains unchanged) and stores it in 00367 // a correspondingly-named variable, $node. 00368 $node = isset($context->data) ? drupal_clone($context->data) : NULL; 00369 $block->module = 'node'; 00370 // Stores the nid from the context, to ensure it is acecssible later. 00371 $block->delta = $node->nid; 00372 00373 // Just in case the context didn't load, but managed to get past the initial 00374 // checks, this adds filler content to the $block. 00375 if (empty($node)) { 00376 $block->delta = 'placeholder'; 00377 $block->subject = t('Node title.'); 00378 $block->content = t('Node content goes here.'); 00379 } 00380 else { 00381 if (!empty($conf['identifier'])) { 00382 $node->panel_identifier = $conf['identifier']; 00383 } 00384 00385 $block->subject = $node->title; 00386 00387 unset($node->title); 00388 // The pane's content is a complex enough operation that we delegate creating 00389 // it to a helper function. 00390 $block->content = panels_admin_SAMPLE_CT($node, $conf); 00391 } 00392 00393 // If the user has the necessary permissions, an 'admin link' is generated. 00394 // Admin links are the special links that appear above the pane's title when 00395 // you mouse over the pane. 00396 if (node_access('update', $node)) { 00397 $block->admin_links['update'] = array( 00398 'title' => t('Edit node'), 00399 'alt' => t("Edit this node"), 00400 'href' => "node/$node->nid/edit", 00401 'query' => drupal_get_destination(), 00402 ); 00403 } 00404 00405 if (!empty($conf['link']) && $node) { 00406 $block->title_link = "node/$node->nid"; 00407 } 00408 00409 return $block; 00410 } 00411 @endcode 00412 00413 Probably the most important thing to be noted about this helper function is 00414 just how similar it is to node.module's routine for node rendering. 00415 In fact, it's little more than a minor rewrite of 00416 <a href="http://api.drupal.org/api/function/node_view/5">node_view()</a>; the first lines are lifted directly from <a href="http://api.drupal.org/api/function/node_build_content/5">node_build_content()</a>, 00417 and the latter half from node_view(). 00418 00419 @code 00420 function panels_admin_SAMPLE_CT($node, $conf) { 00421 // Remove the delimiter (if any) that separates the teaser from the body. 00422 $node->body = str_replace('<!--break-->', '', $node->body); 00423 00424 // The 'view' hook can be implemented to overwrite the default function 00425 // to display nodes. 00426 if (node_hook($node, 'view')) { 00427 $node = node_invoke($node, 'view', $conf['teaser'], $conf['page']); 00428 } 00429 else { 00430 $node = node_prepare($node, $conf['teaser']); 00431 } 00432 00433 if (empty($conf['no_extras'])) { 00434 // Allow modules to make their own additions to the node. 00435 node_invoke_nodeapi($node, 'view', $conf['teaser'], $conf['page']); 00436 } 00437 00438 if ($conf['links']) { 00439 $node->links = module_invoke_all('link', 'node', $node, $conf['teaser']); 00440 00441 foreach (module_implements('link_alter') AS $module) { 00442 $function = $module .'_link_alter'; 00443 $function($node, $node->links); 00444 } 00445 } 00446 00447 // Set the proper node part, then unset unused $node part so that a bad 00448 // theme can not open a security hole. 00449 $content = drupal_render($node->content); 00450 if ($conf['teaser']) { 00451 $node->teaser = $content; 00452 unset($node->body); 00453 } 00454 else { 00455 $node->body = $content; 00456 unset($node->teaser); 00457 } 00458 00459 // Allow modules to modify the fully-built node. 00460 node_invoke_nodeapi($node, 'alter', $conf['teaser'], $conf['page']); 00461 00462 return theme('node', $node, $conf['teaser'], $conf['page']); 00463 } 00464 @endcode 00465 00466 00467 <hr> 00468 00469 00470 @SSECplug{content_types,callbacks,add-callback,Callback Property: 'add callback'} 00471 Callback function set by the @LAP{content_types,t-add-callback,add callback} property. This callback constructs 00472 the pane configuration form for newly-added panes. This sample is lifted from 00473 the block content type plugin (block.inc); it is the only built-in Panels content 00474 type that implements an add callback that is different from the edit callback. 00475 00476 Clearly there's relatively little need to differentiate between the add and edit 00477 callbacks; the only thing this one does is make sure that $conf has some of the 00478 right values before heading into the edit form. You still need to define both the 00479 'add callback' and 'edit callback' properties in the plugin declaration array, but you 00480 can just make them point to the same function. 00481 00482 See the edit callback for more detailed discussion. \n 00483 00484 @code 00485 function panels_admin_add_SAMPLE_CT($id, $parents, $conf = array()) { 00486 list($conf['module'], $conf['delta']) = explode('-', $id, 2); 00487 return panels_admin_edit_SAMPLE_CT($id, $parents, $conf); 00488 } 00489 @endcode 00490 00491 00492 <hr> 00493 00494 00495 @SSECplug{content_types,callbacks,edit-callback,Callback Property: 'edit callback'} 00496 Callback function set by the @LAP{content_types,t-edit-callback,edit callback} property in the plugin declaration array. 00497 This callback constructs the configuration form for panes that have already been added; 00498 the callback is fired when the 'Configure' button is clicked. 00499 00500 This function essentially operates like a limited and targeted implementation of 00501 <a href="http://api.drupal.org/api/function/hook_form_alter/5">hook_form_alter()</a>; the Panels API wrangles FAPI as needed, so all you need to do 00502 is add the widgets you want for your content type/subtype. 00503 00504 \note 00505 In future versions, the 'Block visibility' properties are likely to be moved into 00506 the appropriate visibility callbacks. They're here now because the block content type 00507 plugin was written long before the visibility system was introduced. 00508 00509 Some of the techniques used in this edit callback are pretty advanced. For a more basic 00510 but quite thorough implementation of this callback, see panels_admin_edit_node_content(). 00511 00512 @param string $id 00513 The subtype of the pane being edited. The block panels content type plugin calls this 00514 variable '$id' for legacy reasons; we recommend you call this variable $subtype if you 00515 want your variable names to be optimally descriptive of their values. 00516 @param array $parents 00517 This parameter is largely deprecated, and is included for legacy API compatibility. Its 00518 intention was to provide information to form widgets about where they live on the $form. 00519 It is likely to disappear in Panels3. For all add/edit callbacks: 00520 @code $parents = array('configuration'); @endcode 00521 This corresponds to the fact that the $form returned from this callback will not be added 00522 to the root of the overall $form array, but to the $form['configuration'] sub-array. 00523 See panels_content_config_form(). 00524 @param array $conf 00525 The contents of $pane->configuration, if any. 00526 @return array $form 00527 A standard FAPI form array. 00528 00529 00530 @code 00531 function panels_admin_edit_SAMPLE_CT($id, $parents, $conf) { 00532 $form['module'] = array( 00533 '#type' => 'value', 00534 '#value' => $conf['module'], 00535 ); 00536 $form['delta'] = array( 00537 '#type' => 'value', 00538 '#value' => $conf['delta'], 00539 ); 00540 00541 if (user_access('administer advanced pane settings')) { 00542 $form['block_visibility'] = array( 00543 '#type' => 'checkbox', 00544 '#title' => t('Use block visibility settings (see block config)'), 00545 '#default_value' => $conf['block_visibility'], 00546 '#description' => t('If checked, the block visibility settings for this block will apply to this block.'), 00547 ); 00548 // Module-specific block configurations. 00549 if ($settings = module_invoke($conf['module'], 'block', 'configure', $conf['delta'])) { 00550 // Specifically modify a couple of core block forms. 00551 if ($conf['module'] == 'block') { 00552 unset($settings['submit']); 00553 $settings['info']['#type'] = 'value'; 00554 $settings['info']['#value'] = $settings['info']['#default_value']; 00555 } 00556 panels_admin_fix_block_tree($settings); 00557 $form['block_settings'] = array( 00558 '#type' => 'fieldset', 00559 '#title' => t('Block settings'), 00560 '#description' => t('Settings in this section are global and are for all blocks of this type, anywhere in the system.'), 00561 '#tree' => FALSE, 00562 ); 00563 00564 00565 $form['block_settings'] += $settings; 00566 } 00567 } 00568 00569 return $form; 00570 } 00571 @endcode 00572 00573 00574 <hr> 00575 00576 00577 @SSECplug{content_types,callbacks,addedit-submit-callback,Callback Property: 'add/edit submit callback'} 00578 Callback function set by the @LAP{content_types,t-addedit-submit-callback,edit callback}'[add|edit] submit callback' property in the plugin 00579 declaration array. In simpler plugins, you won't need to implement this callback; 00580 any values set by the widgets you added to the config form will automatically 00581 have their data added to the $pane->configuration array, which is serialized 00582 and stored in the panels_pane table. 00583 00584 However, in cases where the settings being changed on this form need to be 00585 reflected in some other data structure, this callback can be used to ensure 00586 that the necessary changes are made. In this example (again from the block 00587 content type plugin), hook_block() is invoked with $op = 'save' for the module 00588 that owns the block, thereby allowing the normal block saving routine to 00589 do its thing. 00590 00591 00592 @code 00593 function panels_admin_submit_SAMPLE_CT(&$form_values) { 00594 if (!empty($form_values['block_settings'])) { 00595 module_invoke($form_values['module'], 'block', 'save', $form_values['delta'], $form_values['block_settings']); 00596 } 00597 } 00598 @endcode 00599 00600 00601 <hr> 00602 00603 00604 @SSECplug{content_types,callbacks,title-callback,Callback Property: 'title callback'} 00605 Callback function set by the @LAP{content_types,t-title-callback,title callback} property in the plugin definition array. 00606 00607 Returns the title to be used in the display editor ONLY. When the pane is 00608 rendered for viewing, the value of $obj->title or $obj->subject, as returned 00609 from the callback defined in the 'render callback' property, will become the 00610 pane's title. The only way the value returned from here will show up as the 00611 pane's title upon viewing is if this callback is explicitly called from the 00612 render callback itself (i.e., @code $obj->title = panels_admin_title_SAMPLE_CT($conf, $context); @endcode) 00613 00614 @param array $conf 00615 The contents of $pane->configuration, if any. 00616 @param mixed $context 00617 The contents of $context can vary; see other sample callback parameters for details. 00618 @return string $title 00619 The title to use for the pane in the display editor. Make sure to run it through t(), first. 00620 00621 00622 @code 00623 function panels_admin_title_SAMPLE_CT($conf, $context) { 00624 return t('"@s" content', array('@s' => $context->identifier)); 00625 } 00626 @endcode 00627 00628 @SSECplug{content_types,callbacks,editor-render-callback,Callback Property: 'editor render callback'} 00629 Callback function set by the @LAP{content_types,t-editor-render-callback,editor render callback} property in the plugin definition array. 00630 00631 Returns object used to populate the content area of the pane in the display 00632 editor ONLY. 00633 00634 @param object $display 00635 The full display object being edited. 00636 @param object $pane 00637 The pane object being rendered for display editing. 00638 00639 @return object 00640 The object containing the data to use when rendering the pane. The two fields in the object that are used for rendering are @code $obj->title @endcode, which is used as the title on the collapsible fieldset, and @code $obj->content @endcode, which is the contents of fieldset (if any). 00641 00642 @code 00643 function panels_admin_pane_render_SAMPLE_CT($display, $pane) { 00644 // Pretend your content type stores a node id in the $configuration array 00645 // and that you want the title of that node as the fieldset title, and the 00646 // teaser for that node as the content. 00647 $node = node_load($pane->configuration['nid']); 00648 $block = new stdClass(); 00649 $block->title = check_plain($node->title); 00650 $block->content = node_view($node, TRUE); 00651 return $block; 00652 } 00653 @endcode 00654 00655 <hr> 00656 00657 00658 @SSECplug{content_types,callbacks,visibility-control,Callback Property: 'visibility control'} 00659 Callback function set by the @LAP{content_types,t-visibility-control,visibility control} property in the plugin declaration array. 00660 00661 If your plugin defines this property, you'll need to be cognizant of your definitions for several other properties: @LAP{content_types,callbacks_visibility-submit,visibility submit}, @LAP{content_types,callbacks_visibility-check,visibility check} and @LAP{content_types,visibility-serialize,visibility serialize}. @LAP{content_types,roles-and-visibility,roles and visibility} is also relevant, but won't be useful to the vast majority of content type plugins. 00662 00663 Operates quite similarly to the add and edit callbacks, with a few exceptions: 00664 - The FAPI parent is $form['visibility'] instead of $form['configuration'] 00665 - Instead of creating separate callbacks for add and edit, the $add parameter 00666 indicates which operation is taking place. 00667 - Whereas a submit handler is often unnecessary for the add/edit callbacks, 00668 implementing this callback property means you need to be cognizant of several 00669 other properties in the definition array. 00670 The visibility widget defined in this function is a reworked version of one originally 00671 created for og_panels (but was not/has not yet been committed); the goal is to control 00672 pane visibility according to the status of the current user relative to the group. 00673 00674 Remember, this is NOT where you define the logic behind your visibility handling - 00675 all you're doing here is providing a form widget that to get some data. It's up to 00676 your visibility checker, defined in the @LAP{content_types,callbacks_visibility-check,visibility check} callback, to create the 00677 logic that can take the data from here and make the right decision about pane 00678 visibility. 00679 00680 If you define a more complex system that uses multiple widgets, make sure to return 00681 them all stacked inside a single array, and that you set the FAPI <var>tree</var> property to <var>TRUE</var> as needed. 00682 00683 @param mixed $contexts 00684 As in the render callback, this is either a context object, or an array of context 00685 objects. It's unnecessary and probably unwise to include this context 00686 data directly in the values that get saved in your form visibility function; that 00687 very same data will be available via the $display variable that's passed to the 00688 checker. Rather, $context is provided in the event that your visibility widget 00689 needs to vary depending on some information in $context. 00690 @param string $subtype 00691 The contents of $pane->subtype for the pane currently being edited. 00692 @param array $conf 00693 The contents of $pane->configuration, if any. 00694 @param bool $add 00695 If TRUE, then a new pane is being added. If FALSE, then an existing pane is being edited. 00696 @return array $visibility_widget 00697 A standard FAPI widget, to be added to the form. 00698 00699 00700 @code 00701 function panels_admin_visibility_control_SAMPLE_CT($contexts, $subtype, $conf, $add) { 00702 return $visibility_widget = array( 00703 '#type' => 'radios', 00704 '#title' => t('Pane Visibility'), 00705 '#description' => t('Who should this pane be visible to?'), 00706 '#options' => array( 00707 'all' => t('Everyone'), 00708 'member' => t('Only group members'), 00709 'nonmember' => t('Only group non-members'), 00710 'admin' => t('Group administrators'), 00711 ), 00712 '#default_value' => isset($conf['visibility']) ? $conf['visibility'] : 0, 00713 ); 00714 } 00715 @endcode 00716 00717 00718 <hr> 00719 00720 00721 @SSECplug{content_types,callbacks,visibility-check,Callback Property: 'visibility check'} 00722 Callback function set by the @LAP{content_types,t-visibility-check,visibility check} property in the plugin definition array. 00723 00724 This function takes advantage of cached static variables to increase performance. On any 00725 given page request, we know that only ONE group is going to be accessed, and only ONE user 00726 is going to be doing the accessing. Since the static keyword only lasts through a single page 00727 request, and nid and uid are the two variables that visibility depends upon in this case, 00728 we only have to query the database and build the $visibility array once, no matter how many 00729 panes fire this callback to determine visibility during this page request. 00730 00731 @param object $pane 00732 The fully-loaded $pane object that we're running the visibility check against. The 00733 value set by the widget defined in the @LAP{content_types,callbacks_visibility-control,visibility control} callback is contained 00734 in $pane->visibility. 00735 @param object $display 00736 The fully-loaded display object that's currently being rendered. If you need $context 00737 to figure out what action to take, you'll find it/them in $display->context. 00738 @param object $user 00739 The current $user - the same as what you'd get from <var>global $user</var>. 00740 Passed for convenience, since visibility is often dependent on the $user. 00741 @return bool 00742 A boolean indicating if the pane should (TRUE) or should not (FALSE) be visible. 00743 00744 00745 @code 00746 function panels_content_visibility_check_SAMPLE_CT($pane, $display, $user) { 00747 // use static variable to somewhat reduce queries for complex og_panels pages 00748 static $visible; 00749 if (!is_array($visible)) { 00750 $visible = array(); 00751 $visible['all'] = TRUE; 00752 } 00753 if (!isset($visible[$pane->visibility])) { 00754 $members = array(); 00755 $sql = "SELECT u.uid AS uid, ogu.is_admin AS admin FROM {og_uid} ogu 00756 INNER JOIN {users} u ON ogu.uid = u.uid WHERE ogu.nid = %d 00757 AND ogu.is_active = 1 AND u.status = 1 ORDER BY ogu.created DESC"; 00758 // $display holds the context; the $data element of $context holds a node object, 00759 // and we want the nid of that node. 00760 $result = db_query($sql, $display->context->data->nid); 00761 while ($account = db_fetch_array($result)) { 00762 $members[$account['uid']] = $account['admin']; 00763 } 00764 $visible['member'] = in_array($user->uid, array_keys($members)); 00765 $visible['nonmember'] = !$visible['member']; 00766 $visible['admin'] = $visible['member'] ? $members[$user->uid] : FALSE; 00767 } 00768 return $visible[$pane->visibility]; 00769 } 00770 @endcode 00771 00772 00773 <hr> 00774 00775 00776 @SSECplug{content_types,callbacks,visibility-submit,Callback Property: 'visibility submit'} 00777 Callback function set by the @LAP{content_types,t-visibility-submit,visibility submit} property in the plugin definition array. 00778 This sample submit handler below is valid for the sample @LAP{content_types,callbacks_visibility-control,visibility control} callback. However, the submit handler itself is 00779 also completely superfluous - the Panels API's built-in handler is entirely 00780 adequate in this case. It is included purely as an excuse to discuss the 00781 parameters of this function, and the different ways in can work. 00782 00783 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. 00784 00785 Most use cases for the Panels visibility system will not need to implement a 00786 submit handler, as the built-in handler in panels_content_config_form_submit() 00787 is adequate for taking care of the data. However, if you need to manipulate 00788 the data generated by your form widget before it gets saved, or if you need 00789 to inform a custom external storage mechanism, hook, whatever, about the 00790 visibility setting, then you should do so here. 00791 00792 Whatever you return from this callback will be saved as the value of 00793 <var>$pane->visibility</var>. If it is a data type that must be serialized before being 00794 put in the database (arrays and objects), then make sure to set the 'visibility 00795 serialize' property to TRUE for this content type. 00796 00797 Note that in this particular sample implementation, the 'visibility serialize' 00798 property should be set to FALSE, as the value produced by the widget in the 00799 sample @LAP{content_types,callbacks_visibility-control,visibility control} function returns a simple string. Because 'visibility 00800 serialize' is FALSE by default, you can also simply not set the property. 00801 00802 Note also that the visibility field in the panels_pane table is the standard 00803 'text' field. The save routines in panels_save_display() will always 00804 convert the value to a string, so bear in mind: even if you send an integer 00805 in, you'll get a string of that number back out on the other end. 00806 00807 @param string $form_value_visibility 00808 $form_values['visibility'], is the sole argument passed to this callback. 00809 @param bool $add 00810 As in the @LAP{content_types,callbacks_visibility-control,visibility control} callback parameters: if TRUE, then we're 00811 in the submit phase for adding a new pane. If <var>FALSE</var>, then we're in the submit phase of an existing pane. 00812 @param object $pane 00813 The fully-loaded $pane object that we're submitting. Provided primarily for 00814 content types with complex data storage needs that may be dependent on data 00815 contained $pane object. 00816 @param object $display 00817 The fully-loaded $display object that's currently being edited. As with $pane, 00818 provided primarily for complex implementations that may need some of the data. 00819 @return mixed $form_value_visibility 00820 This value will be assigned to $pane->visibility, and eventually saved to the 00821 panel_pane table into the corresponding 'visibility' field. 00822 00823 00824 @code 00825 function panels_admin_visibility_submit_SAMPLE_CT($form_value_visibility, $add, $pane, $display) { 00826 return $form_value_visibility; 00827 } 00828 @endcode 00829 00830 00831 <hr> 00832 00833 00834 @SSECplug{content_types,callbacks,form-control,Callback Property: 'form control'} 00835 Callback function set by the @LAP{content_types,t-form-control,form control} property in the plugin declaration array. 00836 00837 This callback allows you full control over not only the $form array, but also the 00838 current $pane and $display objects; all three are passed by reference. Note that 00839 they will be passed by reference whether or not you prefix the parameters with 00840 the reference operator; it is included in the below parameters purely for 00841 explanatory purposes. See panels_content_config_form() for the context. 00842 00843 The callback is fired after all other form operations have been performed 00844 immediately, before the fully-built $form is sent back to FAPI for handling. 00845 Changes you make to the $pane and $display objects will also be preserved, as 00846 a call to panels_cache_set() is made. The TRULY intrepid can extract the $cache 00847 object from the value property in <var>$form['vars']</var> and play with that. 00848 00849 This property is provided primarily as a means of allowing significant control 00850 over the innerworkings of the Panels content editing API, without necessitating 00851 direct hacks of the API itself. Probably the most common use case is hiding 00852 some/all of the form widgets that the Panels API adds to all content forms - 00853 the sample function contains a technique for doing just that. 00854 00855 CAUTION: This is an advanced, dangerous feature. Improper use of it can severely 00856 break your content type, and even has the potential to wreak havoc with your 00857 permanent Panels data. Be sure to test implementations of it thoroughly before 00858 putting them in a production environment. 00859 00860 @param array $form 00861 The all-but-complete FAPI $form array that generates the add/edit content 00862 form. 00863 @param object $pane 00864 The fully-loaded $pane object that's currently being edited. 00865 @param object $display 00866 The fully-loaded $display object that's currently being edited. 00867 @param bool $add 00868 As in the @LAP{content_types,callbacks_visibility-control,visibility control} callback parameters: if TRUE, then we're 00869 in the submit phase for adding a new pane. If <var>FALSE</var>, then we're in the submit phase of an existing pane. 00870 00871 00872 @code 00873 function panels_admin_form_control_SAMPLE_CT(&$form, &$pane, &$display) { 00874 // A technique for stripping all the Panels-provided form widgets from the add/edit form. 00875 // We'll just replace them all with 'value'-type elements and store the default values. 00876 foreach ($form['configuration'] as $element => $settings) { 00877 // Get rid of the formatting elements, if they exist. 00878 if (in_array($element, array('aligner_start', 'aligner_stop', 'override_title_markup'))) { 00879 unset($form['configuration'][$element]); 00880 continue; 00881 } 00882 // Skip form elements that aren't generated by the Panels API. 00883 if (!in_array($element, array('override_title', 'override_title_text', 'css_id', 'css_class'))) { 00884 continue; 00885 } 00886 // We're down to the four we want to get rid of. Now, let's pull out the default values 00887 $defaults[$element] = $settings['#default_value']; 00888 unset($form['configuration'][$element]); 00889 } 00890 reset($defaults); 00891 while (list($element, $value) = each($defaults)) { 00892 $form['configuration'][$element] = array( 00893 '#type' => 'value', 00894 '#value' => $value, 00895 ); 00896 } 00897 // Now, the (default) values will still be present, and the submit function will work normally 00898 } 00899 @endcode 00900 00901 00902 */
1.5.6