Left base folder: C:\Documents and Settings\Olga\My Documents\Downloads\webform-6.x-3.0-beta5(2)\webform Right base folder: C:\xampp\htdocs\ogorun\sites\all\modules\webform --- webform.module 2010-04-11 09:13:52.000000000 +-0300 +++ webform.module 2010-06-02 00:49:28.000000000 +-0300 @@ -1338,18 +1338,46 @@ // Shorten up our variable names. $component_tree = $form_state['webform']['component_tree']; $page_count = $form_state['webform']['page_count']; $page_num = $form_state['webform']['page_num']; + // Start gathering JS settings for conditional fields engine + $settings = array( + 'objects' => array(), + 'mandatory' => array(), + 'previousPagesFields' => array(), + 'nid' => $node->nid + ); + // Recursively add components to the form. foreach ($component_tree['children'] as $cid => $component) { - $component_value = isset($form_state['values']['submitted'][$component['form_key']]) ? $form_state['values']['submitted'][$component['form_key']] : NULL; - if (_webform_client_form_rule_check($node, $component, $page_num, $form_state)) { - _webform_client_form_add_component($component, $component_value, $form['submitted'], $form, $submission, 'form', $page_num, $filter); - } + if (isset($form_state['values']['submitted'][$component['form_key']])) { + $component_value = $form_state['values']['submitted'][$component['form_key']]; + } + elseif (isset($form_state['storage']['submitted'][$component['form_key']])) { + $component_value = $form_state['storage']['submitted'][$component['form_key']]; + } + else { + $component_value = null; + } + _webform_client_form_add_component($component, $component_value, $form['submitted'], $form, $settings, $submission, 'form', $page_num, $filter); + } + + $form['webform_conditional_mandatory']=array( + '#type' => 'hidden', + '#default_value' => implode('|',$settings['mandatory']), + ); + + if (empty($_POST)) { + $path = drupal_get_path('module','webform'); + drupal_add_js($path . '/le/jquery.json-2.2.min.js'); + drupal_add_js($path . '/le/le.conditionsConstructor.js'); + drupal_add_js($path . '/le/le.conditionalFormFields.js'); + drupal_add_js(array('webformConditional' => $settings), 'setting'); + drupal_add_js($path . '/js/webform_conditional.js'); } // These form details help managing data upon submission. $form['details']['nid'] = array( '#type' => 'value', '#value' => $node->nid, @@ -1421,79 +1449,12 @@ } } return $form; } -/** - * Check if a component should be displayed on the current page. - */ -function _webform_client_form_rule_check($node, $component, $page_num, $form_state = NULL, $submission = NULL) { - $conditional_values = isset($component['extra']['conditional_values']) ? $component['extra']['conditional_values'] : NULL; - $conditional_component = isset($component['extra']['conditional_component']) && isset($node->webform['components'][$component['extra']['conditional_component']]) ? $node->webform['components'][$component['extra']['conditional_component']] : NULL; - - // Check the rules for this entire page. Note individual page breaks are - // checked down below in the individual component rule checks. - $show_page = TRUE; - if ($component['page_num'] > 1 && $component['type'] != 'pagebreak') { - foreach ($node->webform['components'] as $cid => $page_component) { - if ($page_component['type'] == 'pagebreak' && $page_component['page_num'] == $page_num) { - $show_page = _webform_client_form_rule_check($node, $page_component, $page_num, $form_state, $submission); - break; - } - } - } - - // Check any parents' visibility rules. - $show_parent = $show_page; - if ($show_parent && $component['pid'] && isset($node->webform['components'][$component['pid']])) { - $parent_component = $node->webform['components'][$component['pid']]; - $show_parent = _webform_client_form_rule_check($node, $parent_component, $page_num, $form_state, $submission); - } - - // Check the individual component rules. - $show_component = $show_parent; - if ($show_component && $component['page_num'] == $page_num && $conditional_component && strlen(trim($conditional_values))) { - $input_values = array(); - if (isset($form_state)) { - $parents = webform_component_parent_keys($node, $conditional_component); - $input_value = isset($form_state['values']['submitted']) ? $form_state['values']['submitted'] : array(); - foreach ($parents as $parent) { - if (isset($input_value[$parent])) { - $input_value = $input_value[$parent]; - } - else { - $input_value = NULL; - break; - } - } - $input_values = is_array($input_value) ? array_keys($input_value) : array($input_value); - } - elseif (isset($submission)) { - $input_values = $submission->data[$conditional_component['cid']]['value']; - } - - $test_values = array_map('trim', explode("\n", $conditional_values)); - if (empty($input_values) && !empty($test_values)) { - $show_component = FALSE; - } - else { - foreach ($input_values as $input_value) { - if ($show_component = in_array($input_value, $test_values)) { - break; - } - } - } - - if ($component['extra']['conditional_operator'] == '!=') { - $show_component = !$show_component; - } - } - - return $show_component; -} /** * Add a component to a renderable array. Called recursively for fieldsets. * * This function assists in the building of the client form, as well as the * display of results, and the text of e-mails. @@ -1519,13 +1480,13 @@ * Whether the form element properties should be filtered. Only set to FALSE * if needing the raw properties for editing. * * @see webform_client_form * @see webform_submission_render */ -function _webform_client_form_add_component($component, $component_value, &$parent_fieldset, &$form, $submission, $format = 'form', $page_num = 0, $filter = TRUE) { +function _webform_client_form_add_component($component, $component_value, &$parent_fieldset, &$form, &$js_settings, $submission, $format = 'form', $page_num = 0, $filter = TRUE) { $cid = $component['cid']; // Load with submission information if necessary. if ($format != 'form') { // This component is display only. $data = empty($submission->data[$cid]['value']) ? NULL : $submission->data[$cid]['value']; @@ -1540,12 +1501,15 @@ if (!isset($display_element['#id'])) { $display_element['#id'] = form_clean_id('edit-' . implode('-', $display_element['#parents'])); } $parent_fieldset[$component['form_key']] = $display_element; } } + elseif ($component['page_num'] < $page_num) { + $js_settings['previousPagesFields'][$component['form_key']] = !is_null($component_value) ? $component_value[0] : $data; + } elseif ($component['page_num'] == $page_num) { // Add this user-defined field to the form (with all the values that are always available). $data = isset($submission->data[$cid]['value']) ? $submission->data[$cid]['value'] : NULL; if ($element = webform_component_invoke($component['type'], 'render', $component, $data, $filter)) { $parent_fieldset[$component['form_key']] = $element; @@ -1555,12 +1519,28 @@ if (is_array($component_value)) { foreach ($component_value as $key => $value) { if (isset($parent_fieldset[$component['form_key']][$key])) { $parent_fieldset[$component['form_key']][$key]['#default_value'] = $value; } } + } + } + + + if(!empty($component['extra']['condition'])){ + $condition = json_decode($component['extra']['condition']); + if (!empty($condition->optype)) { + $js_settings['objects'][$component['form_key']] = array( + 'type' => $component['type'], + 'fieldsetId' => !empty($component['#parent_fieldsets']) ? implode('_', $component['#parent_fieldsets']) : '', // array_slice($parent_fieldset, 0, -1)), + 'expression' => $condition, + 'mandatory' => (bool)$component['extra']['conditional_mandatory'], + ); + } + if($component['extra']['conditional_mandatory']){ + $js_settings['mandatory'][] = $component['form_key']; } } } else { drupal_set_message(t('The webform component @type is not able to be displayed', array('@type' => $component['type']))); } @@ -1573,13 +1553,16 @@ $parent_fieldset[$component['form_key']]['#webform_validated'] = FALSE; } if (isset($component['children']) && is_array($component['children'])) { foreach ($component['children'] as $scid => $subcomponent) { $subcomponent_value = isset($component_value[$subcomponent['form_key']]) ? $component_value[$subcomponent['form_key']] : NULL; - _webform_client_form_add_component($subcomponent, $subcomponent_value, $parent_fieldset[$component['form_key']], $form, $submission, $format, $page_num, $filter); + $fieldsets = !empty($component['#parent_fieldsets']) ? $component['#parent_fieldsets'] : array(); + $fieldsets[] = $component['form_key']; + $subcomponent['#parent_fieldsets'] = $fieldsets; + _webform_client_form_add_component($subcomponent, $subcomponent_value, $parent_fieldset[$component['form_key']], $form, $js_settings, $submission, $format, $page_num, $filter); } } } function webform_client_form_validate($form, &$form_state) { $node = node_load($form_state['values']['details']['nid']); @@ -1595,15 +1578,59 @@ $error = theme('webform_view_messages', $node, 0, 1, 0, $limit_exceeded, array_keys(user_roles())); form_set_error('', $error); return; } } + // Check conditional mandatory fields + _webform_client_form_conditional_mandatory_validate($form, $form_state); + // Run all #element_validate and #required checks. These are skipped initially // by setting #validated = TRUE on all components when they are added. _webform_client_form_validate($form, $form_state); +} + +/** + * Check conditional mandatory fields - fields that should be filled in in case they are visible + * + * To avoid duplication in condition evaluation all calculation is done only on client side + * and pass fields that should be considered not mandatory with hidden field 'webform_conditional_mandatory' + * + * @param array $form + * @param array $form_state + */ +function _webform_client_form_conditional_mandatory_validate($form, $form_state) { + if (!empty($form_state['values']['webform_conditional_mandatory'])) { + $mandatory = explode('|', $form_state['values']['webform_conditional_mandatory']); + $values = _webform_flattern_submitted_tree($form_state['values']['submitted']); + foreach($mandatory as $name) { + if ($values[$name]['value'] == '') { + form_set_error('submitted][' . implode('][', $values[$name]['parents']) . $name, t('Field !name is required', array('!name' => $name))); + } + } + } +} + +function _webform_flattern_submitted_tree($submitted, $parents = array()) { + $values = array(); + if (!empty($submitted)) { + foreach ($submitted as $field => $val) { + if (is_array($val)) { + $next_parents = $parents; + array_push($next_parents, $field); + $values = array_merge($values, _webform_flattern_submitted_tree($val, $next_parents)); + } + else { + $values[$field]['value'] = $val; + $values[$field]['parents'] = $parents; + } + } + } + + return $values; + } /** * Recursive validation function to trigger normal Drupal validation. * * This function imitates _form_validate in Drupal's form.inc, only it sets Left base folder: C:\Documents and Settings\Olga\My Documents\Downloads\webform-6.x-3.0-beta5(2)\webform Right base folder: C:\xampp\htdocs\ogorun\sites\all\modules\webform --- includes\webform.components.inc 2010-06-02 00:54:12.000000000 +-0300 +++ includes\webform.components.inc 2010-06-02 00:53:26.000000000 +-0300 @@ -420,72 +420,74 @@ '#description' => t('Optional. In the menu, the heavier items will sink and the lighter items will be positioned nearer the top.'), '#weight' => 4, ); // Add conditional fields. $conditional_components = array(); - $counter = 0; $last_pagebreak_slice = 0; foreach ($node->webform['components'] as $cid => $test_component) { // Only components before the pagebreak can be considered. - if ($test_component['type'] == 'pagebreak') { - $last_pagebreak_slice = $counter; - } - if (isset($component['cid']) && $cid == $component['cid']) { + if (isset($component['cid']) && $cid == $component['cid']) { break; } if (webform_component_feature($test_component['type'], 'conditional')) { - $conditional_components[$cid] = $test_component; + $conditional_components[$test_component['form_key']] = $test_component['name']; } - $counter++; } - if ($component['type'] != 'pagebreak') { - $fieldset_description = t('Create a rule to control whether or not to skip this page.'); - } - else { - $fieldset_description = t('Create a rule to control whether or not to show this form element.'); - } - $conditional_components = array_slice($conditional_components, 0, $last_pagebreak_slice, TRUE); $form['conditional'] = array( '#weight' => 10, '#type' => 'fieldset', '#title' => t('Conditional rules'), '#collapsible' => TRUE, - '#collapsed' => TRUE, + '#collapsed' => !$component['extra']['condition'], '#description' => t('Create a rule to control whether or not to show this form element.'), '#tree' => FALSE, ); $form['conditional']['extra'] = array( '#tree' => TRUE, ); - $form['conditional']['extra']['conditional_component'] = array( - '#type' => 'select', - '#title' => t('Component'), - '#options' => webform_component_list($node, $conditional_components, FALSE, TRUE), - '#description' => t('Select another component to decide whether to show or hide this component. You can only select components occurring before the most recent pagebreak.'), - '#default_value' => $component['extra']['conditional_component'], + + $form['conditional']['extra']['condition'] = array( + '#type' => 'hidden', + '#default_value' => $component['extra']['condition'] ); - $form['conditional']['extra']['conditional_operator'] = array( - '#type' => 'select', - '#title' => t('Operator'), - '#options' => array( - '=' => t('Is one of'), - '!=' => t('Is not one of') - ), - '#description' => t('Determines whether the list below is inclusive or exclusive.'), - '#default_value' => $component['extra']['conditional_operator'], + + $form['conditional']['extra']['conditional_mandatory'] = array( + '#type' => 'checkbox', + '#title' => t('Conditional mandatory'), + '#default_value' => $component['extra']['conditional_mandatory'] ); - $form['conditional']['extra']['conditional_values'] = array( - '#type' => 'textarea', - '#title' => t('Values'), - '#description' => t('List values, one per line, that will trigger this action. If you leave this blank, this component will always display.'), - '#default_value' => $component['extra']['conditional_values'], + + $form['conditional']['extra']['constructor'] = array( + '#type' => 'markup', + '#value' => '
' ); if (empty($conditional_components)) { $form['conditional']['#access'] = FALSE; + } + else { + $settings = array( + 'webformConditionalSettings' => array( + 'currentConditions' => $component['extra']['condition'] ? json_decode($component['extra']['condition']) : new StdClass(), + 'lOperandFormElement' => 'select', + 'availableLOperands' => $conditional_components, + 'buttonFlags' => array( + 'addElement' => true, + 'addAndGroup' => true, + 'addOrGroup' => true, + 'showExpression' => true + ), + ) + ); + drupal_add_js($settings, 'setting'); + $path = drupal_get_path('module','webform'); + drupal_add_js($path . '/js/webform_conditional.js'); + drupal_add_js($path . '/le/jquery.json-2.2.min.js'); + drupal_add_js($path . '/le/le.conditionsConstructor.js'); + drupal_add_css($path . '/le/conditions_constructor.css'); } // Add the fields specific to this component type: $additional_form_elements = (array) webform_component_invoke($component['type'], 'edit', $component); if (empty($additional_form_elements)) { drupal_set_message(t('The webform component of type @type does not have an edit function defined.', array('@type' => $component['type']))); Left base folder: C:\Documents and Settings\Olga\My Documents\Downloads\webform-6.x-3.0-beta5(2)\webform Right base folder: C:\xampp\htdocs\ogorun\sites\all\modules\webform --- includes\webform.submissions.inc 2010-03-26 03:50:36.000000000 +-0300 +++ includes\webform.submissions.inc 2010-06-02 00:48:00.000000000 +-0300 @@ -223,13 +223,13 @@ $excluded_components = isset($email) ? $email['excluded_components'] : array(); _webform_components_tree_build($node->webform['components'], $component_tree, 0, $page_count); // Recursively add components to the form. foreach ($component_tree['children'] as $cid => $component) { - if (!in_array($cid, $excluded_components) && _webform_client_form_rule_check($node, $component, $component['page_num'], NULL, $submission)) { + if (!in_array($cid, $excluded_components)) { _webform_client_form_add_component($component, NULL, $renderable, $renderable, $submission, $format); } } return drupal_render($renderable); } Left base folder: C:\Documents and Settings\Olga\My Documents\Downloads\webform-6.x-3.0-beta5(2)\webform Right base folder: C:\xampp\htdocs\ogorun\sites\all\modules\webform --- +++ js\webform_conditional.js 2010-05-29 17:23:20.000000000 +-0300 @@ -0,0 +1,97 @@ +Drupal.behaviors.webformCond = function(context) { + if (typeof Drupal.settings['webformConditional'] != 'undefined' && Drupal.settings['webformConditional']['objects']) { + var options = { + mandatoryFieldSelector: '#edit-webform-conditional-mandatory', + objectByName: function(name) { + return $('[name*="['+name+']"]'); + }, + feldsetByName: function(name) { + return $('#webform-component-' + name); + }, + getWrapper: function(name,info) { + if(info['type']=='fieldset' || info['type']=='markup'){ + return $('#webform-component-' + name); + } + var cssName = name.replace(/_/g,"-"); + if(info['fieldsetId']!=''){ + cssName = info['fieldsetId'].replace(/_/g,"-") + "-" + cssName; + } + + if (info['type'] == 'grid') { + return $('[name*=\"['+name+']\"]').parent('.form-item').parents('.form-item'); + } + var cssId = "#edit-submitted-" + cssName + "-wrapper"; + if($(cssId).length==0) { + cssId = "#edit-submitted-" + cssName + "-1-wrapper"; + return $(cssId).parent().parent(); + } else { + return $(cssId); + } + }, + expression: $.le.defaultOptions.conditionalForm.expression + }; + + + options.setMandatoryLikeView = function(name, info) { + var wrap = options.getWrapper(name, info).parent(); + if (!wrap.find('label span.form-required').length) { + $('*').appendTo(wrap.find('label')); + } + }; + + options.expression.operandHandler.left = function (val) { + var obj = options.objectByName(val); + if (obj.length) { + if(obj.filter('input:checked').length == 1) { + return obj.filter('input:checked').val(); + } else if(obj.filter('input:checkbox,input:radio').length) { + return false; + } else if(obj.filter('option:selected').length == 1) { + return obj.filter('option:selected').val(); + } else { + return obj.val(); + } + } + else if (Drupal.settings.webformConditional.previousPagesFields[val]) { + return Drupal.settings.webformConditional.previousPagesFields[val]; + } + else { + return false; + } + + }; + + $.extend(options, Drupal.settings.webformConditional); + + $('form.webform-client-form').leConditionalForm(options); + } +}; + +Drupal.behaviors.webformCondSettings = function(context) { + + $('#webform-conditions-constructor:not(.webformCondSettings-processed)', context) + .addClass('webformCondSettings-processed') + .each(function() { + var domObj = this; + var $this = $(this); + var options = Drupal.settings.webformConditionalSettings; + options.defaultNewElement = function () { + var el; + for (el in options.availableLOperands) { + break; + } + return { + optype: 'el', + left: el, + op: 'opEqual', + right: '', + neg: false + }; + } + var conditions = $this.leConstructor(Drupal.settings.webformConditionalSettings.currentConditions, options); + + $('#webform-component-edit-form').submit(function () { + $('#edit-extra-condition').val(domObj.le.expression.getSerialised()); + }); + }); +}; Left base folder: C:\Documents and Settings\Olga\My Documents\Downloads\webform-6.x-3.0-beta5(2)\webform Right base folder: C:\xampp\htdocs\ogorun\sites\all\modules\webform --- +++ le\conditions_constructor.css 2010-05-22 21:33:56.000000000 +-0300 @@ -0,0 +1,47 @@ +@CHARSET "UTF-8"; +.constructor ul, .constructor ul li{ + list-style-image:url("icons/condition.png"); + list-style-type:square; + background: none; +/* padding-left: 15px;*/ +} + +.constructor-group { + padding: 10px 20px; + border: 1px solid #DDE5EA; +} + +.constructor ul.constructor-admin-links { + display: inline; + list-style-type:none; +} + +.constructor .constructor-admin-links li { + display: inline; + padding: 0px 7px; + /* background: white url("icons/add.png") no-repeat; */ +} + +.conditions-constructor ul, .conditions-constructor ul li{ + list-style-image:url("icons/condition.png"); + list-style-type:square; + background: none; + padding-left: 15px; +} + +.conditions-constructor-group { + padding: 10px 20px; + border: 1px solid #DDE5EA; +} + +.conditions-constructor ul.conditions-constructor-admin-links { + display: inline; + list-style-image:url("icons/add.png"); + list-style-type:square; +} + +.conditions-constructor .conditions-constructor-admin-links li { + display: inline; + padding: 0px 7px; + /* background: white url("icons/add.png") no-repeat; */ +} \ No newline at end of file Left base folder: C:\Documents and Settings\Olga\My Documents\Downloads\webform-6.x-3.0-beta5(2)\webform Right base folder: C:\xampp\htdocs\ogorun\sites\all\modules\webform --- +++ le\jquery.json-2.2.min.js 2010-05-13 09:50:38.000000000 +-0300 @@ -0,0 +1,31 @@ + +(function($){$.toJSON=function(o) +{if(typeof(JSON)=='object'&&JSON.stringify) +return JSON.stringify(o);var type=typeof(o);if(o===null) +return"null";if(type=="undefined") +return undefined;if(type=="number"||type=="boolean") +return o+"";if(type=="string") +return $.quoteString(o);if(type=='object') +{if(typeof o.toJSON=="function") +return $.toJSON(o.toJSON());if(o.constructor===Date) +{var month=o.getUTCMonth()+1;if(month<10)month='0'+month;var day=o.getUTCDate();if(day<10)day='0'+day;var year=o.getUTCFullYear();var hours=o.getUTCHours();if(hours<10)hours='0'+hours;var minutes=o.getUTCMinutes();if(minutes<10)minutes='0'+minutes;var seconds=o.getUTCSeconds();if(seconds<10)seconds='0'+seconds;var milli=o.getUTCMilliseconds();if(milli<100)milli='0'+milli;if(milli<10)milli='0'+milli;return'"'+year+'-'+month+'-'+day+'T'+ +hours+':'+minutes+':'+seconds+'.'+milli+'Z"';} +if(o.constructor===Array) +{var ret=[];for(var i=0;i