? form_element_2.patch
? views_form_element1.patch
Index: views.module
===================================================================
RCS file: /cvs/drupal/contributions/modules/views/views.module,v
retrieving revision 1.166.2.43
diff -u -p -r1.166.2.43 views.module
--- views.module	14 Jul 2007 18:54:16 -0000	1.166.2.43
+++ views.module	18 Aug 2007 18:37:15 -0000
@@ -1,6 +1,9 @@
 <?php
 // $Id: views.module,v 1.166.2.43 2007/07/14 18:54:16 merlinofchaos Exp $
 
+// Definitions
+define(VIEWS_FORM_DEFAULT_VIEW, 'frontpage');
+
 // ---------------------------------------------------------------------------
 // Drupal Hooks
 
@@ -302,7 +305,7 @@ function views_access($view, $account = 
   }
 
   // All views with an empty access setting are available to all roles.
-  if (!$view->access) { 
+  if (!$view->access) {
     return TRUE;
   }
 
@@ -523,7 +526,7 @@ function views_build_view($type, &$view,
   if ($args == NULL) {
     $args = array();
   }
-  
+
   // if no filter values are passed in, get them from the $_GET array
   if ($filters == NULL) {
     $filters = views_get_filter_values();
@@ -592,6 +595,15 @@ function views_build_view($type, &$view,
     return $info;
   }
 
+  // modify the view if it is a form -- remove exposed filters and add in
+  // any items that have been selected by the user
+  if($view->page_type == 'form') {
+    if ($view->missing) {
+      $items = array_merge($view->missing, $items);
+    }
+    $view->exposed_filter = array();
+  }
+
   // Call a hook that'll let modules modify the view just before it is displayed.
   foreach (module_implements('views_pre_view') as $module) {
     $function = $module .'_views_pre_view';
@@ -832,47 +844,47 @@ function views_invalidate_cache() {
  */
 function _views_view_fields() {
   return array(
-    'vid' => '%d', 
-    'name' => "'%s'", 
-    'description' => "'%s'", 
-    'access' => "'%s'", 
-    'page' => '%d', 
-    'page_title' => "'%s'", 
-    'page_header' => "'%s'", 
-    'page_header_format' => '%d', 
-    'page_footer' => "'%s'", 
-    'page_footer_format' => '%d', 
-    'page_empty' => "'%s'", 
-    'page_empty_format' => '%d', 
-    'page_type' => "'%s'", 
-    'use_pager' => '%d', 
-    'nodes_per_page' => '%d', 
-    'url' => "'%s'", 
-    'menu' => '%d', 
-    'menu_tab' => '%d', 
-    'menu_tab_default' => '%d', 
-    'menu_tab_weight' => '%d', 
-    'menu_title' => "'%s'", 
+    'vid' => '%d',
+    'name' => "'%s'",
+    'description' => "'%s'",
+    'access' => "'%s'",
+    'page' => '%d',
+    'page_title' => "'%s'",
+    'page_header' => "'%s'",
+    'page_header_format' => '%d',
+    'page_footer' => "'%s'",
+    'page_footer_format' => '%d',
+    'page_empty' => "'%s'",
+    'page_empty_format' => '%d',
+    'page_type' => "'%s'",
+    'use_pager' => '%d',
+    'nodes_per_page' => '%d',
+    'url' => "'%s'",
+    'menu' => '%d',
+    'menu_tab' => '%d',
+    'menu_tab_default' => '%d',
+    'menu_tab_weight' => '%d',
+    'menu_title' => "'%s'",
     'menu_tab_default_parent_type' => "'%s'",
-    'menu_parent_title' => "'%s'", 
-    'menu_parent_tab_weight' => '%d', 
-    'block' => '%d', 
-    'block_title' => "'%s'", 
-    'block_use_page_header' => '%d', 
-    'block_header' => "'%s'", 
-    'block_header_format' => '%d', 
-    'block_use_page_footer' => '%d', 
-    'block_footer' => "'%s'", 
-    'block_footer_format' => '%d', 
-    'block_use_page_empty' => '%d', 
-    'block_empty' => "'%s'", 
-    'block_empty_format' => '%d', 
-    'block_type' => "'%s'", 
-    'nodes_per_block' => '%d', 
-    'block_more' => '%d', 
-    'breadcrumb_no_home' => '%d', 
-    'changed' => '%d', 
-    'view_args_php' => "'%s'", 
+    'menu_parent_title' => "'%s'",
+    'menu_parent_tab_weight' => '%d',
+    'block' => '%d',
+    'block_title' => "'%s'",
+    'block_use_page_header' => '%d',
+    'block_header' => "'%s'",
+    'block_header_format' => '%d',
+    'block_use_page_footer' => '%d',
+    'block_footer' => "'%s'",
+    'block_footer_format' => '%d',
+    'block_use_page_empty' => '%d',
+    'block_empty' => "'%s'",
+    'block_empty_format' => '%d',
+    'block_type' => "'%s'",
+    'nodes_per_block' => '%d',
+    'block_more' => '%d',
+    'breadcrumb_no_home' => '%d',
+    'changed' => '%d',
+    'view_args_php' => "'%s'",
     'is_cacheable' => '%d',
   );
 }
@@ -1314,7 +1326,7 @@ function views_filters($view) {
   $form['#action'] = url($view->real_url ? $view->real_url : $view->url, NULL, NULL, true);
   $form['view'] = array('#type' => 'value', '#value' => $view);
   $form['submit'] = array('#type' => 'button', '#name' => '', '#value' => t('Submit'));
-  
+
   // clean URL get forms breaks if we don't give it a 'q'.
   if (!(bool)variable_get('clean_url', '0')) {
     $form['q'] = array(
@@ -1386,7 +1398,7 @@ function _views_build_filters_form($view
     }
     $form["filter$count"] = $item;
   }
-  
+
   return $form;
 }
 
@@ -1854,6 +1866,14 @@ function views_views_style_plugins() {
       'theme' => 'views_view_nodes',
       'weight' => -7,
     ),
+    'form' => array(
+      'name' => t('View Form'),
+      'theme' => 'views_view_form',
+      'needs_fields' => true,
+      'needs_table_header' => true,
+      'weight' => -9,
+      'hide' => true,
+    ),
   );
 }
 
@@ -1999,7 +2019,7 @@ function views_handler_filter_null($op, 
       $operator = $filter['operator'];
       $query->add_where("$field $operator NULL");
       break;
-  }  
+  }
 }
 
 /**
@@ -2143,4 +2163,40 @@ function views_form_alter($form_id, &$fo
 // An implementation of hook_devel_caches() from devel.module. Must be in views.module so it always is included.
 function views_devel_caches() {
   return array('cache_views');
-}
\ No newline at end of file
+}
+
+/**
+* Implementation of hook_elements();
+*
+* Here we define a views_node_selector form element that can be used to
+* get back 1 or more nids using a view. We use the front page view as a
+* default, as this ships with every views install. Of course, you'll want to
+* override this default in your use of the element.
+*
+* We also provide a #multiple field to indicate if this field is a single
+* select or multi-select.
+*/
+function views_elements() {
+
+  $type['views_node_selector'] = array(
+    '#input' => TRUE,
+    '#process' => array('views_form_process_element' => array()),
+    '#view' => VIEWS_FORM_DEFAULT_VIEW,
+    '#multiple' => false,
+    '#page' => 0,
+    '#collapsed' => false,
+    '#collapsible' => false,
+    '#arguments' => array(),
+    '#embedded' => false, // kratib: This new setting controls whether we're embedding the selector in its own view.
+    );
+
+  return $type;
+}
+
+/**
+ * A place holder for the element processing function, located in views_form.inc
+ */
+function views_form_process_element($element) {
+  include_once drupal_get_path('module', 'views') . '/views_form.inc';
+  return _views_form_process_element($element);
+}
Index: views_cache.inc
===================================================================
RCS file: /cvs/drupal/contributions/modules/views/Attic/views_cache.inc,v
retrieving revision 1.2.2.18
diff -u -p -r1.2.2.18 views_cache.inc
--- views_cache.inc	14 Jul 2007 19:12:02 -0000	1.2.2.18
+++ views_cache.inc	18 Aug 2007 18:37:15 -0000
@@ -307,7 +307,7 @@ function _views_get_query(&$view, $args,
  * array is cached in a static variable so that arguments
  * are only constructed once per run.
  */
-function _views_get_style_plugins($titles = false) {
+function _views_get_style_plugins($titles = false, $display_only = false) {
   static $views_style_plugins;
   global $locale;
 
@@ -331,5 +331,16 @@ function _views_get_style_plugins($title
       cache_set("views_style_plugins:$locale", 'cache_views', serialize($cache));
     }
   }
-  return ($titles ? $views_style_plugins['title'] : $views_style_plugins['base']);
-}
\ No newline at end of file
+  $return = $views_style_plugins;
+  // remove any plugins that are not listed in drop down menu if $display_only is true
+  if ($display_only) {
+    foreach($views_style_plugins['base'] as $plugin => $def) {
+      if ($def['hide'] == true) {
+        unset($return['base'][$plugin]);
+        unset($return['titles'][$plugin]);
+      }
+    }
+  }
+
+  return ($titles ? $return['title'] : $return['base']);
+}
Index: views_form.inc
===================================================================
RCS file: views_form.inc
diff -N views_form.inc
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ views_form.inc	18 Aug 2007 18:37:15 -0000
@@ -0,0 +1,407 @@
+<?php
+// $Id$
+
+/**
+ * @file
+ * Helper functions for the views_node_selector form element type. We modify
+ * the view display page when views_node_selector element is in use, creating
+ * radio or checkboxes for each node listed and modifying the exposed filters
+ * handling (if present).
+ */
+
+/**
+ * Theme function for the views_node_selector form element
+ */
+function theme_views_node_selector($element) {
+  // build the real view
+  $built = _views_form_build_view_from_element($element, 'embed');
+
+  // some other stuff in our element array
+  $filters = $element['filters']['#children'];
+  $pager = $element['pager']['#children'];
+
+  // the entire bundle of output goes in one div
+  $element['#children'] = '<div class = "views_form-group">';
+
+  // add the filters, if any
+  if ($filters) {
+    $element['#children'] .= $filters;
+  }
+
+  // users always seen something, regardless
+  $element['#children'] .= '<div class = "views_form-view">';
+  if ($built) {
+    $element['#children'] .= $built;
+  }
+  else {
+    $element['#children'] .= t('No content to display. You may not make any selections at this time.');
+  }
+  $element['#children'] .= '</div>';
+
+  // finally, the pager, if there is one.
+  if ($pager) {
+    $element['#children'] .= '<div class = "views_form-view">' . $pager . '</div>';
+  }
+  $element['#children'] .= '</div>';
+
+  // add the standard required textualizin'
+  $element['#title'] .= $element['#required'] ?
+    ' <span class="form-required" title="'. t('This field is required.') .'">*</span>' : '';
+
+  unset($element['#value']); // fieldsets can have values? strange....
+
+  return theme('fieldset', $element);
+}
+
+/**
+ * Display the nodes of a view as a form. Should only be used by the
+ * views_node_selector form element.
+ */
+function theme_views_view_form($view, $nodes, $type) {
+  $fields = _views_get_fields();
+  $props = $view->select_column_props;
+  $output = '';
+
+  foreach ($nodes as $node) {
+    $row = array();
+
+    // add the first cell as a selection element
+    if ($props['multiple']) {
+      // this form is a set of checkboxes
+      $element = array (
+        '#name' => $props['parent_name'] . '[' . $node->nid . ']',
+        '#id' => $props['parent_id'] . '-' . $node->nid,
+        '#return_value' => $node->nid,
+        '#parents' => $props['super_parents'],
+      );
+
+      if (in_array($node->nid, $props['setval'] )) {
+        $element['#value'] = true;
+      }
+
+      $form = theme('checkbox', $element);
+    }
+    else {
+      // this form is a set of radio buttons
+      $element = array (
+        '#name' => $props['parent_name'],
+        '#value' => $props['setval'],
+        '#return_value' => $node->nid,
+        '#parents' => $props['super_parents'],
+      );
+
+      $form = theme('radio', $element);
+    }
+
+    $row[] = array(
+      'data' => $form,
+      'class' => "view-field view-form-element"
+    );
+
+    foreach ($view->field as $field) {
+      if ($fields[$field['id']]['visible'] !== FALSE) {
+        $cell['data'] = views_theme_field('views_handle_field', $field['queryname'],
+          $fields, $field, $node, $view);
+        $cell['class'] = "view-field ". views_css_safe('view-field-'. $field['queryname']);
+        $row[] = $cell;
+      }
+    }
+    $rows[] = $row;
+  }
+
+  // add a column to the header to accomodate our selection column, include
+  // 'select all' checkbox
+  array_unshift($view->table_header, theme('table_select_header_cell'));
+
+  $output = theme('table', $view->table_header, $rows);
+
+  return $output;
+}
+
+/**
+ * Theme function for the filter form. A wrapper for the default filter form.
+ */
+function theme_views_form_filter_form($form) {
+  $output = '';
+  // move the 'filter_button' element over to 'submit'
+  // to conform to what the views theme function expects
+  $form['submit'] = $form['filter_button'];
+  unset($form['filter_button']);
+
+  $output = '<div class = "views_form-filters">';
+  $output .= theme('views_filters', $form);
+  $output .= '</div>';
+  return $output;
+}
+
+/**
+ * True form element expansion function. Turns the element into a view, using
+ * the style plugin 'form'. Can either be a single or multiple form (checkboxes
+ * vs. radio). See views_elements() for a complete list of the properties.
+ */
+function _views_form_process_element($element) {
+  $element['#tree'] = true;
+
+  if (!isset($element['#value']['selected']) || !is_array($element['#value'])) {
+    if (is_array($element['#value'])) {
+      foreach($element['#value'] as $key => $value) {
+        if (is_int($key)) {
+          $element['#value']['selected'][] = $value;
+        }
+      }
+    }
+    else {
+      $tmp = $element['#value'];
+      unset($element['#value']);
+      $element['#value']['selected'] = $tmp;
+    }
+  }
+
+  // the clone is necessary in case we're on php5 and we run on the same
+  // view multiple times in a single form (quite possible really)
+  $view = drupal_clone(views_get_view($element['#view']));
+  _views_form_add_title_if_empty($view);
+
+  if ($element['#multiple']) {
+    $item_value = is_array($element['#value']['selected']) ? $element['#value']['selected'] : array();
+  }
+  else {
+    $item_value = $element['#value']['selected'];
+  }
+
+  // add the selection field
+  $view->select_column_props = array (
+    // these next items are shoved into the field to pass to the themeing func
+    'multiple' => $element['#multiple'],
+    'setval' => $item_value,
+    'parent_name' => $element['#name'] . '[selected]',
+    'parent_id' => $element['#id'] . '-selected',
+    'super_parents' => array_merge($element['#parents'], array('selected')),
+  );
+
+  // change the view to our internal rendering plugin definition
+  $view->page_type = 'form';
+
+  // save the view for render time
+  $element['#view'] = $view;
+
+  // parse out the exposed filters
+  if (isset($element['#value']['filters'])) {
+    $element['#filters'] = views_get_filter_values($element['#value']['filters']);
+  }
+
+  // get the exposed filters form
+  if (!$element['#embedded'] && ($filter_form = _views_build_filters_form($view))) {
+    $filter_form['view'] = array (
+      '#type' => 'value',
+      '#value' => $view,
+    );
+
+    $filter_form['#theme'] = 'views_form_filter_form';
+
+    $element['filters'] = $filter_form;
+    $element['filters']['filter_button'] = array (
+      '#type' => 'button',
+      '#value' => t('Filter results'),
+      '#name' => $element['#name'] . '[filter_button]',
+    );
+  }
+
+  // if someone intentionally filtered, we return to page zero, to avoid invalid values
+  if ($element['#value']['filter_button']) {
+    $element['#value']['pager']['pages'] = 0;
+  }
+
+  // retrieve the total number of possible pages for this view
+  // reset the page to 0 if it is greater than the max possible pages
+  $page_data = _views_form_get_page_data($view, $element['#arguments'], $element['#filters']);
+  $page_opts = array();
+  $max = 0;
+
+  for($i = 0; $i < $page_data['pages']; ++$i) {
+    $page_opts[$i] = t('Page') . ' ' . ($i + 1);
+    $max = max($max, $i);
+  }
+
+  // update the page if we have a value for it
+  if (isset($element['#value']['pager']['pages'])) {
+    $element['#page'] = $element['#value']['pager']['pages'] > $max ? 0 : $element['#value']['pager']['pages'];
+  }
+
+  // build the view as specified in the element. just for counting
+  $view_nodes = _views_form_build_view_from_element($element, 'items');
+
+  // use the result of the query to build a list of possible nodes to select
+  $options = array();
+  if (!empty($view_nodes['items'])) {
+    foreach($view_nodes['items'] as $node) {
+      $options[$node->nid] = '';
+    }
+  }
+
+  // find any missing nodes (ie. things that won't be in the view)
+  $missing = array();
+  // make sure there is a selected value, or we get extra queries later
+  if (isset($element['#value']['selected'])) {
+    if (is_array($element['#value']['selected'])) {
+      foreach($element['#value']['selected'] as $nid) {
+        if (!isset($options[$nid]) && $nid) {
+          $missing[$nid] = true;
+        }
+      }
+    }
+    else if (!isset($options[$element['#value']['selected']])) {
+      $missing[$element['#value']['selected']] = true;
+    }
+  }
+
+  if (!empty($missing)) {
+
+    $missing_view = drupal_clone($element['#view']);
+
+    // run the missing query and stash it in the $element['view']->missing
+    // making the already selected table
+    // the only filter we want is nid in missing array
+    $missing_view->filter = array();
+    $missing_view->filter[] = array (
+        'vid' => $missing_view->vid,
+        'tablename' => '',
+        'field' => 'node.include_exclude',
+        'value' => array_keys($missing),
+        'operator' => 'OR',
+        'options' => '',
+        'position' => 0,
+        'id' => 'node.include_exclude',
+    );
+
+    // remove some caching of queries and other data
+    // we do not need on the "missing" view
+    $missing_view->is_cacheable = false;
+    unset($missing_view->query);
+    unset($missing_view->countquery);
+    unset($missing_view->num_rows);
+
+    $missing_built = views_build_view('items', $missing_view);
+    $missing_items = $missing_built['items'];
+    $element['#view']->missing = $missing_items;
+    // add the missing nodes to the options list
+    foreach(array_keys($missing) as $new_opt) {
+     $options[$new_opt] = '';
+    }
+  }
+
+
+  // a set of checkboxes or radio buttons to make sure the form
+  // passes validation
+  $element['selected'] = array (
+    '#type' => $element['#multiple'] ? 'checkboxes' : 'radios',
+    '#options' => $options,
+    '#required' => $element['#required'],
+    '#title' => $element['#title'],
+    //'#parents' => array_merge($element['#parents'], array('selected')),
+  );
+
+  // form based pager
+  // hide on views with only 1 page or no pages, also hide if paging is turned off
+  if (!$element['#embedded'] && (count($page_opts) > 1) && $view->use_pager) {
+    $element['pager']['pages'] = array (
+      '#type' => 'select',
+      '#options' => $page_opts,
+      '#title' => t('Select another page'),
+      '#value' => $element['#page'],
+    );
+    $element['pager']['go'] = array (
+      '#type' => 'button',
+      '#value' => t('Go!'),
+      '#name' => $element['#name'] . '[go]',
+    );
+  }
+
+  return $element;
+}
+
+function _views_form_build_view_from_element($element, $mode = 'embed', $view = NULL, $filters = NULL) {
+  if (is_null($view)) {
+    $view = $element['#view'];
+  }
+
+  // clone for safety
+  $view = drupal_clone($view);
+
+  if (is_null($filters)) {
+    $filters = $element['#filters'];
+  }
+
+  // Remove internal pager except for embedded views
+  $output = views_build_view(
+    $mode,
+    $view,
+    $element['#arguments'],
+    $element['#embedded'] ? $view->use_pager : FALSE,
+    $view->nodes_per_page,
+    $element['#page'],
+    0,
+    $filters
+  );
+
+  if ($element['#embedded'] && $mode == 'embed') {
+    $output = preg_replace('(<div class="pager">.*?<\/div>)', '', $output);
+  }
+
+  return $output;
+}
+
+function _views_form_add_title_if_empty(&$view) {
+  // if the form doesn't have any fields, we add the node title (with link)
+  // option. This is fragile as it depends on the field code not changing. if
+  // something breaks, look here. the views_view_add_field would be prefered, but
+  // we need the query name computed by this point and views_view_add_field
+  // doesn't have arguments for queryname
+  if (empty($view->field)) {
+    $title = array (
+      'vid' => $view->vid,
+      'tablename' => 'node',
+      'field' => 'title',
+      'label' => t('Title'),
+      'handler' => 'views_handler_field_nodelink',
+      'sortable' => 0,
+      'defaultsort' => 0,
+      'options' => 'link',
+      'position' => 0,
+      'fullname' => 'node.title',
+      'id' => 'node.title',
+      'queryname' => 'node_title',
+    );
+    $view->field[] = $title;
+  }
+}
+
+function _views_form_get_page_data($view, $args = NULL, $filters = NULL) {
+  $element = array(
+    '#arguments' => $args,
+    '#page' => 0,
+    '#filters' => $filters,
+    '#view' => $view,
+  );
+
+  $queries = _views_form_build_view_from_element($element, 'queries');
+
+  if (empty($queries)) {
+    return array ('count' => 0, 'pages' => 0);
+  }
+  // rewrite the query appropriately
+  $cquery = db_rewrite_sql($queries['countquery'], 'node', 'nid');
+  $count = db_result(db_query($cquery, $queries['args']));
+
+  if ($view->nodes_per_page) {
+    $pages = ceil($count / $view->nodes_per_page);
+  }
+  else {
+    $pages = 1;
+  }
+
+  return array (
+    'count' => $count,
+    'pages' => $pages,
+  );
+}
Index: views_ui.module
===================================================================
RCS file: /cvs/drupal/contributions/modules/views/Attic/views_ui.module,v
retrieving revision 1.44.2.25
diff -u -p -r1.44.2.25 views_ui.module
--- views_ui.module	14 Jul 2007 19:54:20 -0000	1.44.2.25
+++ views_ui.module	18 Aug 2007 18:37:15 -0000
@@ -715,7 +715,7 @@ function _views_check_ops(&$view, $op, $
 /**
  * Custom form element to do our nice images.
  */
-function views_elements() {
+function views_ui_elements() {
   $type['views_imagebutton'] = array('#input' => TRUE, '#button_type' => 'submit',);
   return $type;
 }
@@ -848,7 +848,7 @@ function views_edit_view($view, $op = ''
     '#type' => 'select',
     '#title' => t('View Type'),
     '#default_value' => $view->page_type,
-    '#options' => _views_get_style_plugins(true),
+    '#options' => _views_get_style_plugins(true, true),
     '#description' => t('How the nodes should be displayed to the user.'),
   );
 
@@ -1037,7 +1037,7 @@ function views_edit_view($view, $op = ''
     '#type' => 'select',
     '#title' => t('View Type'),
     '#default_value' => $view->block_type,
-    '#options' => _views_get_style_plugins(true),
+    '#options' => _views_get_style_plugins(true, true),
     '#description' => t('How the nodes should be displayed to the user.'),
   );
 
Index: modules/views_node.inc
===================================================================
RCS file: /cvs/drupal/contributions/modules/views/modules/views_node.inc,v
retrieving revision 1.30.2.19
diff -u -p -r1.30.2.19 views_node.inc
--- modules/views_node.inc	14 Jul 2007 19:30:51 -0000	1.30.2.19
+++ modules/views_node.inc	18 Aug 2007 18:37:15 -0000
@@ -241,6 +241,14 @@ function node_views_tables() {
         'handler' => 'views_handler_filter_body',
         'help' => t('This filter allows nodes to be filtered by their body.'),
       ),
+      'include_exclude' => array(
+        'name' => t('Node: Include/Exclude Nodes'),
+        'field' => 'nid',
+        'operator' => 'views_handler_operator_or',
+        'value' => views_handler_filter_nid_value_form(VIEWS_FORM_DEFAULT_VIEW),
+        'value-type' => 'array',
+        'help' => t('This filter allows you to limit your view to only apply to some specific nodes. You can limit your view to a specific list of nodes, or you can exclude specific nodes from every being returned.'),
+      ),
     ),
   );
 
@@ -925,3 +933,17 @@ function views_handler_node_delete_desti
 function views_handler_node_nid($fieldinfo, $fielddata, $value, $data) {
   return $data->nid;
 }
+
+/**
+ * Provide a configuration form for the include/exclude filter
+ */
+function views_handler_filter_nid_value_form($view) {
+
+  $form['vns'] = array (
+    '#type' => 'views_node_selector',
+    '#view' => $view,
+    '#multiple' => true,
+  );
+
+  return $form;
+}
