all contrib module security updates done
This commit is contained in:
Executable → Regular
+71
-8
@@ -1,12 +1,75 @@
|
||||
/* $Id*/
|
||||
CHANGELOG for field_group for Drupal 7
|
||||
|
||||
Field_group 7.x-1.x-dev
|
||||
o Issue #1095316: Field Groups disappear when Content Type is renamed.
|
||||
o Issue #1095316 by swentel: Support for Entity API.
|
||||
o Issue #1095002 by animelion: Upgrading removes all existing field groups.
|
||||
o Issue #1095130 by willvincent: Features export not working with rc2.
|
||||
|
||||
Field Group origin/7.x-1.x, 2017-11-04
|
||||
--------------------------------------
|
||||
- Tests fail after new 'administer fields' permission.
|
||||
- Update CHANGELOG.txt to 7.x-1.5 Release.
|
||||
- Revert "Issue #2646106 by jribeiro: Update CHANGELOG.txt to 7.x-1.5 Release".
|
||||
- Update CHANGELOG.txt to 7.x-1.5 Release.
|
||||
- Fix whitespace between "&" and parameters for some functions.
|
||||
- Avoid bad code pattern (that is picked up by security scanners) when getting hash.
|
||||
- Should #array_parents be updated in field_group_fields_nest()?.
|
||||
- "Show" appears in horizontal tab title if node form submitted through ajax.
|
||||
- CSS breaks i18n string translation interface.
|
||||
- "uncaught exception: Syntax error, unrecognized expression: #" after 7.x-1.4 update.
|
||||
- PHP7 - Uniform Variable Syntax updates are causing exported field_groups to not have names.
|
||||
|
||||
Field_group 7.x-1.5
|
||||
o Added extra formatting on html element attributes.
|
||||
o Fixed bug on automatic ID generation in javascript.
|
||||
o Issue #2511960 by rrfegade: spelling errors in D7.
|
||||
o Issue #2194279 by Georgique: Export translatables (label and descriptions) in Features.
|
||||
o Issue #2386335 by Dubs: Integer Group Array Keys can create empty node forms.
|
||||
o Issue #2269133 by sammuell: Accordion is always closed when using jQuery update module.
|
||||
o Issue #2258939 by Snipon: Fixed Change to preventDefault on multipage controls.
|
||||
o Issue #2173937 by jantoine, omerida | Bernieman: Fixed Accordion Element has fixed height in HTML.
|
||||
o Issue #2311789 by eelkeblok: Fixed Group is incorrectly determined to be empty with nested form elements.
|
||||
o Issue #2272003 by karolrybak | Kwb: Fixed Fieldgroup no longer renders after using more than one 'HTML Element' on the same level.
|
||||
o Issue #2309219 by czigor: Fixed Proper CTools exportable loading.
|
||||
o Issue #2283245 by stefan.r | jrb: Fixed New id functionality breaks tests and CSS on existing field groups.
|
||||
o Issue #2295133 by boyan.borisov, interX: Fixed field_group_update_7007 error.
|
||||
o Remove field_group.css, the css is not needed anymore.
|
||||
o Fix horiziontal tabs when used as standalone element.
|
||||
|
||||
Field_group 7.x-1.4
|
||||
o Issue #2129805 by RaF: Incorrect markup when open div & custom classes provided.
|
||||
o Issue #2037731 by maximpodorov, Zach Harkey: Remove id attribute from HTML elements.
|
||||
o Issue #2099505 by borisson_ | Maks: Warning: Invalid argument supplied for foreach() in field_group_build_entity_groups() (line 1894 ..sites/all/modules/field_group.
|
||||
o Issue #2102397 by thebruce: Field Group 7006 update fails with "Cannot use string offset as an array in /field_group/field_group.install on line 318.
|
||||
o Issue #2122733 by Alan D.: Remove test module from admin listing.
|
||||
o Issue #2212431 by tim.plunkett: Field groups that contain only elements that use #markup are hidden.
|
||||
o Issue #2175731 by tobiasb, daggerhart: HTML produced by module is broken.
|
||||
o Issue #2190425 by tobiasb: Fix attach behavior.
|
||||
o Issue #1911530 by tobiasb, Simon Georges: Remove useless files[] directive from .info files.
|
||||
o Issue #1358430 by oushen, tobiasb | FriedJam: JS "Error: uncaught exception: Syntax error, unrecognized expression:".
|
||||
o Issue #2104391 by mike_san: Package descriptions.
|
||||
o Issue #2189777 by gmercer: Exporting a feature with field groups sometimes leads to $field_group->data being set to an empty string.
|
||||
o Issue #2168689 by Paul B: Beïng should be being.
|
||||
o Issue #2037731 by rreiss, maximpodorov, Zach Harkey: Make id attribute optional. Current installations will default get the old ids
|
||||
o Issue #2173351 by axe312: Add a label to html-element wrappers.
|
||||
o Issue #1954056 by klonos: Proper capitalization in the project name.
|
||||
o Issue #2224547 by sdrycroft: Fixed field_group_info_groups() clears all CTools caches when it should only clear the field_group caches.
|
||||
o Issue #2099505 by joelpittet, borisson_ | Maks: Fixed Warning: Invalid argument supplied for foreach() in field_group_build_entity_groups() (line 1894 ..sites/all/modules/field_group.
|
||||
o Issue #2223269 by barraponto: Fixed Don't filter and translate empty strings.
|
||||
|
||||
Field_group 7.x-1.3
|
||||
o Issue #2077695 by FreekVR | ChoY: Fixed field group entity display bug after update.
|
||||
o Issue #2078201 by DeFr, eneko1907 | cmriley: Fixed Started getting a ton of notices.
|
||||
o Issue #2085137 by ar-jan: Fixed Collapse speed no longer works with field_group() 1.2
|
||||
o Issue #2085225 by dtarc: Fixed Featurized Field Groups no longer have classes since marked optional.
|
||||
o Issue #2080645 by bassthiam: Fixed Warning: Illegal string offset 'label' in field_group_unpack() line 1682 of field_group()/field_group().module.
|
||||
o Issue #1942676 by Harlor | El Bandito: Added Change the text 'Mark group for required fields.'.
|
||||
o Issue #1358430 by FriedJam: Fixed JS 'Error: uncaught exception: Syntax error, unrecognized expression:".
|
||||
o Issue #2084999 by activelink: Fixed Empty field groups aren't being suppressed since move to 7.x-1.2.
|
||||
o Issue #1557258 by Mark Trapp | Chris Gillis: Fixed Existing field groups disappear and Add Field Group UI is disabled when no fields are available.
|
||||
o Issue #2000110 by bigjim: Fixed Comments on field_group_read_groups() wrong
|
||||
o Issue #1169146 by ankur, Nephele | jeffrey.dalton: Fixed upgrade from 7.x-1.0-rc2 to 7.x-1.0 deletes all field_groups().
|
||||
o Vertical tabs always appear when children elements have no access
|
||||
o #1966624 Feature 7.x-2.0-beta2 defines fields in a new way
|
||||
o #2090415 Make horizontal tabs not dependent on form.js. Also change drupal_add_x to #attached
|
||||
o
|
||||
|
||||
Field_group 7.x-1.0-rc2
|
||||
o Ran through coder, minor.
|
||||
o Issue #1033036 by Stalski, swentel: Create a field_group.api.php.
|
||||
@@ -20,7 +83,7 @@ Field_group 7.x-1.0-rc2
|
||||
o #1045526 by stalski: Make formatter options more user-friendly and logical.
|
||||
o #1041880 by robertgarrigos: duplicated entries in field_group table.
|
||||
o #1043834 by amsri: Field Group module just does not work with profiles 2.
|
||||
|
||||
|
||||
Field_group 7.x-1.0-rc1
|
||||
o #1006464 Change #groups to #fieldgroups because of name collapsing with form_process_fieldset
|
||||
o #1024184 fix collapsible when mode is set to open
|
||||
@@ -34,7 +97,7 @@ Field_group 7.x-1.0-rc1
|
||||
o #961106 Fixed notice on entity type and bundle check.
|
||||
o #962072 by mori: Improve CSS for horizontal tabs & accordion.
|
||||
o Changed Fieldgroup API: defaults and instance_settings are now merged.
|
||||
o Changed save action so everything is gathered during form_state to
|
||||
o Changed save action so everything is gathered during form_state to
|
||||
postpone saving until the save button is hit.
|
||||
o Changed some important variable name, so it makes more sense and easier to read.
|
||||
o Add basic crud functions.
|
||||
Executable → Regular
+20
-10
@@ -1,28 +1,38 @@
|
||||
|
||||
History:
|
||||
Field_group was written for Drupal 7. For drupal 6, the module is
|
||||
Field_group was written for Drupal 7. For drupal 6, the module is
|
||||
located in the CCK module (http://drupal.org/project/cck).
|
||||
As drupal core has a fields API drupal > 6, the field_group module
|
||||
As drupal core has a fields API drupal > 6, the field_group module
|
||||
is considered a contribution.
|
||||
|
||||
|
||||
Description:
|
||||
field_group is a module that will group a set of fields. In Drupal7,
|
||||
field_group is a module that will group a set of fields. In Drupal7,
|
||||
with fields, one means all fields that come from fieldable entities.
|
||||
You can add fieldgroups in several types with their own format settings.
|
||||
field_group has API functions to add your own formatter and rendering for
|
||||
it.
|
||||
field_group has API functions to add your own formatter and rendering for
|
||||
it.
|
||||
One of the biggest improvements to previous versions, is that fieldgroups
|
||||
have unlimited nesting, better display control.
|
||||
Note that field_group will only group fields, it can not be used to hide
|
||||
Note that field_group will only group fields, it can not be used to hide
|
||||
certain fields since this a permission matter.
|
||||
|
||||
Module project page:
|
||||
http://drupal.org/project/field_group
|
||||
|
||||
Documentation page:
|
||||
|
||||
Documentation page:
|
||||
http://drupal.org/node/1017838
|
||||
http://drupal.org/node/1017962
|
||||
|
||||
|
||||
Available group types:
|
||||
- Fieldsets
|
||||
- Horizontal tabs
|
||||
- Vertical tabs
|
||||
- Accordions
|
||||
- Divs
|
||||
- Multipage steps: <strong>Note: This is only client side.
|
||||
- HTML5 group type
|
||||
- Html element
|
||||
|
||||
To submit bug reports and feature suggestions, or to track changes:
|
||||
http://drupal.org/project/issues/field_group
|
||||
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
/**
|
||||
* Override the accordion default style for view_modes.
|
||||
*/
|
||||
form .ui-accordion h3, form .ui-accordion h3.ui-state-active {
|
||||
padding-left: 0;
|
||||
padding-right: 2em;
|
||||
}
|
||||
Executable → Regular
+32
-7
@@ -2,7 +2,7 @@
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Hooks provided by the Field group module.
|
||||
* Hooks provided by the Field Group module.
|
||||
*
|
||||
* Fieldgroup is a module that will wrap fields and other fieldgroups. Nothing more, nothing less.
|
||||
* For this there are formatters we can create on forms and view modes.
|
||||
@@ -87,7 +87,7 @@ function hook_field_group_formatter_info() {
|
||||
'form' => array(
|
||||
'fieldset' => array(
|
||||
'label' => t('Fieldset'),
|
||||
'description' => t('This fieldgroup renders the inner content in a fieldset with the titel as legend.'),
|
||||
'description' => t('This fieldgroup renders the inner content in a fieldset with the title as legend.'),
|
||||
'format_types' => array('open', 'collapsible', 'collapsed'),
|
||||
'instance_settings' => array('classes' => ''),
|
||||
'default_formatter' => 'collapsible',
|
||||
@@ -96,7 +96,7 @@ function hook_field_group_formatter_info() {
|
||||
'display' => array(
|
||||
'div' => array(
|
||||
'label' => t('Div'),
|
||||
'description' => t('This fieldgroup renders the inner content in a simple div with the titel as legend.'),
|
||||
'description' => t('This fieldgroup renders the inner content in a simple div with the title as legend.'),
|
||||
'format_types' => array('open', 'collapsible', 'collapsed'),
|
||||
'instance_settings' => array('effect' => 'none', 'speed' => 'fast', 'classes' => ''),
|
||||
'default_formatter' => 'collapsible',
|
||||
@@ -220,7 +220,7 @@ function hook_field_group_format_settings($group) {
|
||||
* @param Array $elements by address.
|
||||
* @param Object $group The Field group info.
|
||||
*/
|
||||
function hook_field_group_pre_render(& $element, $group, & $form) {
|
||||
function hook_field_group_pre_render(&$element, $group, &$form) {
|
||||
|
||||
// You can prepare some variables to use in the logic.
|
||||
$view_mode = isset($form['#view_mode']) ? $form['#view_mode'] : 'form';
|
||||
@@ -268,7 +268,7 @@ function hook_field_group_pre_render(& $element, $group, & $form) {
|
||||
*
|
||||
* Function that fungates as last resort to alter the pre_render build.
|
||||
*/
|
||||
function hook_field_group_pre_render_alter(&$element, $group, & $form) {
|
||||
function hook_field_group_pre_render_alter(&$element, $group, &$form) {
|
||||
|
||||
if ($group->format_type == 'htab') {
|
||||
$element['#theme_wrappers'] = array('my_horizontal_tab');
|
||||
@@ -285,7 +285,7 @@ function hook_field_group_pre_render_alter(&$element, $group, & $form) {
|
||||
*
|
||||
* @param Array $elements by address.
|
||||
*/
|
||||
function hook_field_group_build_pre_render_alter(& $element) {
|
||||
function hook_field_group_build_pre_render_alter(&$element) {
|
||||
|
||||
// Prepare variables.
|
||||
$display = isset($element['#view_mode']);
|
||||
@@ -345,6 +345,18 @@ function hook_field_group_info() {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Alter the field group definitions provided by other modules.
|
||||
*
|
||||
* @param array $groups
|
||||
* Reference to an array of field group definition objects.
|
||||
*/
|
||||
function hook_field_group_info_alter(&$groups) {
|
||||
if (!empty($groups['group_issue_metadata|node|project_issue|form'])) {
|
||||
$groups['group_issue_metadata|node|project_issue|form']->data['children'][] = 'taxonomy_vocabulary_9';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_field_group_update_field_group().
|
||||
*
|
||||
@@ -421,14 +433,27 @@ function field_group_info_groups($entity_type = NULL, $bundle = NULL, $view_mode
|
||||
*
|
||||
* @param Array $params
|
||||
* The Entity type where field groups are requested.
|
||||
* @param $enabled
|
||||
* Return enabled or disabled groups.*
|
||||
*
|
||||
* @see field_group_info_groups()
|
||||
* @see ctools_export_load_object()
|
||||
*/
|
||||
function field_group_read_groups($params = array()) {
|
||||
function field_group_read_groups($conditions = array(), $enabled = TRUE) {
|
||||
// This function loads the requested groups through ctools export api.
|
||||
}
|
||||
|
||||
/**
|
||||
* Hides field groups including children in a render array.
|
||||
*
|
||||
* @param array $element
|
||||
* A render array. Can be a form, node, user, ...
|
||||
* @param array $group_names
|
||||
* An array of field group names that should be hidden.
|
||||
*/
|
||||
function field_group_hide_field_groups(&$element, $group_names) {
|
||||
}
|
||||
|
||||
/**
|
||||
* @} End of "addtogroup utility functions".
|
||||
*/
|
||||
|
||||
@@ -1,32 +0,0 @@
|
||||
/* $Id: field_group.css,v 1.1.2.12 2010/12/22 22:22:35 stalski Exp $ */
|
||||
|
||||
/**
|
||||
* Fix for fieldsets in vertical tabs.
|
||||
* Note that this can only be hardcoded to the Seven theme
|
||||
* where people who override this, are in trouble.
|
||||
*/
|
||||
.vertical-tabs fieldset.default-fallback,
|
||||
div.field-group-tabs-wrapper div.field-type-image fieldset,
|
||||
div.field-group-tabs-wrapper div.field-type-file fieldset {
|
||||
border: 1px solid #CCCCCC;
|
||||
margin: 1em 0;
|
||||
padding: 2.5em 0 0;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
div.field-group-tabs-wrapper div.field-type-image legend,
|
||||
div.field-group-tabs-wrapper div.field-type-file legend,
|
||||
.vertical-tabs .field-group-fieldset legend {
|
||||
display: block;
|
||||
}
|
||||
|
||||
/**
|
||||
* Override the accordion default style for view_modes.
|
||||
*/
|
||||
form .ui-accordion h3, form .ui-accordion h3.ui-state-active {
|
||||
padding-left: 2em;
|
||||
}
|
||||
|
||||
.ui-accordion h3.ui-state-active {
|
||||
background: url("/misc/ui/images/ui-bg_glass_75_e6e6e6_1x400.png") repeat-x scroll 50% 50% #E6E6E6;
|
||||
}
|
||||
Executable → Regular
+31
-9
@@ -9,25 +9,42 @@
|
||||
* that are a part of this feature.
|
||||
*/
|
||||
function field_group_features_export_alter(&$export, $module_name) {
|
||||
//Make sure we have fresh data by loading directly.
|
||||
// Make sure we have fresh data by loading directly.
|
||||
ctools_include('export');
|
||||
$field_groups = ctools_export_load_object('field_group');
|
||||
|
||||
//Add fieldgroups based on the fields that are present.
|
||||
if (!empty($export['features']['field'])) {
|
||||
foreach ($export['features']['field'] as $field) {
|
||||
// Support the separate field base -vs- field instance structure that was
|
||||
// added in Features v7.x-2.0-beta2.
|
||||
if (function_exists('field_instance_features_export')) {
|
||||
$export_var = 'field_instance';
|
||||
}
|
||||
else {
|
||||
$export_var = 'field';
|
||||
}
|
||||
|
||||
// Add fieldgroups based on the fields that are present.
|
||||
if (!empty($export['features'][$export_var])) {
|
||||
if (!isset($export['features']['field_group'])) {
|
||||
$export['features']['field_group'] = array();
|
||||
}
|
||||
foreach ($export['features'][$export_var] as $field) {
|
||||
list($entity_type, $bundle, $field_name) = explode('-', $field);
|
||||
|
||||
foreach ($field_groups as $group_id => $group) {
|
||||
if ($group->entity_type == $entity_type && $group->bundle == $bundle
|
||||
&& in_array($field_name, $group->data['children'])) {
|
||||
$export['features']['field_group'][$group_id] = $group_id;
|
||||
|
||||
if ($group->entity_type == $entity_type && $group->bundle == $bundle && in_array($field_name, $group->data['children']) && !in_array($group->identifier, $export['features']['field_group'])) {
|
||||
if (isset($group->export_module) && $group->export_module != $module_name) {
|
||||
$export['dependencies'][$group->export_module] = $group->export_module;
|
||||
}
|
||||
else {
|
||||
$export['features']['field_group'][$group_id] = $group_id;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//Add any parent field groups that haven't been selected.
|
||||
// Add any parent field groups that haven't been selected.
|
||||
if (!empty($export['features']['field_group'])) {
|
||||
foreach ($export['features']['field_group'] as $id) {
|
||||
$group = isset($field_groups[$id]) ? $field_groups[$id] : FALSE;
|
||||
@@ -37,7 +54,12 @@ function field_group_features_export_alter(&$export, $module_name) {
|
||||
$parent_group = isset($field_groups[$parent_id]) ? $field_groups[$parent_id] : FALSE;
|
||||
|
||||
if ($parent_group && !isset($export['features']['field_group'][$parent_id])) {
|
||||
$export['features']['field_group'][$parent_id] = $parent_id;
|
||||
if (isset($parent_group->export_module) && $parent_group->export_module != $module_name) {
|
||||
$export['dependencies'][$parent_group->export_module] = $parent_group->export_module;
|
||||
}
|
||||
else {
|
||||
$export['features']['field_group'][$parent_id] = $parent_id;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Executable → Regular
Executable → Regular
+1013
-815
File diff suppressed because it is too large
Load Diff
Executable → Regular
@@ -1,18 +1,14 @@
|
||||
name = Fieldgroup
|
||||
description = Fieldgroup
|
||||
name = Field Group
|
||||
description = "Provides the ability to group your fields on both form and display."
|
||||
package = Fields
|
||||
dependencies[] = field
|
||||
dependencies[] = ctools
|
||||
core = 7.x
|
||||
files[] = field_group.install
|
||||
files[] = field_group.module
|
||||
files[] = field_group.field_ui.inc
|
||||
files[] = field_group.form.inc
|
||||
files[] = field_group.features.inc
|
||||
files[] = field_group.test
|
||||
; Information added by drupal.org packaging script on 2011-10-19
|
||||
version = "7.x-1.1"
|
||||
files[] = tests/field_group.ui.test
|
||||
files[] = tests/field_group.display.test
|
||||
; Information added by Drupal.org packaging script on 2017-11-03
|
||||
version = "7.x-1.6"
|
||||
core = "7.x"
|
||||
project = "field_group"
|
||||
datestamp = "1319051133"
|
||||
datestamp = "1509751991"
|
||||
|
||||
|
||||
Executable → Regular
+138
-2
@@ -17,6 +17,7 @@ function field_group_schema() {
|
||||
'key' => 'identifier',
|
||||
'identifier' => 'field_group',
|
||||
'default hook' => 'field_group_info',
|
||||
'load callback' => 'field_group_group_load_by_identifier',
|
||||
'save callback' => 'field_group_group_save',
|
||||
'delete callback' => 'field_group_group_export_delete',
|
||||
'can disable' => TRUE,
|
||||
@@ -108,14 +109,14 @@ function _field_group_install_read_groups() {
|
||||
->condition('group_type', 'standard');
|
||||
foreach ($query->execute() as $record) {
|
||||
$record['settings'] = unserialize($record['settings']);
|
||||
$groups[$record['group_name']] = $record;
|
||||
$groups[$record['group_name'] . '-' . $record['type_name']] = $record;
|
||||
}
|
||||
foreach ($groups as $key => $group) {
|
||||
$query2 = db_select('content_group_fields', 'cgf', array('fetch' => PDO::FETCH_ASSOC))
|
||||
->fields('cgf')
|
||||
->condition('group_name', $group['group_name']);
|
||||
foreach ($query2->execute() as $field) {
|
||||
$groups[$field['group_name']]['children'][] = $field;
|
||||
$groups[$field['group_name'] . '-' . $field['type_name']]['children'][] = $field;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -216,6 +217,8 @@ function field_group_update_7001() {
|
||||
'default' => '',
|
||||
'description' => 'The unique string identifier for a group.',
|
||||
));
|
||||
// Force drupal's schema to be rebuilt
|
||||
drupal_get_schema('field_group', TRUE);
|
||||
|
||||
module_load_include('module', 'field_group');
|
||||
_field_group_recreate_identifiers();
|
||||
@@ -227,6 +230,8 @@ function field_group_update_7001() {
|
||||
->condition('name', 'field_group')
|
||||
->execute();
|
||||
|
||||
// Clear drupal and static cache.
|
||||
field_group_info_groups(NULL, NULL, NULL, TRUE);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -244,6 +249,8 @@ function field_group_update_7002() {
|
||||
// See http://drupal.org/node/1018550.
|
||||
_field_group_recreate_identifiers();
|
||||
|
||||
// Clear drupal and static cache.
|
||||
field_group_info_groups(NULL, NULL, NULL, TRUE);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -255,4 +262,133 @@ function field_group_update_7003() {
|
||||
module_load_include('module', 'field_group');
|
||||
_field_group_recreate_identifiers();
|
||||
|
||||
// Clear drupal and static cache.
|
||||
field_group_info_groups(NULL, NULL, NULL, TRUE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update hook to make sure identifier is set as unique key.
|
||||
*/
|
||||
function field_group_update_7004() {
|
||||
db_drop_unique_key('field_group', 'identifier');
|
||||
db_add_unique_key('field_group', 'identifier', array('identifier'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks all existing groups and removes optional HTML classes
|
||||
* while adding them as extra classes.
|
||||
*/
|
||||
function field_group_update_7005() {
|
||||
|
||||
// Migrate the field groups so they have a unique identifier.
|
||||
$result = db_select('field_group', 'fg')
|
||||
->fields('fg')
|
||||
->execute();
|
||||
$rows = array();
|
||||
foreach($result as $row) {
|
||||
//$row->identifier = $row->group_name . '|' . $row->entity_type . '|' . $row->bundle . '|' . $row->mode;
|
||||
$row->data = unserialize($row->data);
|
||||
$classes = explode(" ", $row->data['format_settings']['instance_settings']['classes']);
|
||||
$optional_classes = array(str_replace("_", "-", $row->group_name), 'field-group-' . $row->data['format_type']);
|
||||
foreach ($optional_classes as $optional_class) {
|
||||
if (!in_array($optional_class, $classes)) {
|
||||
$classes[] = $optional_class;
|
||||
}
|
||||
}
|
||||
$row->data['format_settings']['instance_settings']['classes'] = implode(" ", $classes);
|
||||
$rows[] = $row;
|
||||
}
|
||||
foreach ($rows as $row) {
|
||||
drupal_write_record('field_group', $row, array('id'));
|
||||
}
|
||||
|
||||
// Clear drupal and static cache.
|
||||
field_group_info_groups(NULL, NULL, NULL, TRUE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Save all optional HTML classes for fieldgroups located in features.
|
||||
* If you need the optional classes, recreate feature after this update.
|
||||
* If not, you can revert it.
|
||||
*/
|
||||
function field_group_update_7006() {
|
||||
ctools_include("export");
|
||||
// Migrate the field groups so they have a unique identifier.
|
||||
$field_groups = ctools_export_load_object("field_group");
|
||||
foreach ($field_groups as $row) {
|
||||
|
||||
// Only update feature field_groups this time.
|
||||
// Don't touch the fieldgroups in db.
|
||||
|
||||
if ($row->export_type == EXPORT_IN_CODE) {
|
||||
$classes = array();
|
||||
|
||||
if (isset($row->data['format_settings'], $row->data['format_settings']['instance_settings'], $row->data['format_settings']['instance_settings']['classes'])) {
|
||||
$classes = explode(" ", $row->data['format_settings']['instance_settings']['classes']);
|
||||
}
|
||||
|
||||
$optional_classes = array(str_replace("_", "-", $row->group_name), 'field-group-' . $row->data['format_type']);
|
||||
foreach ($optional_classes as $optional_class) {
|
||||
if (!in_array($optional_class, $classes)) {
|
||||
$classes[] = $optional_class;
|
||||
}
|
||||
}
|
||||
$row->data['format_settings']['instance_settings']['classes'] = implode(" ", $classes);
|
||||
unset($row->id);
|
||||
drupal_write_record('field_group', $row);
|
||||
}
|
||||
}
|
||||
|
||||
// Clear drupal and static cache.
|
||||
field_group_info_groups(NULL, NULL, NULL, TRUE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Id attributes are now a setting. This update will insert the old id in the setting so old
|
||||
* markup doesn't get broken. If you don't want an attribute, you can delete
|
||||
* them on the fieldgroup settings.
|
||||
*/
|
||||
function field_group_update_7007() {
|
||||
|
||||
ctools_include("export");
|
||||
|
||||
// Migrate the field groups so they have a unique identifier.
|
||||
$field_groups = ctools_export_load_object("field_group");
|
||||
foreach ($field_groups as $row) {
|
||||
// These field group types have an ID setting to populate.
|
||||
$populate_id_setting = array(
|
||||
'html-element',
|
||||
'div',
|
||||
'html5',
|
||||
'fieldset',
|
||||
'tabs',
|
||||
'htabs',
|
||||
'accordion',
|
||||
);
|
||||
|
||||
if (in_array($row->data['format_type'], $populate_id_setting)) {
|
||||
// If mode is default, we don't know what view mode it was. Take full then.
|
||||
$view_mode = $row->mode == 'default' ? 'full' : $row->mode;
|
||||
$id = $row->entity_type . '_' . $row->bundle . '_' . $view_mode . '_' . $row->group_name;
|
||||
$row->data['format_settings']['instance_settings']['id'] = $id;
|
||||
}
|
||||
|
||||
if ($row->export_type == EXPORT_IN_CODE) {
|
||||
unset($row->id);
|
||||
drupal_write_record('field_group', $row);
|
||||
}
|
||||
else {
|
||||
drupal_write_record('field_group', $row, array('id'));
|
||||
}
|
||||
}
|
||||
|
||||
// Clear drupal and static cache.
|
||||
field_group_info_groups(NULL, NULL, NULL, TRUE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear cache to notice the CTools load callback.
|
||||
*/
|
||||
function field_group_update_7008() {
|
||||
drupal_flush_all_caches();
|
||||
}
|
||||
Executable → Regular
+54
-9
@@ -41,9 +41,17 @@ Drupal.FieldGroup.Effects.processAccordion = {
|
||||
$('div.field-group-accordion-wrapper', context).once('fieldgroup-effects', function () {
|
||||
var wrapper = $(this);
|
||||
|
||||
// Get the index to set active.
|
||||
var active_index = false;
|
||||
wrapper.find('.accordion-item').each(function(i) {
|
||||
if ($(this).hasClass('field-group-accordion-active')) {
|
||||
active_index = i;
|
||||
}
|
||||
});
|
||||
|
||||
wrapper.accordion({
|
||||
autoHeight: false,
|
||||
active: '.field-group-accordion-active',
|
||||
heightStyle: "content",
|
||||
active: active_index,
|
||||
collapsible: true,
|
||||
changestart: function(event, ui) {
|
||||
if ($(this).hasClass('effect-none')) {
|
||||
@@ -56,17 +64,29 @@ Drupal.FieldGroup.Effects.processAccordion = {
|
||||
});
|
||||
|
||||
if (type == 'form') {
|
||||
|
||||
var $firstErrorItem = false;
|
||||
|
||||
// Add required fields mark to any element containing required fields
|
||||
wrapper.find('div.accordion-item').each(function(i){
|
||||
wrapper.find('div.field-group-accordion-item').each(function(i) {
|
||||
|
||||
if ($(this).is('.required-fields') && $(this).find('.form-required').length > 0) {
|
||||
$('h3.ui-accordion-header').eq(i).append(' ').append($('.form-required').eq(0).clone());
|
||||
$('h3.ui-accordion-header a').eq(i).append(' ').append($('.form-required').eq(0).clone());
|
||||
}
|
||||
if ($('.error', $(this)).length) {
|
||||
// Save first error item, for focussing it.
|
||||
if (!$firstErrorItem) {
|
||||
$firstErrorItem = $(this).parent().accordion("activate" , i);
|
||||
}
|
||||
$('h3.ui-accordion-header').eq(i).addClass('error');
|
||||
var activeOne = $(this).parent().accordion("activate" , i);
|
||||
$('.ui-accordion-content-active', activeOne).css({height: 'auto', width: 'auto', display: 'block'});
|
||||
}
|
||||
});
|
||||
|
||||
// Save first error item, for focussing it.
|
||||
if (!$firstErrorItem) {
|
||||
$('.ui-accordion-content-active', $firstErrorItem).css({height: 'auto', width: 'auto', display: 'block'});
|
||||
}
|
||||
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -99,6 +119,9 @@ Drupal.FieldGroup.Effects.processHtabs = {
|
||||
Drupal.FieldGroup.Effects.processTabs = {
|
||||
execute: function (context, settings, type) {
|
||||
if (type == 'form') {
|
||||
|
||||
var errorFocussed = false;
|
||||
|
||||
// Add required fields mark to any fieldsets containing required fields
|
||||
$('fieldset.vertical-tabs-pane', context).once('fieldgroup-effects', function(i) {
|
||||
if ($(this).is('.required-fields') && $(this).find('.form-required').length > 0) {
|
||||
@@ -106,8 +129,12 @@ Drupal.FieldGroup.Effects.processTabs = {
|
||||
}
|
||||
if ($('.error', $(this)).length) {
|
||||
$(this).data('verticalTab').link.parent().addClass('error');
|
||||
Drupal.FieldGroup.setGroupWithfocus($(this));
|
||||
$(this).data('verticalTab').focus();
|
||||
// Focus the first tab with error.
|
||||
if (!errorFocussed) {
|
||||
Drupal.FieldGroup.setGroupWithfocus($(this));
|
||||
$(this).data('verticalTab').focus();
|
||||
errorFocussed = true;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -171,6 +198,7 @@ Drupal.FieldGroup.Effects.processDiv = {
|
||||
*/
|
||||
Drupal.behaviors.fieldGroup = {
|
||||
attach: function (context, settings) {
|
||||
settings.field_group = settings.field_group || Drupal.settings.field_group;
|
||||
if (settings.field_group == undefined) {
|
||||
return;
|
||||
}
|
||||
@@ -189,7 +217,24 @@ Drupal.behaviors.fieldGroup = {
|
||||
$('.fieldset-wrapper .fieldset > legend').css({display: 'block'});
|
||||
$('.vertical-tabs fieldset.fieldset').addClass('default-fallback');
|
||||
|
||||
// Add a new ID to each fieldset.
|
||||
$('.group-wrapper .horizontal-tabs-panes > fieldset', context).once('group-wrapper-panes-processed', function() {
|
||||
// Tats bad, but we have to keep the actual id to prevent layouts to break.
|
||||
var fieldgroupID = 'field_group-' + $(this).attr('id');
|
||||
$(this).attr('id', fieldgroupID);
|
||||
});
|
||||
// Set the hash in url to remember last userselection.
|
||||
$('.group-wrapper ul li').once('group-wrapper-ul-processed', function() {
|
||||
var fieldGroupNavigationListIndex = $(this).index();
|
||||
$(this).children('a').click(function() {
|
||||
var fieldset = $('.group-wrapper fieldset').get(fieldGroupNavigationListIndex);
|
||||
// Grab the first id, holding the wanted hashurl.
|
||||
var hashUrl = $(fieldset).attr('id').replace(/^field_group-/, '').split(' ')[0];
|
||||
window.location.hash = hashUrl;
|
||||
});
|
||||
});
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
})(jQuery);
|
||||
})(jQuery);
|
||||
|
||||
Executable → Regular
+694
-154
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,15 @@
|
||||
div.horizontal-tabs {
|
||||
margin: 0 0 1em 0;
|
||||
}
|
||||
|
||||
.horizontal-tabs ul.horizontal-tabs-list {
|
||||
border-right: 0;
|
||||
border-left: 1px solid #dedede;
|
||||
}
|
||||
|
||||
/* Layout of each tab */
|
||||
.horizontal-tabs ul.horizontal-tabs-list li {
|
||||
border-right: 0;
|
||||
border-left: 1px solid #ccc;
|
||||
float: right;
|
||||
}
|
||||
Executable → Regular
+14
-11
@@ -1,5 +1,3 @@
|
||||
@CHARSET "UTF-8";
|
||||
|
||||
div.horizontal-tabs {
|
||||
margin: 0 0 1em 0; /* LTR */
|
||||
padding: 0;
|
||||
@@ -16,7 +14,7 @@ div.horizontal-tabs {
|
||||
list-style: none;
|
||||
list-style-image: none; /* IE6 */
|
||||
background-color: #dedede;
|
||||
border-right: 1px solid #dedede;
|
||||
border-right: 1px solid #dedede; /* LTR */
|
||||
width: 100%;
|
||||
height: auto;
|
||||
clear: both;
|
||||
@@ -27,19 +25,20 @@ div.horizontal-tabs {
|
||||
border: 0;
|
||||
}
|
||||
|
||||
.horizontal-tabs-pane>legend {
|
||||
fieldset.horizontal-tabs-pane > legend,
|
||||
fieldset.vertical-tabs-pane fieldset.horizontal-tabs-pane > legend {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* Layout of each tab */
|
||||
.horizontal-tabs ul.horizontal-tabs-list li {
|
||||
background: #eee;
|
||||
border-right: 1px solid #ccc;
|
||||
border-right: 1px solid #ccc; /* LTR */
|
||||
padding: 1px;
|
||||
padding-top: 0;
|
||||
padding-top: 0;
|
||||
margin: 0;
|
||||
min-width: 5em; /* IE7 */
|
||||
float: left;
|
||||
float: left; /* LTR */
|
||||
}
|
||||
.horizontal-tabs ul.horizontal-tabs-list li.selected {
|
||||
background-color: #fff;
|
||||
@@ -58,6 +57,9 @@ div.horizontal-tabs {
|
||||
.horizontal-tabs ul.horizontal-tabs-list li:focus {
|
||||
background-color: #ddd;
|
||||
}
|
||||
.horizontal-tabs ul.horizontal-tabs-list :focus {
|
||||
outline: none;
|
||||
}
|
||||
.horizontal-tabs ul.horizontal-tabs-list li a:focus strong,
|
||||
.horizontal-tabs ul.horizontal-tabs-list li a:active strong,
|
||||
.horizontal-tabs ul.horizontal-tabs-list li a:hover strong {
|
||||
@@ -70,7 +72,7 @@ div.horizontal-tabs {
|
||||
text-decoration: none;
|
||||
padding: 0.5em 0.6em 0.3em 0.6em;
|
||||
position:relative;
|
||||
top: 0px;
|
||||
top: 0px;
|
||||
}
|
||||
.horizontal-tabs ul.horizontal-tabs-list .selected strong {
|
||||
color: #000;
|
||||
@@ -84,9 +86,9 @@ div.horizontal-tabs {
|
||||
}
|
||||
|
||||
/**
|
||||
* tab content
|
||||
* tab content
|
||||
*/
|
||||
div.field-group-htabs-wrapper .field-group-format-wrapper {
|
||||
div.field-group-htabs-wrapper .field-group-format-wrapper {
|
||||
clear: both;
|
||||
padding: 0 0 0.6em;
|
||||
}
|
||||
@@ -95,4 +97,5 @@ div.field-group-htabs-wrapper .field-group-format-wrapper {
|
||||
display: block;
|
||||
position: absolute;
|
||||
top: -100000px;
|
||||
}
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
Executable → Regular
+206
-205
@@ -1,205 +1,206 @@
|
||||
(function ($) {
|
||||
|
||||
/**
|
||||
* This script transforms a set of fieldsets into a stack of horizontal
|
||||
* tabs. Another tab pane can be selected by clicking on the respective
|
||||
* tab.
|
||||
*
|
||||
* Each tab may have a summary which can be updated by another
|
||||
* script. For that to work, each fieldset has an associated
|
||||
* 'horizontalTabCallback' (with jQuery.data() attached to the fieldset),
|
||||
* which is called every time the user performs an update to a form
|
||||
* element inside the tab pane.
|
||||
*/
|
||||
Drupal.behaviors.horizontalTabs = {
|
||||
attach: function (context) {
|
||||
$('.horizontal-tabs-panes', context).once('horizontal-tabs', function () {
|
||||
var focusID = $(':hidden.horizontal-tabs-active-tab', this).val();
|
||||
var tab_focus;
|
||||
|
||||
// Check if there are some fieldsets that can be converted to horizontal-tabs
|
||||
var $fieldsets = $('> fieldset', this);
|
||||
if ($fieldsets.length == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Create the tab column.
|
||||
var tab_list = $('<ul class="horizontal-tabs-list"></ul>');
|
||||
$(this).wrap('<div class="horizontal-tabs clearfix"></div>').before(tab_list);
|
||||
|
||||
// Transform each fieldset into a tab.
|
||||
$fieldsets.each(function (i) {
|
||||
var horizontal_tab = new Drupal.horizontalTab({
|
||||
title: $('> legend', this).text(),
|
||||
fieldset: $(this)
|
||||
});
|
||||
horizontal_tab.item.addClass('horizontal-tab-button-' + i);
|
||||
tab_list.append(horizontal_tab.item);
|
||||
$(this)
|
||||
.removeClass('collapsible collapsed')
|
||||
.addClass('horizontal-tabs-pane')
|
||||
.data('horizontalTab', horizontal_tab);
|
||||
if (this.id == focusID) {
|
||||
tab_focus = $(this);
|
||||
}
|
||||
});
|
||||
|
||||
$('> li:first', tab_list).addClass('first');
|
||||
$('> li:last', tab_list).addClass('last');
|
||||
|
||||
if (!tab_focus) {
|
||||
// If the current URL has a fragment and one of the tabs contains an
|
||||
// element that matches the URL fragment, activate that tab.
|
||||
if (window.location.hash && $(window.location.hash, this).length) {
|
||||
tab_focus = $(window.location.hash, this).closest('.horizontal-tabs-pane');
|
||||
}
|
||||
else {
|
||||
tab_focus = $('> .horizontal-tabs-pane:first', this);
|
||||
}
|
||||
}
|
||||
if (tab_focus.length) {
|
||||
tab_focus.data('horizontalTab').focus();
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* The horizontal tab object represents a single tab within a tab group.
|
||||
*
|
||||
* @param settings
|
||||
* An object with the following keys:
|
||||
* - title: The name of the tab.
|
||||
* - fieldset: The jQuery object of the fieldset that is the tab pane.
|
||||
*/
|
||||
Drupal.horizontalTab = function (settings) {
|
||||
var self = this;
|
||||
$.extend(this, settings, Drupal.theme('horizontalTab', settings));
|
||||
|
||||
this.link.click(function () {
|
||||
self.focus();
|
||||
return false;
|
||||
});
|
||||
|
||||
// Keyboard events added:
|
||||
// Pressing the Enter key will open the tab pane.
|
||||
this.link.keydown(function(event) {
|
||||
if (event.keyCode == 13) {
|
||||
self.focus();
|
||||
// Set focus on the first input field of the visible fieldset/tab pane.
|
||||
$("fieldset.horizontal-tabs-pane :input:visible:enabled:first").focus();
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
// Pressing the Enter key lets you leave the tab again.
|
||||
this.fieldset.keydown(function(event) {
|
||||
// Enter key should not trigger inside <textarea> to allow for multi-line entries.
|
||||
if (event.keyCode == 13 && event.target.nodeName != "TEXTAREA") {
|
||||
// Set focus on the selected tab button again.
|
||||
$(".horizontal-tab-button.selected a").focus();
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
this.fieldset
|
||||
.bind('summaryUpdated', function () {
|
||||
self.updateSummary();
|
||||
})
|
||||
.trigger('summaryUpdated');
|
||||
};
|
||||
|
||||
Drupal.horizontalTab.prototype = {
|
||||
/**
|
||||
* Displays the tab's content pane.
|
||||
*/
|
||||
focus: function () {
|
||||
this.fieldset
|
||||
.removeClass('horizontal-tab-hidden')
|
||||
.siblings('fieldset.horizontal-tabs-pane')
|
||||
.each(function () {
|
||||
var tab = $(this).data('horizontalTab');
|
||||
tab.fieldset.addClass('horizontal-tab-hidden');
|
||||
tab.item.removeClass('selected');
|
||||
})
|
||||
.end()
|
||||
.siblings(':hidden.horizontal-tabs-active-tab')
|
||||
.val(this.fieldset.attr('id'));
|
||||
this.item.addClass('selected');
|
||||
// Mark the active tab for screen readers.
|
||||
$('#active-horizontal-tab').remove();
|
||||
this.link.append('<span id="active-horizontal-tab" class="element-invisible">' + Drupal.t('(active tab)') + '</span>');
|
||||
},
|
||||
|
||||
/**
|
||||
* Updates the tab's summary.
|
||||
*/
|
||||
updateSummary: function () {
|
||||
this.summary.html(this.fieldset.drupalGetSummary());
|
||||
},
|
||||
|
||||
/**
|
||||
* Shows a horizontal tab pane.
|
||||
*/
|
||||
tabShow: function () {
|
||||
// Display the tab.
|
||||
this.item.removeClass('horizontal-tab-hidden');
|
||||
// Update .first marker for items. We need recurse from parent to retain the
|
||||
// actual DOM element order as jQuery implements sortOrder, but not as public
|
||||
// method.
|
||||
this.item.parent().children('.horizontal-tab-button').removeClass('first')
|
||||
.filter(':visible:first').addClass('first');
|
||||
// Display the fieldset.
|
||||
this.fieldset.removeClass('horizontal-tab-hidden');
|
||||
// Focus this tab.
|
||||
this.focus();
|
||||
return this;
|
||||
},
|
||||
|
||||
/**
|
||||
* Hides a horizontal tab pane.
|
||||
*/
|
||||
tabHide: function () {
|
||||
// Hide this tab.
|
||||
this.item.addClass('horizontal-tab-hidden');
|
||||
// Update .first marker for items. We need recurse from parent to retain the
|
||||
// actual DOM element order as jQuery implements sortOrder, but not as public
|
||||
// method.
|
||||
this.item.parent().children('.horizontal-tab-button').removeClass('first')
|
||||
.filter(':visible:first').addClass('first');
|
||||
// Hide the fieldset.
|
||||
this.fieldset.addClass('horizontal-tab-hidden');
|
||||
// Focus the first visible tab (if there is one).
|
||||
var $firstTab = this.fieldset.siblings('.horizontal-tabs-pane:not(.horizontal-tab-hidden):first');
|
||||
if ($firstTab.length) {
|
||||
$firstTab.data('horizontalTab').focus();
|
||||
}
|
||||
return this;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Theme function for a horizontal tab.
|
||||
*
|
||||
* @param settings
|
||||
* An object with the following keys:
|
||||
* - title: The name of the tab.
|
||||
* @return
|
||||
* This function has to return an object with at least these keys:
|
||||
* - item: The root tab jQuery element
|
||||
* - link: The anchor tag that acts as the clickable area of the tab
|
||||
* (jQuery version)
|
||||
* - summary: The jQuery element that contains the tab summary
|
||||
*/
|
||||
Drupal.theme.prototype.horizontalTab = function (settings) {
|
||||
var tab = {};
|
||||
tab.item = $('<li class="horizontal-tab-button" tabindex="-1"></li>')
|
||||
.append(tab.link = $('<a href="#"></a>')
|
||||
.append(tab.title = $('<strong></strong>').text(settings.title))
|
||||
.append(tab.summary = $('<span class="summary"></span>')
|
||||
)
|
||||
);
|
||||
return tab;
|
||||
};
|
||||
|
||||
})(jQuery);
|
||||
(function ($) {
|
||||
|
||||
/**
|
||||
* This script transforms a set of fieldsets into a stack of horizontal
|
||||
* tabs. Another tab pane can be selected by clicking on the respective
|
||||
* tab.
|
||||
*
|
||||
* Each tab may have a summary which can be updated by another
|
||||
* script. For that to work, each fieldset has an associated
|
||||
* 'horizontalTabCallback' (with jQuery.data() attached to the fieldset),
|
||||
* which is called every time the user performs an update to a form
|
||||
* element inside the tab pane.
|
||||
*/
|
||||
Drupal.behaviors.horizontalTabs = {
|
||||
attach: function (context) {
|
||||
$('.horizontal-tabs-panes', context).once('horizontal-tabs', function () {
|
||||
var focusID = $(':hidden.horizontal-tabs-active-tab', this).val();
|
||||
var tab_focus;
|
||||
|
||||
// Check if there are some fieldsets that can be converted to horizontal-tabs
|
||||
var $fieldsets = $('> fieldset', this);
|
||||
if ($fieldsets.length == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Create the tab column.
|
||||
var tab_list = $('<ul class="horizontal-tabs-list"></ul>');
|
||||
$(this).wrap('<div class="horizontal-tabs clearfix"></div>').before(tab_list);
|
||||
|
||||
// Transform each fieldset into a tab.
|
||||
$fieldsets.each(function (i) {
|
||||
var $legend = $('> legend', this);
|
||||
$('.element-invisible', $legend).remove();
|
||||
var horizontal_tab = new Drupal.horizontalTab({
|
||||
title: $legend.text(),
|
||||
fieldset: $(this)
|
||||
});
|
||||
horizontal_tab.item.addClass('horizontal-tab-button-' + i);
|
||||
tab_list.append(horizontal_tab.item);
|
||||
$(this)
|
||||
.removeClass('collapsible collapsed')
|
||||
.addClass('horizontal-tabs-pane')
|
||||
.data('horizontalTab', horizontal_tab);
|
||||
if (this.id == focusID) {
|
||||
tab_focus = $(this);
|
||||
}
|
||||
});
|
||||
|
||||
$('> li:first', tab_list).addClass('first');
|
||||
$('> li:last', tab_list).addClass('last');
|
||||
|
||||
if (!tab_focus) {
|
||||
// If the current URL has a fragment and one of the tabs contains an
|
||||
// element that matches the URL fragment, activate that tab.
|
||||
var hash = window.location.hash.replace(/[=%;,\/]/g, "");
|
||||
if (hash !== '#' && $(hash, this).length) {
|
||||
tab_focus = $(hash, this).closest('.horizontal-tabs-pane');
|
||||
}
|
||||
else {
|
||||
tab_focus = $('> .horizontal-tabs-pane:first', this);
|
||||
}
|
||||
}
|
||||
if (tab_focus.length) {
|
||||
tab_focus.data('horizontalTab').focus();
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* The horizontal tab object represents a single tab within a tab group.
|
||||
*
|
||||
* @param settings
|
||||
* An object with the following keys:
|
||||
* - title: The name of the tab.
|
||||
* - fieldset: The jQuery object of the fieldset that is the tab pane.
|
||||
*/
|
||||
Drupal.horizontalTab = function (settings) {
|
||||
var self = this;
|
||||
$.extend(this, settings, Drupal.theme('horizontalTab', settings));
|
||||
|
||||
this.link.click(function () {
|
||||
self.focus();
|
||||
return false;
|
||||
});
|
||||
|
||||
// Keyboard events added:
|
||||
// Pressing the Enter key will open the tab pane.
|
||||
this.link.keydown(function(event) {
|
||||
if (event.keyCode == 13) {
|
||||
self.focus();
|
||||
// Set focus on the first input field of the visible fieldset/tab pane.
|
||||
$("fieldset.horizontal-tabs-pane :input:visible:enabled:first").focus();
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
// Only bind update summary on forms.
|
||||
if (this.fieldset.drupalGetSummary) {
|
||||
this.fieldset.bind('summaryUpdated', function() {
|
||||
self.updateSummary();
|
||||
}).trigger('summaryUpdated');
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
Drupal.horizontalTab.prototype = {
|
||||
/**
|
||||
* Displays the tab's content pane.
|
||||
*/
|
||||
focus: function () {
|
||||
this.fieldset
|
||||
.removeClass('horizontal-tab-hidden')
|
||||
.siblings('fieldset.horizontal-tabs-pane')
|
||||
.each(function () {
|
||||
var tab = $(this).data('horizontalTab');
|
||||
tab.fieldset.addClass('horizontal-tab-hidden');
|
||||
tab.item.removeClass('selected');
|
||||
})
|
||||
.end()
|
||||
.siblings(':hidden.horizontal-tabs-active-tab')
|
||||
.val(this.fieldset.attr('id'));
|
||||
this.item.addClass('selected');
|
||||
// Mark the active tab for screen readers.
|
||||
$('#active-horizontal-tab').remove();
|
||||
this.link.append('<span id="active-horizontal-tab" class="element-invisible">' + Drupal.t('(active tab)') + '</span>');
|
||||
},
|
||||
|
||||
/**
|
||||
* Updates the tab's summary.
|
||||
*/
|
||||
updateSummary: function () {
|
||||
this.summary.html(this.fieldset.drupalGetSummary());
|
||||
},
|
||||
|
||||
/**
|
||||
* Shows a horizontal tab pane.
|
||||
*/
|
||||
tabShow: function () {
|
||||
// Display the tab.
|
||||
this.item.removeClass('horizontal-tab-hidden');
|
||||
// Update .first marker for items. We need recurse from parent to retain the
|
||||
// actual DOM element order as jQuery implements sortOrder, but not as public
|
||||
// method.
|
||||
this.item.parent().children('.horizontal-tab-button').removeClass('first')
|
||||
.filter(':visible:first').addClass('first');
|
||||
// Display the fieldset.
|
||||
this.fieldset.removeClass('horizontal-tab-hidden');
|
||||
// Focus this tab.
|
||||
this.focus();
|
||||
return this;
|
||||
},
|
||||
|
||||
/**
|
||||
* Hides a horizontal tab pane.
|
||||
*/
|
||||
tabHide: function () {
|
||||
// Hide this tab.
|
||||
this.item.addClass('horizontal-tab-hidden');
|
||||
// Update .first marker for items. We need recurse from parent to retain the
|
||||
// actual DOM element order as jQuery implements sortOrder, but not as public
|
||||
// method.
|
||||
this.item.parent().children('.horizontal-tab-button').removeClass('first')
|
||||
.filter(':visible:first').addClass('first');
|
||||
// Hide the fieldset.
|
||||
this.fieldset.addClass('horizontal-tab-hidden');
|
||||
// Focus the first visible tab (if there is one).
|
||||
var $firstTab = this.fieldset.siblings('.horizontal-tabs-pane:not(.horizontal-tab-hidden):first');
|
||||
if ($firstTab.length) {
|
||||
$firstTab.data('horizontalTab').focus();
|
||||
}
|
||||
return this;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Theme function for a horizontal tab.
|
||||
*
|
||||
* @param settings
|
||||
* An object with the following keys:
|
||||
* - title: The name of the tab.
|
||||
* @return
|
||||
* This function has to return an object with at least these keys:
|
||||
* - item: The root tab jQuery element
|
||||
* - link: The anchor tag that acts as the clickable area of the tab
|
||||
* (jQuery version)
|
||||
* - summary: The jQuery element that contains the tab summary
|
||||
*/
|
||||
Drupal.theme.prototype.horizontalTab = function (settings) {
|
||||
var tab = {};
|
||||
var idAttr = settings.fieldset.attr('id');
|
||||
|
||||
tab.item = $('<li class="horizontal-tab-button" tabindex="-1"></li>')
|
||||
.append(tab.link = $('<a href="#' + idAttr + '"></a>')
|
||||
.append(tab.title = $('<strong></strong>').text(settings.title))
|
||||
);
|
||||
|
||||
// No need to add summary on frontend.
|
||||
if (settings.fieldset.drupalGetSummary) {
|
||||
tab.link.append(tab.summary = $('<span class="summary"></span>'))
|
||||
}
|
||||
|
||||
return tab;
|
||||
};
|
||||
|
||||
})(jQuery);
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
.multipage-controls-list #edit-actions {
|
||||
float: right !important;
|
||||
}
|
||||
|
||||
.multipage-button {
|
||||
float: right !important;
|
||||
}
|
||||
|
||||
.multipage-counter{
|
||||
float: left !important;
|
||||
margin-right: 0 !important;
|
||||
margin-left: 5px !important;
|
||||
}
|
||||
Executable → Regular
+90
-87
@@ -1,125 +1,128 @@
|
||||
.multipage-controls-list #edit-actions {
|
||||
float: left !important;
|
||||
float: left; /* LTR */
|
||||
}
|
||||
|
||||
.multipage-button {
|
||||
margin-bottom: 1em !important;
|
||||
margin-top: 0 !important;
|
||||
float: left !important;
|
||||
line-height: 36px !important;
|
||||
margin-bottom: 1em;
|
||||
margin-top: 0;
|
||||
float: left; /* LTR */
|
||||
line-height: 36px;
|
||||
}
|
||||
|
||||
.multipage-button a {
|
||||
padding-top: 10px !important;
|
||||
padding-top: 10px;
|
||||
}
|
||||
|
||||
.multipage-counter{
|
||||
float: right !important;
|
||||
margin-right: 5px !important;
|
||||
height: 0 !important;
|
||||
position: relative !important;
|
||||
top: 1.8em !important;
|
||||
line-height: 30px !important;
|
||||
font: 12px arial,sans-serif !important;
|
||||
font-weight: bold !important;
|
||||
color:#666 !important;
|
||||
.multipage-counter {
|
||||
float: right; /* LTR */
|
||||
margin-right: 5px; /* LTR */
|
||||
height: 0;
|
||||
position: relative;
|
||||
top: 1.8em;
|
||||
line-height: 30px;
|
||||
font: 12px arial,sans-serif;
|
||||
font-weight: bold;
|
||||
color:#666;
|
||||
}
|
||||
|
||||
a.multipage-link-previous{
|
||||
font: 12px arial,sans-serif !important;
|
||||
font-weight: bold !important;
|
||||
color:#666 !important;
|
||||
-webkit-transition: color 218ms !important;
|
||||
-moz-transition: color 218ms !important;
|
||||
-o-transition: color 218ms !important;
|
||||
transition: color 218ms !important;
|
||||
a.multipage-link-previous {
|
||||
font: 12px arial,sans-serif;
|
||||
font-weight: bold;
|
||||
color:#666;
|
||||
-webkit-transition: color 218ms;
|
||||
-moz-transition: color 218ms;
|
||||
-o-transition: color 218ms;
|
||||
transition: color 218ms;
|
||||
}
|
||||
|
||||
a.multipage-link-previous:hover{
|
||||
text-decoration:none !important;
|
||||
color: #333 !important;
|
||||
a.multipage-link-previous:hover {
|
||||
text-decoration:none;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.multipage-controls-list input.form-submit{
|
||||
.multipage-controls-list input.form-submit {
|
||||
background:none;
|
||||
border: none;
|
||||
border-radius: 2px !important;
|
||||
-moz-border-radius: 2px !important;
|
||||
-webkit-border-radius: 2px !important;
|
||||
border: 1px solid rgba(0, 0, 0, 0.1) !important;
|
||||
font: 12px arial,sans-serif !important;
|
||||
font-weight: bold !important;
|
||||
color: #666 !important;
|
||||
text-shadow: 0 1px 0 white !important;
|
||||
padding: 7px 12px !important;
|
||||
background: -webkit-gradient(linear,0% 40%,0% 70%,from(whiteSmoke),to(#F1F1F1)) !important;
|
||||
-o-transition: border-top-color 0.218s,border-right-color 0.218s,border-bottom-color 0.218s,border-left-color .218s !important;
|
||||
-webkit-transition: border-color .218s !important;
|
||||
border-radius: 2px;
|
||||
-moz-border-radius: 2px;
|
||||
-webkit-border-radius: 2px;
|
||||
border: 1px solid rgba(0, 0, 0, 0.1);
|
||||
font: 12px arial,sans-serif;
|
||||
font-weight: bold;
|
||||
color: #666;
|
||||
text-shadow: 0 1px 0 white;
|
||||
padding: 7px 12px;
|
||||
background: -webkit-gradient(linear,0% 40%,0% 70%,from(whiteSmoke),to(#F1F1F1));
|
||||
-o-transition: border-top-color 0.218s,border-right-color 0.218s,border-bottom-color 0.218s,border-left-color .218s;
|
||||
-webkit-transition: border-color .218s;
|
||||
}
|
||||
|
||||
.multipage-controls-list input.form-submit:hover{
|
||||
color:#333 !important;
|
||||
box-shadow: 0 1px 1px rgba(0,0,0,0.1) !important;
|
||||
-webkit-box-shadow: 0 1px 1px rgba(0,0,0,0.1) !important;
|
||||
border-color: #939393 !important;
|
||||
.multipage-controls-list input.form-submit:hover {
|
||||
color:#333;
|
||||
box-shadow: 0 1px 1px rgba(0,0,0,0.1);
|
||||
-webkit-box-shadow: 0 1px 1px rgba(0,0,0,0.1);
|
||||
border-color: #939393;
|
||||
}
|
||||
|
||||
.multipage-controls-list input.form-submit:active{
|
||||
background: -webkit-gradient(linear,0% 40%,0% 70%,from(#F1F1F1),to(whiteSmoke)) !important;
|
||||
.multipage-controls-list input.form-submit:active {
|
||||
background: -webkit-gradient(linear,0% 40%,0% 70%,from(#F1F1F1),to(whiteSmoke));
|
||||
}
|
||||
|
||||
.multipage-controls-list input#edit-submit{
|
||||
background-image: -moz-linear-gradient(top,#4d90fe,#4787ed) !important;
|
||||
background-image: -o-linear-gradient(top,#4d90fe,#4787ed) !important;
|
||||
background-image: -webkit-gradient(linear,left top,left bottom,from(#4d90fe),to(#4787ed)) !important;
|
||||
color: white !important;
|
||||
text-shadow: none !important;
|
||||
text-transform: uppercase !important;
|
||||
min-width: 79px !important;
|
||||
.multipage-controls-list input#edit-submit {
|
||||
background: #4D90FE; /* for non-css3 browsers */
|
||||
background-image: #4D90FE; /* for non-css3 browsers */
|
||||
background-image: -o-linear-gradient(top,#4d90fe,#4787ed);
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#4D90FE', endColorstr='#4787ED'); /* for IE */
|
||||
background: -webkit-gradient(linear, center top, center bottom, from(#4D90FE), to(#4787ED)); /* for webkit browsers */
|
||||
background: -moz-linear-gradient(center top, #4D90FE, #4787ED); /* for firefox 3.6+ */
|
||||
color: white;
|
||||
text-shadow: none;
|
||||
text-transform: uppercase;
|
||||
min-width: 79px;
|
||||
}
|
||||
|
||||
.multipage-controls-list input#edit-submit:hover{
|
||||
background-image: -moz-linear-gradient(top,#4d90fe,#357ae8) !important;
|
||||
background-image: -o-linear-gradient(top,#4d90fe,#357ae8) !important;
|
||||
background-image: -webkit-gradient(linear,left top,left bottom,from(#4d90fe),to(#357ae8)) !important;
|
||||
color: white !important;
|
||||
text-shadow: none !important;
|
||||
box-shadow: 0 1px 1px rgba(0,0,0,0.2) !important;
|
||||
-webkit-box-shadow: 0 1px 1px rgba(0,0,0,0.2) !important;
|
||||
.multipage-controls-list input#edit-submit:hover {
|
||||
background-image: -moz-linear-gradient(top,#4d90fe,#357ae8);
|
||||
background-image: -o-linear-gradient(top,#4d90fe,#357ae8);
|
||||
background-image: -webkit-gradient(linear,left top,left bottom,from(#4d90fe),to(#357ae8));
|
||||
color: white;
|
||||
text-shadow: none;
|
||||
box-shadow: 0 1px 1px rgba(0,0,0,0.2);
|
||||
-webkit-box-shadow: 0 1px 1px rgba(0,0,0,0.2);
|
||||
}
|
||||
|
||||
.multipage-controls-list input#edit-submit:active{
|
||||
background: #4D90FE !important;
|
||||
border-color: #2F5BB7 !important;
|
||||
.multipage-controls-list input#edit-submit:active {
|
||||
background: #4D90FE;
|
||||
border-color: #2F5BB7;
|
||||
}
|
||||
|
||||
.multipage-controls-list input#edit-delete{
|
||||
background-image: -moz-linear-gradient(top,#dd4b39,#d14836) !important;
|
||||
background-image: -o-linear-gradient(top,#dd4b39,#d14836) !important;
|
||||
background-image: -webkit-gradient(linear,left top,left bottom,from(#dd4b39),to(#d14836)) !important;
|
||||
text-shadow: 0 1px rgba(0, 0, 0, 0.1) !important;
|
||||
border: 1px solid transparent !important;
|
||||
color: white !important;
|
||||
text-shadow: none !important;
|
||||
.multipage-controls-list input#edit-delete {
|
||||
background-image: -moz-linear-gradient(top,#dd4b39,#d14836);
|
||||
background-image: -o-linear-gradient(top,#dd4b39,#d14836);
|
||||
background-image: -webkit-gradient(linear,left top,left bottom,from(#dd4b39),to(#d14836));
|
||||
text-shadow: 0 1px rgba(0, 0, 0, 0.1);
|
||||
border: 1px solid transparent;
|
||||
color: white;
|
||||
text-shadow: none;
|
||||
|
||||
}
|
||||
|
||||
.multipage-controls-list input#edit-delete:hover{
|
||||
background-image: -moz-linear-gradient(top,#dd4b39,#c53727) !important;
|
||||
background-image: -o-linear-gradient(top,#dd4b39,#c53727) !important;
|
||||
background-image: -webkit-gradient(linear,left top,left bottom,from(#dd4b39),to(#c53727)) !important;
|
||||
.multipage-controls-list input#edit-delete:hover {
|
||||
background-image: -moz-linear-gradient(top,#dd4b39,#c53727);
|
||||
background-image: -o-linear-gradient(top,#dd4b39,#c53727);
|
||||
background-image: -webkit-gradient(linear,left top,left bottom,from(#dd4b39),to(#c53727));
|
||||
border: 1px solid #B0281A!important;
|
||||
border-bottom: 1px solid #AF301F!important;
|
||||
box-shadow: 0 1px 1px rgba(0,0,0,0.2) !important;
|
||||
-webkit-box-shadow: 0 1px 1px rgba(0,0,0,0.2) !important;
|
||||
color: white !important;
|
||||
box-shadow: 0 1px 1px rgba(0,0,0,0.2);
|
||||
-webkit-box-shadow: 0 1px 1px rgba(0,0,0,0.2);
|
||||
color: white;
|
||||
}
|
||||
|
||||
.multipage-controls-list input#edit-delete:active{
|
||||
background-image: -moz-linear-gradient(top,#dd4b39,#b0281a) !important;
|
||||
background-image: -o-linear-gradient(top,#dd4b39,#b0281a) !important;
|
||||
background-image: -webkit-gradient(linear,left top,left bottom,from(#dd4b39),to(#b0281a)) !important;
|
||||
.multipage-controls-list input#edit-delete:active {
|
||||
background-image: -moz-linear-gradient(top,#dd4b39,#b0281a);
|
||||
background-image: -o-linear-gradient(top,#dd4b39,#b0281a);
|
||||
background-image: -webkit-gradient(linear,left top,left bottom,from(#dd4b39),to(#b0281a));
|
||||
border: 1px solid #992A1b!important;
|
||||
box-shadow: 0 1px 2px rgba(0,0,0,0.3) !important;
|
||||
-webkit-box-shadow: 0 1px 2px rgba(0,0,0,0.3) !important;
|
||||
box-shadow: 0 1px 2px rgba(0,0,0,0.3);
|
||||
-webkit-box-shadow: 0 1px 2px rgba(0,0,0,0.3);
|
||||
}
|
||||
Executable → Regular
+269
-241
@@ -1,241 +1,269 @@
|
||||
(function ($) {
|
||||
|
||||
/**
|
||||
* This script transforms a set of wrappers into a stack of multipage pages.
|
||||
* Another pane can be entered by clicking next/previous.
|
||||
*
|
||||
*/
|
||||
Drupal.behaviors.MultiPage = {
|
||||
attach: function (context) {
|
||||
$('.multipage-panes', context).once('multipage', function () {
|
||||
|
||||
var focusID = $(':hidden.multipage-active-control', this).val();
|
||||
var paneWithFocus;
|
||||
|
||||
// Check if there are some wrappers that can be converted to multipages.
|
||||
var $panes = $('> div.field-group-multipage', this);
|
||||
var $form = $panes.parents('form');
|
||||
if ($panes.length == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Create the next/previous controls.
|
||||
var $controls;
|
||||
|
||||
// Transform each div.multipage-pane into a multipage with controls.
|
||||
$panes.each(function () {
|
||||
|
||||
$controls = $('<div class="multipage-controls-list"></div>');
|
||||
$(this).append('<div class="multipage-controls clearfix"></div>').append($controls);
|
||||
|
||||
// Check if the submit button needs to move to the latest pane.
|
||||
if (Drupal.settings.field_group.multipage_move_submit && $('.form-actions').length) {
|
||||
$('.form-actions', $form).remove().appendTo($($controls, $panes.last()));
|
||||
}
|
||||
|
||||
var multipageControl = new Drupal.multipageControl({
|
||||
title: $('> .multipage-pane-title', this).text(),
|
||||
wrapper: $(this),
|
||||
has_next: $(this).next().length,
|
||||
has_previous: $(this).prev().length
|
||||
});
|
||||
|
||||
$controls.append(multipageControl.item);
|
||||
$(this)
|
||||
.addClass('multipage-pane')
|
||||
.data('multipageControl', multipageControl);
|
||||
|
||||
if (this.id == focusID) {
|
||||
paneWithFocus = $(this);
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
if (!paneWithFocus) {
|
||||
// If the current URL has a fragment and one of the tabs contains an
|
||||
// element that matches the URL fragment, activate that tab.
|
||||
if (window.location.hash && $(window.location.hash, this).length) {
|
||||
paneWithFocus = $(window.location.hash, this).closest('.multipage-pane');
|
||||
}
|
||||
else {
|
||||
paneWithFocus = $('multipage-open', this).length ? $('multipage-open', this) : $('> .multipage-pane:first', this);
|
||||
}
|
||||
}
|
||||
if (paneWithFocus.length) {
|
||||
paneWithFocus.data('multipageControl').focus();
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* The multipagePane object represents a single div as a page.
|
||||
*
|
||||
* @param settings
|
||||
* An object with the following keys:
|
||||
* - title: The name of the tab.
|
||||
* - wrapper: The jQuery object of the <div> that is the tab pane.
|
||||
*/
|
||||
Drupal.multipageControl = function (settings) {
|
||||
var self = this;
|
||||
$.extend(this, settings, Drupal.theme('multipage', settings));
|
||||
|
||||
this.nextLink.click(function () {
|
||||
self.nextPage();
|
||||
return false;
|
||||
});
|
||||
|
||||
this.previousLink.click(function () {
|
||||
self.previousPage();
|
||||
return false;
|
||||
});
|
||||
|
||||
/*
|
||||
// Keyboard events added:
|
||||
// Pressing the Enter key will open the tab pane.
|
||||
this.nextLink.keydown(function(event) {
|
||||
if (event.keyCode == 13) {
|
||||
self.focus();
|
||||
// Set focus on the first input field of the visible wrapper/tab pane.
|
||||
$("div.multipage-pane :input:visible:enabled:first").focus();
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
// Pressing the Enter key lets you leave the tab again.
|
||||
this.wrapper.keydown(function(event) {
|
||||
// Enter key should not trigger inside <textarea> to allow for multi-line entries.
|
||||
if (event.keyCode == 13 && event.target.nodeName != "TEXTAREA") {
|
||||
// Set focus on the selected tab button again.
|
||||
$(".multipage-tab-button.selected a").focus();
|
||||
return false;
|
||||
}
|
||||
});
|
||||
*/
|
||||
};
|
||||
|
||||
Drupal.multipageControl.prototype = {
|
||||
|
||||
/**
|
||||
* Displays the tab's content pane.
|
||||
*/
|
||||
focus: function () {
|
||||
this.wrapper
|
||||
.show()
|
||||
.siblings('div.multipage-pane')
|
||||
.each(function () {
|
||||
var tab = $(this).data('multipageControl');
|
||||
tab.wrapper.hide();
|
||||
})
|
||||
.end()
|
||||
.siblings(':hidden.multipage-active-control')
|
||||
.val(this.wrapper.attr('id'));
|
||||
// Mark the active control for screen readers.
|
||||
$('#active-multipage-control').remove();
|
||||
this.nextLink.append('<span id="active-multipage-control" class="element-invisible">' + Drupal.t('(active page)') + '</span>');
|
||||
},
|
||||
|
||||
/**
|
||||
* Continues to the next page or step in the form.
|
||||
*/
|
||||
nextPage: function () {
|
||||
this.wrapper.next().data('multipageControl').focus();
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns to the previous page or step in the form.
|
||||
*/
|
||||
previousPage: function () {
|
||||
this.wrapper.prev().data('multipageControl').focus();
|
||||
},
|
||||
|
||||
/**
|
||||
* Shows a horizontal tab pane.
|
||||
*/
|
||||
tabShow: function () {
|
||||
// Display the tab.
|
||||
this.item.show();
|
||||
// Update .first marker for items. We need recurse from parent to retain the
|
||||
// actual DOM element order as jQuery implements sortOrder, but not as public
|
||||
// method.
|
||||
this.item.parent().children('.multipage-control').removeClass('first')
|
||||
.filter(':visible:first').addClass('first');
|
||||
// Display the wrapper.
|
||||
this.wrapper.removeClass('multipage-control-hidden').show();
|
||||
// Focus this tab.
|
||||
this.focus();
|
||||
return this;
|
||||
},
|
||||
|
||||
/**
|
||||
* Hides a horizontal tab pane.
|
||||
*/
|
||||
tabHide: function () {
|
||||
// Hide this tab.
|
||||
this.item.hide();
|
||||
// Update .first marker for items. We need recurse from parent to retain the
|
||||
// actual DOM element order as jQuery implements sortOrder, but not as public
|
||||
// method.
|
||||
this.item.parent().children('.multipage-control').removeClass('first')
|
||||
.filter(':visible:first').addClass('first');
|
||||
// Hide the wrapper.
|
||||
this.wrapper.addClass('horizontal-tab-hidden').hide();
|
||||
// Focus the first visible tab (if there is one).
|
||||
var $firstTab = this.wrapper.siblings('.multipage-pane:not(.multipage-control-hidden):first');
|
||||
if ($firstTab.length) {
|
||||
$firstTab.data('multipageControl').focus();
|
||||
}
|
||||
return this;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Theme function for a multipage control.
|
||||
*
|
||||
* @param settings
|
||||
* An object with the following keys:
|
||||
* - title: The name of the tab.
|
||||
* @return
|
||||
* This function has to return an object with at least these keys:
|
||||
* - item: The root tab jQuery element
|
||||
* - nextLink: The anchor tag that acts as the clickable area of the control
|
||||
* - nextTitle: The jQuery element that contains the group title
|
||||
* - previousLink: The anchor tag that acts as the clickable area of the control
|
||||
* - previousTitle: The jQuery element that contains the group title
|
||||
*/
|
||||
Drupal.theme.prototype.multipage = function (settings) {
|
||||
var controls = {};
|
||||
controls.item = $('<span class="multipage-button"></span>');
|
||||
controls.item.append(controls.nextLink = $('<input type="button" class="form-submit multipage-link-next" value="" />').val(controls.nextTitle = Drupal.t('Next page')));
|
||||
controls.item.append(controls.previousLink = $('<a class="multipage-link-previous" href="#"></a>'));
|
||||
if (!settings.has_next) {
|
||||
controls.nextLink.hide();
|
||||
}
|
||||
if (settings.has_previous) {
|
||||
controls.previousLink.append(controls.previousTitle = $('<strong></strong>').text(Drupal.t('Previous')));
|
||||
}
|
||||
return controls;
|
||||
};
|
||||
|
||||
|
||||
Drupal.FieldGroup = Drupal.FieldGroup || {};
|
||||
Drupal.FieldGroup.Effects = Drupal.FieldGroup.Effects || {};
|
||||
|
||||
/**
|
||||
* Implements Drupal.FieldGroup.processHook().
|
||||
*/
|
||||
Drupal.FieldGroup.Effects.processMultipage = {
|
||||
execute: function (context, settings, type) {
|
||||
if (type == 'form') {
|
||||
// Add required fields mark to any element containing required fields
|
||||
$('div.multipage-pane').each(function(i){
|
||||
if ($('.error', $(this)).length) {
|
||||
Drupal.FieldGroup.setGroupWithfocus($(this));
|
||||
$(this).data('multipageControl').focus();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
})(jQuery);
|
||||
(function ($) {
|
||||
|
||||
/**
|
||||
* This script transforms a set of wrappers into a stack of multipage pages.
|
||||
* Another pane can be entered by clicking next/previous.
|
||||
*
|
||||
*/
|
||||
Drupal.behaviors.MultiPage = {
|
||||
attach: function (context) {
|
||||
$('.multipage-panes', context).once('multipage', function () {
|
||||
|
||||
var focusID = $(':hidden.multipage-active-control', this).val();
|
||||
var paneWithFocus;
|
||||
|
||||
// Check if there are some wrappers that can be converted to multipages.
|
||||
var $panes = $('> div.field-group-multipage', this);
|
||||
var $form = $panes.parents('form');
|
||||
if ($panes.length == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Create the next/previous controls.
|
||||
var $controls;
|
||||
|
||||
// Transform each div.multipage-pane into a multipage with controls.
|
||||
$panes.each(function () {
|
||||
|
||||
$controls = $('<div class="multipage-controls-list clearfix"></div>');
|
||||
$(this).append($controls);
|
||||
|
||||
// Check if the submit button needs to move to the latest pane.
|
||||
if (Drupal.settings.field_group.multipage_move_submit && $('.form-actions').length) {
|
||||
$('.form-actions', $form).remove().appendTo($($controls, $panes.last()));
|
||||
}
|
||||
|
||||
var multipageControl = new Drupal.multipageControl({
|
||||
title: $('> .multipage-pane-title', this).text(),
|
||||
wrapper: $(this),
|
||||
has_next: $(this).next().length,
|
||||
has_previous: $(this).prev().length
|
||||
});
|
||||
|
||||
$controls.append(multipageControl.item);
|
||||
$(this)
|
||||
.addClass('multipage-pane')
|
||||
.data('multipageControl', multipageControl);
|
||||
|
||||
if (this.id == focusID) {
|
||||
paneWithFocus = $(this);
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
if (paneWithFocus === undefined) {
|
||||
// If the current URL has a fragment and one of the tabs contains an
|
||||
// element that matches the URL fragment, activate that tab.
|
||||
var hash = window.location.hash.replace(/[=%;,\/]/g, "");
|
||||
if (hash !== '#' && $(hash, this).length) {
|
||||
paneWithFocus = $(window.location.hash, this).closest('.multipage-pane');
|
||||
}
|
||||
else {
|
||||
paneWithFocus = $('multipage-open', this).length ? $('multipage-open', this) : $('> .multipage-pane:first', this);
|
||||
}
|
||||
}
|
||||
if (paneWithFocus !== undefined) {
|
||||
paneWithFocus.data('multipageControl').focus();
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* The multipagePane object represents a single div as a page.
|
||||
*
|
||||
* @param settings
|
||||
* An object with the following keys:
|
||||
* - title: The name of the tab.
|
||||
* - wrapper: The jQuery object of the <div> that is the tab pane.
|
||||
*/
|
||||
Drupal.multipageControl = function (settings) {
|
||||
var self = this;
|
||||
var controls = Drupal.theme('multipage', settings);
|
||||
$.extend(self, settings, controls);
|
||||
|
||||
this.nextLink.click(function (e) {
|
||||
e.preventDefault();
|
||||
self.nextPage();
|
||||
});
|
||||
|
||||
this.previousLink.click(function (e) {
|
||||
e.preventDefault();
|
||||
self.previousPage();
|
||||
});
|
||||
|
||||
/*
|
||||
// Keyboard events added:
|
||||
// Pressing the Enter key will open the tab pane.
|
||||
this.nextLink.keydown(function(event) {
|
||||
if (event.keyCode == 13) {
|
||||
self.focus();
|
||||
// Set focus on the first input field of the visible wrapper/tab pane.
|
||||
$("div.multipage-pane :input:visible:enabled:first").focus();
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
// Pressing the Enter key lets you leave the tab again.
|
||||
this.wrapper.keydown(function(event) {
|
||||
// Enter key should not trigger inside <textarea> to allow for multi-line entries.
|
||||
if (event.keyCode == 13 && event.target.nodeName != "TEXTAREA") {
|
||||
// Set focus on the selected tab button again.
|
||||
$(".multipage-tab-button.selected a").focus();
|
||||
return false;
|
||||
}
|
||||
});
|
||||
*/
|
||||
};
|
||||
|
||||
Drupal.multipageControl.prototype = {
|
||||
|
||||
/**
|
||||
* Displays the tab's content pane.
|
||||
*/
|
||||
focus: function () {
|
||||
this.wrapper
|
||||
.show()
|
||||
.siblings('div.multipage-pane')
|
||||
.each(function () {
|
||||
var tab = $(this).data('multipageControl');
|
||||
tab.wrapper.hide();
|
||||
})
|
||||
.end()
|
||||
.siblings(':hidden.multipage-active-control')
|
||||
.val(this.wrapper.attr('id'));
|
||||
// Mark the active control for screen readers.
|
||||
$('#active-multipage-control').remove();
|
||||
this.nextLink.after('<span id="active-multipage-control" class="element-invisible">' + Drupal.t('(active page)') + '</span>');
|
||||
},
|
||||
|
||||
/**
|
||||
* Continues to the next page or step in the form.
|
||||
*/
|
||||
nextPage: function () {
|
||||
this.wrapper.next().data('multipageControl').focus();
|
||||
$('html, body').scrollTop(this.wrapper.parents('.field-group-multipage-group-wrapper').offset().top);
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns to the previous page or step in the form.
|
||||
*/
|
||||
previousPage: function () {
|
||||
this.wrapper.prev().data('multipageControl').focus();
|
||||
$('html, body').scrollTop(this.wrapper.parents('.field-group-multipage-group-wrapper').offset().top);
|
||||
},
|
||||
|
||||
/**
|
||||
* Shows a horizontal tab pane.
|
||||
*/
|
||||
tabShow: function () {
|
||||
// Display the tab.
|
||||
this.item.show();
|
||||
// Update .first marker for items. We need recurse from parent to retain the
|
||||
// actual DOM element order as jQuery implements sortOrder, but not as public
|
||||
// method.
|
||||
this.item.parent().children('.multipage-control').removeClass('first')
|
||||
.filter(':visible:first').addClass('first');
|
||||
// Display the wrapper.
|
||||
this.wrapper.removeClass('multipage-control-hidden').show();
|
||||
// Focus this tab.
|
||||
this.focus();
|
||||
return this;
|
||||
},
|
||||
|
||||
/**
|
||||
* Hides a horizontal tab pane.
|
||||
*/
|
||||
tabHide: function () {
|
||||
// Hide this tab.
|
||||
this.item.hide();
|
||||
// Update .first marker for items. We need recurse from parent to retain the
|
||||
// actual DOM element order as jQuery implements sortOrder, but not as public
|
||||
// method.
|
||||
this.item.parent().children('.multipage-control').removeClass('first')
|
||||
.filter(':visible:first').addClass('first');
|
||||
// Hide the wrapper.
|
||||
this.wrapper.addClass('horizontal-tab-hidden').hide();
|
||||
// Focus the first visible tab (if there is one).
|
||||
var $firstTab = this.wrapper.siblings('.multipage-pane:not(.multipage-control-hidden):first');
|
||||
if ($firstTab.length) {
|
||||
$firstTab.data('multipageControl').focus();
|
||||
}
|
||||
return this;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Theme function for a multipage control.
|
||||
*
|
||||
* @param settings
|
||||
* An object with the following keys:
|
||||
* - title: The name of the tab.
|
||||
* @return
|
||||
* This function has to return an object with at least these keys:
|
||||
* - item: The root tab jQuery element
|
||||
* - nextLink: The anchor tag that acts as the clickable area of the control
|
||||
* - nextTitle: The jQuery element that contains the group title
|
||||
* - previousLink: The anchor tag that acts as the clickable area of the control
|
||||
* - previousTitle: The jQuery element that contains the group title
|
||||
*/
|
||||
Drupal.theme.prototype.multipage = function (settings) {
|
||||
|
||||
var controls = {};
|
||||
controls.item = $('<span class="multipage-button"></span>');
|
||||
|
||||
controls.previousLink = $('<input type="button" class="form-submit multipage-link-previous" value="" />');
|
||||
controls.previousTitle = Drupal.t('Previous page');
|
||||
controls.item.append(controls.previousLink.val(controls.previousTitle));
|
||||
|
||||
controls.nextLink = $('<input type="button" class="form-submit multipage-link-next" value="" />');
|
||||
controls.nextTitle = Drupal.t('Next page');
|
||||
controls.item.append(controls.nextLink.val(controls.nextTitle));
|
||||
|
||||
if (!settings.has_next) {
|
||||
controls.nextLink.hide();
|
||||
}
|
||||
if (!settings.has_previous) {
|
||||
controls.previousLink.hide();
|
||||
}
|
||||
|
||||
return controls;
|
||||
};
|
||||
|
||||
|
||||
Drupal.FieldGroup = Drupal.FieldGroup || {};
|
||||
Drupal.FieldGroup.Effects = Drupal.FieldGroup.Effects || {};
|
||||
|
||||
/**
|
||||
* Implements Drupal.FieldGroup.processHook().
|
||||
*/
|
||||
Drupal.FieldGroup.Effects.processMultipage = {
|
||||
execute: function (context, settings, type) {
|
||||
if (type == 'form') {
|
||||
|
||||
var $firstErrorItem = false;
|
||||
|
||||
// Add required fields mark to any element containing required fields
|
||||
$('div.multipage-pane').each(function(i){
|
||||
if ($('.error', $(this)).length) {
|
||||
|
||||
// Save first error item, for focussing it.
|
||||
if (!$firstErrorItem) {
|
||||
$firstErrorItem = $(this).data('multipageControl');
|
||||
}
|
||||
|
||||
Drupal.FieldGroup.setGroupWithfocus($(this));
|
||||
$(this).data('multipageControl').focus();
|
||||
}
|
||||
});
|
||||
|
||||
// Focus on first multipage that has an error.
|
||||
if ($firstErrorItem) {
|
||||
$firstErrorItem.focus();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
})(jQuery);
|
||||
|
||||
@@ -0,0 +1,414 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Test file for fieldgroup display.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Group display tests
|
||||
*/
|
||||
class GroupDisplayTestCase extends DrupalWebTestCase {
|
||||
|
||||
protected $node;
|
||||
|
||||
public static function getInfo() {
|
||||
return array(
|
||||
'name' => 'Display tests',
|
||||
'description' => 'Test the field group display.',
|
||||
'group' => 'Field Group',
|
||||
);
|
||||
}
|
||||
|
||||
function setUp() {
|
||||
|
||||
parent::setUp('field_test', 'field_group', 'field_group_test');
|
||||
|
||||
$node = new stdClass();
|
||||
$node->type = 'article';
|
||||
$node->title = $this->randomName();
|
||||
$node->status = 1;
|
||||
|
||||
// Create test fields.
|
||||
$test_fields = array('field_test', 'field_test_2', 'field_no_access');
|
||||
foreach ($test_fields as $field_name) {
|
||||
|
||||
$field = array(
|
||||
'field_name' => $field_name,
|
||||
'type' => 'test_field',
|
||||
'cardinality' => 1,
|
||||
);
|
||||
$instance = array(
|
||||
'field_name' => $field_name,
|
||||
'entity_type' => 'node',
|
||||
'bundle' => 'article',
|
||||
'label' => $this->randomName(),
|
||||
'display' => array(
|
||||
'default' => array(
|
||||
'type' => 'field_test_default',
|
||||
'settings' => array(
|
||||
'test_formatter_setting' => $this->randomName(),
|
||||
),
|
||||
),
|
||||
'teaser' => array(
|
||||
'type' => 'field_test_default',
|
||||
'settings' => array(
|
||||
'test_formatter_setting' => $this->randomName(),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
field_create_field($field);
|
||||
field_create_instance($instance);
|
||||
|
||||
$node->{$field_name}[LANGUAGE_NONE][0]['value'] = mt_rand(1, 127);
|
||||
}
|
||||
|
||||
node_save($node);
|
||||
$this->node = $node;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new group.
|
||||
* @param array $data
|
||||
* Data for the field group.
|
||||
*/
|
||||
function createGroup($mode, array $data) {
|
||||
|
||||
$group_name = 'group_' . drupal_strtolower($this->randomName(8));
|
||||
$identifier = $group_name . '|node|article|' . $mode;
|
||||
|
||||
$field_group = new stdClass;
|
||||
$field_group->disabled = FALSE;
|
||||
$field_group->api_version = 1;
|
||||
$field_group->identifier = $identifier;
|
||||
$field_group->group_name = $group_name;
|
||||
$field_group->entity_type = 'node';
|
||||
$field_group->bundle = 'article';
|
||||
$field_group->mode = $mode;
|
||||
$field_group->parent_name = '';
|
||||
$field_group->children = $data['children'];
|
||||
$field_group->data = $data;
|
||||
drupal_write_record('field_group', $field_group);
|
||||
ctools_export_crud_enable('field_group', $field_group->identifier);
|
||||
|
||||
return $field_group;
|
||||
}
|
||||
|
||||
/**
|
||||
* Test if an empty formatter.
|
||||
*/
|
||||
function testFieldAccess() {
|
||||
|
||||
$data = array(
|
||||
'label' => 'Wrapper',
|
||||
'weight' => '1',
|
||||
'children' => array(
|
||||
0 => 'field_no_access',
|
||||
),
|
||||
'format_type' => 'div',
|
||||
'format_settings' => array(
|
||||
'label' => 'Link',
|
||||
'instance_settings' => array(
|
||||
'required_fields' => 0,
|
||||
'id' => 'wrapper-id',
|
||||
'classes' => 'test-class',
|
||||
'description' => '',
|
||||
'show_label' => FALSE,
|
||||
'label_element' => 'h3',
|
||||
'effect' => 'blink',
|
||||
'speed' => 'fast',
|
||||
),
|
||||
'formatter' => 'open',
|
||||
),
|
||||
);
|
||||
$group = $this->createGroup('default', $data);
|
||||
|
||||
$groups = field_group_info_groups('node', 'article', 'default', TRUE);
|
||||
$this->drupalGet('node/' . $this->node->nid);
|
||||
|
||||
// Test if group is not shown.
|
||||
$this->assertNoFieldByXPath("//div[contains(@id, 'wrapper-id')]", NULL, t('Div that contains fields with no access is not shown.'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test the div formatter.
|
||||
*/
|
||||
function testDiv() {
|
||||
|
||||
$data = array(
|
||||
'label' => 'Wrapper',
|
||||
'weight' => '1',
|
||||
'children' => array(
|
||||
0 => 'field_test',
|
||||
),
|
||||
'format_type' => 'div',
|
||||
'format_settings' => array(
|
||||
'label' => 'Link',
|
||||
'instance_settings' => array(
|
||||
'required_fields' => 0,
|
||||
'id' => 'wrapper-id',
|
||||
'classes' => 'test-class',
|
||||
'description' => '',
|
||||
'show_label' => FALSE,
|
||||
'label_element' => 'h3',
|
||||
'effect' => 'blink',
|
||||
'speed' => 'fast',
|
||||
),
|
||||
'formatter' => 'open',
|
||||
),
|
||||
);
|
||||
$group = $this->createGroup('default', $data);
|
||||
|
||||
$groups = field_group_info_groups('node', 'article', 'default', TRUE);
|
||||
$this->drupalGet('node/' . $this->node->nid);
|
||||
|
||||
// Test group ids and classes.
|
||||
$this->assertFieldByXPath("//div[contains(@id, 'wrapper-id')]", NULL, t('Wrapper id set on wrapper div'));
|
||||
$this->assertFieldByXPath("//div[contains(@class, 'test-class')]", NULL, t('Test class set on wrapper div') . 'class="' . $group->group_name . ' test-class');
|
||||
|
||||
// Test group label.
|
||||
$this->assertNoRaw('<h3><span>' . $data['label'] . '</span></h3>', t('Label is not shown'));
|
||||
|
||||
// Set show label to true.
|
||||
$group->data['format_settings']['instance_settings']['show_label'] = TRUE;
|
||||
|
||||
drupal_write_record('field_group', $group, array('identifier'));
|
||||
$groups = field_group_info_groups('node', 'article', 'default', TRUE);
|
||||
$this->drupalGet('node/' . $this->node->nid);
|
||||
$this->assertRaw('<h3><span>' . $data['label'] . '</span></h3>', t('Label is shown'));
|
||||
|
||||
// Change to collapsible
|
||||
$group->data['format_settings']['formatter'] = 'collapsible';
|
||||
drupal_write_record('field_group', $group, array('identifier'));
|
||||
$groups = field_group_info_groups('node', 'article', 'default', TRUE);
|
||||
$this->drupalGet('node/' . $this->node->nid);
|
||||
$this->assertFieldByXPath("//div[contains(@class, 'speed-fast')]", NULL, t('Speed class is set'));
|
||||
$this->assertFieldByXPath("//div[contains(@class, 'effect-blink')]", NULL, t('Effect class is set'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test the horizontal tabs formatter.
|
||||
*/
|
||||
function testHorizontalTabs() {
|
||||
|
||||
$data = array(
|
||||
'label' => 'Tab 1',
|
||||
'weight' => '1',
|
||||
'children' => array(
|
||||
0 => 'field_test',
|
||||
),
|
||||
'format_type' => 'htab',
|
||||
'format_settings' => array(
|
||||
'label' => 'Tab 1',
|
||||
'instance_settings' => array(
|
||||
'classes' => 'test-class',
|
||||
'description' => '',
|
||||
),
|
||||
'formatter' => 'open',
|
||||
),
|
||||
);
|
||||
$first_tab = $this->createGroup('default', $data);
|
||||
|
||||
$data = array(
|
||||
'label' => 'Tab 2',
|
||||
'weight' => '1',
|
||||
'children' => array(
|
||||
0 => 'field_test_2',
|
||||
),
|
||||
'format_type' => 'htab',
|
||||
'format_settings' => array(
|
||||
'label' => 'Tab 1',
|
||||
'instance_settings' => array(
|
||||
'classes' => 'test-class-2',
|
||||
'description' => 'description of second tab',
|
||||
),
|
||||
'formatter' => 'closed',
|
||||
),
|
||||
);
|
||||
$second_tab = $this->createGroup('default', $data);
|
||||
|
||||
$data = array(
|
||||
'label' => 'Tabs',
|
||||
'weight' => '1',
|
||||
'children' => array(
|
||||
0 => $first_tab->group_name,
|
||||
1 => $second_tab->group_name,
|
||||
),
|
||||
'format_type' => 'htabs',
|
||||
'format_settings' => array(
|
||||
'label' => 'Tab 1',
|
||||
'instance_settings' => array(
|
||||
'classes' => 'test-class-wrapper',
|
||||
),
|
||||
),
|
||||
);
|
||||
$tabs = $this->createGroup('default', $data);
|
||||
|
||||
$groups = field_group_info_groups('node', 'article', 'default', TRUE);
|
||||
|
||||
$this->drupalGet('node/' . $this->node->nid);
|
||||
|
||||
// Test properties.
|
||||
$this->assertFieldByXPath("//div[contains(@class, 'test-class-wrapper')]", NULL, t('Test class set on tabs wrapper'));
|
||||
$this->assertFieldByXPath("//fieldset[contains(@class, 'test-class-2')]", NULL, t('Test class set on second tab'));
|
||||
$this->assertRaw('<div class="fieldset-description">description of second tab</div>', t('Description of tab is shown'));
|
||||
$this->assertRaw('class="collapsible collapsed test-class-2', t('Second tab is default collapsed'));
|
||||
|
||||
// Test if correctly nested
|
||||
$this->assertFieldByXPath("//div[contains(@class, 'test-class-wrapper')]//fieldset[contains(@class, 'test-class')]", NULL, 'First tab is displayed as child of the wrapper.');
|
||||
$this->assertFieldByXPath("//div[contains(@class, 'test-class-wrapper')]//fieldset[contains(@class, 'test-class-2')]", NULL, 'Second tab is displayed as child of the wrapper.');
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Test the vertical tabs formatter.
|
||||
*/
|
||||
function testVerticalTabs() {
|
||||
|
||||
$data = array(
|
||||
'label' => 'Tab 1',
|
||||
'weight' => '1',
|
||||
'children' => array(
|
||||
0 => 'field_test',
|
||||
),
|
||||
'format_type' => 'tab',
|
||||
'format_settings' => array(
|
||||
'label' => 'Tab 1',
|
||||
'instance_settings' => array(
|
||||
'classes' => 'test-class',
|
||||
'description' => '',
|
||||
),
|
||||
'formatter' => 'open',
|
||||
),
|
||||
);
|
||||
$first_tab = $this->createGroup('default', $data);
|
||||
$first_tab_id = 'edit-' . $first_tab->group_name;
|
||||
|
||||
$data = array(
|
||||
'label' => 'Tab 2',
|
||||
'weight' => '1',
|
||||
'children' => array(
|
||||
0 => 'field_test_2',
|
||||
),
|
||||
'format_type' => 'tab',
|
||||
'format_settings' => array(
|
||||
'label' => 'Tab 1',
|
||||
'instance_settings' => array(
|
||||
'classes' => 'test-class-2',
|
||||
'description' => 'description of second tab',
|
||||
),
|
||||
'formatter' => 'closed',
|
||||
),
|
||||
);
|
||||
$second_tab = $this->createGroup('default', $data);
|
||||
$second_tab_id = 'edit-' . $second_tab->group_name;
|
||||
|
||||
$data = array(
|
||||
'label' => 'Tabs',
|
||||
'weight' => '1',
|
||||
'children' => array(
|
||||
0 => $first_tab->group_name,
|
||||
1 => $second_tab->group_name,
|
||||
),
|
||||
'format_type' => 'tabs',
|
||||
'format_settings' => array(
|
||||
'label' => 'Tab 1',
|
||||
'instance_settings' => array(
|
||||
'classes' => 'test-class-wrapper',
|
||||
),
|
||||
),
|
||||
);
|
||||
$tabs = $this->createGroup('default', $data);
|
||||
|
||||
$groups = field_group_info_groups('node', 'article', 'default', TRUE);
|
||||
|
||||
$this->drupalGet('node/' . $this->node->nid);
|
||||
|
||||
// Test properties.
|
||||
$this->assertFieldByXPath("//div[contains(@class, 'test-class-wrapper')]", NULL, t('Test class set on tabs wrapper'));
|
||||
$this->assertFieldByXPath("//fieldset[contains(@class, 'test-class-2')]", NULL, t('Test class set on second tab'));
|
||||
$this->assertRaw('<div class="fieldset-description">description of second tab</div>', t('Description of tab is shown'));
|
||||
$this->assertRaw('class="collapsible collapsed test-class-2', t('Second tab is default collapsed'));
|
||||
|
||||
// Test if correctly nested
|
||||
$this->assertFieldByXPath("//div[contains(@class, 'test-class-wrapper')]//fieldset[contains(@id, '$first_tab_id')]", NULL, 'First tab is displayed as child of the wrapper.');
|
||||
$this->assertFieldByXPath("//div[contains(@class, 'test-class-wrapper')]//fieldset[contains(@id, '$second_tab_id')]", NULL, 'Second tab is displayed as child of the wrapper.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Test the accordion formatter.
|
||||
*/
|
||||
function testAccordion() {
|
||||
|
||||
$data = array(
|
||||
'label' => 'Accordion item 1',
|
||||
'weight' => '1',
|
||||
'children' => array(
|
||||
0 => 'field_test',
|
||||
),
|
||||
'format_type' => 'accordion-item',
|
||||
'format_settings' => array(
|
||||
'label' => 'Accordion item 1',
|
||||
'instance_settings' => array(
|
||||
'classes' => 'test-class',
|
||||
),
|
||||
'formatter' => 'closed',
|
||||
),
|
||||
);
|
||||
$first_item = $this->createGroup('default', $data);
|
||||
$first_item_id = 'node_article_full_' . $first_item->group_name;
|
||||
|
||||
$data = array(
|
||||
'label' => 'Accordion item 2',
|
||||
'weight' => '1',
|
||||
'children' => array(
|
||||
0 => 'field_test_2',
|
||||
),
|
||||
'format_type' => 'accordion-item',
|
||||
'format_settings' => array(
|
||||
'label' => 'Tab 2',
|
||||
'instance_settings' => array(
|
||||
'classes' => 'test-class-2',
|
||||
),
|
||||
'formatter' => 'open',
|
||||
),
|
||||
);
|
||||
$second_item = $this->createGroup('default', $data);
|
||||
$second_item_id = 'node_article_full_' . $second_item->group_name;
|
||||
|
||||
$data = array(
|
||||
'label' => 'Accordion',
|
||||
'weight' => '1',
|
||||
'children' => array(
|
||||
0 => $first_item->group_name,
|
||||
1 => $second_item->group_name,
|
||||
),
|
||||
'format_type' => 'accordion',
|
||||
'format_settings' => array(
|
||||
'label' => 'Tab 1',
|
||||
'instance_settings' => array(
|
||||
'classes' => 'test-class-wrapper',
|
||||
'effect' => 'bounceslide'
|
||||
),
|
||||
),
|
||||
);
|
||||
$accordion = $this->createGroup('default', $data);
|
||||
|
||||
$groups = field_group_info_groups('node', 'article', 'default', TRUE);
|
||||
|
||||
$this->drupalGet('node/' . $this->node->nid);
|
||||
|
||||
// Test properties.
|
||||
$this->assertFieldByXPath("//div[contains(@class, 'test-class-wrapper')]", NULL, t('Test class set on tabs wrapper'));
|
||||
$this->assertFieldByXPath("//div[contains(@class, 'effect-bounceslide')]", NULL, t('Correct effect is set on the accordion'));
|
||||
$this->assertFieldByXPath("//div[contains(@class, 'test-class')]", NULL, t('Accordion item with test-class is shown'));
|
||||
$this->assertFieldByXPath("//div[contains(@class, 'test-class-2')]", NULL, t('Accordion item with test-class-2 is shown'));
|
||||
$this->assertFieldByXPath("//h3[contains(@class, 'field-group-accordion-active')]", NULL, t('Accordion item 2 was set active'));
|
||||
|
||||
// Test if correctly nested
|
||||
$this->assertFieldByXPath("//div[contains(@class, 'test-class-wrapper')]//div[contains(@class, 'test-class')]", NULL, 'First item is displayed as child of the wrapper.');
|
||||
$this->assertFieldByXPath("//div[contains(@class, 'test-class-wrapper')]//div[contains(@class, 'test-class-2')]", NULL, 'Second item is displayed as child of the wrapper.');
|
||||
}
|
||||
|
||||
}
|
||||
Executable → Regular
+23
-30
@@ -2,53 +2,39 @@
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Unit test file for groups.
|
||||
*
|
||||
* @todo - fill along if we know more.
|
||||
*
|
||||
* - nest a group under another group and see if parent_name is first group
|
||||
* - nest a group under second group and see if parent_name is second group
|
||||
* - settings: open f and verify on front-end (classes)
|
||||
* - settings: collapsible f and verify on front-end (classes)
|
||||
* - settings: collapsed f and verify on front-end (classes)
|
||||
* - settings: vertical tabs and verify on front-end (classes)
|
||||
* - settings: horizontal item and verify on front-end (classes)
|
||||
* - settings: vertical item and verify on front-end (classes)
|
||||
* - settings: hidden (simple div) and verify on front-end (classes)
|
||||
* - settings: label and verify on front-end
|
||||
* - delete a fieldgroup - make sure we have a couple with the same name
|
||||
* and make sure the right one is deleted!
|
||||
* Test file for fieldgroup UI.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Group UI tests.
|
||||
*/
|
||||
class GroupUITestCase extends DrupalWebTestCase {
|
||||
|
||||
public static function getInfo() {
|
||||
return array(
|
||||
'name' => 'Group UI tests',
|
||||
'description' => 'Test the group UI functionality.',
|
||||
'group' => 'Field group',
|
||||
'name' => 'UI tests',
|
||||
'description' => 'Test the Field Group UI.',
|
||||
'group' => 'Field Group',
|
||||
);
|
||||
}
|
||||
|
||||
function setUp() {
|
||||
parent::setUp('field_test', 'field_group');
|
||||
parent::setUp('field_test', 'field_group', 'field_group_test');
|
||||
|
||||
// Create test user.
|
||||
$admin_user = $this->drupalCreateUser(array('administer content types'));
|
||||
$admin_user = $this->drupalCreateUser(array('administer content types', 'administer nodes', 'access administration pages', 'bypass node access', 'administer fields'));
|
||||
$this->drupalLogin($admin_user);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test the creation a group on the article content type.
|
||||
*/
|
||||
function createGroup() {
|
||||
|
||||
// Create random group name.
|
||||
$this->group_label = $this->randomName(8);
|
||||
$this->group_name_input = drupal_strtolower($this->randomName(8));
|
||||
$this->group_name = 'group_' . $this->group_name_input;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a group on the article content type.
|
||||
*/
|
||||
function createGroup() {
|
||||
|
||||
// Setup new group.
|
||||
$group = array(
|
||||
@@ -57,7 +43,8 @@ class GroupUITestCase extends DrupalWebTestCase {
|
||||
);
|
||||
|
||||
// Add new group on the 'Manage fields' page.
|
||||
$this->drupalPost('admin/structure/types/manage/article/fields', $group, t('Save'));
|
||||
$this->drupalPost('admin/structure/types/manage/article/fields', $group, t('Save'));
|
||||
|
||||
$this->assertRaw(t('New group %label successfully created.', array('%label' => $this->group_label)), t('Group message displayed on screen.'));
|
||||
|
||||
// Test if group is in the $groups array.
|
||||
@@ -65,7 +52,7 @@ class GroupUITestCase extends DrupalWebTestCase {
|
||||
$this->assertTrue(array_key_exists($this->group_name, $groups), t('Group found in groups array'));
|
||||
|
||||
// Add new group on the 'Manage display' page.
|
||||
$this->drupalPost('admin/structure/types/manage/article/display', $group, t('Save'));
|
||||
$this->drupalPost('admin/structure/types/manage/article/display', $group, t('Save'));
|
||||
$this->assertRaw(t('New group %label successfully created.', array('%label' => $this->group_label)), t('Group message displayed on screen.'));
|
||||
|
||||
// Test if group is in the $groups array.
|
||||
@@ -77,6 +64,7 @@ class GroupUITestCase extends DrupalWebTestCase {
|
||||
* Delete a group.
|
||||
*/
|
||||
function deleteGroup() {
|
||||
|
||||
$this->drupalPost('admin/structure/types/manage/article/groups/' . $this->group_name . '/delete/form', array(), t('Delete'));
|
||||
$this->assertRaw(t('The group %label has been deleted from the %article content type.', array('%label' => $this->group_label, '%article' => 'Article')), t('Group removal message displayed on screen.'));
|
||||
|
||||
@@ -104,13 +92,18 @@ class GroupUITestCase extends DrupalWebTestCase {
|
||||
* Nest a field underneath a group.
|
||||
*/
|
||||
function testNestField() {
|
||||
|
||||
$this->createGroup();
|
||||
|
||||
$edit = array(
|
||||
'fields[field_image][parent]' => $this->group_name,
|
||||
);
|
||||
$this->drupalPost('admin/structure/types/manage/article/fields', $edit, t('Save'));
|
||||
$this->assertRaw(t('Your settings have been saved.'), t('Settings saved'));
|
||||
|
||||
$groups = field_group_info_groups('node', 'article', 'form', TRUE);
|
||||
$this->assertTrue(in_array('field_image', $groups[$this->group_name]->children), t('Image is a child of %group', array('%group' => $this->group_label)));
|
||||
$this->assertTrue(in_array('field_image', $groups[$this->group_name]->children), t('Image is a child of %group', array('%group' => $this->group_name)));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
name = "Field Group Test"
|
||||
description = "Test module for Field Group"
|
||||
core = "7.x"
|
||||
package = Fields
|
||||
hidden = TRUE
|
||||
|
||||
|
||||
; Information added by Drupal.org packaging script on 2017-11-03
|
||||
version = "7.x-1.6"
|
||||
core = "7.x"
|
||||
project = "field_group"
|
||||
datestamp = "1509751991"
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Fieldgroup test module.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Implements hook_field_access().
|
||||
*/
|
||||
function field_group_test_field_access($op, $field, $entity_type, $entity, $account) {
|
||||
// Set access to false for field_no_access.
|
||||
if ($op == 'view' && $field['field_name'] == 'field_no_access') {
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user