diff --git help/images/semantic-output-style-options.png help/images/semantic-output-style-options.png new file mode 100644 index 0000000..5cbbc65 Binary files /dev/null and help/images/semantic-output-style-options.png differ diff --git help/images/semantic-row-style-options.png help/images/semantic-row-style-options.png new file mode 100644 index 0000000..11b840b Binary files /dev/null and help/images/semantic-row-style-options.png differ diff --git help/style-semantic-fields.html help/style-semantic-fields.html new file mode 100644 index 0000000..2d359c5 --- /dev/null +++ help/style-semantic-fields.html @@ -0,0 +1,13 @@ + +

Row style options

+ +

Row style options form

+ +

For each field in your view, you can specify the HTML element and class attribute. These are both optional. Omitting the HTML element will cause the class attribute to be ignored and that content will be output without any HTML wrapping it. For example, you may want to omit the HTML element from the wrapper on a node teaser or user picture because these may already have adequate markup.

+ + + +

Important! There is no good way for Semantic Views to provide a default value for these options when new fields are added to your view. If you do not update the options for the row style, your field output will have no HTML element around it. The

div
that appears when you view the settings form for the row style is only saved when you actually click the Update button on the settings form.

diff --git help/style-semantic.html help/style-semantic.html new file mode 100644 index 0000000..f6faf7d --- /dev/null +++ help/style-semantic.html @@ -0,0 +1,54 @@ + +

Output style options

+ +

Output style options form

+ +

All of these options are optional. When an option allows a HTML element and a class attribute, omitting the HTML element will cause the class attribute to be ignored and that content will be output without any HTML wrapping it.

+ +

Note Always input HTML elements without the < and >. Your valid input will be inserted between the angle brackets in the template.

+ +

Note Any class attributes you input will be concatenated and rendered as the class attribute's value in the template. For example, your valid input row node blog as the class attribute for a div element will be rendered as <div class="row node blog">.

+ +

Grouping title

+ +

For Views where the results are grouped, the HTML element and class attribute can be specified for the element that wraps the title.

+ +

List

+ +

With this option, Semantic Views can behave like the Views 2 HTML List display plugin. HTML unordered, ordered and definition lists can be created in the by choosing a list type. It's important to remember that HTML lists have additional constraints on their child elements. <ul> and <ol> must have <li> children and <dl> must have <dt> and <dd> children.

+ +

Row

+ +

Rows are the results of the executed view. The number of rows in a view display is determined by the pager (or if the number of results is less than the pager limit, the number of results).

+ +

HTML element

+ +

The HTML element for the row is usually <div>.

+ +

Class attribute

+ +

This is the basic class attribute for each row. If it includes a # the row number will be substituted. Multiple class attributes can be specified: row or row row-#.

+ +

First and last classes

+ +

By default, Views row style adds a first class attribute to the first result in the pager and a last class attribute to the last result in the pager.

+ +
First/Last every nth
+ +

When this is set to 0, the first and last class attributes are added first and last results in the pager. If you specify a number greater than 1, first and last class attributes are added at that interval within the pager result set. This can be used to improve upon the Grid display plugin that comes with Views 2.

+ +

For example, if you have a grid layout with 5 column units with a gutter maintained by right margins on all units except the last one, setting this option to 5 will add a last class to every 5th result row (not to be confused with rows in your grid layout). first class attributes are added to the first result row in the pager and to each result that follows a last result.

+ +

If the following two options are left empty, the first/last every nth option has no effect.

+ +
FIRST class attribute
+ +

This is the actual class attribute that is inserted. It is optional and defaults to first.

+ +
LAST class attribute
+ +

This is the actual class attribute that is inserted. It is optional and defaults to last.

+ +

Striping class attributes

+ +

When this is set, every row will have one of the class attributes specified here. The default is odd even so that n row has a class attribute of odd and n + 1 row has a class attribute of even. You are not limited to only two striping class attributes. It's perfectly valid to use north south east west to stripe your rows 4 different ways or to leave this option empty to disable striping.

diff --git help/views.help.ini help/views.help.ini index 6acb0a8..5ecd461 100644 --- help/views.help.ini +++ help/views.help.ini @@ -113,10 +113,22 @@ title = "RSS output style" parent = style weight = -5 +[style-semantic] +title = Semantic style +file = style-semantic +weight = -5 +parent = style + [style-fields] title = "Fields row style" parent = style-row +[style-semantic-fields] +title = "Semantic Fields row style" +file = style-semantic-fields +weight = 0 +parent = style-row + [style-node] title = "Node row style" parent = style-row diff --git includes/plugins.inc includes/plugins.inc index c8711c0..f6d4f55 100644 --- includes/plugins.inc +++ includes/plugins.inc @@ -160,6 +160,17 @@ function views_views_plugins() { 'type' => 'feed', 'help topic' => 'style-rss', ), + 'semantic' => array( + 'title' => t('Semantic'), + 'help' => t('Displays rows one after another.'), + 'handler' => 'views_plugin_style_semantic', + 'theme' => 'views_view_semantic', + 'uses row plugin' => TRUE, + 'uses options' => TRUE, + 'uses grouping' => TRUE, + 'type' => 'normal', + 'help topic' => 'style-semantic', + ), ), 'row' => array( 'parent' => array( @@ -179,6 +190,16 @@ function views_views_plugins() { 'type' => 'normal', 'help topic' => 'style-row-fields', ), + 'semantic_fields' => array( + 'title' => t('Semantic Fields'), + 'help' => t('Displays the fields with an optional template.'), + 'handler' => 'views_plugin_row_semantic_fields', + 'theme' => 'views_view_semantic_fields', + 'uses fields' => TRUE, + 'uses options' => TRUE, + 'type' => 'normal', + 'help topic' => 'style-semantic-fields', + ), ), 'argument default' => array( 'parent' => array( diff --git plugins/views_plugin_row_semantic_fields.inc plugins/views_plugin_row_semantic_fields.inc new file mode 100644 index 0000000..8a913da --- /dev/null +++ plugins/views_plugin_row_semantic_fields.inc @@ -0,0 +1,102 @@ + FALSE); + // Field element_type and classes cannot be defined in the options + // definition because the field handlers are not attached when the option + // defaults are set up in the object's init() method. + $options['semantic_html'] = array('default' => array()); + return $options; + } + + /** + * Provide a form for setting options. + */ + function options_form(&$form, &$form_state) { + parent::options_form($form, $form_state); + $form['semantic_html'] = array( + '#tree' => TRUE, + ); + foreach ($this->display->handler->get_handlers('field') as $field => $handler) { + if (!$handler->options['exclude']) { + $default_value = is_array($this->options['semantic_html'][$field]) ? $this->options['semantic_html'][$field] : array( + 'element_type' => 'div', + 'class' => '', + 'label_element_type' => 'label', + 'label_class' => '' + ); + $form['semantic_html'][$field] = array( + '#title' => $handler->label() ? $handler->label() : $handler->ui_name(), + '#type' => 'fieldset', + '#attributes' => array( + 'class' => 'clear-block', + ), + ); + $form['semantic_html'][$field]['element_type'] = array( + '#prefix' => '
', + '#suffix' => '
', + '#title' => 'Element', + '#type' => 'textfield', + '#size' => '10', + '#default_value' => $default_value['element_type'], + ); + $form['semantic_html'][$field]['class'] = array( + '#prefix' => '
', + '#suffix' => '
', + '#title' => 'Class attribute', + '#type' => 'textfield', + '#size' => '30', + '#default_value' => $default_value['class'], + ); + if ($handler->label()) { + $form['semantic_html'][$field]['label_element_type'] = array( + '#prefix' => '
', + '#suffix' => '
', + '#title' => 'Label element', + '#type' => 'textfield', + '#size' => '10', + '#default_value' => $default_value['label_element_type'], + ); + $form['semantic_html'][$field]['label_class'] = array( + '#prefix' => '
', + '#suffix' => '
', + '#title' => 'Label class attribute', + '#type' => 'textfield', + '#size' => '30', + '#default_value' => $default_value['label_class'], + ); + } + } + } + $form['skip_blank'] = array( + '#type' => 'checkbox', + '#default_value' => $this->options['skip_blank'], + '#title' => t('Skip empty fields'), + '#description' => t('Do not output anything when a field has no content. This has the same outcome as enabling the Hide if empty option for every field in this display.'), + ); + } + + /** + * Validate the options form. + */ + function options_validate(&$form, &$form_state) { + parent::options_validate($form, $form_state); + // TODO: validate that the elements and classes are valid HTML. This is not + // a substitute for output filtering. + } +} diff --git plugins/views_plugin_style_semantic.inc plugins/views_plugin_style_semantic.inc new file mode 100644 index 0000000..10d7644 --- /dev/null +++ plugins/views_plugin_style_semantic.inc @@ -0,0 +1,205 @@ + array( + 'element_type' => array('default' => 'h3'), + 'class' => array('default' => 'title'), + ), + ); + + // List option definition. + $options['list'] = array( + 'contains' => array( + 'element_type' => array('default' => ''), + 'class' => array('default' => ''), + ), + ); + + // Row option definition. + $options['row'] = array( + 'contains' => array( + 'element_type' => array('default' => 'div'), + 'class' => array('default' => ''), + 'first_class' => array('default' => 'first'), + 'last_class' => array('default' => 'last'), + 'last_every_nth' => array('default' => '0'), + 'striping_classes' => array('default' => 'odd even'), + ), + ); + + return $options; + } + + /** + * Render the given style. + */ + function options_form(&$form, &$form_state) { + parent::options_form($form, $form_state); + + // Group options. + $form['group'] = array( + '#type' => 'fieldset', + '#title' => 'Grouping title', + '#description' => 'If using groups, the view will insert the grouping’s title field.', + '#attributes' => array( + 'class' => 'clear-block', + ), + ); + $form['group']['element_type'] = array( + '#prefix' => '
', + '#suffix' => '
', + '#title' => 'Element', + '#type' => 'textfield', + '#size' => '10', + '#default_value' => $this->options['group']['element_type'], + ); + $form['group']['class'] = array( + '#prefix' => '
', + '#suffix' => '
', + '#title' => 'Class attribute', + '#type' => 'textfield', + '#size' => '30', + '#default_value' => $this->options['group']['class'], + ); + + // List options. + $form['list'] = array( + '#type' => 'fieldset', + '#title' => 'List', + '#description' => t('If the output should be a HTML list, select the element and class attribute. The row element should also be set to %li.', array('%li' => 'li')), + '#attributes' => array( + 'class' => 'clear-block', + ), + ); + $form['list']['element_type'] = array( + '#prefix' => '
', + '#suffix' => '
', + '#type' => 'radios', + '#title' => t('List type'), + '#options' => array( + '' => t('None'), + 'ul' => t('Unordered list'), + 'ol' => t('Ordered list'), + 'dl' => t('Definition list'), + ), + '#default_value' => $this->options['list']['element_type'], + ); + $form['list']['class'] = array( + '#prefix' => '
', + '#suffix' => '
', + '#title' => 'Class attribute', + '#type' => 'textfield', + '#size' => '30', + '#default_value' => $this->options['list']['class'], + ); + + // Row options. + $form['row'] = array( + '#type' => 'fieldset', + '#title' => 'Row', + '#attributes' => array( + 'class' => 'clear-block', + ), + ); + $form['row']['element_type'] = array( + '#prefix' => '
', + '#suffix' => '
', + '#title' => 'Element', + '#type' => 'textfield', + '#size' => '10', + '#default_value' => $this->options['row']['element_type'], + ); + $form['row']['class'] = array( + '#prefix' => '
', + '#suffix' => '
', + '#title' => 'Class attribute', + '#type' => 'textfield', + '#size' => '30', + '#default_value' => $this->options['row']['class'], + '#description' => t('Insert a %hash where you want row enumeration.', array('%hash' => '#')), + ); + + // First and last class options. + $form['row']['first_last'] = array( + '#type' => 'fieldset', + '#title' => 'First and last classes', + // The #parents attribute must be set to avoid another array layer around + // the group of FIRST/LAST class attribute options. + '#parents' => array('style_options', 'row'), + '#description' => t('If the %last_every_nth option is empty or zero, the %first_class and %last_class are added once to only the first and last rows in the pager set. If this option is greater than 1, these classes are added to every nth row, which may be useful for grid layouts where the initial and final unit’s lateral margins must be 0.', array( + '%last_every_nth' => 'FIRST/LAST every nth', + '%first_class' => 'FIRST class attribute', + '%last_class' => 'LAST class attribute', + ) + ), + '#attributes' => array( + 'class' => 'clear-block', + ), + ); + $form['row']['first_last']['last_every_nth'] = array( + '#type' => 'textfield', + '#size' => '10', + '#title' => t('FIRST/LAST every nth'), + '#default_value' => $this->options['row']['last_every_nth'], + ); + $form['row']['first_last']['first_class'] = array( + '#prefix' => '
', + '#suffix' => '
', + '#title' => 'FIRST class attribute', + '#type' => 'textfield', + '#size' => '30', + '#default_value' => $this->options['row']['first_class'], + ); + $form['row']['first_last']['last_class'] = array( + '#prefix' => '
', + '#suffix' => '
', + '#title' => 'LAST class attribute', + '#type' => 'textfield', + '#size' => '30', + '#default_value' => $this->options['row']['last_class'], + ); + + // Striping class options. + $form['row']['striping_classes'] = array( + '#title' => 'Striping class attributes', + '#type' => 'textfield', + '#size' => '30', + '#default_value' => $this->options['row']['striping_classes'], + '#description' => 'One striping class attribute is applied to each row. Separate multiple attributes with a space.', + ); + } + + /** + * Validate the options form. + */ + function options_validate(&$form, &$form_state) { + parent::options_validate($form, $form_state); + // TODO: validate that the elements and classes are valid HTML. This is not + // a substitute for output filtering. + } +} diff --git theme/theme.inc theme/theme.inc index 8728df1..888fec1 100644 --- theme/theme.inc +++ theme/theme.inc @@ -178,6 +178,51 @@ function template_preprocess_views_view_fields(&$vars) { } /** + * Preprocess theme function to print a single record from a row, with fields + */ +function template_preprocess_views_view_semantic_fields(&$variables) { + $view = $variables['view']; + // Loop through the fields for this view. + $variables['fields'] = array(); // ensure it's at least an empty array. + foreach ($view->field as $id => $field) { + // render this even if set to exclude so it can be used elsewhere. + $field_output = $view->style_plugin->get_field($view->row_index, $id); + if (empty($field->options['exclude']) && !(($variables['options']['skip_blank'] || $field->options['hide_empty']) && empty($field_output))) { + $object = new stdClass(); + + $object->content = $field_output; + if (isset($view->field[$id]->field_alias) && isset($variables['row']->{$view->field[$id]->field_alias})) { + $object->raw = $variables['row']->{$view->field[$id]->field_alias}; + } + else { + $object->raw = NULL; // make sure it exists to reduce NOTICE + } + + $object->handler = &$view->field[$id]; + + $semantic_html = $variables['options']['semantic_html'][$id]; + + // Field content + $object->element_type = check_plain($semantic_html['element_type']); + $object->attributes = array(); + if ($semantic_html['class']) { + $object->attributes['class'] = $semantic_html['class']; + } + + // Field label + $object->label = check_plain($view->field[$id]->label()); + $object->label_element_type = check_plain($semantic_html['label_element_type']); + $object->label_attributes = array(); + if ($semantic_html['label_class']) { + $object->label_attributes['class'] = $semantic_html['label_class']; + } + + $variables['fields'][$id] = $object; + } + } +} + +/** * Display a single views field. * * Interesting bits of info: @@ -520,6 +565,76 @@ function template_preprocess_views_view_rss(&$vars) { } /** + * Display the simple view of rows one after another + */ +function template_preprocess_views_view_semantic(&$variables) { + $variables['group_element'] = check_plain($variables['options']['group']['element_type']); + $variables['group_attributes'] = array(); + if ($variables['options']['group']['class']) { + $variables['group_attributes']['class'] = $variables['options']['group']['class']; + } + + $variables['list_element'] = check_plain($variables['options']['list']['element_type']); + $variables['list_attributes'] = array(); + if ($variables['options']['list']['class']) { + $variables['list_attributes']['class'] = $variables['options']['list']['class']; + } + + // TODO: set a default or handle empty value. + $variables['row_element'] = check_plain($variables['options']['row']['element_type']); + $last_every_nth = $variables['options']['row']['last_every_nth']; + + $variables['row_attributes'] = array(); + + // Set up striping class array. + $stripes = array(); + if (trim($variables['options']['row']['striping_classes'])) { + $stripes = explode(' ', trim($variables['options']['row']['striping_classes'])); + } + $striping = count($stripes); + + foreach ($variables['rows'] as $id => $row) { + $variables['row_attributes'][$id] = array(); + $classes = array(); + if ($variables['options']['row']['class']) { + $classes[] = str_replace('#', $id, $variables['options']['row']['class']); + } + if ($variables['options']['row']['first_class']) { + // The FIRST class attribute can be used in two ways. When the "last every + // nth" option is specified, the FIRST attribute is added to the class in + // those intervals. This could be useful for grid designs where the first + // unit in a row needs a zero width margin. + if (($last_every_nth && $id % $last_every_nth == 0) || + // Otherwise when last every nth is not set, the FIRST class is added + // to the first row in the pager set. + (!$last_every_nth && $id == 0)) { + $classes[] = $variables['options']['row']['first_class']; + } + } + if ($variables['options']['row']['last_class']) { + // The LAST class attribute can be used in two ways. When the "last every + // nth" option is specified, the LAST attribute is added to the class in + // those intervals. This could be useful for grid designs where the last + // unit in a row needs a zero width margin. + if (($last_every_nth && ($id + 1) % $last_every_nth == 0) || + // Otherwise when last every nth is not set, the LAST class is added + // to the last row in the pager set. + (!$last_every_nth && ($id + 1) == count($variables['rows']))) { + $classes[] = $variables['options']['row']['last_class']; + } + } + + if ($striping) { + $classes[] = $stripes[$id % $striping]; + } + + if (!empty($classes)) { + $variables['row_attributes'][$id]['class'] = implode(' ', $classes); + } + } +} + +/** * Default theme function for all RSS rows. */ function template_preprocess_views_view_row_rss(&$vars) { diff --git theme/views-view-semantic-fields.tpl.php theme/views-view-semantic-fields.tpl.php new file mode 100644 index 0000000..344fd6d --- /dev/null +++ theme/views-view-semantic-fields.tpl.php @@ -0,0 +1,56 @@ + element with + * the same class attributes as the field's HTML element. + * + * - $view: The view in use. + * - $fields: an array of $field objects. Each one contains: + * - $field->content: The output of the field. + * - $field->raw: The raw data for the field, if it exists. This is NOT output + * safe. + * - $field->element_type: The HTML element wrapping the field content and + * label. + * - $field->attributes: An array of attributes for the field wrapper. + * - $field->handler: The Views field handler object controlling this field. + * Do not use var_export to dump this object, as it can't handle the + * recursion. + * - $row: The raw result object from the query, with all data it fetched. + * + * @see template_preprocess_semanticviews_view_fields() + * @ingroup views_templates + * @todo Justify this template. Excluding the PHP, this template outputs angle + * brackets, the label element, slashes and whitespace. + */ +?> + $field): ?> + + element_type): ?> + <element_type; ?>attributes); ?>> + + + label): ?> + + label_element_type): ?> + <label_element_type; ?>label_attributes); ?>> + + + label; ?>: + + label_element_type): ?> + label_element_type; ?>> + + + + + content; ?> + + element_type): ?> + element_type; ?>> + + + diff --git theme/views-view-semantic.tpl.php theme/views-view-semantic.tpl.php new file mode 100644 index 0000000..e969f6f --- /dev/null +++ theme/views-view-semantic.tpl.php @@ -0,0 +1,29 @@ + + + <> + + > + + + <> + + $row): ?> + + <> + + + + > + + + + > +