(function () { 'use strict'; var mod = angular.module('myformsAdmin'); var templatePath = modulesSharedResourcesUrl + 'Modules/MyFormsAdmin/Views/'; //directive for building / designing a form - also handles viewing all forms as a form admin mod.directive('formTemplateBuilder', ['common', 'myFormsDataContext', '$location', '$modal', 'myFormsFileUpload', '$q', '$routeParams', 'myFormsAdminService', 'myUsersDataContext', 'myFormsService', 'myFormsAdminDataContext', 'myFormsAdminConfig', 'formBuilderConfig', 'myBadgesSelectorDataContext', 'myBadgesAdminDataContext', 'taskAdminDataContext', '$filter', function (common, myFormsDataContext, $location, $modal, myFormsFileUpload, $q, $routeParams, myFormsAdminService, myUsersDataContext, myFormsService, myFormsAdminDataContext, myFormsAdminConfig, formBuilderConfig, myBadgesSelectorDataContext, myBadgesAdminDataContext, taskAdminDataContext, $filter) { return { restrict: 'E', scope: { onFormTemplatePublished: '&', onFormTemplateRetired: '&', onFormTemplateSaved: '&', onFormTemplateDeleted: '&', onFormTemplateCloned: '&', onTemplateAvailabilityChanged: '&', userDetailsFetcher: '&', currentUserFetcher: '&', toolName: '@', toolType: '@', templateBuilderName: '@', }, templateUrl: templatePath + 'templatebuilder.html', link: link }; function link(scope, $scope, elem, attrs) { scope.formType = $routeParams.formType; scope.templateId = $routeParams.templateId; scope.templatePath = templatePath; scope.standardTemplate = templatePath + 'standardformtemplate.html'; scope.activityTemplate = templatePath + 'activityformtemplate.html'; scope.showTemplateBuilder = false; scope.isHelpOpen = false; scope.showTemplates = true; scope.formIconBlank = myFormsAdminConfig.formIconBlank; scope.templatesLoaded = false; scope.orderByPredicate = '-createdOn'; scope.orderByChoice = 'Order by creation date'; scope.tabs = [{ active: true }, { active: false }, { active: false }, { active: false }]; scope.rulesDisabled = false; //Tool name scope.toolName = scope.toolName == null || scope.toolName == '' ? "Forms" : scope.toolName; //Display options for the form template builder scope.formBuilderOptions = { displayHeader: displayHeader, formBuilderName: scope.templateBuilderName == null || scope.templateBuilderName == '' ? formBuilderName : scope.templateBuilderName == '' ? formBuilderName : scope.templateBuilderName, displaySettingsTab: formBuilderConfig.displaySettingsTab, displayStylingTab: formBuilderConfig.displayStylingTab, displayFormTypeSelector: formBuilderConfig.displayFormTypeSelector, defaultFormType: formBuilderConfig.defaultFormType, displayDatePickerQuestion: formBuilderConfig.displayDatePickerQuestion, displayTimePickerQuestion: formBuilderConfig.displayTimePickerQuestion, displayRatingQuestion: formBuilderConfig.displayRatingQuestion, displayChoiceQuestion: formBuilderConfig.displayChoiceQuestion, displayChecklistQuestion: formBuilderConfig.displayChecklistQuestion, titleLabel: formBuilderConfig.titleLabel, supportingTextLabel: formBuilderConfig.supportingTextLabel, displayExcludeCheckbox: formBuilderConfig.displayExcludeCheckbox, displayDataIdentifier: formBuilderConfig.displayDataIdentifier, displayRequiredCheckbox: formBuilderConfig.displayRequiredCheckbox, displayBackButton: formBuilderConfig.displayBackButton, displaySaveButton: formBuilderConfig.displaySaveButton, displayPublishButton: formBuilderConfig.displayPublishButton, displayHeadingItem: formBuilderConfig.displayHeadingItem, displayDescriptiveTextItem: formBuilderConfig.displayDescriptiveTextItem, displayImageFormItem: formBuilderConfig.displayImageFormItem, displayValidationHelp: formBuilderConfig.displayValidationHelp, displayDescriptionOnForm: formBuilderConfig.displayDescriptionOnForm, enableNameEditing: formBuilderConfig.enableNameEditing, enableDescriptionEditing: formBuilderConfig.enableNameEditing, enableTitleEditing: formBuilderConfig.enableNameEditing, displayRulesTab: formBuilderConfig.displayRulesTab, displayBadgesTab: formBuilderConfig.displayBadgesTab, displayScoringTab: formBuilderConfig.displayScoringTab, //displayWordCount: displayWordCount, displayScoringBandBadges: formBuilderConfig.displayScoringBandBadges, displayCorrectAnswersPassMarkCheckbox: formBuilderConfig.displayCorrectAnswersPassMarkCheckbox, displayIconManager: formBuilderConfig.displayIconManager, displayNumberQuestion: formBuilderConfig.displayNumberQuestion, displaySupportingText: formBuilderConfig.displaySupportingText, displayTextQuestion: formBuilderConfig.displayTextQuestion, displayTextBlockQuestion: formBuilderConfig.displayTextBlockQuestion, displayNumberMatchQuestion: formBuilderConfig.displayNumberMatchQuestion, displayTextMatchQuestion: formBuilderConfig.displayTextMatchQuestion, useNameAsTitle: formBuilderConfig.useNameAsTitle, allowImagesOnQuestions: formBuilderConfig.allowImagesOnQuestions, uploadsStore: formBuilderConfig.uploadsStore }; if (scope.formType == 5) { scope.formBuilderOptions.displayChecklistQuestion = false; scope.formBuilderOptions.displayRatingQuestion = false; } //get the existing form templates to show if (!scope.templateId) { getFormTemplates(); } else { getFormTemplateById(); } if ($location.search().showHelp) { scope.isHelpOpen = true; } //set the icon help text function setIconHelpText() { if (formBuilderConfig.displaySuppliedIcons) { scope.iconHelpText = "Each " + scope.formTerm + " requires an icon. You can either choose one of the supplied form icons or provide a custom icon yourself."; } if (!formBuilderConfig.displaySuppliedIcons) { scope.iconHelpText = "Each " + scope.formTerm + " requires an icon. You can select an icon by dropping it below or clicking 'Select Files'."; } if (!formBuilderConfig.displaySuppliedIcons && !formBuilderConfig.displayIconUploader) { scope.iconHelpText = ""; } } setIconHelpText(); //set the colour palette for the template scope.setColourPalette = function (colourPalette) { scope.template.backgroundColour = colourPalette.backgroundColour; scope.template.highlightColour = colourPalette.highlightColour; scope.template.fontColour = colourPalette.fontColour; } function getFormTemplates() { scope.templatesLoaded = true; //now get the forms for the orgs myFormsAdminDataContext.getTemplatesICanManage().then(function (formTemplates) { scope.formTemplates = []; if (scope.formType) { if (scope.formType == 0) { scope.pageTitle = 'Standard forms'; scope.formTerm = 'standard form'; } if (scope.formType == 1) { scope.pageTitle = 'Reflection forms'; scope.formTerm = 'reflection form'; } if (scope.formType == 2) { scope.pageTitle = 'Goal forms'; scope.formTerm = 'goal form'; } if (scope.formType == 3) { scope.pageTitle = 'CPD forms'; scope.formTerm = 'CPD form'; } if (scope.formType == 4) { scope.pageTitle = 'Quizzes'; scope.formTerm = 'quiz'; } if (scope.formType == 5) { scope.pageTitle = 'Activities'; scope.formTerm = 'activity'; scope.formBuilderOptions.formBuilderName = "Activity builder"; } scope.formTermPlural = scope.formTerm; //If the term ends with 'y' we need to change it to 'ie' so that it pluralises nicely scope.formTermPlural = scope.formTerm.substring(scope.formTerm.length - 1, scope.formTerm.length) == 'y' ? scope.formTerm.substring(0, scope.formTerm.length - 1) + 'ie' : scope.formTerm; for (var i = 0; i < formTemplates.length; i++) { if (formTemplates[i].formType == scope.formType) { if (scope.formType == 5) { //activities truncate name and description to 47 chars with ellipses appended formTemplates[i].name = formTemplates[i].name.length > 47 ? formTemplates[i].name.substr(0, 46) + '...' : formTemplates[i].name; formTemplates[i].description = formTemplates[i].description.length > 47 ? formTemplates[i].description.substr(0, 46) + '...' : formTemplates[i].description; } scope.formTemplates.push(formTemplates[i]); } } } else { scope.formTemplates = formTemplates; scope.pageTitle = scope.toolName == null || scope.toolName == '' ? 'Forms' : scope.toolName; scope.formTerm = scope.toolType == null || scope.toolType == '' ? 'form' : scope.toolType == null ? 'form' : scope.toolType; //If the term ends with 'y' we need to change it to 'ie' so that it pluralises nicely scope.formTermPlural = scope.formTerm.substring(scope.formTerm.length - 1, scope.formTerm.length) == 'y' ? scope.formTerm.substring(0, scope.formTerm.length - 1) + 'ie' : scope.formTerm == '' ? 'form' : scope.formTerm; } scope.formFilter = { filter: { undefined: undefined }, menuEntry: 'Show all' } //now we need to get all the ids' of the users involved in creating or publishing these templates - because the user details exist elsewhere var userIds = []; for (var i = 0; i < scope.formTemplates.length; i++) { if (scope.formTemplates[i].authorId && userIds.indexOf(scope.formTemplates[i].authorId) === -1) userIds.push(scope.formTemplates[i].authorId); if (scope.formTemplates[i].lastUpdatedById && userIds.indexOf(scope.formTemplates[i].lastUpdatedById) === -1) userIds.push(scope.formTemplates[i].lastUpdatedById); if (scope.formTemplates[i].lastPublishededById && userIds.indexOf(scope.formTemplates[i].lastPublishededById) === -1) userIds.push(scope.formTemplates[i].lastPublishededById); if (scope.formTemplates[i].lastRetiredById && userIds.indexOf(scope.formTemplates[i].lastRetiredById) === -1) userIds.push(scope.formTemplates[i].lastRetiredById); } angular.forEach(scope.formTemplates, function(formTemplate){ formTemplate.withdrawTooltip = "Withdraw"; //Check if the form template is being used by any tasks and if so, do something on the ui to block withdrawing it taskAdminDataContext.getTaskRecordsByFormTemplate(formTemplate.id).then(function(tasksUsingForm){ if(tasksUsingForm != null && tasksUsingForm.length){ formTemplate.canBeWithdrawn = false; formTemplate.withdrawTooltip = "This form is currently being used by one or more tasks:\n"; for (var i = 0; i < tasksUsingForm.length; i++) { formTemplate.withdrawTooltip = formTemplate.withdrawTooltip + " - '" + tasksUsingForm[i].name + "'\n"; } }else{ formTemplate.canBeWithdrawn = true; } }); }); //now we have an array of user ids of all actors use the passed in function to get the user details scope.userDetailsFetcher()(userIds).then(function (users) { //now we have the full user details (name etc) set them in the form template for (var j = 0; j < users.length; j++) { for (var i = 0; i < scope.formTemplates.length; i++) { if (scope.formTemplates[i].authorId && scope.formTemplates[i].authorId === users[j].userId) scope.formTemplates[i].author = users[j]; if (scope.formTemplates[i].lastUpdatedById && scope.formTemplates[i].lastUpdatedById === users[j].userId) scope.formTemplates[i].lastUpdatedBy = users[j]; if (scope.formTemplates[i].lastPublishededById && scope.formTemplates[i].lastPublishededById === users[j].userId) scope.formTemplates[i].lastPublishededBy = users[j]; if (scope.formTemplates[i].lastRetiredById && scope.formTemplates[i].lastRetiredById === users[j].userId) scope.formTemplates[i].lastRetiredBy = users[j]; } } }); scope.loadedyet = true; }); } function getFormTemplateById() { myFormsAdminDataContext.getFormTemplate(scope.templateId).then(function (formTemplate) { scope.template = formTemplate; scope.showTemplates = false; scope.showTemplateBuilder = true; scope.templatesLoaded = true; scope.rulesDisabled = formTemplate.individualCanView; }); } //set the possible colour palletes that can be chosen for the form template scope.colourPalettes = myFormsAdminService.getColourPalettes(); scope.idealTextColor = function (bgColor) { if (bgColor === 'transparent' || bgColor === null || bgColor === undefined) return ''; var nThreshold = 105; var components = getRgbComponents(bgColor); var bgDelta = (components.R * 0.299) + (components.G * 0.587) + (components.B * 0.114); return ((255 - bgDelta) < nThreshold) ? "#000000" : "#ffffff"; } function getRgbComponents(color) { var r = color.substring(1, 3); var g = color.substring(3, 5); var b = color.substring(5, 7); return { R: parseInt(r, 16), G: parseInt(g, 16), B: parseInt(b, 16) }; } scope.formTypes = [ { type: 0, name: 'Standard' }, { type: 1, name: 'Reflection' }, { type: 2, name: 'Goal' }, { type: 3, name: 'CPD' }, { type: 4, name: 'Quiz' }, ] //initialise the form items scope.formItems = [ { name: 'Heading', questionText: 'A heading', isHeading: true, validation: { isRequired: false }, icon: 'fa-header', display: scope.formBuilderOptions.displayHeadingItem, }, { name: 'Descriptive text', questionText: 'Some rich text', isTextBlock: true, validation: { isRequired: false }, icon: 'fa-align-left', display: scope.formBuilderOptions.displayDescriptiveTextItem, }, { name: 'Image', questionText: '', isImageQuestion: true, imageHorizontalPosition: 'Center', validation: { isRequired: false }, icon: 'fa-image', display: scope.formBuilderOptions.displayImageFormItem, } ]; /* * when the 'form items' label renders only display if there are form items to display */ scope.formItemsLabelShow = false; function isFormItemLabelShown() { scope.formItems.filter(function (formItem) { if (formItem.display) scope.formItemsLabelShow = true; }); } ; isFormItemLabelShown(); //these question types can be added to a form template - they come preset with appropiate data scope.inputItems = [ { name: 'Text input', questionText: 'Untitled', isSimpleTextQuestion: true, validation: { isRequired: false, maxTextLength: 10, minTextLength: 0 }, icon: 'fa-minus', minNumAllowed: 0, display: scope.formBuilderOptions.displayTextQuestion, }, { name: 'Text match', questionText: 'Select an answer', supportingText: 'Select an answer', isTextMatchQuestion: true, validation: { isRequired: false, maxTextLength: 10, minTextLength: 0 }, choices: [ { choice: 'answer', correct: true, feedback: "Your feedback" } ], icon: 'fa-minus', minNumAllowed: 0, display: scope.formBuilderOptions.displayTextMatchQuestion, }, { name: 'Text block', questionText: 'Untitled', isRichTextQuestion: true, validation: { isRequired: false, maxTextLength: 100, minTextLength: 0 }, icon: 'fa-align-justify', minNumAllowed: 0, display: scope.formBuilderOptions.displayTextBlockQuestion, }, { name: 'Date picker', questionText: 'Untitled', isDateQuestion: true, validation: { isRequired: false }, icon: 'fa-calendar', display: scope.formBuilderOptions.displayDatePickerQuestion, }, { name: 'Time picker', questionText: 'Untitled', isTimeQuestion: true, validation: { isRequired: false }, icon: 'fa-clock', display: scope.formBuilderOptions.displayTimePickerQuestion, }, { name: 'Number', questionText: 'Untitled', isNumberQuestion: true, validation: { isRequired: false }, icon: 'fa-sort-numeric-up', display: scope.formBuilderOptions.displayNumberQuestion, }, { name: 'Number match', questionText: 'Select an answer', supportingText: 'Select an answer', isNumberMatchQuestion: true, validation: { isRequired: false }, choices: [ { choice: 0, correct: true, feedback: "Your feedback" } ], icon: 'fa-sort-numeric-asc', display: scope.formBuilderOptions.displayNumberMatchQuestion, }, { name: 'Rating', questionText: 'Untitled', isNumberRangeQuestion: true, validation: { isRequired: false }, maxNumberRange: 5, icon: 'fa-star', display: scope.formBuilderOptions.displayRatingQuestion, }, { name: 'Choice', questionText: 'Select a choice', isChoiceQuestion: true, validation: { isRequired: false }, choices: [ { choice: 'Choice 1' } ], icon: 'fa-bars', display: scope.formBuilderOptions.displayChoiceQuestion, }, { name: 'Check list', questionText: 'Click all that apply', isCheckListQuestion: true, validation: { isRequired: false }, choices: [ { choice: 'Choice 1' }, { choice: 'Choice 2' }, { choice: 'Choice 3' } ], icon: 'fa-check-square', display: scope.formBuilderOptions.displayChecklistQuestion, }, { name: 'Multiple choice question', questionText: 'Select an answer', isMultiChoiceQuestion: true, supportingText: "Select an answer", validation: { isRequired: false }, choices: [ { choice: 'answer', correct: true, feedback: "Your feedback" } ], icon: 'fa-check-square', display: true, } ]; //set the colour palette for the template scope.setColourPalette = function (colourPalette) { scope.template.backgroundColour = colourPalette.backgroundColour; scope.template.highlightColour = colourPalette.highlightColour; scope.template.fontColour = colourPalette.fontColour; scope.template.backgroundColour = scope.template.backgroundColour; scope.template.highlightColour = scope.template.highlightColour; scope.template.fontColour = scope.template.fontColour; } //delete the template scope.deleteTemplate = function (formTemplate) { myFormsAdminDataContext.deleteFormTemplate(formTemplate.id).then(function () { var index = scope.formTemplates.indexOf(formTemplate); if (index > -1) scope.formTemplates.splice(index, 1); scope.onFormTemplateDeleted()(formTemplate); }); } //clone / copy a form template scope.cloneFormTemplate = function (formTemplate) { myFormsAdminDataContext.cloneFormTemplate(formTemplate.id).then(function () { scope.onFormTemplateCloned()(formTemplate); getFormTemplates(); }); } //get the default answer for a question scope.getDefaultAnswer = function (question) { if (!question) return null; if (question.isSimpleTextQuestion || question.isRichTextQuestion) return { textAnswer: '' } else if (question.isDateQuestion) return { dateAnswer: '' } else if (question.isTimeQuestion) return { timeAnswer: '' } else if (question.isNumberRangeQuestion || question.isNumberQuestion) return { numberAnswer: '' } else if (question.isCheckListQuestion || question.isChoiceQuestion) return { chosen: [] } return {}; } //create a new template scope.createNewTemplate = function () { //initialise some values //reset the tabs so we always show the first tab scope.tabs = [{ active: true }, { active: false }, { active: false }, { active: false }]; scope.template = { iconUri: myFormsAdminConfig.defaultImagePublicId, title: "Form Title Goes Here", formType: scope.formBuilderOptions.defaultFormType, sections: [ { name: 'default', questions: [] }], icons: [], quizPassMark: 1, // we have to set a default passmark, more validation is needed for this as the forms marking logic can cause problems if there are no marks provided. adminCanCreate: true, individualCanCreate: true, individualCanView: true, individualCanEdit: true, individualCanDelete: true, } if (scope.formType) { scope.template.formType = scope.formType; } //show the correct UI for editing the new template scope.showTemplateBuilder = true; scope.showTemplates = false; scope.rulesDisabled = false; scope.template.totalScore = 0; scope.template.usePercentagesForScoringBands = false; }; scope.createFromDefault = function () { $modal.open({ templateUrl: templatePath + 'defaulttemplateschooser.html', controller: defaultTemplatesChooser, windowClass: 'publish-modal', backdrop: 'static', resolve: { scope: function () { return scope; } } }); }; scope.scoringBandAddStatus = true; /* * handle what happens when the scoring bands 'use percentages' checkbox is checked */ scope.usePercentagesForScoringBands = function () { angular.forEach(scope.template.scoringBands, function (scoringBand) { scoringBand.startPercentage = Math.round((scoringBand.start / scope.template.totalScore) * 100); scoringBand.endPercentage = Math.round((scoringBand.end / scope.template.totalScore) * 100); }); }; /* * handle when 'select badges' is clicked on a scoring band */ scope.selectScoringBandBadges = function (scoringBand) { if (scoringBand.badges == null) { scoringBand.badges = []; } myBadgesSelectorDataContext.showSelectorModal(scoringBand.badges, false); }; /* * handle clicking the 'add' button on scoring bands */ scope.addScoringBand = function () { if (scope.template.scoringBands == null || !scope.template.scoringBands.length) { //just create a new array with one in scope.template.scoringBands = [ { ordinal: 0, feedback: '', start: 0, end: scope.template.totalScore < 10 ? scope.template.totalScore : 10, startPercentage: 0, endPercentage: scope.template.totalScore < 10 ? 100 : Math.round((10 / scope.template.totalScore) * 100), badges: [] } ]; } else { //find a number that is not in any band yet, set that as start and end values myFormsDataContext.getNewScoringBand(scope.template.scoringBands, scope.template.totalScore) .then(function (scoringBands) { if (scoringBands == null) { scope.scoringBandsError = true; scope.scoringBandsErrorMessage = "There is no space for a new scoring band"; } else { scope.scoringBandAddStatus = false; if (scoringBands.length != scope.template.scoringBands.length) { scope.scoringBandAddStatus = true; } scope.template.scoringBands = scoringBands; } }); } }; /* * handle clicking the 'x' button on scoring bands */ scope.removeScoringBand = function (scoringBand) { //we trust that the scoringbands are in ordinal order scope.template.scoringBands.splice(scoringBand.ordinal, 1); //update ordinals after the one we just cut for (var i = scoringBand.ordinal; i < scope.template.scoringBands.length; i++) { scope.template.scoringBands[i].ordinal = i; } }; /* * handle a start/end value incrementing or decrementing - this could be a flat value or a percentage */ scope.rangeChanged = function (scoringBand, start, end, originalStart, originalEnd) { //Check that the start and end are valid var validStartAndEnd = startAndEndAreValid(scoringBand, start, end, originalStart, originalEnd); if (validStartAndEnd !== true) { return false; } //Now do overlap checking var check = checkOverlappingBand(scoringBand, start, end, originalStart, originalEnd); //handle the result of check - could be 'true' meaning there is an overlap, otherwise set the new value if (check !== true) { //are we dealing with percentage? if (scope.template.usePercentagesForScoringBands) { scoringBand.start = parseInt((scoringBand.startPercentage / 100) * scope.template.totalScore); scoringBand.end = parseInt((scoringBand.endPercentage / 100) * scope.template.totalScore); } else { //Set the percentage for this value scoringBand.startPercentage = Math.ceil((scoringBand.start / scope.template.totalScore) * 100); //round up scoringBand.endPercentage = Math.floor((scoringBand.end / scope.template.totalScore) * 100); //round down } scope.scoringBandAddStatus = true; } return check; }; /* * check for overlaps with the given scoring band's potential new start or end value against other scoring bands */ function checkOverlappingBand(scoringBand, start, end, originalStart, originalEnd) { //Does the given value exist within any of the scoring band ranges? //go through all scoring bands for (var i in scope.template.scoringBands) { var sb = scope.template.scoringBands[i]; //ignore self if (sb.ordinal == scoringBand.ordinal) continue; //Go from the start to end checking if there's an overlap in the current band start and end values for (var j = start; j <= end; j++) { var nextStart = sb.start; var nextEnd = sb.end; if (scope.template.usePercentagesForScoringBands) { nextStart = sb.startPercentage; nextEnd = sb.endPercentage; } if (j.between(nextStart, nextEnd)) { //Overlaps so set start or end back to original then return if (start != originalStart) { if (scope.template.usePercentagesForScoringBands) { scoringBand.startPercentage = originalStart; } else { scoringBand.start = originalStart; } scoringBand.isError = true; scoringBand.error = "Range start must not overlap with any other scoring band"; return start; } if (end != originalEnd) { if (scope.template.usePercentagesForScoringBands) { scoringBand.endPercentage = originalEnd; } else { scoringBand.end = originalEnd; } scoringBand.isError = true; scoringBand.error = "Range end must not overlap with any other scoring band"; return end; } } } } scoringBand.isError = false; if (start != originalStart) { return start; } if (end != originalEnd) { return end; } return true; } ; /* * Check that the given start and end values are valid */ function startAndEndAreValid(scoringBand, start, end, originalStart, originalEnd) { //validity check that we're not trying to go higher than max score or 100% if ((scope.template.usePercentagesForScoringBands && (start > 100 || end > 100)) || (!scope.template.usePercentagesForScoringBands && (start > scope.template.totalScore || end > scope.template.totalScore))) { scoringBand.isError = true; scoringBand.error = scope.template.usePercentagesForScoringBands ? "You cannot exceed 100%" : "You cannot exceed the maximum score"; //return the unchanged start value if we changed that if (start != originalStart) { scoringBand.start = originalStart; if (scope.template.usePercentagesForScoringBands) { scoringBand.startPercentage = originalStart; } return originalStart; } //return the unchanged end value if we changed that if (end != originalEnd) { scoringBand.end = originalEnd; if (scope.template.usePercentagesForScoringBands) { scoringBand.endPercentage = originalEnd; } return originalEnd; } } //validity check that the start or end are not null and not -1 if ((start == null || start < 0) || (end == null || end < 0) || start > end || end < start) { if (start != originalStart) { scoringBand.start = originalStart; if (scope.template.usePercentagesForScoringBands) { scoringBand.startPercentage = originalStart; } scoringBand.isError = true; scoringBand.error = "Start must be positive and less than End"; return originalStart; } if (end != originalEnd) { scoringBand.end = originalEnd; if (scope.template.usePercentagesForScoringBands) { scoringBand.endPercentage = originalEnd; } scoringBand.isError = true; scoringBand.error = "End must be positive and more than Start"; return originalEnd; } } return true; } //is 'this' number between a given range of numbers (inclusive)? Number.prototype.between = function (min, max) { return this >= min && this <= max; }; /* * reload the scoring bands based on the current total score */ function refreshScoringBands() { if (scope.template.totalScore == 0) { scope.template.scoringBands = []; } angular.forEach(scope.template.scoringBands, function (scoringBand) { var checkEnd = true; if (scoringBand.start > scope.template.totalScore) { scope.template.scoringBands.splice(scope.template.scoringBands.indexOf(scoringBand)); checkEnd = false; } if (checkEnd) { if (scoringBand.end > scope.template.totalScore) { scoringBand.end = scope.template.totalScore; } } }); } /* * calculate the current total score based on question scores */ function calculateTemplateTotalScore() { scope.template.totalScore = 0; angular.forEach(scope.template.sections[0].questions, function (qn) { if (qn) { if (qn.totalScore) { scope.template.totalScore = parseInt(scope.template.totalScore) + parseInt(qn.totalScore); } } }); } //get the correct icon for the template scope.getIconUri = function (template) { var publicId = myFormsAdminConfig.defaultImagePublicId; if (template && template.iconUri) publicId = template.iconUri; return myFormsAdminConfig.formIconBlank + 'h_150/' + publicId + '.png'; } //show all existing templates scope.showAllTemplates = function () { scope.showTemplateBuilder = false; scope.showTemplates = true; } //called whenever a file is selected scope.onFileSelect = function ($files) { //ensure the file is a valid image and not too big if ($files[0]) { if (!scope.template.fileIsImage) { $scope.fileTypeIsWrong = true; return; } scope.template.fileTypeIsWrong = false; if ($files[0].size <= myFormsAdminConfig.maxFileUpload) { scope.files = $files; scope.fileIsOkSize = true; } else { scope.fileIsOkSize = false; alert("File is too large!"); } } }; //edit an existing template scope.manageFormTemplate = function (simpleFormTemplate) { scope.tabs = [{ active: true }, { active: false }, { active: false }, { active: false }]; //get the template data (e.g. from server) myFormsDataContext.getFormTemplate(simpleFormTemplate.id).then(function (formTemplate) { scope.template = formTemplate; scope.showTemplateBuilder = true; scope.showTemplates = false; //scope.template.totalScore = 250; //Do we allow editing of name, description and title? scope.formBuilderOptions.enableNameEditing = formBuilderConfig.enableNameEditing; scope.formBuilderOptions.enableDescriptionEditing = formBuilderConfig.enableDescriptionEditing; scope.formBuilderOptions.enableTitleEditing = formBuilderConfig.enableTitleEditing; disableRules(); }); } //validate a form title scope.validateTitle = function (title) { var maxLength = 250; if (title && title.length > maxLength) { alert("title must be less than " + maxLength + " characters."); return title.substring(0, maxLength); } return title; } //preview a form template in a modal scope.viewFormTemplate = function (simpleFormTemplate) { //get the template and then pass it to the modal controller function to display it myFormsDataContext.getFormTemplate(simpleFormTemplate.id).then(function (formTemplate) { $modal.open({ templateUrl: templatePath + 'previewform.html', controller: previewFormModalController, size: 'lg', resolve: { formTemplate: function () { return formTemplate; } } }); }); } //manage a question in a modal scope.manageQuestion = function (question) { var cssClass; if (scope.formType == 5) { cssClass = ''; } else { cssClass = 'manage-question-modal'; } $modal.open({ templateUrl: templatePath + 'managequestion.html', controller: manageQuestionModalController, windowClass: cssClass, backdrop: 'static', resolve: { question: function () { return question; }, allQuestions: function () { return scope.template.sections[0].questions; }, template: function () { return scope.template; }, formType: function () { return scope.formType; } } }).result.then(function () { //Update total score for all questions calculateTemplateTotalScore(); //Update the scoring bands based on the new total score refreshScoringBands(); }); } //manage the form title in a modal scope.manageTitle = function (template) { $modal.open({ templateUrl: templatePath + 'managetitle.html', controller: manageTitleModalController, windowClass: 'manage-question-modal', resolve: { template: function () { return template; } } }); } //delete a question scope.deleteQuestion = function (question) { var index = scope.template.sections[0].questions.indexOf(question); if (index > -1) { scope.template.sections[0].questions.splice(index, 1); scope.$apply; } //Update total score for all questions calculateTemplateTotalScore(); //Update the scoring bands based on the new total score refreshScoringBands(); }; //change the availability dates and orgs for the template scope.changeAvailabilityTemplate = function (simpleFormTemplate) { myFormsDataContext.getFormTemplate(simpleFormTemplate.id).then(function (formTemplate) { scope.publishTemplate(formTemplate, false, true); }); } scope.changeAvailabilityChooser = function (simpleFormTemplate) { myFormsDataContext.getFormTemplate(simpleFormTemplate.id).then(function (formTemplate) { $modal.open({ templateUrl: templatePath + 'sharechooser.html', controller: shareChooserController, windowClass: 'publish-modal', backdrop: 'static', resolve: { scope: function () { return scope; }, formTemplate: function () { return formTemplate; }, saveBeforePublish: function () { return false; }, saveTemplate: function () { return false; }, templatePublished: function () { return true; }, isChangeAvailabilityOnly: function () { return true; }, templateAvailabilityChanged: function () { return true; } } }); }); }; scope.publishAndShareActivity = function (formTemplate, isChangeAvailabilityOnly) { scope.saveTemplate().then(function (id) { doPublish(id, null); formTemplate.id = id; $modal.open({ templateUrl: templatePath + 'sharechooser.html', controller: shareChooserController, windowClass: 'publish-modal', backdrop: 'static', resolve: { scope: function () { return scope; }, formTemplate: function () { return formTemplate; }, saveBeforePublish: function () { return false; //don't need to save before publishing }, saveTemplate: function () { return false; //don't need to save before publishing }, templatePublished: function () { return scope.templatePublished; }, isChangeAvailabilityOnly: function () { return isChangeAvailabilityOnly; }, templateAvailabilityChanged: function () { return scope.templateAvailabilityChanged; } } }); }); }; //close the modal without doing owt $scope.cancel = function () { $modal.close(); }; //publish a template - once published it cannot be edited scope.publishTemplate = function (formTemplate, saveBeforePublish, isChangeAvailabilityOnly, dontSetAvailability) { //scope.isSaving = true; if (!dontSetAvailability) { $modal.open({ templateUrl: templatePath + 'publishform.html', controller: publishController, windowClass: 'publish-modal', backdrop: 'static', resolve: { scope: function () { return scope; }, formTemplate: function () { return formTemplate; }, saveBeforePublish: function () { return saveBeforePublish; }, saveTemplate: function () { return scope.saveTemplate; }, templatePublished: function () { return scope.templatePublished; }, isChangeAvailabilityOnly: function () { return isChangeAvailabilityOnly; }, templateAvailabilityChanged: function () { return scope.templateAvailabilityChanged; } } }); } else { //Don't need to show availability screen, just set save then publish scope.saveTemplate().then(function (templateId) { return doPublish(templateId, null); }); } }; function doPublish(templateId, data) { myFormsAdminDataContext.publishFormTemplate(templateId, data).then(function (result) { scope.templatePublished(); }); } //retire a form template (users cannot complete any new forms from the template) scope.retireTemplate = function (simpleFormTemplate) { //First see if the form template is being used on any task definition taskAdminDataContext.getTaskRecordsByFormTemplate(simpleFormTemplate.id).then(function(formsUsingTask){ if(formsUsingTask != null && formsUsingTask.length){ //Yes, is in use simpleFormTemplate.formIsBeingUsedOnTask = true; }else{ //No, not in use simpleFormTemplate.formIsBeingUsedOnTask = false; myFormsAdminDataContext.retireFormTemplate(simpleFormTemplate.id).then(function () { //tell the directive host that the retire was a success scope.onFormTemplateRetired()(simpleFormTemplate); getFormTemplates(); }); } }); } //select badges to issue when a form is successfully completed scope.selectBadges = function () { if (scope.template.formTemplateBadges == null) { scope.template.formTemplateBadges = []; } myBadgesSelectorDataContext.showSelectorModal(scope.template.formTemplateBadges, false); }; // Get the already selected badge details scope.getBadgeDetails = function (badge) { myBadgesAdminDataContext.getTemplate(badge.badgeId).then(function (data) { badge.badge = data; return badge; }); }; //file upload perecnt complete function fileUploadProgress(percentComplete) { scope.fileUploadProgress = percentComplete; scope.fileUploadStatus = "Uploading... " + percentComplete + "%"; } function imageUploadError() { } //once the icon image has successfully uploaded save the template function formIconImageUploadSuccess(template, data) { template.iconUri = data.public_id; } scope.iconChanged = function ($files, $file) { if ($files[$files.length - 1].type === 'image/gif' || $files[$files.length - 1].type === 'image/png' || $files[$files.length - 1].type === 'image/jpg' || $files[$files.length - 1].type === 'image/jpeg') { scope.fileIsBad = false; } else { scope.fileIsBad = true; } $file if ($files === null || $files.length === 0) return; if ($files[$files.length - 1].$error && $files[$files.length - 1].$error === 'maxSize') { alert('Maximum size of icon is ' + $files[$files.length - 1].$errorParam); scope.template.icons.pop(); } else { scope.template.isIconChanged = true; scope.template.iconUri = ''; } } //the template has been published scope.templatePublished = function () { scope.showTemplateBuilder = false; scope.showTemplates = true; getFormTemplates(); scope.onFormTemplatePublished()(); } //the template availability has changed scope.templateAvailabilityChanged = function () { scope.showTemplateBuilder = false; scope.showTemplates = true; getFormTemplates(); scope.onTemplateAvailabilityChanged()(); } /* * Event listener for external messages - * e.g. if template builder is in an iframe, the parent can post messages for capturing here */ $.receiveMessage( // Do stuff with the captured event function (event) { //if the event is called 'saveFormTemplate' if (event.data.event === "saveFormTemplate") { if (event.data.name != null && event.data.name !== "") scope.template.name = event.data.name; if (event.data.description != null && event.data.description !== "") scope.template.description = event.data.description; if (event.data.title != null && event.data.title !== "") scope.template.title = event.data.title; doSaveTemplate(); } }); //save the form template function doSaveTemplate() { //Make sure there is at least one question if (scope.template.sections[0].questions.length > 0) { //canDelete is controlled by canEdit scope.template.individualCanDelete = scope.template.individualCanEdit; //canEdit and canCreate are semi-controlled by canView - //i.e. if canView is false, then canCreate and canEdit should be set to false too if(!scope.template.individualCanView){ scope.template.individualCanCreate = false; scope.template.individualCanEdit = false; } if (scope.template.id) { //edit the template return myFormsAdminDataContext.editFormTemplate(scope.template.id, scope.template).then(function (result) { //ensure we have formTemplates (used for single-template editing) if (!scope.formTemplates) { scope.formTemplates = []; scope.formTemplates.push(scope.template); } for (var i = 0; i < scope.formTemplates.length; i++) { if (scope.formTemplates[i].id === scope.template.id) { scope.formTemplates[i].name = scope.template.name; scope.formTemplates[i].description = scope.template.description; scope.formTemplates[i].iconUri = scope.template.iconUri; break; } } //call the form template saved callback if (scope.onFormTemplateSaved) scope.onFormTemplateSaved()(); scope.showTemplateBuilder = false; scope.showTemplates = true; getFormTemplates(); scope.isSaving = false; scope.formTemplates = scope.formTemplates; return scope.template.id; }); } else { //create a new template return myFormsAdminDataContext.createFormTemplate(scope.template).then(function (createdTemplate) { scope.currentUserFetcher()().then(function (user) { scope.template.id = createdTemplate.id; scope.formTemplates.unshift( { id: createdTemplate.id, name: scope.template.name, description: scope.template.description, iconUri: scope.template.iconUri, isPublished: false, isRetired: false, createdOn: new Date(), author: { firstName: user.firstName, lastName: user.lastName } }); scope.onFormTemplateSaved()(); scope.showTemplateBuilder = false; scope.showTemplates = true; getFormTemplates(); scope.isSaving = false; }); return createdTemplate.id; }); } } } //called when an image for an image question has been successfully uploaded function imageQuestionImageUploadSuccess(imageQuestion, data) { //set the url to be the (Cloudinary) public id imageQuestion.imageUrl = data.public_id; } function handleImageUpload() { var imageUploadPromises = []; //check if the template icon needs to be uploaded if (scope.template.icons && scope.template.icons.length > 0 && scope.template.isIconChanged) imageUploadPromises.push(myFormsFileUpload.uploadImage(scope.template, scope.template.icons[scope.template.icons.length - 1], formIconImageUploadSuccess, fileUploadProgress, imageUploadError, myFormsAdminConfig.cloudinaryFormIconUploadPath)); //loop through all the questions for (var i = 0; i < scope.template.sections[0].questions.length; i++) { //for all image questions where a new image has been selected push the upload to our promises array var question = scope.template.sections[0].questions[i]; if (question != null && question.isImageQuestion && question.image && question.isImageChanged) { var image = myFormsAdminService.getQuestionImage(question.image.lastModified); if (image) { imageUploadPromises.push(myFormsFileUpload.uploadImage(question, image, imageQuestionImageUploadSuccess, fileUploadProgress, imageUploadError, myFormsAdminConfig.cloudinaryFormIconUploadPath)); } } } //when all the promises are complete the template details will be updated with the latest public ids etc. - we can now go ahead and save the template return $q.all(imageUploadPromises).then(function (data) { return doSaveTemplate(); }); } //save the form template scope.saveTemplate = function () { scope.isSaving = true; var requiresImageUpload = false; //if the form icon has changed we will need to upload images before saving the form template (because the image urls are part of the template data) if (scope.template.isIconChanged) requiresImageUpload = true; else { //the form icon hasn't changed so lets check if any image question images have changed - loop through all the questions in the template and check for (var i = 0; i < scope.template.sections[0].questions.length; i++) { var question = scope.template.sections[0].questions[i]; var imageChanged = question != null ? question.isImageChanged : false; var imageQuestion = question != null ? question.isImageQuestion : false; if (imageChanged && imageQuestion) { requiresImageUpload = true; break; } } } //if any images (icon or image questions) have changed handle the image uploads if (requiresImageUpload) return handleImageUpload(); //if not go ahead and save the template directly return doSaveTemplate(); } function disableRules() { if (scope.template) { if (scope.template.individualCanView) { scope.rulesDisabled = false; } else { scope.rulesDisabled = true; } } } scope.handleRules = function(){ disableRules(); } //controller for publishing form template var publishController = function ($scope, $modalInstance, $filter, formTemplate, saveBeforePublish, saveTemplate, templatePublished, isChangeAvailabilityOnly, templateAvailabilityChanged) { $scope.formTemplate = formTemplate; $scope.saveBeforePublish = saveBeforePublish; $scope.saveTemplate = saveTemplate; $scope.templatePublished = templatePublished; $scope.templateAvailabilityChanged = templateAvailabilityChanged; $scope.isChangeAvailabilityOnly = isChangeAvailabilityOnly; $scope.formTerm = "form"; $scope.activityInteractionRules = { adminCanCreate: formTemplate.adminCanCreate, canCreate: formTemplate.IndividualCanCreate, canView: formTemplate.IndividualCanView, canEdit: formTemplate.IndividualCanEdit, canDelete: formTemplate.IndividualCanDelete, }; //Allocated orgs if (formTemplate.organisations) $scope.chosenOrganisations = formTemplate.organisations; else $scope.chosenOrganisations = []; //Allocated users if (formTemplate.individualAllocations) $scope.chosenUsers = formTemplate.individualAllocations; else $scope.chosenUsers = []; //Activity type specific settings if (formTemplate.formType == '5') { //If it's an activity allow individual allocations $scope.allocateUsers = true; //If if's an activity show the interaction rules $scope.showInteractionRules = true; $scope.formTerm = "activity"; } //Set up interaction rules $scope.activityInteractionRules = { adminCanCreate: { isSelected: formTemplate.adminCanCreate }, canCreate: { isSelected: formTemplate.individualCanCreate, }, canView: { isSelected: formTemplate.individualCanView, }, canEdit: { isSelected: formTemplate.individualCanEdit, }, canDelete: { isSelected: formTemplate.individualCanDelete, } }; $scope.minStartDate = new Date(); $scope.minEndDate = new Date(); //whenever the from date changes set the mimimum possible value for the end date - so the user cannot set an earlier date for the end date than the from date $scope.$watch('formTemplate.availableFrom', function (theDate) { if (theDate) $scope.minEndDate = theDate; }); //close the modal $scope.cancel = function () { $modalInstance.dismiss('cancel'); }; $scope.isDisabled = false; //set isDisabled to true $scope.disableButton = function () { $scope.isDisabled = true; }; //publish the form template $scope.publish = function () { $scope.buttonClicked = true; var data = { organisations: $scope.chosenOrganisations, makeAvailableToUsers: $scope.formTemplate.makeAvailableToUsers, users: getSelectedUsers() }; if ($scope.formTemplate.availableFrom) data.availableFrom = $scope.formTemplate.availableFrom; if ($scope.formTemplate.availableTo) data.availableTo = $scope.formTemplate.availableTo; //the template may need to be saved before it can be published so do that or else just publish it if ($scope.saveBeforePublish && $scope.saveTemplate) { $scope.saveTemplate().then(function (newId) { if (!$scope.formTemplate.id) $scope.formTemplate.id = newId; doPublish($scope.formTemplate.id, data); }); } else doPublish($scope.formTemplate.id, data); } /* * Get a nice list of the chosen users with their interaction rules set */ function getSelectedUsers() { var selectedUsers = []; angular.forEach($scope.chosenUsers, function (userId) { //user.canCreate = $scope.activityInteractionRules.canCreate.isSelected; //user.canView = $scope.activityInteractionRules.canView.isSelected; //user.canEdit = $scope.activityInteractionRules.canEdit.isSelected; //user.canDelete = $scope.activityInteractionRules.canEdit.isSelected; selectedUsers.push({ userId: userId, canCreate: $scope.activityInteractionRules.canCreate.isSelected, canView: $scope.activityInteractionRules.canView.isSelected, canEdit: $scope.activityInteractionRules.canEdit.isSelected, canDelete: $scope.activityInteractionRules.canEdit.isSelected, //note: delete is controlled by edit }); }); return selectedUsers; } //change the form availability $scope.changeAvailability = function () { //var selectedUserIds = $filter('filter')($scope.chosenUsers, { isSelected: true }); var data = { organisations: $scope.chosenOrganisations, makeAvailableToUsers: $scope.formTemplate.makeAvailableToUsers, users: getSelectedUsers(), allocationRulesModel: { adminCanCreate: $scope.activityInteractionRules.adminCanCreate.isSelected, individualCanCreate: $scope.activityInteractionRules.canCreate.isSelected, individualCanView: $scope.activityInteractionRules.canView.isSelected, individualCanEdit: $scope.activityInteractionRules.canEdit.isSelected, individualCanDelete: $scope.activityInteractionRules.canEdit.isSelected } }; if ($scope.formTemplate.availableFrom) data.availableFrom = $scope.formTemplate.availableFrom; if ($scope.formTemplate.availableTo) data.availableTo = $scope.formTemplate.availableTo; myFormsAdminDataContext.changeAvailabilityFormTemplate($scope.formTemplate.id, data).then(function (result) { $modalInstance.close(); $scope.templateAvailabilityChanged(); }); } //publish the form template function doPublish(templateId, data) { myFormsAdminDataContext.publishFormTemplate($scope.formTemplate.id, data).then(function (result) { $scope.formTemplate.isPublished = true; $modalInstance.close(); $scope.templatePublished(); }); } }; //controller for the preview form modal var previewFormModalController = function ($scope, $modalInstance, formTemplate) { //we are previewing what the form would look like for the end user - so we need to convert the template into a form - the forms service does this for us $scope.theForm = myFormsService.createFormFromTemplate(formTemplate); //close the modal $scope.ok = function () { $modalInstance.close(); } }; var manageTitleModalController = function ($scope, $modalInstance, template) { $scope.titleSaving = false; $scope.template = template; if ($scope.template.title === "Form Title Goes Here") $scope.template.tempTitle = ""; else $scope.template.tempTitle = template.title; $scope.ok = function () { $scope.template.title = $scope.template.tempTitle; $modalInstance.close(); } $scope.isDisabled = false; //set isDisabled to true $scope.disableButton = function () { $scope.isDisabled = true; }; //close the modal without doing owt $scope.cancel = function () { $scope.titleSaving = true; $scope.template.tempTitle = ''; $modalInstance.dismiss('cancel'); }; } //controller for the manage question modal var manageQuestionModalController = function ($scope, $modalInstance, question, allQuestions, template, formType) { $scope.choicesValid = true; $scope.formType = formType; $scope.originalQuestion = question; $scope.isDataIdentifierUnique = true; $scope.allQuestions = allQuestions; $scope.questionOptions = angular.copy(formBuilderConfig); //By default, wordcount should be visible $scope.questionOptions.displayWordCount = true; if ($scope.formType == 5) { $scope.questionOptions.displayExcludeCheckbox = false; $scope.questionOptions.displayRequiredCheckbox = false; $scope.questionOptions.displaySupportingText = false; $scope.questionOptions.displayWordCount = false; $scope.questionOptions.displayChecklistQuestion = false; $scope.questionOptions.displayDataIdentifier = false; $scope.questionOptions.allowNumberTarget = true; $scope.questionOptions.titleLabel = "Field name"; $scope.questionOptions.titleLabel = "Field name"; } //default value for the image position for image questions if (question.isImageQuestion && !question.imageHorizontalPosition) question.imageHorizontalPosition = 'Center'; //default choices if (question.isChoiceQuestion || question.isCheckListQuestion) { if (!question.choices) question.choices = []; } //we are editing and may want to cancel the edit - so deep copy the original question in case we need to revert to it $scope.question = angular.copy(question); $scope.question.validation.maxTextLength = parseInt($scope.question.validation.maxTextLength); $scope.question.validation.minTextLength = parseInt($scope.question.validation.minTextLength); if ($scope.question.questionText === 'Untitled') $scope.question.questionText = ''; $scope.question.currentChoice = {}; $scope.question.currentAnswer = { correct: false, }; $scope.showQuizScore = !template.isPassMarkBasedOnCorrectAnswers; //add a choice to the list of choices $scope.addChoice = function () { for (var i = 0; i < $scope.question.choices.length; i++) { //ensure the added choice doesn't already exist if ($scope.question.choices[i].choice === $scope.question.currentChoice.choice) { alert("cannot choose the same value."); return false; } } $scope.question.choices.push($scope.question.currentChoice); $scope.question.currentChoice = {}; } function setCurrentAnswer() { $scope.question.currentAnswer = { correct: true, }; if ($scope.question.isMultiChoiceQuestion) { $scope.question.currentAnswer = { correct: false, }; } } setCurrentAnswer(); //add a choice to the list of choices $scope.addAnswer = function () { for (var i = 0; i < $scope.question.choices.length; i++) { //ensure the added choice doesn't already exist if ($scope.question.choices[i].choice === $scope.question.currentAnswer.choice) { alert("cannot choose the same value."); return false; } } $scope.question.choices.push($scope.question.currentAnswer); //Text and number match question choices are all 'correct' $scope.question.currentAnswer = { correct: true, }; if ($scope.question.isMultiChoiceQuestion) { $scope.question.currentAnswer = { correct: false, }; } } $scope.getTextPlaceholder = function (question) { if (question.isHeading) return "Heading goes here"; if ($scope.formType == 5) { return $scope.questionOptions.titleLabel + " goes here"; } return formBuilderConfig.titleLabel + " goes here"; } //stop the user entering none numeric characters $scope.filterPoint = function ($event) { if (isNaN(String.fromCharCode($event.keyCode))) $event.preventDefault(); }; $scope.handleIsQuestionRequired = function (question) { if (question.validation.isRequired) { if (question.validation.minTextLength === null || question.validation.minTextLength === 0) question.validation.minTextLength = 1; question.minNumAllowed = 1; } else { question.validation.minTextLength = 0; question.minNumAllowed = 0; } } //what is the maximum minium number of choices the question can have $scope.getMaxMinChoicesRequired = function (question) { var value; if (question.validation.maxChoicesRequired && question.validation.maxChoicesRequired > 0) value = question.validation.maxChoicesRequired; else value = question.choices.length; if (!$scope.$$phase) $scope.$apply(); return value; } //what is the minimum maximum number of choices the question can have $scope.getMinMaxChoicesRequired = function (question) { if (question.validation.minChoicesRequired && question.validation.minChoicesRequired > 0) return question.validation.minChoicesRequired; return 0; } //some validation for a question can't be handled by the standard forms validation functionality - this handles stuff that can't $scope.isOtherValidationBad = function (question) { //if its a checklist question do some checks on the number of min / max choices allowed - so they make sense if (question.isCheckListQuestion) { if (question.validation.minChoicesRequired > question.choices.length) return true; if (question.validation.maxChoicesRequired > question.choices.length) return true; return false; } //always check if the question has a label or supporting text (or both) return $scope.hasNeitherLabelOrText(question); } //determine if a question has nether a label or supporting text (a question must have at least one) $scope.hasNeitherLabelOrText = function (question) { if (question.isImageQuestion || question.isTextBlock) return false; if (question.isHeading && !question.questionText) return true; if (question.supportingText && question.questionText !== undefined) return question.supportingText.trim().length === 0 && question.questionText.trim().length === 0; return false; } //validate the text entered as a choice for choice questions $scope.validateChoice = function (value, choices) { var maxLength = 100; var minLength = 1; //make sure the number of characters is not too high if (value && value.length > maxLength) { $scope.choicesValid = false; alert("text must be less than " + maxLength + " characters."); return value.substring(0, maxLength); } //make sure the number of characters is not too low if (value.length < minLength) { $scope.choicesValid = false; alert("text must be more than " + minLength + " characters."); return value; } //ensure the value is not present already for (var i = 0; i < choices.length; i++) { if (choices[i].choice === value) { $scope.choicesValid = false; alert("cannot choose the same value."); return false; } } return value; } $scope.getQuestionTextMaxLength = function (question) { if (question.isHeading) return 500; return 100; } $scope.getMaxPossibleForMaxWordCount = function (question) { if (question.isSimpleTextQuestion) return 100; return 1000; } $scope.imageChanged = function (tempImage) { $scope.question.isImageChanged = true; } //remove a choice $scope.removeChoice = function (choice) { for (var i = 0; i < $scope.question.choices.length; i++) { if ($scope.question.choices[i].choice === choice.choice) { $scope.question.choices.splice(i, 1); break; } } } $scope.getImageUrl = function (question) { var publicId = 'v1441893874/placeholder_vlosor'; if (question && question.imageUrl) publicId = question.imageUrl; return myFormsAdminConfig.formIconBlank + 'h_250/' + publicId + '.png'; } //ensure a dataidentifier entered by the form author is unique $scope.checkDataIdentifierUnique = function (dataIdentifier) { if (!dataIdentifier) $scope.isDataIdentifierUnique = true; else if (dataIdentifier.length < 5) $scope.isDataIdentifierUnique = true; else { for (var i = 0; i < $scope.allQuestions.length; i++) { if ($scope.allQuestions[i].dataIdentifier === dataIdentifier) { $scope.isDataIdentifierUnique = false; return; } } return myFormsAdminDataContext.isDataIdentifierUnique(dataIdentifier).then(function (result) { $scope.isDataIdentifierUnique = result.isUnique; }); } } //commit the changes - copy the temporary object over the original $scope.ok = function () { if ($scope.question.isMultiChoiceQuestion || $scope.question.isNumberMatchQuestion || $scope.question.isTextMatchQuestion) { //Update total score for question var highestScore = 0; angular.forEach($scope.question.choices, function (choice) { if (isNaN(choice.score) || choice.score == null) { choice.score = 0; } if (choice.score > highestScore) { highestScore = choice.score; } }); $scope.question.totalScore = highestScore; } angular.copy($scope.question, $scope.originalQuestion); if ($scope.question.image) { myFormsAdminService.addQuestionImage($scope.question.image.lastModified, $scope.question.image); $scope.originalQuestion.image = { blobUrl: $scope.question.image.blobUrl, lastModified: $scope.question.image.lastModified, name: $scope.question.image.name, type: $scope.question.image.type, size: $scope.question.image.size, lastModifiedDate: $scope.question.image.lastModifiedDate } } //Question image //$scope.question.image = null; //$scope.originalQuestion.image = $scope.question.image; $modalInstance.close(); } //close the modal without doing owt $scope.cancel = function () { $modalInstance.dismiss('cancel'); }; $scope.getRegexPattern = function (question) { if (question != null) { if (question.isNumberMatchQuestion) { return "/^\d+$/"; } } return ""; }; }; var shareChooserController = function ($scope, scope, $modalInstance, $filter, formTemplate, saveBeforePublish, saveTemplate, templatePublished, isChangeAvailabilityOnly, templateAvailabilityChanged) { //$scope.publishTemplate = scope.publishTemplate(formTemplate, saveBeforePublish, isChangeAvailabilityOnly, dontSetAvailability); $scope.templateName = formTemplate.name; //close the modal without doing owt $scope.cancel = function () { $modalInstance.close(); }; //publish a template - once published it cannot be edited $scope.groupSharer = function () { $modalInstance.close(); $modal.open({ templateUrl: templatePath + 'publishform.html', controller: publishController, windowClass: 'publish-modal', backdrop: 'static', resolve: { scope: function () { return scope; }, formTemplate: function () { return formTemplate; }, saveBeforePublish: function () { return saveBeforePublish; }, saveTemplate: function () { return scope.saveTemplate; }, templatePublished: function () { return scope.templatePublished; }, isChangeAvailabilityOnly: function () { return isChangeAvailabilityOnly; }, templateAvailabilityChanged: function () { return scope.templateAvailabilityChanged; } } }); }; $scope.individualSharer = function () { $modalInstance.close(); $modal.open({ templateUrl: templatePath + 'individualsharer.html', controller: userChooserController, windowClass: 'publish-modal', backdrop: 'static', size: 'lg', resolve: { scope: function () { return scope; }, formTemplate: function () { return formTemplate; }, saveBeforePublish: function () { return saveBeforePublish; }, saveTemplate: function () { return scope.saveTemplate; }, templatePublished: function () { return scope.templatePublished; }, isChangeAvailabilityOnly: function () { return isChangeAvailabilityOnly; }, templateAvailabilityChanged: function () { return scope.templateAvailabilityChanged; } } }); }; }; var userChooserController = function ($scope, scope, $modalInstance, $filter, formTemplate, saveBeforePublish, saveTemplate, templatePublished, isChangeAvailabilityOnly, templateAvailabilityChanged) { var controllerId = 'userChooserController'; var getLogFn = common.logger.getLogFn; var logSuccess = getLogFn(controllerId, "success"); var logError = getLogFn(controllerId, "error"); $scope.formTemplate = formTemplate; //close the modal without doing owt $scope.cancel = function () { $modalInstance.close(); }; $scope.selectedUsers = []; //the user(s) to be allocated angular.forEach(formTemplate.individualAllocations, function (allocation) { $scope.selectedUsers.push(allocation.userId); }); $scope.share = function () { myFormsAdminDataContext.allocateIndividuals(formTemplate.id, $scope.selectedUsers, formTemplate.availableFrom, formTemplate.availableTo).then(function (data) { logSuccess("Successfully allocated activity") //Todo: tell the user if it succeeded or not $modalInstance.close(); }); }; }; var defaultTemplatesChooser = function ($scope, scope, $modalInstance) { var controllerId = 'defaultTemplatesChooser'; var getLogFn = common.logger.getLogFn; var logSuccess = getLogFn(controllerId, "success"); var logError = getLogFn(controllerId, "error"); getDefaultActivities(); function getDefaultActivities() { myFormsAdminDataContext.getDefaultActivities().then(function (templates) { $scope.activityTemplates = templates; $scope.defaultsLoaded = true; }); } $scope.cancel = function () { $modalInstance.close(); }; $scope.cloneFromDefault = function (template) { myFormsAdminDataContext.cloneFormTemplate(template.id).then(function (newId) { scope.onFormTemplateCloned()(template); $modalInstance.close(); getFormTemplates(); }); }; }; function setActivityLibraryForOrg() { myFormsAdminDataContext.getActivityLibraryIdsForOrg().then(function (ids) { scope.activityLibrary = ids; }); } setActivityLibraryForOrg(); } }]); //a directive to show all the activities available for admin user to log entries against mod.directive('publishedActivities', ['myFormsDataContext', 'myFormsConfig', 'myUsersDataContext', 'myFormsService', '$rootScope', 'user', '$modal', function (myFormsDataContext, config, myUsersDataContext, myFormsService, $rootScope, user, $modal) { return { restrict: 'E', scope: { publicOnly: '=', //just show public templates onFormSelected: '&', //tell the directive host when a form template has been selected noUser: '=', onActivityEntriesRequested: '&' //tell the directive host when an activity's entries have been fetched }, templateUrl: templatePath + 'publishedactivities.html', link: link }; function link(scope, elem, attrs) { scope.loadedyet = false; scope.myAvailibleFormsLoaded = false; scope.formIconBlank = config.formIconBlank; scope.orderByPredicate = '-lastPublishedOn'; scope.orderByChoice = 'Order by date'; function getUser() { user.getProfile().then(function (user) { scope.user = user; //get all forms available to the user getPublishedActivityTemplates(); }); } if (typeof mobile != 'undefined') { scope.isMobile = mobile; } if (!scope.noUser) { getUser(); } else { getPublishedActivityTemplates(); } //user selected an activity template to complete scope.completeActivity = function (simpleFormTemplate) { var selectedForm; if (simpleFormTemplate.inProgress) { myFormsDataContext.getForm(simpleFormTemplate.inProgressFormId).then(function (theForm) { // handleChoiceQuestionsOnLoading(theForm); selectedForm = theForm; scope.onFormSelected()(selectedForm); $modal.open({ templateUrl: 'app/activities/completeactivity.html', controller: completeActivityController, size: 'sm', backdrop: 'static', resolve: { selectedForm: function () { return selectedForm; } } }); }); } else { myFormsDataContext.getFormTemplate(simpleFormTemplate.id).then(function (formTemplate) { if (scope.user) { formTemplate.externalId = scope.user.id; } selectedForm = myFormsService.createFormFromTemplate(formTemplate); scope.onFormSelected()(selectedForm); $modal.open({ templateUrl: 'app/activities/completeactivity.html', controller: completeActivityController, size: 'sm', backdrop: 'static', resolve: { selectedForm: function () { return selectedForm; } } }); }); } } //user selected an activity to view entries for scope.viewActivityEntries = function (simpleFormTemplate) { myFormsDataContext.getActivityEntries(simpleFormTemplate.id).then(function (entries) { scope.onActivityEntriesRequested()(simpleFormTemplate, entries); }); }; //get all the activity templates allocated to the user either by individual or group allocation function getPublishedActivityTemplates() { if (scope.user) { myFormsDataContext.getAvailableTemplatesToCreateForms(scope.user.id).then(function (formTemplates) { scope.activityTemplates = formTemplates; scope.myAvailibleFormsLoaded = true; }); } else { myFormsDataContext.getAllAvailableTemplates().then(function (formTemplates) { scope.activityTemplates = formTemplates; scope.myAvailibleFormsLoaded = true; }); } } //TODO /* * Add an entry for a user */ scope.addEntryToUser = function (activityEntry) { myFormsDataContext.saveActivityEntry(scope.user.id).then(function (formTemplates) { scope.activityTemplates = formTemplates; scope.myAvailibleFormsLoaded = true; }); }; /* * Fill in an activity entry */ scope.addEntry = function (activityTemplate) { var selectedForm; if (activityTemplate.inProgress) { myFormsDataContext.getForm(activityTemplate.inProgressFormId).then(function (theForm) { //handleChoiceQuestionsOnLoading(theForm); selectedForm = theForm; scope.onFormSelected()(selectedForm); //$modal.open({ // templateUrl: 'app/activities/completeactivity.html', // controller: completeActivityController, // size: 'sm', // backdrop: 'static', // resolve: { // selectedForm: function () { // return selectedForm; // } // } //}); }); } else { myFormsDataContext.getFormTemplate(activityTemplate.id).then(function (formTemplate) { if (scope.user) { formTemplate.externalId = scope.user.id; } selectedForm = myFormsService.createFormFromTemplate(formTemplate); scope.onFormSelected()(selectedForm); //$modal.open({ // templateUrl: 'app/activities/completeactivity.html', // controller: completeActivityController, // size: 'sm', // backdrop: 'static', // resolve: { // selectedForm: function () { // return selectedForm; // } // } //}); }); } }; var completeActivityController = function (common, $scope, $modalInstance, selectedForm) { scope.selectedForm = selectedForm; //close the modal $scope.cancel = function () { $modalInstance.dismiss('cancel'); }; } } }]); //a directive that renders an activity form for completion by an admin mod.directive('adminActivityFormRenderer', ['myFormsDataContext', '$filter', 'user', 'myBadgesAdminDataContext', 'myFormsAdminService', function (myFormsDataContext, $filter, user, myBadgesAdminDataContext, myFormsAdminService) { return { restrict: 'E', scope: { selectedForm: '=', //the activity form to render selectedUsers: '=', //the user(s) that the activity entry is for formId: '=', hideSave: '=', hideCancel: '=', hideComplete: '=', onFormCreated: '&', //the activity form has been created onFormUpdated: '&', //the activity form has been updated onCancelAdd: '&', onCancelUpdate: '&', formValidation: '=', externalId: '=', noUser: '=', isTaskView: '=', entryMode: '=' }, templateUrl: templatePath + 'activityformrenderer.html', link: link }; function link(scope, elem, attrs) { scope.isSaving = false; scope.activityInteractionRules = { adminCanCreate: { isSelected: false }, canView: { isSelected: false }, canCreate: { isSelected: false }, canEdit: { isSelected: false }, canDelete: { isSelected: false }, }; function getUser() { user.getProfile().then(function (user) { scope.user = user; }); } if (scope.selectedForm) { myFormsAdminService.handleChoiceQuestionsOnLoading(scope.selectedForm); //we need to do a bit of extra work to get choice questions to work correctly if (!scope.noUser) { getUser(); } } scope.selectedForm.rules = { adminCanCreate: scope.activityInteractionRules.adminCanCreate.isSelected, individualCanCreate: scope.activityInteractionRules.canCreate.isSelected, individualCanView: scope.activityInteractionRules.canView.isSelected, individualCanEdit: scope.activityInteractionRules.canEdit.isSelected, individualCanDelete: scope.activityInteractionRules.canDelete.isSelected }; scope.submitForm = function(isCreate){ scope.isSaving = true; if (scope.selectedUsers != null && scope.selectedUsers.length) { scope.selectedForm.externalId = scope.selectedUsers[0]; //the selected user's id goes here scope.selectedForm.completedBy = { myUsersId: scope.selectedUsers[0] }; scope.selectedForm.recipients = scope.selectedUsers; } myFormsAdminService.submitForm(isCreate, scope.selectedForm).then(function(result){ updateOrCreateForm(isCreate, scope.selectedForm, result); scope.isSaving = false; }); }; //call the passed in functions when the form is created or updated function updateOrCreateForm(isCreate, selectedForm, result) { console.log(result); if (isCreate) { scope.onFormCreated()(selectedForm, result); if (result.formRewards) { if (result.formRewards.issueRewards) { issueBadges(result); } } } else scope.onFormUpdated()(selectedForm, result); } //when a form is selected get the form details scope.$watch('formId', function () { if (scope.formId) { myFormsDataContext.getForm(scope.formId).then(function (theForm) { myFormsAdminService.handleChoiceQuestionsOnLoading(theForm); scope.selectedForm = theForm; }); } }); scope.$watchCollection('outerForm', function () { scope.formValidation = scope.outerForm.$invalid; }); scope.$on('saveFormResponse', function (event, args) { scope.submitForm(args.isComplete); }); //cancel adding the entry scope.cancelEntryAdd = function () { if (scope.onCancelAdd) scope.onCancelAdd()('/completeactivities'); //(scope.selectedForm); } //cancel editing in the entry scope.cancelEntryUpdate = function () { if (scope.onCancelUpdate) scope.onCancelUpdate()('/completeactivities'); //(scope.selectedForm); } /* * Create issuedbadge records for each of the form template reward record badge ids, * Then bake and issue the badges to the form completer */ function issueBadges(completedForm) { if (completedForm.formRewards != null) { //For each of the badge ids on the form template create an issuedbadge record var badgeTemplateIds = []; for (var i in completedForm.formRewards.externalRewardIds) { badgeTemplateIds.push(completedForm.formRewards.externalRewardIds[i]); } for (var b in badgeTemplateIds) { //This creates an issuedbadge record and immediately bakes it myBadgesAdminDataContext.issueBadgeThenBake({ email: completedForm.formRewards.rewarderEmail, name: '', badgeTemplateId: badgeTemplateIds[b], createdBy: completedForm.formRewards.rewardee, status: 0 // 0 - Not baked 1 - Baked }).then(function (data) { //done }); //myBadgesAdminDataContext.issueBadge({ // email: completedForm.formRewards.rewarderEmail, // name: '', // badgeTemplateId: badgeTemplateIds[b], // createdBy: completedForm.formRewards.rewardee, // status: 0 // 0 - Not baked 1 - Baked //}).then(function (data) { // //Now we have an issuedbadge record for form template reward, bake and issue the badge // myBadgesAdminDataContext.bakeAndIssue([data.id], completedForm.formRewards.rewarderName).then(function () { // //console.log('A badge was issued to you!'); // }); //}); } } } } }]); //directive for the question title mod.directive('manageQuestionTitle', [function () { return { restrict: 'E', scope: { question: '=' }, templateUrl: templatePath + 'managequestiontitle.html', link: link }; function link(scope, elem, attrs) { } }]); mod.directive('questionPreview', ['myFormsConfig', 'fileUpload', 'formBuilderConfig', function (myFormsConfig, fileUpload, formBuilderConfig) { return { restrict: 'E', scope: { questionAnswer: '=?', answer: '=?', formType: '=' }, templateUrl: templatePath + 'questionpreview.html', link: link }; function link(scope, elem, attrs) { scope.questionOptions = angular.copy(formBuilderConfig); //By default, wordcount should be visible scope.questionOptions.displayWordCount = true; if (scope.formType) { if (scope.formType == 5) { scope.questionOptions.displayExcludeCheckbox = false; scope.questionOptions.displayRequiredCheckbox = false; scope.questionOptions.displaySupportingText = false; scope.questionOptions.displayWordCount = false; scope.questionOptions.displayChecklistQuestion = false; } } //get the image url for a question scope.getImageUrl = function (question) { var publicId = 'v1441893874/placeholder_vlosor'; if (question && question.imageUrl) publicId = question.imageUrl; return myFormsConfig.formIconBlank + 'h_250/' + publicId + '.png'; } //work out what is the max word count is for text type questions scope.getValidationTextMaxLength = function (question) { if (question.validation && question.validation.maxTextLength > 0) return question.validation.maxTextLength; return 0; } //work out what is the min word count is for text type questions scope.getValidationTextMinLength = function (question) { if (question.validation && question.validation.minTextLength >= 0) return question.validation.minTextLength; return 0; } /* * handle uploading an image file to a question - work out where to store the file */ scope.uploadQuestionImage = function ($files, $file, question) { $file = $files[$files.length - 1];//get the last file in the file array $file.fileName = $file.name; if (uploadsStore == 0) { //Upload to database uploadImageToDatabase($file, question); } if (uploadsStore == 1) { //Upload to azure blob storage uploadImageToBlobStorage($file, question); } }; /* * decide whether images can be uploaded onto questions */ scope.allowImagesOnQuestions = allowImagesOnQuestions; /* * upload the given image file to the database and set the record id on the given question */ function uploadImageToDatabase(file, question) { fileUpload.uploadToQuestion(file, file, function (fileItem, response) { question.imageData = response; scope.question = question; scope.questionImageUploadProgress = null; }, function (response) { if (!scope.questionImageUploadProgress) { scope.questionImageUploadProgress = 0; } scope.questionImageUploadProgress = response; }, function (response) { console.log('error'); scope.questionImageUploadProgress = null; }); } /* * upload the given image file to azure blob storage and set the url on the given question */ function uploadImageToBlobStorage(file, question) { fileUpload.uploadToBlob(file, file, function (response) { question.iconUri = response.fileUrl; question.iconFileName = file.fileName; scope.question = question; scope.questionImageUploadProgress = null; }, function (response) { if (!scope.questionImageUploadProgress) { scope.questionImageUploadProgress = 0; } scope.questionImageUploadProgress = response; }, function (response) { console.log('error'); scope.questionImageUploadProgress = null; }); } //get the correct icon for the question scope.getIconUri = function (question) { if (question) { //get from database if (uploadsStore == 0) { return fileUpload.getBase64(question.iconFileId); } //get from azure blob storage if (uploadsStore == 1) { var uriWithSas = fileUpload.getBlobUploadUrl(question.iconFileName); if (uriWithSas.$$state) { if (uriWithSas.$$state.value) { return uriWithSas.$$state.value.fullUrl; } } return uriWithSas.fullUrl; } //get from cloudinary if (uploadsStore == 2) { } } }; } }]); //a directive that renders an activity form for completion by an admin mod.directive('activityReportingFormRenderer', ['myFormsAdminDataContext', 'myFormsDataContext', 'myFormsAdminService', '$filter', 'user', '$modal', function (myFormsAdminDataContext, myFormsDataContext, $filter, user, $modal) { return { restrict: 'E', scope: { selectedForm: '=', //the activity form to render onFormSelected: '&', onFormUpdated: '&', onDeleteSelectedEntry: '&', entryMode: '=' }, templateUrl: templatePath + 'activityformreporter.html', controller: activityReportingFormRendererController, controllerAs: 'vm', bindToController: true, }; }]); var activityReportingFormRendererController = function ($scope, user, myFormsDataContext, myFormsAdminDataContext, myFormsService, myFormsAdminService, $filter, $modal) { var vm = this; $scope.isSaving = false; vm.selectedUsers = []; vm.selectedActivity = null; vm.selectUser = true; vm.viewEntries = false; vm.search = ''; $scope.user = null; $scope.selectedUsers = []; //the user(s) to be reported on function getUser() { user.getProfile().then(function (user) { $scope.user = user; getPublishedActivityTemplates(); }); } vm.startDate = $filter('date')(moment().startOf('year').toDate(), "yyyy-MM-dd"); vm.endDate = $filter('date')(moment().endOf('year').add(1, 'days').toDate(), "yyyy-MM-dd"); vm.date = { startDate: vm.startDate, endDate: vm.endDate }; vm.datePickerOptions = { locale: { separator: ' - ' }, eventHandlers: { 'apply.daterangepicker': function (ev, picker) { var start = $filter('date')(ev.model.startDate._d, "yyyy-MM-dd"); var end = $filter('date')(ev.model.endDate._d, "yyyy-MM-dd"); } } } //get all the activity templates allocated to the user either by individual or group allocation function getPublishedActivityTemplates() { if ($scope.user) { myFormsDataContext.getAvailableTemplatesToViewEntries($scope.user.id).then(function (formTemplates) { $scope.activityTemplates = formTemplates; }); } else { myFormsDataContext.getAllAvailableTemplates().then(function (formTemplates) { $scope.activityTemplates = formTemplates; }); } } $scope.$watch('vm.selectedUsers', function (newValue, oldValue) { if (newValue) { $scope.userId = newValue[0]; vm.getActivityEntries(); user.getUserDetail($scope.userId).then(function (user) { vm.selectedUser = user[0]; console.log(user[0]); }); } if (newValue.length > 0) { vm.selectUser = false; vm.viewEntries = true; } }, true); $scope.getUserDetails = function (entry) { user.getUserDetail(entry.createdBy).then(function (data) { entry.user = data[0]; }); } vm.getActivityEntries = function () { if ($scope.userId && vm.selectedActivity) { myFormsAdminDataContext.getActivityEntries(vm.selectedActivity.id, $scope.userId).then(function (entries) { $scope.activityEntries = entries; console.log($scope.activityEntries); }); } } //user selected an activity entry to edit $scope.editEntry = function (simpleFormTemplate) { var selectedForm; simpleFormTemplate.formId = simpleFormTemplate.id; myFormsDataContext.getForm(simpleFormTemplate.id).then(function (theForm) { selectedForm = theForm; return myFormsAdminDataContext.getFormTemplate(theForm.formTemplateId); }).then(function(formTemplate){ selectedForm = myFormsAdminService.createFormFromTemplate(formTemplate, simpleFormTemplate.formId, selectedForm.sections[0].questionAnswers); selectedForm.formId = selectedForm.id; myFormsAdminService.handleChoiceQuestionsOnLoading(selectedForm); vm.onFormSelected()(selectedForm); return $modal.open({ templateUrl: templatePath + 'activityformeditrenderer.html', controller: completeActivityController, size: 'sm', backdrop: 'static', resolve: { selectedForm: function () { return selectedForm; } } }).result; }).then(function(result){ return myFormsAdminService.submitForm(false, result); }).then(function(result){ vm.getActivityEntries(); vm.onFormUpdated()(selectedForm, result); }); }; //user selected an activity entry to edit $scope.deleteEntry = function (simpleFormTemplate) { myFormsDataContext.deleteForm(simpleFormTemplate.id).then(function () { vm.getActivityEntries(); vm.onDeleteSelectedEntry(simpleFormTemplate); }); }; getUser(); } var completeActivityController = function (common, $scope, $modalInstance, selectedForm) { $scope.selectedForm = selectedForm; //close the modal $scope.cancel = function () { $modalInstance.dismiss('cancel'); }; $scope.submitForm = function(){ $modalInstance.close($scope.selectedForm); } } //a directive that renders the activities library chooser for the admin to select a library template for their org mod.directive('activitiesLibraryChooser', ['myFormsAdminDataContext', 'myFormsDataContext', '$filter', 'user', function (myFormsAdminDataContext, myFormsDataContext, $filter, user) { return { restrict: 'E', //scope: { // orgId: '=', //}, templateUrl: templatePath + 'activitieslibrarychooser.html', controller: activitiesLibraryChooserController, controllerAs: 'vm', bindToController: true }; }]); var activitiesLibraryChooserController = function ($scope, user, myFormsDataContext, myFormsAdminDataContext, $filter) { var vm = this; $scope.isSaving = false; $scope.activities = []; $scope.selectedActivities = []; getDefaultActivities(); getSelectedActivities(); function getDefaultActivities() { myFormsAdminDataContext.getDefaultActivities().then(function (templates) { $scope.activities = templates; $scope.ready = true; }); } function getSelectedActivities() { myFormsAdminDataContext.getActivityLibraryIdsForOrg().then(function (ids) { $scope.selectedActivities = ids; angular.forEach($scope.activities, function (act) { var activities = $scope.selectedActivities.filter(function (acti) { return acti == act.id; }); if (activities.length) { act.selected = true; } }); }); } $scope.activityChecked = function (activity) { if (!angular.isArray($scope.selectedActivities)) { return null; } var activities = $scope.selectedActivities.filter(function (act) { return act == activity.id; }); if (activity.selected) { if (activities.length) { } else { $scope.selectedActivities.push(activity.id); } } else { if (activities.length) { angular.forEach(activities, function (act) { var index = $scope.selectedActivities.indexOf(act); $scope.selectedActivities.splice(index, 1); }); } } }; $scope.submit = function () { $scope.isSaving = true; $scope.ready = false; myFormsAdminDataContext.saveActivityLibrary($scope.selectedActivities).then(function (data) { $scope.isSaving = false; $scope.ready = true; }); }; }; mod.filter('dateRange', function () { return function (items, startDate, endDate) { var retArray = []; if (!startDate && !endDate) { return items; } angular.forEach(items, function (obj) { var receivedDate = obj.date; if (moment(receivedDate).isAfter(startDate) && moment(receivedDate).isBefore(endDate)) { retArray.push(obj); } }); return retArray; } }) })();