? 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 @@ 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 @@ +'; + + // add the filters, if any + if ($filters) { + $element['#children'] .= $filters; + } + + // users always seen something, regardless + $element['#children'] .= '
'; + if ($built) { + $element['#children'] .= $built; + } + else { + $element['#children'] .= t('No content to display. You may not make any selections at this time.'); + } + $element['#children'] .= '
'; + + // finally, the pager, if there is one. + if ($pager) { + $element['#children'] .= '
' . $pager . '
'; + } + $element['#children'] .= ''; + + // add the standard required textualizin' + $element['#title'] .= $element['#required'] ? + ' *' : ''; + + 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 = '
'; + $output .= theme('views_filters', $form); + $output .= '
'; + 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>)', '', $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; +}