common.inc

Go to the documentation of this file.
00001 <?php
00002 // $Id: common.inc,v 1.1.2.32 2008/10/05 20:25:17 sdboyer Exp $
00003 
00004 
00005 /**
00006  * @file
00007  * Functions used by more than one panels client module.
00008  */
00009 
00010 /**
00011  * Class definition for the allowed layouts governing structure.
00012  *
00013  * @ingroup MainAPI
00014  *
00015  * This class is designed to handle panels allowed layouts data from start to finish, and sees
00016  * action at two times:\n
00017  *    - When a client module wants to generate a form allowing an admin to create or edit a set
00018  *      of allowed layouts. In this case, either a new panels_allowed_layouts object is created
00019  *      or one is retrieved from storage and panels_allowed_layouts::set_allowed() is called to
00020  *      generate the allowed layouts form. \n
00021  *    - When a client module is calling panels_edit_layout(), a saved instantiation of this object
00022  *      can be called up and passed in to the fourth parameter, and only the allowed layouts saved
00023  *      in that object will be displayed on the form. \n
00024  * Because the panels API does not impose a data structure on the allowed_layouts data, client
00025  * modules can create as many of these objects as they want, and organize them around any concept:
00026  * node types, date published, author roles...anything.
00027  *
00028  * To call the settings form, instantiate this class - or, if your client module's needs are
00029  * heavy-duty, extend this class and instantiate your subclass - assign values to any relevant
00030  * desired members, and call panels_allowed_layouts::set_allowed(). See the documentation on
00031  * that method for a sample implementation.
00032  *
00033  * Note that when unserializing saved tokens of this class, you must
00034  * run panels_load_include('common') before unserializing in order to ensure
00035  * that the object is properly loaded.
00036  *
00037  * Client modules extending this class should implement a save() method and use it for
00038  * their custom data storage routine. You'll need to rewrite other class methods if
00039  * you choose to go another route.
00040  *
00041  * @see panels_edit_layout()
00042  * @see _panels_edit_layout()
00043  *
00044  */
00045 class panels_allowed_layouts {
00046 
00047   /**
00048    *  Specifies whether newly-added layouts (as in, new .inc files) should be automatically
00049    *  allowed (TRUE) or disallowed (FALSE) for $this. Defaults to TRUE, which is more
00050    *  permissive but less of an administrative hassle if/when you add new layouts. Note
00051    *  that this parameter will be derived from $allowed_layouts if a value is passed in.
00052    */
00053   var $allow_new = TRUE;
00054 
00055   /**
00056    *  Optional member. If provided, the Panels API will generate a drupal variable using
00057    *  variable_set($module_name . 'allowed_layouts', serialize($this)), thereby handling the
00058    *  storage of this object entirely within the Panels API. This object will be
00059    *  called and rebuilt by panels_edit_layout() if the same $module_name string is passed in
00060    *  for the $allowed_types parameter. \n
00061    *  This is primarily intended for convenience - client modules doing heavy-duty implementations
00062    *  of the Panels API will probably want to create their own storage method.
00063    * @see panels_edit_layout()
00064    */
00065   var $module_name = NULL;
00066 
00067   /**
00068    *  An associative array of all available layouts, keyed by layout name (as defined
00069    *  in the corresponding layout plugin definition), with value = 1 if the layout is
00070    *  allowed, and value = 0 if the layout is not allowed.
00071    *  Calling array_filter(panels_allowed_layouts::$allowed_layout_settings) will return an associative array
00072    *  containing only the allowed layouts, and wrapping that in array_keys() will
00073    *  return an indexed version of that array.
00074    */
00075   var $allowed_layout_settings = array();
00076 
00077   /**
00078    * Hack-imitation of D6's $form_state. Used by the panels_common_set_allowed_types()
00079    * form to indicate whether the returned value is in its 'render', 'failed-validate',
00080    * or 'submit' stage.
00081    */
00082   var $form_state;
00083 
00084   /**
00085    * Constructor function; loads the $allowed_layout_settings array with initial values according
00086    * to $start_allowed
00087    *
00088    * @param bool $start_allowed
00089    *  $start_allowed determines whether all available layouts will be marked
00090    *  as allowed or not allowed on the initial call to panels_allowed_layouts::set_allowed()
00091    *
00092    */
00093   function panels_allowed_layouts($start_allowed = TRUE) {
00094     // TODO would be nice if there was a way to just fetch the names easily
00095     foreach ($this->list_layouts() as $layout_name) {
00096       $this->allowed_layout_settings[$layout_name] = $start_allowed ? 1 : 0;
00097     }
00098   }
00099 
00100   /**
00101    * Manage panels_common_set_allowed_layouts(), the FAPI code for selecting allowed layouts.
00102    *
00103    * MAKE SURE to set panels_allowed_layouts::allow_new before calling this method. If you want the panels API
00104    * to handle saving these allowed layout settings, panels_allowed_layouts::module_name must also be set.
00105    *
00106    * Below is a sample implementation; refer to the rest of the class documentation to understand all the
00107    * specific pieces. Values that are intended to be replaced are wrapped with <>.
00108    *
00109    * \n @code
00110    *  function docdemo_allowed_layouts() {
00111    *    panels_load_include('common');
00112    *    if (!is_a($allowed_layouts = unserialize(variable_get('panels_common_allowed_layouts', serialize(''))), 'panels_allowed_layouts')) {
00113    *     $allowed_layouts = new panels_allowed_layouts();
00114    *      $allowed_layouts->allow_new = TRUE;
00115    *      $allowed_layouts->module_name = '<client_module_name>';
00116    *    }
00117    *    $result = $allowed_layouts->set_allowed('<Desired client module form title>');
00118    *    if (in_array($allowed_layouts->form_state, array('failed-validate', 'render'))) {
00119    *     return $result;
00120    *    }
00121    *    elseif ($allowed_layouts->form_state == 'submit') {
00122    *      drupal_goto('</path/to/desired/redirect>');
00123    *    }
00124    *  }
00125    * @endcode \n
00126    *
00127    * If $allowed_layouts->form_state == 'failed-validate' || 'render', then you'll need to return
00128    * $result as it contains the structured form HTML generated by drupal_render_form() and is ready
00129    * to be passed through index.php's call to theme('page', ...).
00130    *
00131    * However, if $allowed_layouts->form_state == 'submit', then the form has been submitted and we should
00132    * react. It's really up to your client module how you handle the rest; panels_allowed_layouts::save() (or
00133    * panels_allowed_layouts::api_save(), if that's the route you're going) will have already been called,
00134    * so if those methods handle your save routine, then all there is left to do is handle redirects, if you
00135    * want. The current implementation of the allowed layouts form currently never redirects, so it's up to
00136    * you to control where the user ends up next.
00137    *
00138    * @param string $title
00139    *  Used to set the title of the allowed layouts form. If no value is given, defaults to
00140    *  'Panels: Allowed Layouts'.
00141    *
00142    * @return mixed $result
00143    *  - On the first passthrough when the form is being rendered, $result is the form's structured
00144    *    HTML, ready to be pushed to the screen with a call to theme('page', ...).
00145    *  - A successful second passthrough indicates a successful submit, and
00146    *    $result === panels_allowed_layouts::allowed_layout_settings. Returning it is simply for convenience.
00147    */
00148   function set_allowed($title = 'Panels: Allowed Layouts') {
00149     $this->sync_with_available();
00150     $form_id = 'panels_common_set_allowed_layouts';
00151     $form = drupal_retrieve_form($form_id, $this, $title);
00152 
00153     if ($result = drupal_process_form($form_id, $form)) {
00154       // successful submit
00155       $this->form_state = 'submit';
00156       return $result;
00157     }
00158     $this->form_state = isset($_POST['op']) ? 'failed-validate' : 'render';
00159     $result = drupal_render_form($form_id, $form);
00160     return $result;
00161   }
00162 
00163   /**
00164    * Checks for newly-added layouts and deleted layouts. If any are found, updates panels_allowed_layouts::allowed_layout_settings;
00165    * new additions are made according to panels_allowed_layouts::allow_new, while deletions are unset().
00166    *
00167    * Note that any changes made by this function are not saved in any permanent location.
00168    */
00169   function sync_with_available() {
00170     $layouts = $this->list_layouts();
00171     foreach (array_diff($layouts, array_keys($this->allowed_layout_settings)) as $new_layout) {
00172       $this->allowed_layout_settings[$new_layout] = $this->allow_new ? 1 : 0;
00173     }
00174     foreach (array_diff(array_keys($this->allowed_layout_settings), $layouts) as $deleted_layout) {
00175       unset($this->allowed_layout_settings[$deleted_layout]);
00176     }
00177   }
00178 
00179   /**
00180    * Use panels_allowed_layouts::module_name to generate a variable for variable_set(), in which
00181    * a serialized version of $this will be stored.
00182    *
00183    * Does nothing if panels_allowed_layouts::module_name is not set.
00184    *
00185    * IMPORTANT NOTE: if you use variable_get() in a custom client module save() method, you MUST
00186    * wrap $this in serialize(), then unserialize() what you get from variable_get(). Failure to
00187    * do so will result in an incomplete object. The following code will work:
00188    * @code
00189    *  $allowed_layouts = unserialize(variable_get('your_variable_name', serialize(''));
00190    * @endcode
00191    *
00192    * If you don't serialize the second parameter of variable_get() and the variable name you provide
00193    * can't be found, an E_STRICT warning will be generated for trying to unserialize an entity
00194    * that has not been serialized.
00195    *
00196    */
00197   function api_save() {
00198     if (!is_null($this->module_name)) {
00199       variable_set($this->module_name . "_allowed_layouts", serialize($this));
00200     }
00201   }
00202 
00203   /**
00204    * Snag a list of the current layouts for internal use.
00205    *
00206    * Data is not saved in a class member in order to ensure that it's
00207    * fresh.
00208    *
00209    * @return array $layouts
00210    *  An indexed array of the system names for all currently available layouts.
00211    */
00212   function list_layouts() {
00213     static $layouts = array();
00214     if (empty($layouts)) {
00215       panels_load_include('plugins');
00216       $layouts = array_keys(panels_get_layouts());
00217     }
00218     return $layouts;
00219   }
00220 }
00221 
00222 /**
00223  * A common settings page for Panels modules, because this code is relevant to
00224  * any modules that don't already have special requirements.
00225  */
00226 function panels_common_settings($module_name = 'panels_common') {
00227   panels_load_include('plugins');
00228   $content_types = panels_get_content_types();
00229   $default_types = variable_get($module_name . '_default', NULL);
00230   if (!isset($default_types)) {
00231     $default_types = array('block' => TRUE, 'views' => TRUE, 'other' => TRUE);
00232     $skip = TRUE;
00233   }
00234 
00235   foreach ($content_types as $id => $info) {
00236     if (empty($info['single'])) {
00237       $default_options[$id] = t('New @s', array('@s' => $info['title']));
00238     }
00239   }
00240 
00241   $default_options['other'] = t('New content of other types');
00242   $form['panels_common_default'] = array(
00243     '#type' => 'checkboxes',
00244     '#title' => t('New content behavior'),
00245     '#description' => t('Select the default behavior of new content added to the system. If checked, new content will automatically be immediately available to be added to Panels pages. If not checked, new content will not be available until specifically allowed here.'),
00246     '#options' => $default_options,
00247     '#default_value' => array_keys(array_filter($default_types)),
00248   );
00249 
00250   if ($skip) {
00251     $form['markup'] = array('#value' => t('<p>Click Submit to be presented with a complete list of available content types set to the defaults you selected.</p>'));
00252     $form['skip'] = array('#type' => 'value', '#value' => TRUE);
00253   }
00254   else {
00255     // Rebuild the entire list, setting appropriately from defaults. Give
00256     // each type its own checkboxes set unless it's 'single' in which
00257     // case it can go into our fake other set.
00258     $available_content_types = panels_get_all_content_types();
00259     $allowed_content_types = variable_get($module_name . '_allowed_types', array());
00260 
00261     foreach ($available_content_types as $id => $types) {
00262       foreach ($types as $type => $info) {
00263         $key = $id . '-' . $type;
00264         $checkboxes = empty($content_types[$id]['single']) ? $id : 'other';
00265         $options[$checkboxes][$key] = $info['title'];
00266         if (!isset($allowed_content_types[$key])) {
00267           $allowed[$checkboxes][$key] = isset($default_types[$id]) ? $default_types[$id] : $default_types['other'];
00268         }
00269         else {
00270           $allowed[$checkboxes][$key] = $allowed_content_types[$key];
00271         }
00272       }
00273     }
00274 
00275     $form['content_types'] = array('#tree' => TRUE);
00276     // cheat a bit
00277     $content_types['other'] = array('title' => t('Other'), 'weight' => 10);
00278     foreach ($content_types as $id => $info) {
00279       if (isset($allowed[$id])) {
00280         $form['content_types'][$id] = array(
00281           '#prefix' => '<div class="panels-page-type-container clear-block">',
00282           '#suffix' => '</div>',
00283           '#type' => 'checkboxes',
00284           '#title' => t('Allowed @s content', array('@s' => $info['title'])),
00285           '#options' => $options[$id],
00286           '#default_value' => array_keys(array_filter($allowed[$id])),
00287         );
00288       }
00289     }
00290   }
00291 
00292   $form['module_name'] = array(
00293     '#type' => 'value',
00294     '#value' => $module_name,
00295   );
00296 
00297   $form['submit'] = array(
00298     '#type' => 'submit',
00299     '#value' => t('Submit'),
00300   );
00301 
00302   drupal_add_css(panels_get_path('css/panels_page.css'));
00303   return $form;
00304 }
00305 
00306 /**
00307  * Submit hook for panels_common_settings
00308  */
00309 function panels_common_settings_submit($form_id, $form_values) {
00310   $module_name = $form_values['module_name'];
00311   variable_set($module_name . '_default', $form_values['panels_common_default']);
00312   if (!$form_values['skip']) {
00313     // merge the broken apart array neatly back together
00314     variable_set($module_name . '_allowed_types', call_user_func_array('array_merge', $form_values['content_types']));
00315   }
00316   drupal_set_message(t('Your changes have been saved.'));
00317 }
00318 
00319 /**
00320  * Based upon the settings, get the allowed types for this node.
00321  */
00322 function panels_common_get_allowed_types($module, $contexts = array(), $has_content = FALSE, $default_defaults = array(), $default_allowed_types = array()) {
00323 
00324   // Get a list of all types that are available
00325 
00326   $default_types = variable_get($module . '_defaults', $default_defaults);
00327   $allowed_types = variable_get($module . '_allowed_types', $default_allowed_types);
00328 
00329   // By default, if they haven't gone and done the initial setup here,
00330   // let all 'other' types (which will be all types) be available.
00331   if (!isset($default_types['other'])) {
00332     $default_types['other'] = TRUE;
00333   }
00334 
00335   panels_load_include('plugins');
00336   $content_types = panels_get_available_content_types($contexts, $has_content, $allowed_types, $default_types);
00337 
00338   return $content_types;
00339 }
00340 
00341 /**
00342  * The FAPI code for generating an 'allowed layouts' selection form.
00343  *
00344  * NOTE: Because the Panels API does not guarantee a particular method of storing the data on allowed layouts,
00345  * it is not_possible for the Panels API to implement any checks that determine whether reductions in
00346  * the set of allowed layouts conflict with pre-existing layout selections. $displays in that category
00347  * will continue to function with their current layout as normal until the user/owner/admin attempts
00348  * to change layouts on that display, at which point they will have to select from the new set of
00349  * allowed layouts. If this is not the desired behavior for your client module, it's up to you to
00350  * write a validation routine that determines what should be done with conflicting layouts.
00351  *
00352  * Remember that changing layouts where panes have already been created can result in data loss;
00353  * consult panels_change_layout() to see how the Panels API handles that process. Running
00354  * drupal_execute('panels_change_layout', ...) is one possible starting point.
00355  *
00356  * @ingroup forms
00357  *
00358  * @param array $allowed_layouts
00359  *  The set of allowed layouts that should be used as the default values
00360  *  for this form. If none is provided, then by default no layouts will be restricted.
00361  * @param string $title
00362  *  The title that will be used for the form. Defaults to 'Panels: Allowed Layouts' if
00363  *  no value was provided in panels_allowed_layouts::set_allowed.
00364  */
00365 // TODO need to add something that handles $finish & $destination-type stuff.
00366 function panels_common_set_allowed_layouts($allowed_layouts, $title) {
00367   $layouts = panels_get_layouts();
00368   foreach ($layouts as $id => $layout) {
00369     $options[$id] = panels_print_layout_icon($id, $layout, check_plain($layout['title']));
00370   }
00371 
00372   drupal_set_title($title);
00373 
00374   $form['variables'] = array('#type' => 'value', '#value' => array($allowed_layouts));
00375 
00376   drupal_add_js(panels_get_path('js/layout.js'));
00377   $form['layouts'] = array(
00378     '#type' => 'checkboxes',
00379     '#title' => t('Select allowed layouts'),
00380     '#options' => $options,
00381     '#description' => t('Check the boxes for all layouts you want to allow users choose from when picking a layout. You must allow at least one layout.'),
00382     '#default_value' => array_keys(array_filter($allowed_layouts->allowed_layout_settings)),
00383   );
00384 
00385   $form['clearer'] = array(
00386     // TODO: FIx this to use clear-block instead
00387     '#value' => '<div style="clear: both;"></div>',
00388   );
00389   $form['#redirect'] = FALSE;
00390   $form['submit'] = array(
00391     '#type' => 'submit',
00392     '#value' => t('Save'),
00393   );
00394 
00395   $form['#token'] = FALSE;
00396   return $form;
00397 }
00398 
00399 function panels_common_set_allowed_layouts_validate($form_id, $form_values, $form) {
00400   $selected = array_filter($form_values['layouts']);
00401   if (empty($selected)) {
00402     form_set_error('layouts', 'You must choose at least one layout to allow.');
00403   }
00404 }
00405 
00406 function panels_common_set_allowed_layouts_submit($form_id, $form_values) {
00407   list($allowed_layouts) = $form_values['variables'];
00408   foreach ($form_values['layouts'] as $layout => $setting) {
00409     $allowed_layouts->allowed_layout_settings[$layout] = $setting === 0 ? 0 : 1;
00410   }
00411   method_exists($allowed_layouts, 'save') ? $allowed_layouts->save() : $allowed_layouts->api_save();
00412   return $allowed_layouts->allowed_layout_settings;
00413 }
00414 
00415 /**
00416  * The layout information fieldset displayed at admin/edit/panel-%implementation%/add/%layout%.
00417  */
00418 function panels_common_get_layout_information($panel_implementation, $contexts = array()) {
00419   $form = array();
00420   panels_load_include('plugins');
00421   $layout = panels_get_layout($panel_implementation->display->layout);
00422 
00423   $form = array(
00424     '#type' => 'fieldset',
00425     '#title' => t('Layout'),
00426   );
00427 
00428   $form['layout-icon'] = array(
00429     '#value' => panels_print_layout_icon($panel_implementation->display->layout, $layout),
00430   );
00431 
00432   $form['layout-display'] = array(
00433     '#value' => check_plain($layout['title']),
00434   );
00435   $content = '<dl class="content-list">';
00436 
00437   foreach (panels_get_panels($layout, $panel_implementation->display) as $panel_id => $title) {
00438     $content .= "<dt>$title</dt><dd>";
00439     if ($panel_implementation->display->panels[$panel_id]) {
00440       $content .= '<ol>';
00441       foreach ($panel_implementation->display->panels[$panel_id] as $pid) {
00442         $content .= '<li>'. panels_get_pane_title($panel_implementation->display->content[$pid], $contexts) .'</li>';
00443       }
00444       $content .= '</ol>';
00445     }
00446     else {
00447       $content .= t('Empty');
00448     }
00449     $content .= '</dd>';
00450   }
00451   $content .= '</dl>';
00452 
00453   $form['layout-content'] = array(
00454     '#value' => $content,
00455   );
00456 
00457   return $form;
00458 }
00459 
00460 // ---------------------------------------------------------------------------
00461 // Context, argument, relationship tools
00462 
00463 // ---------------------------------------------------------------------------
00464 // contexts -- highly resemble arguments.
00465 function panels_common_context_info($type = NULL) {
00466   static $info = NULL;
00467 
00468   // static doesn't work with functions like t().
00469   if (empty($info)) {
00470     $info = array(
00471       'argument' => array(
00472         'title' => t('Arguments'),
00473         'singular title' => t('argument'),
00474         'description' => t("Arguments are parsed from the URL and translated into contexts that may be added to the display via the 'content' tab. These arguments are parsed in the order received, and you may use % in your URL to hold the place of an object; the rest of the arguments will come after the URL. For example, if the URL is node/%/panel and your user visits node/1/panel/foo, the first argument will be 1, and the second argument will be foo."),
00475         'add button' => t('Add argument'),
00476         'context function' => 'panels_get_argument',
00477         'sortable' => TRUE,
00478       ),
00479       'relationship' => array(
00480         'title' => t('Relationships'),
00481         'singular title' => t('relationship'),
00482         'description' => t('Relationships are contexts that are created from already existing contexts; the add relationship button will only appear once there is another context available. Relationships can load objects based upon how they are related to each other; for example, the author of a node, or a taxonomy term attached to a node, or the vocabulary of a taxonomy term.'),
00483         'add button' => t('Add relationship'),
00484         'context function' => 'panels_get_relationship',
00485         'sortable' => FALSE,
00486       ),
00487       'context' => array(
00488         'title' => t('Contexts'),
00489         'singular title' => t('context'),
00490         'description' => t('Contexts are embedded directly into the panel; you generally must select an object in the panel. For example, you could select node 5, or the term "animals" or the user "administrator"'),
00491         'add button' => t('Add context'),
00492         'context function' => 'panels_get_context',
00493         'sortable' => FALSE,
00494       ),
00495       'requiredcontext' => array(
00496         'title' => t('Required contexts'),
00497         'singular title' => t('required context'),
00498         'description' => t('Required contexts are passed in from some external source, such as a containing panel. If a mini panel has required contexts, it can only appear when that context is available, and therefore will not show up as a standard Drupal block.'),
00499         'add button' => t('Add required context'),
00500         'context function' => 'panels_get_context',
00501         'sortable' => TRUE,
00502       ),
00503     );
00504   }
00505 
00506   if ($type === NULL) {
00507     return $info;
00508   }
00509 
00510   return $info[$type];
00511 }
00512 
00513 function panels_common_context_data($type, $name) {
00514   $info = panels_common_context_info($type);
00515   if (function_exists($info['context function'])) {
00516     return $info['context function']($name);
00517   }
00518 }
00519 
00520 /**
00521  * Add the argument table plus gadget plus javascript to the form.
00522  */
00523 function panels_common_add_argument_form($module, &$form, &$form_location, $object) {
00524   $form_location = array(
00525     '#theme' => 'panels_common_context_item_form',
00526     '#panel_name' => $object->name,
00527     '#panels_context_type' => 'argument',
00528     '#panels_context_module' => $module,
00529   );
00530 
00531   $form['arguments'] = array(
00532     '#type' => 'value',
00533     '#value' => $object->arguments,
00534   );
00535 
00536   // Store the order the choices are in so javascript can manipulate it.
00537   $form['argument_order'] = array(
00538     '#type' => 'hidden',
00539     '#id' => 'argument-order',
00540     '#default_value' => $object->arguments ? implode(',', array_keys($object->arguments)) : '',
00541   );
00542 
00543   $args = panels_get_arguments();
00544   $choices = array();
00545   foreach ($args as $name => $arg) {
00546     $choices[$name] = $arg['title'];
00547   }
00548 
00549   asort($choices);
00550 
00551   if (!empty($choices) || !empty($object->arguments)) {
00552     panels_common_add_item_table('argument', $form_location, $choices, $object->arguments);
00553   }
00554   return _panels_common_context_js($object->name, $module, 'argument');
00555 }
00556 
00557 function panels_common_add_context_form($module, &$form, &$form_location, $object) {
00558   $form['contexts'] = array(
00559     '#type' => 'value',
00560     '#value' => $object->contexts,
00561   );
00562 
00563   $form_location = array(
00564     '#prefix' => '<div id="panels-contexts-table">',
00565     '#suffix' => '</div>',
00566     '#theme' => 'panels_common_context_item_form',
00567     '#panel_name' => $object->name,
00568     '#panels_context_type' => 'context',
00569     '#panels_context_module' => $module,
00570   );
00571 
00572   // Store the order the choices are in so javascript can manipulate it.
00573   $form_location['markup'] = array(
00574     '#value' => '&nbsp;',
00575   );
00576   $form['context_order'] = array(
00577     '#type' => 'hidden',
00578     '#id' => 'context-order',
00579     '#default_value' => $object->contexts ? implode(',', array_keys($object->contexts)) : '',
00580   );
00581 
00582   $choices = array();
00583   foreach (panels_get_contexts() as $name => $arg) {
00584     if (empty($arg['no ui'])) {
00585       $choices[$name] = $arg['title'];
00586     }
00587   }
00588 
00589   asort($choices);
00590 
00591   if (!empty($choices) || !empty($object->contexts)) {
00592     panels_common_add_item_table('context', $form_location, $choices, $object->contexts);
00593   }
00594   return _panels_common_context_js($object->name, $module, 'context');
00595 }
00596 
00597 function panels_common_add_required_context_form($module, &$form, &$form_location, $object) {
00598   $form['requiredcontexts'] = array(
00599     '#type' => 'value',
00600     '#value' => $object->requiredcontexts,
00601   );
00602 
00603   $form_location = array(
00604     '#prefix' => '<div id="panels-requiredcontexts-table">',
00605     '#suffix' => '</div>',
00606     '#theme' => 'panels_common_context_item_form',
00607     '#panel_name' => $object->name,
00608     '#panels_context_type' => 'requiredcontext',
00609     '#panels_context_module' => $module,
00610   );
00611 
00612   // Store the order the choices are in so javascript can manipulate it.
00613   $form_location['markup'] = array(
00614     '#value' => '&nbsp;',
00615   );
00616   $form['requiredcontext_order'] = array(
00617     '#type' => 'hidden',
00618     '#id' => 'requiredcontext-order',
00619     '#default_value' => $object->requiredcontexts ? implode(',', array_keys($object->requiredcontexts)) : '',
00620   );
00621 
00622   $choices = array();
00623   foreach (panels_get_contexts() as $name => $arg) {
00624     $choices[$name] = $arg['title'];
00625   }
00626 
00627   asort($choices);
00628 
00629   if (!empty($choices) || !empty($object->contexts)) {
00630     panels_common_add_item_table('requiredcontext', $form_location, $choices, $object->requiredcontexts);
00631   }
00632   return _panels_common_context_js($object->name, $module, 'requiredcontext');
00633 }
00634 
00635 function panels_common_add_relationship_form($module, &$form, &$form_location, $object) {
00636   $form['relationships'] = array(
00637     '#type' => 'value',
00638     '#value' => $object->relationships,
00639   );
00640 
00641   $form_location = array(
00642     '#prefix' => '<div id="panels-relationships-table">',
00643     '#suffix' => '</div>',
00644     '#theme' => 'panels_common_context_item_form',
00645     '#panel_name' => $object->name,
00646     '#panels_context_type' => 'relationship',
00647     '#panels_context_module' => $module,
00648   );
00649 
00650   // Store the order the choices are in so javascript can manipulate it.
00651   $form_location['markup'] = array(
00652     '#value' => '&nbsp;',
00653   );
00654   $form['relationship_order'] = array(
00655     '#type' => 'hidden',
00656     '#id' => 'relationship-order',
00657     '#default_value' => $object->relationships ? implode(',', array_keys($object->relationships)) : '',
00658   );
00659 
00660   $available_relationships = panels_get_relevant_relationships(panels_context_load_contexts($object));
00661 
00662 //  if (!empty($available_relationships) || !empty($object->relationships)) {
00663     panels_common_add_item_table('relationship', $form_location, $available_relationships, $object->relationships);
00664 //  }
00665 
00666   return _panels_common_context_js($object->name, $module, 'relationship');
00667 }
00668 
00669 function _panels_common_context_js($name, $module, $type) {
00670   return array($type . '-table' => array(
00671     // The buttons that do stuff.
00672     'remove' => "input.$type-remove",
00673     // The gadget that stores our the order of items.
00674     'order' => "input#$type-order",
00675     'up' => "input.$type-up",
00676     'down' => "input.$type-down",
00677     'configure' => "input.$type-settings",
00678     'configure_path' => url("panels/common/ajax/edit/$module/$type/$name", NULL, NULL, TRUE),
00679 
00680     // The button that adds an item
00681     'add' => "input#edit-buttons-$type-add",
00682     // Path for ajax on adding an item
00683     'path' => url("panels/common/ajax/add/$module/$type/$name", NULL, NULL, TRUE),
00684     // Which items to post when adding
00685     'post' => array("#edit-buttons-$type-item", "input#edit-buttons-$type-add"),
00686     // Where to get the id of an item
00687     'tr' => $type . '-row-',
00688     'row_class' => "tr.$type-row",
00689     // Additional HTML to replace on add.
00690     'replace' => array('div#panels-relationships-table div.buttons' => 'relationships_table'),
00691   ));
00692 }
00693 
00694 function panels_common_add_context_js($base) {
00695   $settings = array(
00696     'list' => $base,
00697     'panels' => array(
00698       'closeText' => t('Close Window'),
00699       'closeImage' => theme('image', panels_get_path('images/close.gif'), t('Close window'), t('Close window')),
00700       'throbber' => theme('image', panels_get_path('images/throbber.gif'), t('Loading...'), t('Loading')),
00701     ),
00702   );
00703 
00704   drupal_add_js($settings, 'setting');
00705   drupal_add_js(panels_get_path('js/list.js'));
00706   drupal_add_js(panels_get_path('js/lib/dimensions.js'));
00707   drupal_add_js(panels_get_path('js/lib/mc.js'));
00708   drupal_add_js(panels_get_path('js/lib/form.js'));
00709   drupal_add_js(panels_get_path('js/modal_forms.js'));
00710   drupal_add_css(panels_get_path('css/panels_dnd.css'));
00711   // while we don't use this directly some of our forms do.
00712   drupal_add_js('misc/collapse.js');
00713   drupal_add_js('misc/autocomplete.js');
00714 }
00715 
00716 /**
00717  * Add the context table to the page.
00718  */
00719 function panels_common_add_item_table($type, &$form, $available_contexts, $items) {
00720   $form[$type] = array(
00721     '#tree' => TRUE,
00722   );
00723 
00724   if (isset($items) && is_array($items)) {
00725     foreach ($items as $position => $context) {
00726       panels_common_add_item_to_form($type, $form[$type][$position], $position, $context);
00727     }
00728   }
00729 
00730   $type_info = panels_common_context_info($type);
00731   $form['description'] = array(
00732     '#prefix' => '<div class="description">',
00733     '#suffix' => '</div>',
00734     '#value' => $type_info['description'],
00735   );
00736 
00737   panels_common_add_item_table_buttons($type, $form, $available_contexts);
00738 }
00739 
00740 function panels_common_add_item_table_buttons($type, &$form, $available_contexts) {
00741   $form['buttons'] = array(
00742     '#tree' => TRUE,
00743   );
00744 
00745   if (!empty($available_contexts)) {
00746     $form['buttons'][$type]['item'] = array(
00747       '#type' => 'select',
00748       '#options' => $available_contexts,
00749     );
00750 
00751     $type_info = panels_common_context_info($type);
00752     $form['buttons'][$type]['add'] = array(
00753       '#type' => 'submit',
00754       '#attributes' => array('class' => $type . '-add'),
00755       '#value' => $type_info['add button'],
00756     );
00757   }
00758 }
00759 
00760 /**
00761  * Add a row to the form. Used both in the main form and by
00762  * the ajax to add an item.
00763  */
00764 function panels_common_add_item_to_form($type, &$form, $position, $item) {
00765   // This is the single function way to load any plugin by variable type.
00766   $info = panels_common_context_data($type, $item['name']);
00767   $form['title'] = array(
00768     '#value' => check_plain($item['identifier']),
00769   );
00770 
00771   // Relationships not sortable.
00772   $type_info = panels_common_context_info($type);
00773 
00774   if (!empty($type_info['sortable'])) {
00775     $form['up'] = panels_add_button('go-up.png', t('Up'),
00776       t('Move this item up'),
00777       $type . '-up',
00778       $type . '-up-' . $position
00779     );
00780 
00781     $form['down'] = panels_add_button('go-down.png', t('Down'),
00782       t('Move this item down'),
00783       $type . '-down',
00784       $type . '-down-' . $position
00785     );
00786   }
00787 
00788   $form['remove'] = panels_add_button('icon-delete.png', t('Remove'),
00789     t('Remove this item'),
00790     $type . '-remove',
00791     $type . '-remove-' . $position
00792   );
00793 
00794   $form['settings'] = panels_add_button('icon-configure.png', t('Configure'),
00795     t('Configure this item'),
00796     $type . '-settings',
00797     $type . '-settings-' . $position
00798   );
00799 }
00800 
00801 /**
00802  * Theme the form item for the context entry.
00803  */
00804 function theme_panels_common_context_item_row($type, $form, $position, $count, $with_tr = TRUE) {
00805   $output = '<td class="title">&nbsp;' . drupal_render($form['title']) . '</td>';
00806   $output .= '<td class="operation">' . drupal_render($form['settings']);
00807   $type_info = panels_common_context_info($type);
00808   if (!empty($type_info['sortable'])) {
00809     $output .= drupal_render($form['up']) . drupal_render($form['down']);
00810   }
00811   $output .= drupal_render($form['remove']) . '</td>';
00812 
00813   if ($with_tr) {
00814     $output = '<tr id="' . $type . '-row-' . $position . '" class="' . $type . '-row ' . ($count % 2 ? 'even' : 'odd') . '">' . $output . '</tr>';
00815   }
00816   return $output;
00817 }
00818 
00819 /**
00820  * Add the contexts form to panel page settings
00821  */
00822 function theme_panels_common_context_item_form($form) {
00823   $output = '';
00824   $type   = $form['#panels_context_type'];
00825   $module = $form['#panels_context_module'];
00826   $name   = $form['#panel_name'];
00827 
00828   $type_info = panels_common_context_info($type);
00829 
00830   if (!empty($form[$type]) && empty($form['#only_buttons'])) {
00831     $output .= '<table id="' . $type . '-table">';
00832     $output .= '<thead>';
00833     $output .= '<tr>';
00834     $output .= '<th class="title">' . $type_info['title'] . '</th>';
00835     $output .= '<th class="operation">' . t('Operation') . '</th>';
00836     $output .= '</tr>';
00837     $output .= '</thead>';
00838     $output .= '<tbody>';
00839 
00840     $count = 0;
00841     foreach (array_keys($form[$type]) as $id) {
00842       if (!is_numeric($id)) {
00843         continue;
00844       }
00845       $output .= theme('panels_common_context_item_row', $type, $form[$type][$id], $id, $count++);
00846     }
00847 
00848     $output .= '</tbody>';
00849     $output .= '</table>';
00850   }
00851 
00852   if (!empty($form['buttons'])) {
00853     // Display the add context item.
00854     $row   = array();
00855     $row[] = array('data' => drupal_render($form['buttons'][$type]['item']), 'class' => 'title');
00856     $row[] = array('data' => drupal_render($form['buttons'][$type]['add']), 'class' => 'add', 'width' => "60%");
00857     $output .= '<div class="buttons">';
00858     $output .= theme('table', array(), array($row), array('id' => $type . '-add-table'));
00859     $output .= '</div>';
00860   }
00861   if (!empty($form['description'])) {
00862     $output .= drupal_render($form['description']);
00863   }
00864 
00865   return $output;
00866 }
00867 
00868 /**
00869  * Ajax entry point to add an context
00870  */
00871 function panels_common_ajax_context_item_add($module, $type, $panel_name) {
00872   $object = panels_common_cache_get("panel_object:$module", $panel_name);
00873   if (!$object || !$type) {
00874     panels_ajax_render();
00875   }
00876 
00877   // Figure out which context we're adding
00878   if (isset($_POST['buttons'][$type]['item'])) {
00879     $name = $_POST['buttons'][$type]['item'];
00880     // Unset $_POST so fapi doesn't get confused and try to process this
00881     // as a form.
00882     unset($_POST);
00883   }
00884   else if (isset($_POST[$type]['name'])) {
00885     $name = $_POST[$type]['name'];
00886   }
00887 
00888   if (empty($name)) {
00889     panels_ajax_render();
00890   }
00891 
00892   $info = panels_common_context_data($type, $name);
00893   if (empty($info)) {
00894     panels_ajax_render();
00895   }
00896 
00897   // Create a reference to the place our context lives.
00898   $keyword = $type . 's';
00899   $ref = &$object->$keyword;
00900 
00901   // Give this argument an id, which is really just the nth version
00902   // of this particular context.
00903   $id = panels_common_get_arg_id($ref, $name) + 1;
00904 
00905   // Figure out the position for our new context.
00906   $position = empty($ref) ? 0 : max(array_keys($ref)) + 1;
00907 
00908   // Create the basis for our new context.
00909   $ref[$position] = array(
00910     'identifier' => $info['title'] . ($id > 1 ? ' ' . $id : ''),
00911     'keyword' => panels_common_get_keyword($object, $info['keyword']),
00912     'id' => $id,
00913   );
00914 
00915   $contexts = panels_context_load_contexts($object);
00916 
00917   $form_id = 'panels_common_edit_' . $type . '_form';
00918   $form = drupal_retrieve_form($form_id, $object, $info, $position, $contexts);
00919   if ($_POST && $_POST['form_id'] == $form_id) {
00920     $form['#redirect'] = FALSE;
00921   }
00922 
00923   $retval = drupal_process_form($form_id, $form);
00924   if ($retval) {
00925     // successful submit
00926 
00927     // Save changes
00928     $ref[$position] = $retval;
00929     panels_common_cache_set("panel_object:$module", $panel_name, $object);
00930 
00931     // Build a chunk of the form to merge into the displayed form
00932     $arg_form[$type] = array(
00933       '#tree' => TRUE,
00934     );
00935 
00936     panels_common_add_item_to_form($type, $arg_form[$type], $position, $retval);
00937     $arg_form = form_builder($form_id, $arg_form);
00938 
00939     // Build the relationships table so we can ajax it in.
00940     // This is an additional thing that goes in here.
00941     $rel_form = array(
00942       '#theme' => 'panels_common_context_item_form',
00943       '#panel_name' => $panel_name,
00944       '#panels_context_type' => 'relationship',
00945       '#panels_context_module' => $module,
00946       '#only_buttons' => TRUE,
00947     );
00948 
00949     $rel_form['relationship'] = array(
00950       '#tree' => TRUE,
00951     );
00952 
00953     $available_relationships = panels_get_relevant_relationships(panels_context_load_contexts($object));
00954 
00955     $output = new stdClass();
00956     if (!empty($available_relationships)) {
00957       panels_common_add_item_table_buttons('relationship', $rel_form, $available_relationships);
00958       $rel_form = form_builder('dummy_form_id', $rel_form);
00959       $output->relationships_table = drupal_render($rel_form);
00960     }
00961 
00962     $output->type     = 'add';
00963     $output->output   = theme('panels_common_context_item_row', $type, $arg_form[$type], $position, $position);
00964     $output->position = $position;
00965     panels_ajax_render($output);
00966   }
00967   else {
00968     $type_info = panels_common_context_info($type);
00969     $title = t('Add @type "@context"', array('@type' => $type_info['singular title'], '@context' => $info['title']));
00970     $output = theme('status_messages');
00971     $output .= drupal_render_form($form_id, $form);
00972     panels_ajax_render($output, $title, url($_GET['q'], NULL, NULL, TRUE));
00973   }
00974 }
00975 
00976 /**
00977  * Ajax entry point to edit an item
00978  */
00979 function panels_common_ajax_context_item_edit($module, $type, $panel_name) {
00980   $object = panels_common_cache_get("panel_object:$module", $panel_name);
00981   if (!$object) {
00982     panels_ajax_render();
00983   }
00984 
00985   // Figure out which context we're adding
00986   if (isset($_POST['position'])) {
00987     $position = $_POST['position'];
00988   }
00989 
00990   if (!isset($_POST['form_id'])) {
00991     // Unset $_POST so fapi doesn't get confused and try to process this
00992     // as a form.
00993     unset($_POST);
00994   }
00995 
00996   // Create a reference to the place our context lives.
00997   $keyword = $type . 's';
00998   $ref = &$object->$keyword;
00999 
01000   $name = $ref[$position]['name'];
01001   if (empty($name)) {
01002     panels_ajax_render();
01003   }
01004 
01005   // load the context
01006   $info = panels_common_context_data($type, $name);
01007   if (empty($info)) {
01008     panels_ajax_render();
01009   }
01010 
01011   $type_info = panels_common_context_info($type);
01012   $title = t('Edit @type "@context"', array('@type' => $type_info['singular title'], '@context' => $info['title']));
01013 
01014   $contexts = panels_context_load_contexts($object);
01015 
01016   // Remove this context, because we can't really allow circular contexts.
01017   // TODO: FIX THIS!!!
01018   unset($contexts[panels_context_context_id($ref[$position])]);
01019 
01020   $form_id = 'panels_common_edit_' . $type . '_form';
01021   $form = drupal_retrieve_form($form_id, $object, $info, $position, $contexts);
01022   if ($_POST && $_POST['form_id'] == $form_id) {
01023     // TODO: Make sure the form does this.
01024     $form['#redirect'] = FALSE;
01025   }
01026 
01027   $retval = drupal_process_form($form_id, $form);
01028   if ($retval) {
01029     $output = new stdClass();
01030     // successful submit
01031 
01032     // Save changes
01033     $ref[$position] = $retval;
01034     panels_common_cache_set("panel_object:$module", $panel_name, $object);
01035 
01036     $output->type = $output->output = 'dismiss';
01037 
01038 
01039     // Build a chunk of the form to merge into the displayed form
01040     $arg_form[$type] = array(
01041       '#tree' => TRUE,
01042     );
01043 
01044     panels_common_add_item_to_form($type, $arg_form[$type], $position, $retval);
01045     $arg_form = form_builder($form_id, $arg_form);
01046 
01047     $output->replace = theme('panels_common_context_item_row', $type, $arg_form[$type], $position, $position, FALSE);
01048     $output->replace_id = '#' . $type . '-row-' . $position;
01049 
01050     panels_ajax_render($output);
01051   }
01052   else {
01053     $output = theme('status_messages');
01054     $output .= drupal_render_form($form_id, $form);
01055     panels_ajax_render($output, $title, url($_GET['q'], NULL, NULL, TRUE));
01056   }
01057 }
01058 
01059 /**
01060  * Form (for ajax use) to add a context
01061  */
01062 function panels_common_edit_context_form($object, $context, $position, $contexts) {
01063   $ctext = $object->contexts[$position];
01064   $form['position'] = array(
01065     '#type' => 'hidden',
01066     '#value' => $position,
01067   );
01068 
01069   $form['start_form'] = array('#value' => '<div class="modal-form clear-block">');
01070 
01071   $form['description'] = array(
01072     '#prefix' => '<div class="description">',
01073     '#suffix' => '</div>',
01074     '#value' => check_plain($context['description']),
01075   );
01076 
01077   // Basic context values
01078   $form['context']['#tree'] = TRUE;
01079 
01080   $form['context']['name'] = array(
01081     '#type' => 'hidden',
01082     '#value' => $context['name'],
01083   );
01084 
01085   $form['context']['id'] = array(
01086     '#type' => 'value',
01087     '#value' => $ctext['id'],
01088   );
01089 
01090   $form['context']['identifier'] = array(
01091     '#type' => 'textfield',
01092     '#title' => t('Identifier'),
01093     '#description' => t('Enter a name to identify this !type on administrative screens.', array('!type' =>t('context'))),
01094     '#default_value' => $ctext['identifier'],
01095   );
01096 
01097   $form['context']['keyword'] = array(
01098     '#type' => 'textfield',
01099     '#title' => t('Keyword'),
01100     '#description' => t('Enter a keyword to use for substitution in titles.'),
01101     '#default_value' => $ctext['keyword'],
01102   );
01103 
01104   // Settings particular to this context
01105   $context_settings = array();
01106   if (isset($ctext['context_settings'])) {
01107     $context_settings = $ctext['context_settings'];
01108   }
01109 
01110   if (isset($context['settings form']) && function_exists($context['settings form'])) {
01111     $form['context']['context_settings'] = $context['settings form']($context_settings);
01112     $form['context']['context_settings']['#tree'] = TRUE;
01113   }
01114 
01115   $form['context_info'] = array(
01116     '#type' => 'value',
01117     '#value' => $context,
01118   );
01119 
01120   $form['end_form'] = array('#value' => '</div>');
01121 
01122   $form['next'] = array(
01123     '#type' => 'submit',
01124     '#value' => t('Save'),
01125   );
01126   return $form;
01127 }
01128 
01129 /**
01130  * validate a  context edited/added via ajax
01131  */
01132 function panels_common_edit_context_form_validate($form_id, $form_values, $form) {
01133   $context = $form_values['context_info'];
01134 
01135   if (isset($context['settings form validate']) && function_exists($context['settings form validate'])) {
01136     $context['settings form validate']($form['context']['context_settings'], $form_values['context']['context_settings']);
01137   }
01138 }
01139 
01140 /**
01141  * Updates an context edited/added via ajax
01142  */
01143 function panels_common_edit_context_form_submit($form_id, $form_values) {
01144   $context = $form_values['context'];
01145   $info = $form_values['context_info'];
01146 
01147   if (isset($info['settings form submit']) && function_exists($info['settings form submit'])) {
01148     $info['settings form submit']($form_values['context_settings']);
01149   }
01150 
01151   return $context;
01152 }
01153 
01154 /**
01155  * Form (for ajax use) to add a context
01156  */
01157 function panels_common_edit_requiredcontext_form($object, $context, $position, $contexts) {
01158   $ctext = $object->requiredcontexts[$position];
01159   $form['position'] = array(
01160     '#type' => 'hidden',
01161     '#value' => $position,
01162   );
01163 
01164   $form['start_form'] = array('#value' => '<div class="modal-form clear-block">');
01165 
01166   $form['description'] = array(
01167     '#prefix' => '<div class="description">',
01168     '#suffix' => '</div>',
01169     '#value' => check_plain($context['description']),
01170   );
01171 
01172   // Basic context values
01173   $form['requiredcontext']['#tree'] = TRUE;
01174 
01175   $form['requiredcontext']['name'] = array(
01176     '#type' => 'hidden',
01177     '#value' => $context['name'],
01178   );
01179 
01180   $form['requiredcontext']['id'] = array(
01181     '#type' => 'value',
01182     '#value' => $ctext['id'],
01183   );
01184 
01185   $form['requiredcontext']['identifier'] = array(
01186     '#type' => 'textfield',
01187     '#title' => t('Identifier'),
01188     '#description' => t('Enter a name to identify this !type on administrative screens.', array('!type' =>t('required context'))),
01189     '#default_value' => $ctext['identifier'],
01190   );
01191 
01192   $form['requiredcontext']['keyword'] = array(
01193     '#type' => 'textfield',
01194     '#title' => t('Keyword'),
01195     '#description' => t('Enter a keyword to use for substitution in titles.'),
01196     '#default_value' => $ctext['keyword'],
01197   );
01198 
01199   $form['context_info'] = array(
01200     '#type' => 'value',
01201     '#value' => $context,
01202   );
01203 
01204   $form['end_form'] = array('#value' => '</div>');
01205 
01206   $form['next'] = array(
01207     '#type' => 'submit',
01208     '#value' => t('Save'),
01209   );
01210   return $form;
01211 }
01212 
01213 /**
01214  * Updates a required context edited/added via ajax
01215  */
01216 function panels_common_edit_requiredcontext_form_submit($form_id, $form_values) {
01217   $context = $form_values['requiredcontext'];
01218   return $context;
01219 }
01220 
01221 /**
01222  * Form (for ajax use) to add a relationship
01223  */
01224 function panels_common_edit_relationship_form($panel_page, $relationship, $position, $contexts) {
01225   $rel = $panel_page->relationships[$position];
01226   $form['position'] = array(
01227     '#type' => 'hidden',
01228     '#value' => $position,
01229   );
01230 
01231   $form['start_form'] = array('#value' => '<div class="modal-form clear-block">');
01232 
01233   $form['description'] = array(
01234     '#prefix' => '<div class="description">',
01235     '#suffix' => '</div>',
01236     '#value' => check_plain($relationship['description']),
01237   );
01238 
01239   // Basic relationship values
01240   $form['relationship']['#tree'] = TRUE;
01241 
01242   $form['relationship']['context'] = panels_context_selector($contexts, $relationship['required context'], $rel['context']);
01243 
01244   $form['relationship']['name'] = array(
01245     '#type' => 'hidden',
01246     '#value' => $relationship['name'],
01247   );
01248 
01249   $form['relationship']['id'] = array(
01250     '#type' => 'value',
01251     '#value' => $rel['id'],
01252   );
01253 
01254   $form['relationship']['identifier'] = array(
01255     '#type' => 'textfield',
01256     '#title' => t('Identifier'),
01257     '#description' => t('Enter a name to identify this !type on administrative screens.', array('!type' =>t('relationship'))),
01258     '#default_value' => $rel['identifier'],
01259   );
01260 
01261   $form['relationship']['keyword'] = array(
01262     '#type' => 'textfield',
01263     '#title' => t('Keyword'),
01264     '#description' => t('Enter a keyword to use for substitution in titles.'),
01265     '#default_value' => $rel['keyword'],
01266   );
01267 
01268   // Settings particular to this relationship
01269   $relationship_settings = array();
01270   if (isset($rel['relationship_settings'])) {
01271     $relationship_settings = $rel['relationship_settings'];
01272   }
01273 
01274   if (isset($relationship['settings form']) && function_exists($relationship['settings form'])) {
01275     $form['relationship']['relationship_settings'] = $relationship['settings form']($relationship_settings);
01276     $form['relationship']['relationship_settings']['#tree'] = TRUE;
01277   }
01278 
01279   $form['relationship_info'] = array(
01280     '#type' => 'value',
01281     '#value' => $relationship,
01282   );
01283 
01284   $form['end_form'] = array('#value' => '</div>');
01285 
01286   $form['next'] = array(
01287     '#type' => 'submit',
01288     '#value' => t('Save'),
01289   );
01290   return $form;
01291 }
01292 
01293 /**
01294  * validate an relationship edited/added via ajax
01295  */
01296 function panels_common_edit_relationship_form_validate($form_id, $form_values, $form) {
01297   $relationship = $form_values['relationship_info'];
01298 
01299   if (isset($relationship['settings form validate']) && function_exists($relationship['settings form validate'])) {
01300     $relationship['settings form validate']($form['relationship']['relationship_settings'], $form_values['relationship']['relationship_settings']);
01301   }
01302 }
01303 
01304 /**
01305  * Updates an relationship edited/added via ajax
01306  */
01307 function panels_common_edit_relationship_form_submit($form_id, $form_values) {
01308   $relationship = $form_values['relationship'];
01309 
01310   if (isset($relationship['settings form submit']) && function_exists($relationship['settings form submit'])) {
01311     $relationship['settings form submit']($form_values['relationship_settings']);
01312   }
01313 
01314   return $relationship;
01315 }
01316 
01317 /**
01318  * Form (for ajax use) to add an argument
01319  */
01320 function panels_common_edit_argument_form($panel_page, $argument, $position) {
01321   // Basic values required to orient ourselves
01322   $arg = $panel_page->arguments[$position];
01323 
01324   $form['position'] = array(
01325     '#type' => 'hidden',
01326     '#value' => $position,
01327   );
01328 
01329   $form['start_form'] = array('#value' => '<div class="modal-form">');
01330 
01331   $form['description'] = array(
01332     '#prefix' => '<div class="description">',
01333     '#suffix' => '</div>',
01334     '#value' => check_plain($argument['description']),
01335   );
01336 
01337   // Basic argument values
01338   $form['argument']['#tree'] = TRUE;
01339   $form['argument']['name'] = array(
01340     '#type' => 'hidden',
01341     '#value' => $argument['name'],
01342   );
01343 
01344   $form['argument']['default'] = array(
01345     '#type' => 'select',
01346     '#title' => t('Default'),
01347     '#options' => array(
01348       'ignore' => t('Ignore it; content that requires this context will not be available.'),
01349       '404' => t('Display page not found.'),
01350     ),
01351     '#default_value' => $arg['default'],
01352     '#description' => t('If the argument is missing or is not valid, select how this should behave.'),
01353   );
01354 
01355   $form['argument']['title'] = array(
01356     '#type' => 'textfield',
01357     '#title' => t('Title'),
01358     '#default_value' => $arg['title'],
01359     '#description' => t('Enter a title to use when this argument is present. You may use %KEYWORD substitution, where the keyword is specified by the administrator.'),
01360   );
01361 
01362   $form['argument']['id'] = array(
01363     '#type' => 'value',
01364     '#value' => $arg['id'],
01365   );
01366 
01367   $form['argument']['identifier'] = array(
01368     '#type' => 'textfield',
01369     '#title' => t('Identifier'),
01370     '#description' => t('Enter a name to identify this !type on administrative screens.', array('!type' =>t('argument'))),
01371     '#default_value' => $arg['identifier'],
01372   );
01373 
01374   $form['argument']['keyword'] = array(
01375     '#type' => 'textfield',
01376     '#title' => t('Keyword'),
01377     '#description' => t('Enter a keyword to use for substitution in titles.'),
01378     '#default_value' => $arg['keyword'],
01379   );
01380 
01381   // Settings particular to this argument
01382   $argument_settings = array();
01383   if (isset($arg['argument_settings'])) {
01384     $argument_settings = $arg['argument_settings'];
01385   }
01386 
01387   $form['arg'] = array(
01388     '#type' => 'value',
01389     '#value' => $argument,
01390   );
01391 
01392   if (isset($argument['settings form']) && function_exists($argument['settings form'])) {
01393     $form['argument']['argument_settings'] = $argument['settings form']($argument_settings);
01394     $form['argument']['argument_settings']['#tree'] = TRUE;
01395   }
01396 
01397   $form['end_form'] = array('#value' => '</div>');
01398 
01399   $form['next'] = array(
01400     '#type' => 'submit',
01401     '#value' => t('Save'),
01402   );
01403 
01404   return $form;
01405 }
01406 
01407 /**
01408  * validate an argument edited/added via ajax
01409  */
01410 function panels_common_edit_argument_form_validate($form_id, $form_values, $form) {
01411   $argument = $form_values['arg'];
01412 
01413   if (isset($argument['settings form validate']) && function_exists($argument['settings form validate'])) {
01414     $argument['settings form validate']($form['argument']['argument_settings'], $form_values['argument']['argument_settings']);
01415   }
01416 }
01417 
01418 /**
01419  * Updates an argument edited/added via ajax
01420  */
01421 function panels_common_edit_argument_form_submit($form_id, $form_values) {
01422   $argument = $form_values['arg'];
01423   $position = $form_values['position'];
01424 
01425   if (isset($argument['settings form submit']) && function_exists($argument['settings form submit'])) {
01426     $argument['settings form submit']($form_values['argument']['argument_settings']);
01427   }
01428 
01429   // Because we're being all ajaxy, our caller will save this for us.
01430   return $form_values['argument'];
01431 }
01432 
01433 // --- End of contexts
01434 
01435 function panels_common_save_context($type, &$ref, $form_values) {
01436   // Organize arguments
01437   $ref = array();
01438   if (isset($form_values[$type . '_order']) && $form_values[$type . '_order'] !== '') {
01439     foreach (explode(',', $form_values[$type . '_order']) as $position) {
01440       // We retain the original position here because we need argument IDs.
01441       $ref[$position] = $form_values[$type . 's'][$position];
01442     }
01443   }
01444 }
01445 
01446 // ---------------------------------------------------------------------------
01447 // Ajax tools
01448 
01449 /**
01450  * Incoming menu function for ajax calls. This routes to the proper 'module'
01451  * -- we really only need this because common.inc relies on panels.module for
01452  * its menu hook, and this way the code stays together.
01453  */
01454 function panels_common_ajax($module = NULL, $data = NULL, $info = NULL, $info2 = NULL) {
01455   switch ($module) {
01456     case 'edit':
01457       return panels_common_ajax_context_item_edit($data, $info, $info2);
01458 
01459     case 'add':
01460       return panels_common_ajax_context_item_add($data, $info, $info2);
01461 
01462     case 'panel_settings':
01463       return panels_common_panel_settings_ajax($data, $info);
01464 
01465     default:
01466       panels_ajax_render(t('An error occurred'), t('Error'));
01467   }
01468 }
01469 
01470 // TODO: Move this somewhere more appropriate
01471 function panels_common_get_arg_id($arguments, $name) {
01472   // Figure out which instance of this argument we're creating
01473   $id = 0;
01474   foreach ($arguments as $arg) {
01475     if ($arg['name'] == $name) {
01476       if ($arg['id'] > $id) {
01477         $id = $arg['id'];
01478       }
01479     }
01480   }
01481   return $id;
01482 }
01483 
01484 function panels_common_get_keyword($page, $word) {
01485   // Create a complete set of keywords
01486   $keywords = array();
01487   foreach (array('arguments', 'relationships', 'contexts', 'requiredcontexts') as $type) {
01488     if (!empty($page->$type) && is_array($page->$type)) {
01489       foreach ($page->$type as $info) {
01490         $keywords[$info['keyword']] = TRUE;
01491       }
01492     }
01493   }
01494 
01495   $keyword = $word;
01496   $count = 0;
01497   while ($keywords[$keyword]) {
01498     $keyword = $word . '_' . ++$count;
01499   }
01500   return $keyword;
01501 }
01502 
01503 /**
01504  * Create a visible list of content in a display.
01505  * Note that the contexts must be pre-loaded.
01506  */
01507 function theme_panels_common_content_list($display) {
01508   $layout = panels_get_layout($display->layout);
01509   $content = '<dl class="content-list">';
01510   foreach (panels_get_panels($layout, $display) as $panel_id => $title) {
01511     $content .= "<dt>$title</dt><dd>";
01512     if ($display->panels[$panel_id]) {
01513       $content .= '<ol>';
01514       foreach ($display->panels[$panel_id] as $pid) {
01515         $content .= '<li>' . panels_get_pane_title($display->content[$pid], $display->context) . '</li>';
01516       }
01517       $content .= '</ol>';
01518     }
01519     else {
01520       $content .= t('Empty');
01521     }
01522     $content .= '</dd>';
01523   }
01524   $content .= '</dl>';
01525   return $content;
01526 }
01527 
01528 /**
01529  * Create a visible list of all the contexts available on an object.
01530  * Assumes arguments, relationships and context objects.
01531  *
01532  * Contexts must be preloaded.
01533  */
01534 function theme_panels_common_context_list($object) {
01535   $titles = array();
01536   $output = '';
01537   $count  = 1;
01538   // First, make a list of arguments. Arguments are pretty simple.
01539   if (!empty($object->arguments)) {
01540     foreach ($object->arguments as $argument) {
01541       $output .= '<tr>';
01542       $output .= '<td><em>' . t('Argument @count', array('@count' => $count)) . '</em></td>';
01543       $output .= '<td>' . check_plain($argument['identifier']) . '</td>';
01544       $output .= '</tr>';
01545       $titles[panels_argument_context_id($argument)] = $argument['identifier'];
01546       $count++;
01547     }
01548   }
01549   $count = 1;
01550   // Then, make a nice list of contexts.
01551   if (!empty($object->contexts)) {
01552     foreach ($object->contexts as $context) {
01553       $output .= '<tr>';
01554       $output .= '<td><em>' . t('Context @count', array('@count' => $count)) . '</em></td>';
01555       $output .= '<td>' . check_plain($context['identifier']) . '</td>';
01556       $output .= '</tr>';
01557       $titles[panels_context_context_id($context)] = $context['identifier'];
01558       $count++;
01559     }
01560   }
01561   // And relationships
01562   if (!empty($object->relationships)) {
01563     foreach ($object->relationships as $relationship) {
01564       $output .= '<tr>';
01565       $output .= '<td><em>' . t('From @title', array('@title' => $titles[$relationship['context']])) . '</em></td>';
01566       $output .= '<td>' . check_plain($relationship['identifier']) . '</td>';
01567       $output .= '</tr>';
01568       $titles[panels_relationship_context_id($relationship)] = $relationship['identifier'];
01569       $count++;
01570     }
01571   }
01572   if ($output) {
01573     return "<table><tbody>$output</tbody></table>\n";
01574   }
01575 }
01576 

Generated on Thu Jul 29 05:00:15 2010 for Panels 2 by  doxygen 1.5.6