1 /* This file is part of Indico.
2 * Copyright (C) 2002 - 2015 European Organization for Nuclear Research (CERN).
4 * Indico is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License as
6 * published by the Free Software Foundation; either version 3 of the
7 * License, or (at your option) any later version.
9 * Indico is distributed in the hope that it will be useful, but
10 * WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with Indico; if not, see <http://www.gnu.org/licenses/>.
18 ndRegForm.controller('SectionCtrl', function($scope, $rootScope, regFormFactory) {
19 $scope.sectionApi = {};
22 var getRequestParams = function(section) {
24 confId: $rootScope.confId,
29 $scope.sectionApi.disableSection = function(section) {
30 $scope.$parent.animations.recoverSectionButton = 'button-highlight';
31 regFormFactory.Sections.disable(getRequestParams(section), function(updatedSection) {
32 regFormFactory.processResponse(updatedSection, {
33 success: function(updatedSection) {
34 section.enabled = updatedSection.enabled;
40 $scope.sectionApi.saveConfig = function(section, data) {
41 var requestParams = angular.extend(getRequestParams(section), data);
42 regFormFactory.Sections.save(requestParams, function(updatedSection) {
43 regFormFactory.processResponse(updatedSection, {
44 success: function(updatedSection) {
45 $scope.section = angular.extend($scope.section, updatedSection);
46 if (updatedSection.id == 'sessions') {
47 $scope.fetchSessions();
54 $scope.sectionApi.updateTitle = function(section, data) {
55 var requestParams = angular.extend(getRequestParams(section), data);
57 regFormFactory.Sections.title(requestParams, function(updatedSection) {
58 regFormFactory.processResponse(updatedSection, {
59 success: function(updatedSection) {
60 $scope.section.title = updatedSection.title;
66 $scope.sectionApi.updateDescription = function(section, data) {
67 var requestParams = angular.extend(getRequestParams(section), data);
69 regFormFactory.Sections.description(requestParams, function(updatedSection) {
70 regFormFactory.processResponse(updatedSection, {
71 success: function(updatedSection) {
72 $scope.section.description = updatedSection.description;
78 $scope.sectionApi.moveField = function(section, field, position) {
79 var requestParams = angular.extend(getRequestParams(section), {
84 regFormFactory.Fields.move(requestParams, function(updatedSection) {
85 regFormFactory.processResponse(updatedSection, {
86 success: function(updatedSection) {}
88 // TODO in case backend rejects request we should update scope with something like:
89 // if (response.error) {
90 // $scope.section.items = response.updatedSection.items;
95 $scope.sectionApi.removeField = function(section, field) {
96 $scope.dialogs.removefield.field = field;
98 $scope.dialogs.removefield.callback = function(success) {
100 var requestParams = angular.extend(getRequestParams(section), {
104 $scope.$apply(regFormFactory.Fields.remove(requestParams, {}, function(updatedSection) {
105 regFormFactory.processResponse(updatedSection, {
106 success: function(updatedSection) {
107 $scope.section.items = updatedSection.items;
114 $scope.dialogs.removefield.open = true;
117 $scope.actions.openAddField = function(section, field, type) {
118 $scope.dialogs.newfield = true;
131 ndRegForm.directive('ndSection', function($rootScope, url) {
135 templateUrl: url.tpl('section.tpl.html'),
136 controller: 'SectionCtrl',
138 link: function(scope, element) {
162 scope.$on('collapse', function(event, collapsed) {
163 scope.state.collapsed = collapsed;
166 scope.$watch('state.collapsed', function(val) {
167 var content = angular.element(element.children()[2]);
175 scope.$watch('section.title', function(newVal, oldVal) {
176 if (newVal !== oldVal) {
177 scope.sectionApi.updateTitle(scope.section, {title: newVal});
181 scope.$watch('section.description', function(newVal, oldVal) {
182 if (newVal !== oldVal) {
183 scope.sectionApi.updateDescription(scope.section, {description: newVal});
187 scope.dialogs.config.actions.onOk = function(dialogScope) {
188 if (dialogScope.sectionForm.$invalid === true) {
189 // TODO Uncomment when AngularJS >= 1.2
190 // Current version doesn't generate ngForm names dynamicly
191 // var forms = _.filter($.map(dialogScope.sectionForm, function(value, index) {
193 // }), function(index) {
194 // return index[0] != '$';
197 // var firstInvalid = _.find(dialogScope.sectionForm, function(form) {
198 // return form.$invalid;
201 // var invalid = _.find(forms, function(f) {
202 // return dialogScope.sectionForm[f].$invalid;
205 // dialogScope.$apply(dialogScope.setSelectedTab(firstInvalid));
209 scope.sectionApi.saveConfig(dialogScope.section, dialogScope.formData);
213 scope.dialogs.config.actions.onCancel = function(dialogScope) {
219 ndRegForm.directive("ndGeneralSection", function($timeout, url, sortableoptions) {
221 require: 'ndSection',
222 controller: 'SectionCtrl',
224 link: function(scope) {
225 scope.buttons.newfield = true;
226 scope.buttons.disable = true;
227 scope.tplGeneralField = url.tpl('sections/generalfield.tpl.html');
229 scope.sectionApi.removeNewField = function() {
230 if (scope.section.items[scope.section.items.length-1].id == -1) {
231 $timeout(function() {
232 scope.section.items.pop();
237 scope.fieldSortableOptions = {
238 update: function(e, ui) {
239 scope.sectionApi.moveField(scope.section, ui.item.scope().field, ui.item.index());
241 // TODO Re-enable when solved: http://bugs.jqueryui.com/ticket/5772
242 // containment: '.field-list',
243 handle: ".regform-field .field-sortable-handle",
244 placeholder: "regform-field-sortable-placeholder"
247 angular.extend(scope.fieldSortableOptions, sortableoptions);
252 ndRegForm.directive("ndPersonalDataSection", function() {
254 require: 'ndGeneralSection',
256 link: function(scope) {
257 scope.buttons.disable = false;
262 ndRegForm.directive("ndAccommodationSection", function($rootScope) {
264 require: 'ndSection',
265 link: function(scope) {
266 scope.buttons.config = true;
267 scope.buttons.disable = true;
269 scope.accommodation = {};
270 scope.$watch('userdata.accommodation', function() {
271 if (scope.userdata.accommodation === undefined ||
272 scope.userdata.accommodation.accommodationType === null) {
275 scope.accommodation.typeId = scope.userdata.accommodation.accommodationType.id;
276 scope.accommodation.arrivalDate = scope.userdata.accommodation.arrivalDate;
277 scope.accommodation.departureDate = scope.userdata.accommodation.departureDate;
280 scope.billableOptionPayed = function(userdata) {
281 if (userdata.accommodation !== undefined) {
282 var accommodation = userdata.accommodation.accommodationType || {};
283 return accommodation.billable === true && userdata.paid === true;
289 scope.updateArrival = function(arrival) {
290 scope.arrival = arrival;
291 scope.arrivalUpdated = true;
294 scope.possibleDeparture = function(departure) {
295 if (scope.arrival !== undefined) {
296 var arrival = moment(scope.arrival, 'DD/MM/YYY');
297 departure = moment(departure[0], 'DD/MM/YYY');
298 return arrival.isBefore(departure);
304 scope.dialogs.config.arrivalDates = {
305 sDate: moment($rootScope.confSdate).format('DD/MM/YYYY'),
306 eDate: moment($rootScope.confEdate).format('DD/MM/YYYY')
309 scope.dialogs.config.departureDates = {
310 sDate: moment($rootScope.confSdate).format('DD/MM/YYYY'),
311 eDate: moment($rootScope.confEdate).format('DD/MM/YYYY')
314 scope.dialogs.config.updateArrivalDates = function(offset) {
315 offset = offset || [0, 0];
316 scope.dialogs.config.arrivalDates.sDate =
317 moment($rootScope.confSdate)
318 .subtract('d', parseInt(offset[0], 10))
319 .format('DD/MM/YYYY');
320 scope.dialogs.config.arrivalDates.eDate =
321 moment($rootScope.confEdate)
322 .subtract('d', parseInt(offset[1], 10))
323 .format('DD/MM/YYYY');
325 scope.dialogs.config.updateArrivalDates(scope.section.arrivalOffsetDates);
327 scope.dialogs.config.updateDepartureDates = function(offset) {
328 offset = offset || [0, 0];
329 scope.dialogs.config.departureDates.sDate =
330 moment($rootScope.confSdate)
331 .add('d', parseInt(offset[0], 10))
332 .format('DD/MM/YYYY');
333 scope.dialogs.config.departureDates.eDate =
334 moment($rootScope.confEdate)
335 .add('d', parseInt(offset[1], 10))
336 .format('DD/MM/YYYY');
338 scope.dialogs.config.updateDepartureDates(scope.section.departureOffsetDates);
340 scope.dialogs.config.formData.push('arrivalOffsetDates');
341 scope.dialogs.config.formData.push('departureOffsetDates');
342 scope.dialogs.config.tabs = [
343 {id: 'config', name: $T("Configuration"), type: 'config' },
344 {id: 'editAccomodation', name: $T("Edit accommodations"), type: 'editionTable' }
347 scope.dialogs.config.editionTable = {
350 $T("Accommodation option"),
363 editoptions: {size:"30",maxlength:"50"},
373 edittype:'bool_select',
384 editoptions:{size:"7",maxlength:"20"}
394 editoptions:{size:"7",maxlength:"20"}
403 edittype:'bool_select'
412 ndRegForm.directive("ndFurtherInformationSection", function() {
414 require: 'ndSection',
415 link: function(scope) {
416 scope.buttons.disable = true;
418 scope.$watch('section.content', function(newVal, oldVal) {
419 if (newVal !== oldVal) {
420 scope.sectionApi.updateDescription(scope.section, {description: newVal});
427 ndRegForm.directive("ndReasonSection", function() {
429 require: 'ndSection',
430 link: function(scope) {
431 scope.buttons.disable = true;
436 ndRegForm.directive("ndSessionsSection", function($rootScope, regFormFactory) {
438 require: 'ndSection',
440 controller: function($scope) {
441 var hasSession = function(id) {
442 return _.find($scope.section.items, function(session) {
443 return session.id == id;
447 $scope.anyBillableSessionPayed = function(userdata) {
449 return _.any(userdata.sessionList, function(item) {
450 var session = _.find($scope.section.items, function(session) {
451 return session.id == item.id;
454 return session.billable && session.price !== 0;
461 $scope.anySessionEnabled = function() {
462 return _.any($scope.section.items, function(session) {
463 return session.enabled === true;
467 $scope.fetchSessions = function() {
468 var sessions = regFormFactory.Sessions.query({confId: $rootScope.confId}, function() {
469 _.each(sessions, function (item, ind) {
470 if(!hasSession(item.id)) {
471 $scope.section.items.push({
483 $scope.fetchSessions();
486 link: function(scope) {
487 scope.buttons.config = true;
488 scope.buttons.disable = true;
490 scope.isSelected = function(sessionId) {
491 return _.any(scope.userdata.sessionList, function(e) {
492 return sessionId == e.id;
496 scope.dialogs.config.formData.push('type');
497 scope.dialogs.config.tabs = [
498 {id: 'config', name: $T("Configuration"), type: 'config'},
499 {id: 'editSessions', name: $T("Manage sessions"), type: 'editionTable'}
502 scope.dialogs.config.editionTable = {
519 editoptions:{size:"30",maxlength:"80"},
529 edittype:'bool_select',
539 editoptions:{size:"7",maxlength:"20"}
547 edittype:'bool_select',
556 ndRegForm.directive("ndSocialEventSection", function() {
558 require: 'ndSection',
559 link: function(scope) {
560 scope.buttons.config = true;
561 scope.buttons.disable = true;
562 // keep track of the selected radio item
563 scope.selectedRadioInput = {};
564 // Keep track of the selected checkbox items (multiple)
565 scope.selectedInputs = _.object(_.map(_.filter(scope.section.items, function(item) {
566 return item.cancelled != 'false';
568 return [item.id, false];
570 // Keep track of the number of accompanying people
571 scope.selectedPlaces = _.object(_.map(_.filter(scope.section.items, function(item) {
572 return item.cancelled != 'false';
577 scope.$watch('userdata.socialEvents', function() {
578 angular.forEach(scope.userdata.socialEvents, function(item){
579 scope.selectedRadioInput.id = item.id; // Used when radio buttons
580 scope.selectedInputs[item.id] = true; // Used when checkboxes
581 scope.selectedPlaces[item.id] = scope.getNoPlacesFromUserData(item);
585 scope.anySelected = function() {
586 return _.any(scope.selectedInputs, function(e) {
591 scope.getMaxRegistrations = function(item) {
592 if (item.placesLimit !== 0) {
593 return Math.min(item.maxPlace + 1, item.noPlacesLeft + scope.getNoPlacesFromUserData(item));
595 return item.maxPlace + 1;
599 scope.noAvailableEvent =function() {
600 if (scope.section.items.length === 0) {
603 return _.every(scope.section.items, function(item) {
604 return item.cancelled;
609 scope.anyCancelledEvent = function() {
610 return _.any(scope.section.items, function(item) {
611 return item.cancelled;
615 scope.anyBillableEventPayed = function(userdata) {
617 return _.any(userdata.socialEvents, function(item) {
618 return item.price !== 0;
625 scope.getNoPlacesFromUserData = function(item) {
626 var e = _.find(scope.userdata.socialEvents, function(e) {
627 return item.id == e.id;
629 if (e !== undefined) {
636 scope.getNoPlaces = function(item) {
637 if (scope.section.selectionType == 'multiple' && !scope.selectedInputs[item.id]) {
640 if (scope.section.selectionType == 'unique' && item.id !== scope.selectedRadioInput.id) {
643 return scope.selectedPlaces[item.id];
646 scope.dialogs.config.formData.push('introSentence');
647 scope.dialogs.config.formData.push('mandatory');
648 scope.dialogs.config.formData.push('selectionType');
649 scope.dialogs.config.tabs = [
650 {id: 'config', name: $T("Configuration"), type: 'config'},
651 {id: 'editEvents', name: $T("Edit events"), type: 'editionTable'},
652 {id: 'canceledEvent', name: $T("Canceled events"), type: 'cancelEvent'}
655 scope.dialogs.config.editionTable = {
670 $T('Uncheck to make the attendance free of charge without changing the price'),
671 $T('Price for attending the event'),
672 $T('Maximum amount of participants on the event'),
674 $T('Limit of accompanying persons a participant can bring'),
675 $T('Whether accompanying persons have to pay attendance or not')
680 ['cancel', $T('Cancel this event'),'#tab-canceledEvent','icon-disable']
689 editoptions: {size: "25", maxlength: "50"},
700 edittype:'bool_select'
709 editoptions: {size: "7", maxlength: "20"},
719 editoptions:{size:"5",maxlength:"20"}
726 className: 'accompanying-col',
730 editoptions: {size:"6", maxlength:"20"}
733 name:'pricePerPlace',
734 index:'pricePerPlace',
738 className: 'accompanying-col',
740 edittype:'bool_select'
745 scope.dialogs.config.canceledTable = {
747 colNames:[$T("Event name"), $T("Reason for cancellation")],
748 actions: ['remove', ['uncancel', $T('Uncancel this event'),'#tab-editEvents','icon-checkmark']],
754 editoptions:{size:"30",maxlength:"50"},
759 index:'cancelledReason',
761 editoptions:{size:"30",maxlength:"50"},
772 ndRegForm.directive('ndSectionDialog', function(url) {
776 controller: function($scope) {
777 $scope.templateUrl = url.tpl('sections/dialogs/base.tpl.html');
778 $scope.actions.init = function() {
779 $scope.section = $scope.data;
781 $scope.formData = {};
782 $scope.formData.items = [];
784 _.each($scope.config.formData, function(item) {
785 if (Array.isArray(item) && $scope.section[item[0]] !== undefined) {
786 $scope.formData[item[1]] = angular.copy($scope.section[item[0]][item[1]]);
788 $scope.formData[item] = angular.copy($scope.section[item]);
792 _.each($scope.section.items, function (item, ind) {
793 $scope.formData.items[ind] = angular.copy(item);
796 $scope.tabSelected = $scope.config.tabs[0].id;
799 $scope.addItem = function () {
800 $scope.formData.items.push({id:'isNew', cancelled: false});
804 link: function(scope) {
805 scope.getTabTpl = function(section_id, tab_type) {
806 return url.tpl('sections/dialogs/{0}-{1}.tpl.html'.format(section_id, tab_type));
812 ndRegForm.filter('possibleDeparture', function () {
813 return function (departure, scope) {
814 if (scope.accommodation.arrival !== undefined) {
815 var arrival = moment(scope.accommodation.arrival, 'DD/MM/YYY');
816 var possibleDepartures = {};
817 _.each(scope.section.departureDates, function(value, key) {
818 var departure = moment(key, 'DD/MM/YYY');
819 if(arrival.isBefore(departure) || arrival.isSame(departure)) {
820 possibleDepartures[key] = value;
823 return possibleDepartures;
825 return scope.section.departureDates;