(feat) Events/tasks sorting in Calendar module

pull/91/head
Francis Lachapelle 2015-07-09 16:35:19 -04:00
parent 9a3aeb0004
commit 7c6716784e
5 changed files with 168 additions and 44 deletions

View File

@ -489,7 +489,7 @@ validate_endbeforestart = "The end date that you entered occurs before the st
"A time conflict exists with one or more attendees.\nWould you like to keep the current settings anyway?" "A time conflict exists with one or more attendees.\nWould you like to keep the current settings anyway?"
= "A time conflict exists with one or more attendees.\nWould you like to keep the current settings anyway?"; = "A time conflict exists with one or more attendees.\nWould you like to keep the current settings anyway?";
/* apt list */ /* events list */
"Title" = "Title"; "Title" = "Title";
"Start" = "Start"; "Start" = "Start";
"End" = "End"; "End" = "End";
@ -502,8 +502,12 @@ vevent_class0 = "(Public event)";
vevent_class1 = "(Private event)"; vevent_class1 = "(Private event)";
vevent_class2 = "(Confidential event)"; vevent_class2 = "(Confidential event)";
/* tasks list */
"Priority" = "Priority"; "Priority" = "Priority";
"Category" = "Category"; "Category" = "Category";
"Status" = "Status";
"Descending Order" = "Descending Order";
vtodo_class0 = "(Public task)"; vtodo_class0 = "(Public task)";
vtodo_class1 = "(Private task)"; vtodo_class1 = "(Private task)";

View File

@ -672,7 +672,7 @@ static NSArray *tasksFields = nil;
* curl -i http://localhost/SOGo/so/sogo1/Calendar/eventslist?day=20141201\&filterpopup=view_selectedday * curl -i http://localhost/SOGo/so/sogo1/Calendar/eventslist?day=20141201\&filterpopup=view_selectedday
* *
* @apiParam {Boolean} [asc] Descending sort when false. Defaults to true (ascending). * @apiParam {Boolean} [asc] Descending sort when false. Defaults to true (ascending).
* @apiParam {String} [sort] Sort field. Either title, end, location, or calendarName. * @apiParam {String} [sort] Sort field. Either title, start, end, location, or calendarName.
* @apiParam {Number} [day] Selected day (YYYYMMDD) * @apiParam {Number} [day] Selected day (YYYYMMDD)
* @apiParam {String} [filterpopup] Time period. Either view_today, view_next7, view_next14, view_next31, view_thismonth, view_future, view_selectedday, or view_all * @apiParam {String} [filterpopup] Time period. Either view_today, view_next7, view_next14, view_next31, view_thismonth, view_future, view_selectedday, or view_all
* @apiParam {String} [search] Search field criteria. Either title_Category_Location or entireContent. * @apiParam {String} [search] Search field criteria. Either title_Category_Location or entireContent.
@ -1361,13 +1361,12 @@ _computeBlocksPosition (NSArray *blocks)
* @apiExample {curl} Example usage: * @apiExample {curl} Example usage:
* curl -i http://localhost/SOGo/so/sogo1/Calendar/taskslist?filterpopup=view_all * curl -i http://localhost/SOGo/so/sogo1/Calendar/taskslist?filterpopup=view_all
* *
* @apiParam {Number} [show-completed] Show completed tasks when set to 1. Defaults to ignore completed tasks. * @apiParam {Number} [show_completed] Show completed tasks when set to 1. Defaults to ignore completed tasks.
* @apiParam {Boolean} [asc] Descending sort when false. Defaults to true (ascending). * @apiParam {Boolean} [asc] Descending sort when false. Defaults to true (ascending).
* @apiParam {Boolean} [sort] Sort field. Either title, priority, end, location, category, or calendarname. * @apiParam {Boolean} [sort] Sort field. Either title, priority, end, location, category, calendarname, or status.
* @apiParam {Number} [day] Selected day (YYYYMMDD) * @apiParam {Number} [day] Selected day (YYYYMMDD)
* @apiParam {String} [filterpopup] Time period. Either view_today, view_next7, view_next14, view_next31, view_thismonth, view_overdue, view_incomplete, view_not_started, or view_all * @apiParam {String} [filterpopup] Time period. Either view_today, view_next7, view_next14, view_next31, view_thismonth, view_overdue, view_incomplete, view_not_started, or view_all
* @apiParam {String} [search] Search field criteria. Either title_Category_Location or entireContent. * @apiParam {String} [search] Search field criteria. Either title_Category_Location or entireContent.
* @apiParam {Boolean} [setud] Save 'show-completed' parameter in user's settings
* *
* @apiSuccess (Success 200) {String[]} fields List of fields for each event definition * @apiSuccess (Success 200) {String[]} fields List of fields for each event definition
* @apiSuccess (Success 200) {String[]} tasks List of events * @apiSuccess (Success 200) {String[]} tasks List of events
@ -1413,14 +1412,10 @@ _computeBlocksPosition (NSArray *blocks)
endsSecs = (unsigned int) [endDate timeIntervalSince1970]; endsSecs = (unsigned int) [endDate timeIntervalSince1970];
tasksView = [request formValueForKey: @"filterpopup"]; tasksView = [request formValueForKey: @"filterpopup"];
#warning see TODO in SchedulerUI.js about "setud" showCompleted = [[request formValueForKey: @"show_completed"] intValue];
showCompleted = [[request formValueForKey: @"show-completed"] intValue]; us = [[context activeUser] userSettings];
if ([request formValueForKey: @"setud"]) [us setBool: showCompleted forKey: @"ShowCompletedTasks"];
{ [us synchronize];
us = [[context activeUser] userSettings];
[us setBool: showCompleted forKey: @"ShowCompletedTasks"];
[us synchronize];
}
tasks = [[self _fetchFields: tasksFields tasks = [[self _fetchFields: tasksFields
forComponentOfType: @"vtodo"] objectEnumerator]; forComponentOfType: @"vtodo"] objectEnumerator];
@ -1468,7 +1463,7 @@ _computeBlocksPosition (NSArray *blocks)
[filteredTasks sortUsingSelector: @selector (compareTasksLocationAscending:)]; [filteredTasks sortUsingSelector: @selector (compareTasksLocationAscending:)];
else if ([sort isEqualToString: @"category"]) else if ([sort isEqualToString: @"category"])
[filteredTasks sortUsingSelector: @selector (compareTasksCategoryAscending:)]; [filteredTasks sortUsingSelector: @selector (compareTasksCategoryAscending:)];
else if ([sort isEqualToString: @"calendarname"]) else if ([sort isEqualToString: @"calendarName"])
[filteredTasks sortUsingSelector: @selector (compareTasksCalendarNameAscending:)]; [filteredTasks sortUsingSelector: @selector (compareTasksCalendarNameAscending:)];
else else
[filteredTasks sortUsingSelector: @selector (compareTasksAscending:)]; [filteredTasks sortUsingSelector: @selector (compareTasksAscending:)];

View File

@ -322,8 +322,8 @@
</div> </div>
</div><!-- .md-toolbar-tools --> </div><!-- .md-toolbar-tools -->
<div class="md-toolbar-tools md-toolbar-tools-bottom" layout="row" layout-align="space-between center"> <div class="md-toolbar-tools md-toolbar-tools-bottom" layout="row" layout-align="space-between center">
<!-- filter mode --> <!-- sort/filter mode -->
<div class="view-list sg-padded--right" layout="row" layout-align="space-between center" <div class="view-list" layout="row" layout-align="space-between center"
ng-hide="list.mode.search"> ng-hide="list.mode.search">
<div class="sg-toolbar-group"> <div class="sg-toolbar-group">
<md-button class="sg-icon-button" label:aria-label="Search" <md-button class="sg-icon-button" label:aria-label="Search"
@ -333,70 +333,167 @@
</div> </div>
<div class="sg-toolbar-group-last"> <div class="sg-toolbar-group-last">
<md-menu> <md-menu>
<md-button class="sg-icon-button" label:aria-label="Search" <md-button class="sg-icon-button" label:aria-label="Filter"
ng-click="$mdOpenMenu()"> ng-click="$mdOpenMenu()">
<md-icon>filter_list</md-icon> <md-icon>filter_list</md-icon>
</md-button> </md-button>
<md-menu-content> <md-menu-content>
<md-menu-item> <md-menu-item>
<md-button ng-click="list.filter('view_all')"> <md-button ng-click="list.filter('view_all')">
<md-icon ng-class="{ 'icon-check': list.component.$query.filterpopup == 'view_all' }"> <md-icon ng-class="{ 'icon-check': list.filteredBy('view_all') }">
<!-- selected --></md-icon> <var:string label:value="view_all"/> <!-- selected --></md-icon> <var:string label:value="view_all"/>
</md-button> </md-button>
</md-menu-item> </md-menu-item>
<md-menu-item> <md-menu-item>
<md-button ng-click="list.filter('view_today')"> <md-button ng-click="list.filter('view_today')">
<md-icon ng-class="{ 'icon-check': list.component.$query.filterpopup == 'view_today' }"> <md-icon ng-class="{ 'icon-check': list.filteredBy('view_today') }">
<!-- selected --></md-icon> <var:string label:value="view_today"/> <!-- selected --></md-icon> <var:string label:value="view_today"/>
</md-button> </md-button>
</md-menu-item> </md-menu-item>
<md-menu-item> <md-menu-item>
<md-button ng-click="list.filter('view_next7')"> <md-button ng-click="list.filter('view_next7')">
<md-icon ng-class="{ 'icon-check': list.component.$query.filterpopup == 'view_next7' }"> <md-icon ng-class="{ 'icon-check': list.filteredBy('view_next7') }">
<!-- selected --></md-icon> <var:string label:value="view_next7"/> <!-- selected --></md-icon> <var:string label:value="view_next7"/>
</md-button> </md-button>
</md-menu-item> </md-menu-item>
<md-menu-item> <md-menu-item>
<md-button ng-click="list.filter('view_next14')"> <md-button ng-click="list.filter('view_next14')">
<md-icon ng-class="{ 'icon-check': list.component.$query.filterpopup == 'view_next14' }"> <md-icon ng-class="{ 'icon-check': list.filteredBy('view_next14') }">
<!-- selected --></md-icon> <var:string label:value="view_next14"/> <!-- selected --></md-icon> <var:string label:value="view_next14"/>
</md-button> </md-button>
</md-menu-item> </md-menu-item>
<md-menu-item> <md-menu-item>
<md-button ng-click="list.filter('view_next31')"> <md-button ng-click="list.filter('view_next31')">
<md-icon ng-class="{ 'icon-check': list.component.$query.filterpopup == 'view_next31' }"> <md-icon ng-class="{ 'icon-check': list.filteredBy('view_next31') }">
<!-- selected --></md-icon><var:string label:value="view_next31"/> <!-- selected --></md-icon><var:string label:value="view_next31"/>
</md-button> </md-button>
</md-menu-item> </md-menu-item>
<md-menu-item> <md-menu-item>
<md-button ng-click="list.filter('view_thismonth')"> <md-button ng-click="list.filter('view_thismonth')">
<md-icon ng-class="{ 'icon-check': list.component.$query.filterpopup == 'view_thismonth' }"> <md-icon ng-class="{ 'icon-check': list.filteredBy('view_thismonth') }">
<!-- selected --></md-icon><var:string label:value="view_thismonth"/> <!-- selected --></md-icon><var:string label:value="view_thismonth"/>
</md-button> </md-button>
</md-menu-item> </md-menu-item>
<md-menu-item> <md-menu-item ng-if="list.componentType == 'events'">
<md-button ng-click="list.filter('view_future')"> <md-button ng-click="list.filter('view_future')">
<md-icon ng-class="{ 'icon-check': list.component.$query.filterpopup == 'view_future' }"> <md-icon ng-class="{ 'icon-check': list.component.$queryEvents.filterpopup == 'view_future' }">
<!-- selected --></md-icon> <var:string label:value="view_future"/> <!-- selected --></md-icon> <var:string label:value="view_future"/>
</md-button> </md-button>
</md-menu-item> </md-menu-item>
<md-menu-item> <md-menu-item ng-if="list.componentType == 'events'">
<md-button ng-click="list.filter('view_selectedday')"> <md-button ng-click="list.filter('view_selectedday')">
<md-icon ng-class="{ 'icon-check': list.component.$query.filterpopup == 'view_selectedday' }"> <md-icon ng-class="{ 'icon-check': list.component.$queryEvents.filterpopup == 'view_selectedday' }">
<!-- selected --></md-icon><var:string label:value="view_selectedday"/> <!-- selected --></md-icon><var:string label:value="view_selectedday"/>
</md-button> </md-button>
</md-menu-item> </md-menu-item>
<md-menu-item ng-if="list.componentType == 'tasks'">
<md-button ng-click="list.filter('view_not_started')">
<md-icon ng-class="{ 'icon-check': list.component.$queryTasks.filterpopup == 'view_not_started' }">
<!-- selected --></md-icon><var:string label:value="view_not_started"/>
</md-button>
</md-menu-item>
<md-menu-item ng-if="list.componentType == 'tasks'">
<md-button ng-click="list.filter('view_overdue')">
<md-icon ng-class="{ 'icon-check': list.component.$queryTasks.filterpopup == 'view_overdue' }">
<!-- selected --></md-icon><var:string label:value="view_overdue"/>
</md-button>
</md-menu-item>
<md-menu-item ng-if="list.componentType == 'tasks'">
<md-button ng-click="list.filter('view_incomplete')">
<md-icon ng-class="{ 'icon-check': list.component.$queryTasks.filterpopup == 'view_incomplete' }">
<!-- selected --></md-icon><var:string label:value="view_incomplete"/>
</md-button>
</md-menu-item>
<md-menu-divider ng-if="list.componentType == 'tasks'"><!-- divider --></md-menu-divider> <md-menu-divider ng-if="list.componentType == 'tasks'"><!-- divider --></md-menu-divider>
<md-menu-item ng-if="list.componentType == 'tasks'"> <md-menu-item ng-if="list.componentType == 'tasks'">
<md-button ng-click="list.filter()"> <md-button ng-click="list.filter()">
<md-checkbox <md-checkbox
ng-model="list.component.$query['show-completed']" ng-model="list.component.$queryTasks.show_completed"
ng-true-value="1" ng-true-value="1"
ng-false-value="0"><var:string label:value="Show completed tasks"/></md-checkbox> ng-false-value="0"><var:string label:value="Show completed tasks"/></md-checkbox>
</md-button> </md-button>
</md-menu-item> </md-menu-item>
</md-menu-content> </md-menu-content>
</md-menu> </md-menu>
<md-menu>
<md-button class="sg-icon-button" label:aria-label="Sort"
ng-click="$mdOpenMenu()">
<md-icon>sort</md-icon>
</md-button>
<md-menu-content>
<md-menu-item>
<md-button ng-click="list.sort('title')">
<md-icon ng-class="{ 'icon-check': list.sortedBy('title') }">
<!-- selected --></md-icon> <var:string label:value="Title"/>
</md-button>
</md-menu-item>
<md-menu-item>
<md-button ng-click="list.sort('location')">
<md-icon ng-class="{ 'icon-check': list.sortedBy('location') }">
<!-- selected --></md-icon> <var:string label:value="Location"/>
</md-button>
</md-menu-item>
<md-menu-item>
<md-button ng-click="list.sort('calendarName')">
<md-icon ng-class="{ 'icon-check': list.sortedBy('calendarName') }">
<!-- selected --></md-icon> <var:string label:value="Calendar"/>
</md-button>
</md-menu-item>
<md-menu-item ng-if="list.componentType == 'events'">
<md-button ng-click="list.sort('start')">
<md-icon ng-class="{ 'icon-check': list.sortedBy('start') }">
<!-- selected --></md-icon> <var:string label:value="Start"/>
</md-button>
</md-menu-item>
<md-menu-item ng-if="list.componentType == 'events'">
<md-button ng-click="list.sort('end')">
<md-icon ng-class="{ 'icon-check': list.sortedBy('end') }">
<!-- selected --></md-icon> <var:string label:value="End"/>
</md-button>
</md-menu-item>
<md-menu-item ng-if="list.componentType == 'tasks'">
<md-button ng-click="list.sort('end')">
<md-icon ng-class="{ 'icon-check': list.sortedBy('end') }">
<!-- selected --></md-icon> <var:string label:value="Due Date"/>
</md-button>
</md-menu-item>
<md-menu-item ng-if="list.componentType == 'tasks'">
<md-button ng-click="list.sort('priority')">
<md-icon ng-class="{ 'icon-check': list.sortedBy('priority') }">
<!-- selected --></md-icon> <var:string label:value="Priority"/>
</md-button>
</md-menu-item>
<md-menu-item ng-if="list.componentType == 'tasks'">
<md-button ng-click="list.sort('category')">
<md-icon ng-class="{ 'icon-check': list.sortedBy('category') }">
<!-- selected --></md-icon> <var:string label:value="Category"/>
</md-button>
</md-menu-item>
<md-menu-item ng-if="list.componentType == 'tasks'">
<md-button ng-click="list.sort('status')">
<md-icon ng-class="{ 'icon-check': list.sortedBy('status') }">
<!-- selected --></md-icon> <var:string label:value="Status"/>
</md-button>
</md-menu-item>
<md-menu-divider><!-- divider --></md-menu-divider>
<md-menu-item ng-if="list.componentType == 'events'">
<md-button ng-click="list.component.$filter(list.componentType)">
<md-checkbox
ng-model="list.component.$queryEvents.asc"
ng-true-value="0"
ng-false-value="1"><var:string label:value="Descending Order"/></md-checkbox>
</md-button>
</md-menu-item>
<md-menu-item ng-if="list.componentType == 'tasks'">
<md-button ng-click="list.component.$filter(list.componentType)">
<md-checkbox
ng-model="list.component.$queryTasks.asc"
ng-true-value="0"
ng-false-value="1"><var:string label:value="Descending Order"/></md-checkbox>
</md-button>
</md-menu-item>
</md-menu-content>
</md-menu>
</div> </div>
</div> </div>
<!-- search mode --> <!-- search mode -->

View File

@ -16,6 +16,9 @@
vm.selectComponentType = selectComponentType; vm.selectComponentType = selectComponentType;
vm.newComponent = newComponent; vm.newComponent = newComponent;
vm.filter = filter; vm.filter = filter;
vm.filteredBy = filteredBy;
vm.sort = sort;
vm.sortedBy = sortedBy;
vm.cancelSearch = cancelSearch; vm.cancelSearch = cancelSearch;
vm.mode = { search: false }; vm.mode = { search: false };
@ -26,7 +29,7 @@
vm.selectedList = 1; vm.selectedList = 1;
type = 'tasks'; type = 'tasks';
} }
vm.selectComponentType(type, { reload: true }); selectComponentType(type, { reload: true });
}); });
// Switch between components tabs // Switch between components tabs
@ -49,15 +52,24 @@
} }
function filter(filterpopup) { function filter(filterpopup) {
if (filterpopup) Component.$filter(vm.componentType, options);
Component.$query.filterpopup = filterpopup; }
Component.$filter(vm.componentType, { value: '' }); function filteredBy(filterpopup) {
return Component['$query' + vm.componentType.capitalize()].filterpopup == filterpopup;
}
function sort(field) {
Component.$filter(vm.componentType, { sort: field });
}
function sortedBy(field) {
return Component['$query' + vm.componentType.capitalize()].sort == field;
} }
function cancelSearch() { function cancelSearch() {
vm.mode.search = false; vm.mode.search = false;
filter(); Component.$filter(vm.componentType, { value: '' });
} }
// Refresh current list when the list of calendars is modified // Refresh current list when the list of calendars is modified

View File

@ -39,11 +39,17 @@
$Preferences: Preferences, $Preferences: Preferences,
$$resource: new Resource(Settings.baseURL, Settings.activeUser), $$resource: new Resource(Settings.baseURL, Settings.activeUser),
$categories: window.UserDefaults.SOGoCalendarCategoriesColors, $categories: window.UserDefaults.SOGoCalendarCategoriesColors,
$query: { search: 'title_Category_Location' } // Filter parameters common to events and tasks
$query: { value: '', search: 'title_Category_Location' },
// Filter paramaters specific to events
$queryEvents: { sort: 'start', asc: 1, filterpopup: 'view_next7' },
// Filter parameters specific to tasks
$queryTasks: { sort: 'status', asc: 1, filterpopup: 'view_incomplete' }
}); });
// Initialize filter parameters from user's settings
Preferences.ready().then(function() { Preferences.ready().then(function() {
Component.$query.filterpopup = Preferences.settings.CalendarDefaultFilter; Component.$queryEvents.filterpopup = Preferences.settings.CalendarDefaultFilter;
Component.$query['show-completed'] = parseInt(Preferences.settings.ShowCompletedTasks); Component.$queryTasks.show_completed = parseInt(Preferences.settings.ShowCompletedTasks);
}); });
if (window.UserDefaults && window.UserDefaults.SOGoTimeFormat) if (window.UserDefaults && window.UserDefaults.SOGoTimeFormat)
Component.timeFormat = window.UserDefaults.SOGoTimeFormat; Component.timeFormat = window.UserDefaults.SOGoTimeFormat;
@ -74,23 +80,33 @@
day = now.getDate(), day = now.getDate(),
month = now.getMonth() + 1, month = now.getMonth() + 1,
year = now.getFullYear(), year = now.getFullYear(),
defaultParams = { queryKey = '$query' + type.capitalize(),
params = {
day: '' + year + (month < 10?'0':'') + month + (day < 10?'0':'') + day, day: '' + year + (month < 10?'0':'') + month + (day < 10?'0':'') + day,
}; };
return this.$Preferences.ready().then(function() { return this.$Preferences.ready().then(function() {
var futureComponentData, dirty = false, otherType; var futureComponentData,
dirty = false,
otherType;
angular.extend(_this.$query, params);
angular.extend(_this.$query, defaultParams);
if (options) { if (options) {
_.find(_.allKeys(options), function(key) { _.each(_.allKeys(options), function(key) {
dirty = (options[key] != Component.$query[key]); // Query parameters common to events and tasks are compared
return dirty; dirty |= (_this.$query[key] && options[key] != Component.$query[key]);
// Update either the common parameters or the type-specific parameters
if (angular.isDefined(_this.$query[key]))
_this.$query[key] = options[key];
else
_this[queryKey][key] = options[key];
}) })
angular.extend(_this.$query, options);
} }
futureComponentData = _this.$$resource.fetch(null, type + 'list', _this.$query); // Perform query with both common and type-specific parameters
futureComponentData = _this.$$resource.fetch(null, type + 'list',
angular.extend(_this[queryKey], _this.$query));
// Invalidate cached results of other type if $query has changed // Invalidate cached results of other type if $query has changed
otherType = (type == 'tasks')? '$events' : '$tasks'; otherType = (type == 'tasks')? '$events' : '$tasks';