From d01ae1af6a8054a18a3703a31119910f6bab68e8 Mon Sep 17 00:00:00 2001 From: John Mitchell Date: Tue, 10 Feb 2015 10:47:01 -0500 Subject: [PATCH] full commit of fixes to the survey by jared --- awx/ui/static/js/forms/SurveyQuestion.js | 495 +++++++++++----------- awx/ui/static/js/helpers/JobSubmission.js | 12 +- awx/ui/static/js/helpers/Survey.js | 125 ++++-- 3 files changed, 357 insertions(+), 275 deletions(-) diff --git a/awx/ui/static/js/forms/SurveyQuestion.js b/awx/ui/static/js/forms/SurveyQuestion.js index 50eca4c52f..a0ef474193 100644 --- a/awx/ui/static/js/forms/SurveyQuestion.js +++ b/awx/ui/static/js/forms/SurveyQuestion.js @@ -24,250 +24,255 @@ export default twoColumns: true, breadcrumbs: false, - fields: { - question_name: { - realName: 'question_text', - label: 'Name', - type: 'text', - addRequired: true, - editRequired: true, - column: 1, - awSurveyQuestion: true - }, - question_description: { - realName: 'question_description', - label: 'Description', - type: 'text', - // rows: 2, - addRequired: false, - editRequired: false, - column: 1 - }, - // variable: { - // label: 'Answer Variable Name', - // type: 'text', - // addRequired: true, - // editRequired: true, - // column: 1, - // awPopOver: '

The suggested format for variable names are lowercase, underscore-separated descriptive nouns.

'+ - // '

For example:
foo_bar
\n user_id
\n host_name
' , - // dataTitle: 'Answer Variable Name', - // dataPlacement: 'right', - // dataContainer: "body" - // }, - variable: { - ealName: 'variable', - type: 'custom', - control:''+ - '

'+ - '
Please enter an answer variable name.
'+ - '
Please remove the illegal character from the survey question variable name.
'+ - '
This question variable is already in use. Please enter a different variable name.
' + - '
'+ - '
', - addRequired: true, - editRequired: true, - column: 1 - }, - type: { - realName: 'answer_type', - label: 'Answer Type', - type: 'select', - defaultText: 'Choose an answer type', - ngOptions: 'answer_types.name for answer_types in answer_types track by answer_types.type', - addRequired: true, - editRequired: true, - column: 2, - ngChange: 'typeChange()' - }, - choices: { - realName: 'answer_options', - label: 'Multiple Choice Options', - type: 'textarea', - rows: 3, - addRequired: true, - editRequired: true, - ngRequired: "type.type=== 'multiselect' || type.type=== 'multiplechoice' " , - ngShow: 'type.type=== "multiselect" || type.type=== "multiplechoice" ', - awPopOver: '

Type an option on each line.

'+ - '

For example the following input:

Apple
\n Banana
\n Cherry

would be displayed as:

\n'+ - '
  1. Apple
  2. Banana
  3. Cherry
', - dataTitle: 'Multiple Choice Options', - dataPlacement: 'right', - dataContainer: "body", - column: 2 - }, - text_options: { - realName: 'answer_options', - type: 'custom', - control:'
'+ - '
'+ - ''+ - '
The minimum length you entered is not a number. Please enter a number.
'+ - '
The minimium length is too high. Please enter a lower number.
'+ - '
The minimum length is too low. Please enter a positive number.
'+ - '
'+ - '
'+ - ''+ - '
The maximum length you entered is not a number. Please enter a number.
'+ - '
The maximum length is too low. Please enter a number larger than the minimum length you set.
'+ - '
'+ - '
', - ngShow: 'type.type==="text" ', - addRequired: true, - editRequired: true, - column: 2 - }, - textarea_options: { - realName: 'answer_options', - type: 'custom', - control:'
'+ - '
'+ - ''+ - '
The minimum length you entered is not a number. Please enter a number.
'+ - '
The minimium length is too high. Please enter a lower number.
'+ - '
The minimum length is too low. Please enter a positive number.
'+ - '
'+ - '
'+ - ''+ - '
The maximum length you entered is not a number. Please enter a number.
'+ - '
The maximum length is too low. Please enter a number larger than the minimum length you set.
'+ - '
'+ - '
', - ngShow: 'type.type==="textarea" ', - addRequired: true, - editRequired: true, - column: 2 - }, - int_options: { - realName: 'answer_options', - type: 'custom', - control:'
'+ - '
'+ - ''+ - '
Please enter a valid integer.
'+ - '
Please enter a smaller integer.
'+ - '
'+ - '
'+ - ''+ - '
Please enter a valid integer.
'+ - '
Please enter a larger integer.
'+ - '
'+ - '
', - ngShow: 'type.type==="integer" ', - addRequired: true, - editRequired: true, - column: 2 - }, - float_options: { - realName: 'answer_options', - type: 'custom', - control: '
'+ - '
'+ - ''+ - '
Please enter a valid float.
'+ - '
Please enter a smaller float.
'+ - '
'+ - '
'+ - ''+ - '
Please enter a valid float.
'+ - '
Please enter a larger float.
'+ - - '
'+ - '
', - ngShow: 'type.type==="float" ', - addRequired: true, - editRequired: true, - column: 2 - }, - default: { - realName: 'default_answer', - label: 'Default Answer', - type: 'text', - addRequired: false, - editRequired: false, - column: 2, - ngHide: 'type.type === "textarea" || type.type === "multiselect" || type.type === "integer" || type.type === "float" ' - }, - // default_text: { - // realName: 'default_answer', - // type: 'custom', - // control: '
'+ - // ''+ - // ''+ - // '
The answer must be between {{text_min}} to {{text_max}} characters long!
'+ - // '
', - // column: 2, - // ngShow: 'type.type === "text" ' - // }, - default_multiselect: { - realName: 'default_answer', - label: 'Default Answer', - type: 'textarea', - rows: 3, - addRequired: false, - editRequired: false, - column: 2, - ngShow: 'type.type === "multiselect" ' - }, - default_int: { - realName: 'default_answer', - type: 'custom', - control: '
'+ - ''+ - ''+ - '
Please enter a valid integer.
'+ - '
Please enter a value in the range of {{int_min}} to {{int_max}}.
'+ - '
', - column: 2, - ngShow: 'type.type === "integer" ' - }, - default_float: { - realName: 'default_answer', - type: 'custom', - control: '
'+ - ''+ - ''+ - '
Please enter a valid float.
'+ - '
Please enter a value in the range of {{float_min}} to {{float_max}}!
'+ - '
', - column: 2, - ngShow: 'type.type=== "float" ' - }, - default_textarea: { - realName: 'default_answer', - label: 'Default Answer', - type: 'textarea', - rows: 3, - addRequired: false, - editRequired: false, - column: 2, - ngShow: 'type.type === "textarea" ' - }, - required: { - realName: 'required_answer', - label: 'Required', - type: 'checkbox', - addRequired: false, - editRequired: false, - column: 2 - } + fields: { + question_name: { + realName: 'question_text', + label: 'Name', + type: 'text', + addRequired: true, + editRequired: true, + column: 1, + awSurveyQuestion: true }, - buttons: { - question_cancel : { - label: 'Cancel', - 'class' : 'btn btn-default', - ngClick: 'cancelQuestion($event)' - }, - submit_question: { - ngClick: 'submitQuestion($event)', - ngDisabled: true, //'survey_question.$valid', //"!question_name || !variable || !type || ((type.type==='multiplechoice' || type.type === 'multiselect' ) && !choices)", //|| type.type===multiselect ',//'!question_name || !variable || !type' , - 'class': 'btn btn-sm btn-primary', - label: 'Add Question' - } - } + question_description: { + realName: 'question_description', + label: 'Description', + type: 'text', + // rows: 2, + addRequired: false, + editRequired: false, + column: 1 + }, + // variable: { + // label: 'Answer Variable Name', + // type: 'text', + // addRequired: true, + // editRequired: true, + // column: 1, + // awPopOver: '

The suggested format for variable names are lowercase, underscore-separated descriptive nouns.

'+ + // '

For example:
foo_bar
\n user_id
\n host_name
' , + // dataTitle: 'Answer Variable Name', + // dataPlacement: 'right', + // dataContainer: "body" + // }, + variable: { + ealName: 'variable', + type: 'custom', + control:''+ + '

'+ + '
Please enter an answer variable name.
'+ + '
Please remove the illegal character from the survey question variable name.
'+ + '
This question variable is already in use. Please enter a different variable name.
' + + '
'+ + '
', + addRequired: true, + editRequired: true, + column: 1 + }, + type: { + realName: 'answer_type', + label: 'Answer Type', + type: 'select', + defaultText: 'Choose an answer type', + ngOptions: 'answer_types.name for answer_types in answer_types track by answer_types.type', + addRequired: true, + editRequired: true, + column: 2, + ngChange: 'typeChange()' + }, + choices: { + realName: 'answer_options', + label: 'Multiple Choice Options', + type: 'textarea', + rows: 3, + addRequired: true, + editRequired: true, + ngRequired: "type.type=== 'multiselect' || type.type=== 'multiplechoice' " , + ngShow: 'type.type=== "multiselect" || type.type=== "multiplechoice" ', + awPopOver: '

Type an option on each line.

'+ + '

For example the following input:

Apple
\n Banana
\n Cherry

would be displayed as:

\n'+ + '
  1. Apple
  2. Banana
  3. Cherry
', + dataTitle: 'Multiple Choice Options', + dataPlacement: 'right', + dataContainer: "body", + column: 2 + }, + text_options: { + realName: 'answer_options', + type: 'custom', + control:'
'+ + '
'+ + ''+ + '
The minimum length you entered is not a number. Please enter a number.
'+ + '
The minimium length is too high. Please enter a lower number.
'+ + '
The minimum length is too low. Please enter a positive number.
'+ + '
'+ + '
'+ + ''+ + '
The maximum length you entered is not a number. Please enter a number.
'+ + '
The maximum length is too low. Please enter a number larger than the minimum length you set.
'+ + '
'+ + '
', + ngShow: 'type.type==="text" ', + addRequired: true, + editRequired: true, + column: 2 + }, + textarea_options: { + realName: 'answer_options', + type: 'custom', + control:'
'+ + '
'+ + ''+ + '
The minimum length you entered is not a number. Please enter a number.
'+ + '
The minimium length is too high. Please enter a lower number.
'+ + '
The minimum length is too low. Please enter a positive number.
'+ + '
'+ + '
'+ + ''+ + '
The maximum length you entered is not a number. Please enter a number.
'+ + '
The maximum length is too low. Please enter a number larger than the minimum length you set.
'+ + '
'+ + '
', + ngShow: 'type.type==="textarea" ', + addRequired: true, + editRequired: true, + column: 2 + }, + int_options: { + realName: 'answer_options', + type: 'custom', + control:'
'+ + '
'+ + ''+ + '
Please enter a valid integer.
'+ + '
Please enter a smaller integer.
'+ + '
'+ + '
'+ + ''+ + '
Please enter a valid integer.
'+ + '
Please enter a larger integer.
'+ + '
'+ + '
', + ngShow: 'type.type==="integer" ', + addRequired: true, + editRequired: true, + column: 2 + }, + float_options: { + realName: 'answer_options', + type: 'custom', + control: '
'+ + '
'+ + ''+ + '
Please enter a valid float.
'+ + '
Please enter a smaller float.
'+ + '
'+ + '
'+ + ''+ + '
Please enter a valid float.
'+ + '
Please enter a larger float.
'+ - }); + '
'+ + '
', + ngShow: 'type.type==="float" ', + addRequired: true, + editRequired: true, + column: 2 + }, + default:{ + realName: 'default_answer', + type: 'custom' , + control: '
'+ + ''+ + '
'+ + ''+ + '
Please enter an answer for the choices listed.
' + + '
The answer is shorter than the minimium length. Please make the answer longer.
' + + '
The answer is longer than the maximum length. Please make the answer shorter.
' + + '
'+ + '
'+ + '
', + column: 2, + ngHide: 'type.type === "textarea" || type.type === "multiselect" || type.type === "integer" || type.type === "float" ' + }, + default_multiselect: { + realName: 'default_answer' , + type: 'custom', + control: '
'+ + ''+ + '
'+ + ''+ + '
Please enter an answer/answers for the choices listed.
' + + '
'+ + '
'+ + '
', + column: 2, + ngShow: 'type.type==="multiselect" ' + }, + default_int: { + realName: 'default_answer', + type: 'custom', + control: '
'+ + ''+ + ''+ + '
Please enter a valid integer.
'+ + '
Please enter a value in the range of {{int_min}} to {{int_max}}.
'+ + '
', + column: 2, + ngShow: 'type.type === "integer" ' + }, + default_float: { + realName: 'default_answer', + type: 'custom', + control: '
'+ + ''+ + ''+ + '
Please enter a valid float.
'+ + '
Please enter a value in the range of {{float_min}} to {{float_max}}!
'+ + '
', + column: 2, + ngShow: 'type.type=== "float" ' + }, + default_textarea: { + realName: "default_answer" , + type: 'custom', + control: '
'+ + ''+ + '
'+ + ''+ + '
The answer is shorter than the minimium length. Please make the answer longer.
' + + '
The answer is longer than the maximum length. Please make the answer shorter.
' + + '
'+ + '
'+ + '
', + column : 2, + ngShow: 'type.type === "textarea" ' + }, + required: { + realName: 'required_answer', + label: 'Required', + type: 'checkbox', + addRequired: false, + editRequired: false, + column: 2 + } + }, + buttons: { + question_cancel : { + label: 'Cancel', + 'class' : 'btn btn-default', + ngClick: 'cancelQuestion($event)' + }, + submit_question: { + ngClick: 'submitQuestion($event)', + ngDisabled: true, //'survey_question.$valid', //"!question_name || !variable || !type || ((type.type==='multiplechoice' || type.type === 'multiselect' ) && !choices)", //|| type.type===multiselect ',//'!question_name || !variable || !type' , + 'class': 'btn btn-sm btn-primary', + label: 'Add Question' + } + } + + }); diff --git a/awx/ui/static/js/helpers/JobSubmission.js b/awx/ui/static/js/helpers/JobSubmission.js index 0b08ba998e..365837a2cf 100644 --- a/awx/ui/static/js/helpers/JobSubmission.js +++ b/awx/ui/static/js/helpers/JobSubmission.js @@ -72,12 +72,18 @@ angular.module('JobSubmissionHelper', [ 'RestServices', 'Utilities', 'Credential } } + if(scope.survey_enabled===true){ - for (var fld in scope.job_launch_form){ - //grab only survey question fields, including those that are zero or a blank answer (for optional questions) - if((scope[fld] || scope[fld] === 0 || scope[fld]==="") && scope.passwords_needed_to_start.indexOf(fld) === -1 && fld !== 'extra_vars'){ + for (var i=0; i < scope.survey_questions.length; i++){ + var fld = scope.survey_questions[i].variable; + // grab all survey questions that have answers + if(scope[fld]) { job_launch_data.extra_vars[fld] = scope[fld]; } + // for optional text and text-areas, submit a blank string if min length is 0 + if(scope.survey_questions[i].required === false && (scope.survey_questions[i].type === "text" || scope.survey_questions[i].type === "textarea") && scope.survey_questions[i].min === 0 && scope[fld] ===""){ + job_launch_data.extra_vars[fld] = ""; + } } } diff --git a/awx/ui/static/js/helpers/Survey.js b/awx/ui/static/js/helpers/Survey.js index 91ff60b988..233eb94121 100644 --- a/awx/ui/static/js/helpers/Survey.js +++ b/awx/ui/static/js/helpers/Survey.js @@ -256,18 +256,18 @@ angular.module('SurveyHelper', [ 'Utilities', 'RestServices', 'SchedulesHelper', return function(params) { var scope = params.scope, - // id = params.id, question = params.question, index = params.index, required, - element, choices, i, checked, - max, min, defaultValue, - - html = ""; - - // if(scope.survey_questions.length>0){ - // $('#survey-save-button').removeAttr('disabled') - // } + element, + choices, + i, + checked, + max, + min, + defaultValue, + answers, + html = ""; question.index = index; question.question_name = question.question_name.replace(/\n'; } - // defaultValue = (question.default) ? question.default : ""; if(question.type === 'text' ){ defaultValue = (question.default) ? question.default : ""; @@ -312,9 +311,10 @@ angular.module('SurveyHelper', [ 'Utilities', 'RestServices', 'SchedulesHelper', choices = question.choices.split(/\n/); element = (question.type==="multiselect") ? "checkbox" : 'radio'; question.default = (question.default) ? question.default : (question.default_multiselect) ? question.default_multiselect : "" ; + answers = question.default.split(/\n/); html += '
'; for( i = 0; i/g, ">"); choices[i] = scope.serialize(choices[i]); @@ -364,17 +364,42 @@ angular.module('SurveyHelper', [ 'Utilities', 'RestServices', 'SchedulesHelper', $('#add_question_btn').focus(); $('#survey_maker_save_btn').removeAttr('disabled'); + // Sometimes the $event.target returns the anchor element that wraps the icon, and sometimes the icon itself + // is returned. So for each icon click event we check to see which target the user clicked, and depending no which one + // they clicked, we move up the dom hierarchy to get the index on the question. Ultimatley the object that is passed to + // each one of these functions should be the index of the question that the user is trying to perform an action on. $('#delete-question_'+question.index+'').on('click', function($event){ - scope.deleteQuestion($event.target.parentElement.parentElement.parentElement.id.split('_')[1]); + if($event.target.nodeName==="A"){ + scope.deleteQuestion($event.target.parentElement.parentElement.id.split('_')[1]); + } + else if($event.target.nodeName === "I"){ + scope.deleteQuestion($event.target.parentElement.parentElement.parentElement.id.split('_')[1]); + } }); $('#edit-question_'+question.index+'').on('click', function($event){ - scope.editQuestion($event.target.parentElement.parentElement.parentElement.id.split('_')[1]); + if($event.target.nodeName==="A"){ + scope.editQuestion($event.target.parentElement.parentElement.id.split('_')[1]); + } + else if($event.target.nodeName === "I"){ + scope.editQuestion($event.target.parentElement.parentElement.parentElement.id.split('_')[1]); + } }); $('#question-up_'+question.index+'').on('click', function($event){ - scope.questionUp($event.target.parentElement.parentElement.parentElement.id.split('_')[1]); + if($event.target.nodeName==="A"){ + scope.questionUp($event.target.parentElement.parentElement.id.split('_')[1]); + } + else if($event.target.nodeName === "I"){ + scope.questionUp($event.target.parentElement.parentElement.parentElement.id.split('_')[1]); + } }); $('#question-down_'+question.index+'').on('click', function($event){ - scope.questionDown($event.target.parentElement.parentElement.parentElement.id.split('_')[1]); + if($event.target.nodeName==="A"){ + scope.questionDown($event.target.parentElement.parentElement.id.split('_')[1]); + } + else if($event.target.nodeName === "I"){ + scope.questionDown($event.target.parentElement.parentElement.parentElement.id.split('_')[1]); + } + }); }; }]) @@ -701,6 +726,8 @@ angular.module('SurveyHelper', [ 'Utilities', 'RestServices', 'SchedulesHelper', }; scope.typeChange = function() { + scope.minTextError = false; + scope.maxTextError = false; scope.default = ""; scope.default_multiselect = ""; scope.default_float = ""; @@ -721,13 +748,63 @@ angular.module('SurveyHelper', [ 'Utilities', 'RestServices', 'SchedulesHelper', scope.survey_question_form.default_int.$setPristine(); scope.survey_question_form.default_textarea.$setPristine(); scope.survey_question_form.choices.$setPristine(); + scope.survey_question_form.int_min.$setPristine(); + scope.survey_question_form.int_max.$setPristine(); }; scope.submitQuestion = function(event){ var data = {}, - fld, + fld, i, + choiceArray, + answerArray, key, elementID; - Wait('start'); + // Wait('start'); + scope.invalidChoice = false; + scope.duplicate = false; + scope.minTextError = false; + scope.maxTextError = false; + + if(scope.type.type==="text"){ + if(scope.default.trim() !== ""){ + if(scope.default.trim().length < scope.text_min && scope.text_min !== "" ){ + scope.minTextError = true; + } + if(scope.text_max < scope.default.trim().length && scope.text_max !== "" ){ + scope.maxTextError = true; + } + } + } + + if(scope.type.type==="textarea"){ + if(scope.default_textarea.trim() !== ""){ + if(scope.default_textarea.trim().length < scope.textarea_min && scope.textarea_min !== "" ){ + scope.minTextError = true; + } + if(scope.textarea_max < scope.default_textarea.trim().length && scope.textarea_max !== "" ){ + scope.maxTextError = true; + } + } + } + + if(scope.type.type==="multiselect" && scope.default_multiselect.trim() !== ""){ + choiceArray = scope.choices.split(/\n/); + answerArray = scope.default_multiselect.split(/\n/); + + if(answerArray.length>0){ + for(i=0; i