+
diff --git a/UI/Templates/UIxPageFrame.wox b/UI/Templates/UIxPageFrame.wox
index a780fad43..a43307a88 100644
--- a/UI/Templates/UIxPageFrame.wox
+++ b/UI/Templates/UIxPageFrame.wox
@@ -220,6 +220,7 @@
+
diff --git a/UI/WebServerResources/Gruntfile.js b/UI/WebServerResources/Gruntfile.js
index 7952eb17e..4c4d22c87 100644
--- a/UI/WebServerResources/Gruntfile.js
+++ b/UI/WebServerResources/Gruntfile.js
@@ -105,6 +105,7 @@ module.exports = function(grunt) {
'<%= src %>/angular-aria/angular-aria{,.min}.js{,.map}',
'<%= src %>/angular-material/angular-material{,.min}.js{,.map}',
'<%= src %>/angular-ui-router/release/angular-ui-router{,.min}.js',
+ '<%= src %>/ui-router-extras/release/ct-ui-router-extras{,.min}.js',
'<%= src %>/angular-recursion/angular-recursion{,.min}.js',
'<%= src %>/angular-vs-repeat/src/angular-vs-repeat{,.min}.js',
'<%= src %>/angular-file-upload/angular-file-upload{,.min}.js{,map}',
diff --git a/UI/WebServerResources/bower.json b/UI/WebServerResources/bower.json
index 0e2cbc367..f93476ae5 100644
--- a/UI/WebServerResources/bower.json
+++ b/UI/WebServerResources/bower.json
@@ -6,6 +6,7 @@
"angular-animate": "1.3.x",
"angular-sanitize": "1.3.x",
"angular-ui-router": "latest",
+ "ui-router-extras": "latest",
"angular-recursion": "latest",
"angular-vs-repeat": "latest",
"angular-file-upload": "latest",
diff --git a/UI/WebServerResources/js/Appointments/component-model.js b/UI/WebServerResources/js/Appointments/component-model.js
index 59905fcef..dab6b1f62 100644
--- a/UI/WebServerResources/js/Appointments/component-model.js
+++ b/UI/WebServerResources/js/Appointments/component-model.js
@@ -11,7 +11,7 @@
function Component(futureComponentData) {
// Data is immediately available
if (typeof futureComponentData.then !== 'function') {
- angular.extend(this, futureComponentData);
+ this.init(futureComponentData);
}
else {
// The promise will be unwrapped first
@@ -29,7 +29,8 @@
$q: $q,
$timeout: $timeout,
$log: $log,
- $$resource: new Resource(Settings.baseURL, Settings.activeUser)
+ $$resource: new Resource(Settings.baseURL, Settings.activeUser),
+ $categories: window.UserDefaults.SOGoCalendarCategoriesColors
});
return Component; // return constructor
@@ -73,6 +74,33 @@
return this.$unwrapCollection(type, futureComponentData);
};
+ /**
+ * @memberof Card
+ * @desc Fetch a card from a specific addressbook.
+ * @param {string} addressbook_id - the addressbook ID
+ * @param {string} card_id - the card ID
+ * @see {@link AddressBook.$getCard}
+ */
+ Component.$find = function(calendarId, componentId) {
+ var futureComponentData = this.$$resource.fetch([calendarId, componentId].join('/'), 'view');
+
+ return new Component(futureComponentData);
+ };
+
+ /**
+ * @function filterCategories
+ * @memberof Component.prototype
+ * @desc Search for categories matching some criterias
+ * @param {string} search - the search string to match
+ * @returns a collection of strings
+ */
+ Component.filterCategories = function(query) {
+ var re = new RegExp(query, 'i');
+ return _.filter(_.keys(Component.$categories), function(category) {
+ return category.search(re) != -1;
+ });
+ };
+
/**
* @function $eventsBlocksForView
* @memberof Component.prototype
@@ -198,6 +226,56 @@
return deferred.promise;
};
+ Component.prototype.init = function(data) {
+ this.categories = [];
+ angular.extend(this, data);
+ };
+
+ /**
+ * @function getClassName
+ * @memberof Component.prototype
+ * @desc Return the component CSS class name based on its container (calendar) ID.
+ * @param {string} [base] - the prefix to add to the class name (defaults to "fg")
+ * @returns a string representing the foreground CSS class name
+ */
+ Component.prototype.getClassName = function(base) {
+ if (angular.isUndefined(base))
+ base = 'fg';
+ return base + '-folder' + (this.pid || this.c_folder);
+ };
+
+ /**
+ * @function $reset
+ * @memberof Card.prototype
+ * @desc Reset the original state the card's data.
+ */
+ Component.prototype.$reset = function() {
+ var _this = this;
+ angular.forEach(this, function(value, key) {
+ if (key != 'constructor' && key[0] != '$') {
+ delete _this[key];
+ }
+ });
+ angular.extend(this, this.$shadowData);
+ this.$shadowData = this.$omit(true);
+ };
+
+ /**
+ * @function $save
+ * @memberof Component.prototype
+ * @desc Save the component to the server.
+ */
+ Component.prototype.$save = function() {
+ var _this = this;
+
+ return Component.$$resource.save([this.pid, this.id].join('/'), this.$omit())
+ .then(function(data) {
+ // Make a copy of the data for an eventual reset
+ _this.$shadowData = _this.$omit(true);
+ return data;
+ });
+ };
+
/**
* @function $unwrap
* @memberof Component.prototype
@@ -215,7 +293,9 @@
this.$futureComponentData.then(function(data) {
// Calling $timeout will force Angular to refresh the view
Component.$timeout(function() {
- angular.extend(_this, data);
+ _this.init(data);
+ // Make a copy of the data for an eventual reset
+ _this.$shadowData = _this.$omit();
deferred.resolve(_this);
});
}, function(data) {
diff --git a/UI/WebServerResources/js/Common/ui-desktop.js b/UI/WebServerResources/js/Common/ui-desktop.js
index 2d8eb488f..c56ad6274 100644
--- a/UI/WebServerResources/js/Common/ui-desktop.js
+++ b/UI/WebServerResources/js/Common/ui-desktop.js
@@ -22,7 +22,7 @@
template:
'
' +
'
' +
- '
' + l('OK') + '' +
+ '
' + l('OK') + '' +
'
',
windowClass: 'small',
controller: function($scope, $modalInstance) {
@@ -455,7 +455,7 @@
* @param {Function} sgSubscribeOnSelect - the function to call when subscribing to a folder
* @example:
-
+
Subscribe ..
*/
.directive('sgSubscribe', [function() {
console.debug('registering sgSubscribe');
@@ -466,11 +466,8 @@
onFolderSelect: '&sgSubscribeOnSelect'
},
replace: false,
- link: function(scope, element, attrs, controller) {
- element.on('click', controller.showDialog);
- },
- controllerAs: 'vm',
bindToController: true,
+ controllerAs: 'vm',
controller: ['$scope', '$mdDialog', function($scope, $mdDialog) {
var vm = this;
vm.showDialog = function() {
@@ -495,6 +492,9 @@
});
};
}],
+ link: function(scope, element, attrs, controller) {
+ element.on('click', controller.showDialog);
+ }
};
}])
@@ -704,9 +704,12 @@
},
template:
''
}
}])
@@ -755,16 +758,22 @@
block: '=sgBlock'
},
replace: true,
- template:
- '
' +
- '
' +
- '
' +
- '
' +
- '
{{ block.component.c_title }}
' +
- '
' +
- '
' +
- '
' +
- '
',
+ template: [
+ '
',
+ '
',
+ '
',
+ '
',
+ '
{{ block.component.c_title }}',
+ ' ',
+ ' ',
+ ' ',
+ ' ',
+ '
',
+ '
',
+ '
',
+ '
',
+ '
'
+ ].join(''),
link: link
};
@@ -786,7 +795,7 @@
iElement.css('right', right + '%');
iElement.addClass('starts' + scope.block.start);
iElement.addClass('lasts' + scope.block.length);
- iElement.addClass('folder' + scope.block.component.c_folder);
+ iElement.addClass('bg-folder' + scope.block.component.c_folder);
}
}])
@@ -852,7 +861,7 @@
};
function link(scope, iElement, attrs) {
- iElement.addClass('folder' + scope.block.component.c_folder);
+ iElement.addClass('bg-folder' + scope.block.component.c_folder);
}
}]);
diff --git a/UI/WebServerResources/js/SchedulerUI.js b/UI/WebServerResources/js/SchedulerUI.js
index d05a42c9a..530797136 100644
--- a/UI/WebServerResources/js/SchedulerUI.js
+++ b/UI/WebServerResources/js/SchedulerUI.js
@@ -7,7 +7,7 @@
angular.module('SOGo.Common', []);
angular.module('SOGo.ContactsUI', []);
- angular.module('SOGo.SchedulerUI', ['ngSanitize', 'ui.router', 'vs-repeat', 'SOGo.Common', 'SOGo.UI', 'SOGo.UIDesktop', 'SOGo.ContactsUI'])
+ angular.module('SOGo.SchedulerUI', ['ngSanitize', 'ui.router', 'ct.ui.router.extras.sticky', 'ct.ui.router.extras.previous', 'vs-repeat', 'SOGo.Common', 'SOGo.UI', 'SOGo.UIDesktop', 'SOGo.ContactsUI'])
.constant('sgSettings', {
baseURL: ApplicationBaseURL,
@@ -39,6 +39,8 @@
})
.state('calendars.view', {
url: '/{view:(?:day|week|month)}/:day',
+ sticky: true,
+ deepStateRedirect: true,
views: {
calendarView: {
templateUrl: function($stateParams) {
@@ -56,6 +58,21 @@
return Component.$eventsBlocksForView($stateParams.view, $stateParams.day.asDate());
}]
}
+ })
+ .state('calendars.component', {
+ url: '/:calendarId/event/:componentId',
+ views: {
+ componentEditor: {
+ templateUrl: 'UIxAppointmentEditorTemplate',
+ controller: 'ComponentController',
+ controllerAs: 'editor'
+ }
+ },
+ resolve: {
+ stateComponent: ['$stateParams', 'sgCalendar', function($stateParams, Calendar) {
+ return Calendar.$get($stateParams.calendarId).$getComponent($stateParams.componentId);
+ }]
+ }
});
$urlRouterProvider.when('/calendar/day', function() {
@@ -198,7 +215,7 @@
};
}])
- .controller('CalendarListController', ['$scope', '$rootScope', '$timeout', 'sgFocus', 'encodeUriFilter', 'sgDialog', 'sgSettings', 'sgCalendar', 'sgComponent', function($scope, $rootScope, $timeout, focus, encodeUriFilter, Dialog, Settings, Calendar, Component) {
+ .controller('CalendarListController', ['$scope', '$rootScope', '$timeout', 'sgFocus', 'encodeUriFilter', 'sgDialog', 'sgSettings', 'sgCalendar', 'sgComponent', '$mdSidenav', function($scope, $rootScope, $timeout, focus, encodeUriFilter, Dialog, Settings, Calendar, Component, $mdSidenav) {
// Scope variables
this.component = Component;
this.componentType = null;
@@ -241,6 +258,64 @@
ctrl.blocks = data;
});
}));
+ }])
+
+ .controller('ComponentController', ['$scope', '$log', '$timeout', '$state', '$previousState', '$mdSidenav', '$mdDialog', 'sgCalendar', 'sgComponent', 'stateCalendars', 'stateComponent', function($scope, $log, $timeout, $state, $previousState, $mdSidenav, $mdDialog, Calendar, Component, stateCalendars, stateComponent) {
+ var vm = this;
+
+ vm.calendars = stateCalendars;
+ vm.event = stateComponent;
+ vm.categories = {};
+ vm.editRecurrence = editRecurrence;
+ vm.cancel = cancel;
+ vm.save = save;
+
+ $scope.$on('$viewContentLoaded', function(event) {
+ $timeout(function() {
+ $mdSidenav('right').open()
+ .then(function() {
+ $scope.$watch($mdSidenav('right').isOpen, function(isOpen, wasOpen) {
+ if (!isOpen) {
+ if ($previousState.get())
+ $previousState.go()
+ else
+ $state.go('calendars');
+ }
+ });
+ });
+ }, 100); // don't ask why
+ });
+
+ function editRecurrence($event) {
+ $mdDialog.show({
+ templateUrl: 'editRecurrence', // UI/Templates/SchedulerUI/UIxRecurrenceEditor.wox
+ controller: RecurrenceController
+ });
+ function RecurrenceController() {
+
+ }
+ }
+
+ function save(form) {
+ if (form.$valid) {
+ vm.event.$save()
+ .then(function(data) {
+ $scope.$emit('calendars:list');
+ $mdSidenav('right').close();
+ }, function(data, status) {
+ console.debug('failed');
+ });
+ }
+ }
+
+ function cancel() {
+ vm.event.$reset();
+ if (vm.event.isNew) {
+ // Cancelling the creation of a card
+ vm.event = null;
+ }
+ $mdSidenav('right').close();
+ }
}]);
})();
diff --git a/UI/WebServerResources/scss/components/sidenav/sidenav.scss b/UI/WebServerResources/scss/components/sidenav/sidenav.scss
index 5707aec70..cb55e463e 100644
--- a/UI/WebServerResources/scss/components/sidenav/sidenav.scss
+++ b/UI/WebServerResources/scss/components/sidenav/sidenav.scss
@@ -21,19 +21,14 @@ md-sidenav {
}
}
-.md-sidenav-right {
- width: $sidenav-right-width;
- max-width: $sidenav-right-width;
-}
-
// MAIN SIDENAV, actually to the left
// ----------------------------------------------------------------------------
.md-sidenav-left {
& md-content,
& md-toolbar {
background-color: inherit;
- //background-image: url("../img/cardboard-transp.png");
- //background-blend-mode: multiply;
+ background-image: url("../img/cardboard-transp.png");
+ background-blend-mode: multiply;
}
}
@@ -41,6 +36,16 @@ md-sidenav {
box-shadow: none;
}
+.md-sidenav-right {
+ width: $sidenav-right-width;
+ max-width: $sidenav-right-width;
+ & md-content,
+ & md-toolbar {
+ background-color: inherit;
+ background-image: none;
+ }
+}
+
// MAILER Folder tree (in the left sidenav
// ---------------------------------------
$i: 1;