(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?";
/* apt list */
/* events list */
"Title" = "Title";
"Start" = "Start";
"End" = "End";
@ -502,8 +502,12 @@ vevent_class0 = "(Public event)";
vevent_class1 = "(Private event)";
vevent_class2 = "(Confidential event)";
/* tasks list */
"Priority" = "Priority";
"Category" = "Category";
"Status" = "Status";
"Descending Order" = "Descending Order";
vtodo_class0 = "(Public 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
*
* @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 {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.
@ -1361,13 +1361,12 @@ _computeBlocksPosition (NSArray *blocks)
* @apiExample {curl} Example usage:
* 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} [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 {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 {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[]} tasks List of events
@ -1413,14 +1412,10 @@ _computeBlocksPosition (NSArray *blocks)
endsSecs = (unsigned int) [endDate timeIntervalSince1970];
tasksView = [request formValueForKey: @"filterpopup"];
#warning see TODO in SchedulerUI.js about "setud"
showCompleted = [[request formValueForKey: @"show-completed"] intValue];
if ([request formValueForKey: @"setud"])
{
us = [[context activeUser] userSettings];
[us setBool: showCompleted forKey: @"ShowCompletedTasks"];
[us synchronize];
}
showCompleted = [[request formValueForKey: @"show_completed"] intValue];
us = [[context activeUser] userSettings];
[us setBool: showCompleted forKey: @"ShowCompletedTasks"];
[us synchronize];
tasks = [[self _fetchFields: tasksFields
forComponentOfType: @"vtodo"] objectEnumerator];
@ -1468,7 +1463,7 @@ _computeBlocksPosition (NSArray *blocks)
[filteredTasks sortUsingSelector: @selector (compareTasksLocationAscending:)];
else if ([sort isEqualToString: @"category"])
[filteredTasks sortUsingSelector: @selector (compareTasksCategoryAscending:)];
else if ([sort isEqualToString: @"calendarname"])
else if ([sort isEqualToString: @"calendarName"])
[filteredTasks sortUsingSelector: @selector (compareTasksCalendarNameAscending:)];
else
[filteredTasks sortUsingSelector: @selector (compareTasksAscending:)];

View File

@ -322,8 +322,8 @@
</div>
</div><!-- .md-toolbar-tools -->
<div class="md-toolbar-tools md-toolbar-tools-bottom" layout="row" layout-align="space-between center">
<!-- filter mode -->
<div class="view-list sg-padded--right" layout="row" layout-align="space-between center"
<!-- sort/filter mode -->
<div class="view-list" layout="row" layout-align="space-between center"
ng-hide="list.mode.search">
<div class="sg-toolbar-group">
<md-button class="sg-icon-button" label:aria-label="Search"
@ -333,70 +333,167 @@
</div>
<div class="sg-toolbar-group-last">
<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()">
<md-icon>filter_list</md-icon>
</md-button>
<md-menu-content>
<md-menu-item>
<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"/>
</md-button>
</md-menu-item>
<md-menu-item>
<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"/>
</md-button>
</md-menu-item>
<md-menu-item>
<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"/>
</md-button>
</md-menu-item>
<md-menu-item>
<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"/>
</md-button>
</md-menu-item>
<md-menu-item>
<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"/>
</md-button>
</md-menu-item>
<md-menu-item>
<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"/>
</md-button>
</md-menu-item>
<md-menu-item>
<md-menu-item ng-if="list.componentType == 'events'">
<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"/>
</md-button>
</md-menu-item>
<md-menu-item>
<md-menu-item ng-if="list.componentType == 'events'">
<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"/>
</md-button>
</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-item ng-if="list.componentType == 'tasks'">
<md-button ng-click="list.filter()">
<md-checkbox
ng-model="list.component.$query['show-completed']"
ng-model="list.component.$queryTasks.show_completed"
ng-true-value="1"
ng-false-value="0"><var:string label:value="Show completed tasks"/></md-checkbox>
</md-button>
</md-menu-item>
</md-menu-content>
</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>
<!-- search mode -->

View File

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

View File

@ -39,11 +39,17 @@
$Preferences: Preferences,
$$resource: new Resource(Settings.baseURL, Settings.activeUser),
$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() {
Component.$query.filterpopup = Preferences.settings.CalendarDefaultFilter;
Component.$query['show-completed'] = parseInt(Preferences.settings.ShowCompletedTasks);
Component.$queryEvents.filterpopup = Preferences.settings.CalendarDefaultFilter;
Component.$queryTasks.show_completed = parseInt(Preferences.settings.ShowCompletedTasks);
});
if (window.UserDefaults && window.UserDefaults.SOGoTimeFormat)
Component.timeFormat = window.UserDefaults.SOGoTimeFormat;
@ -74,23 +80,33 @@
day = now.getDate(),
month = now.getMonth() + 1,
year = now.getFullYear(),
defaultParams = {
queryKey = '$query' + type.capitalize(),
params = {
day: '' + year + (month < 10?'0':'') + month + (day < 10?'0':'') + day,
};
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) {
_.find(_.allKeys(options), function(key) {
dirty = (options[key] != Component.$query[key]);
return dirty;
_.each(_.allKeys(options), function(key) {
// Query parameters common to events and tasks are compared
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
otherType = (type == 'tasks')? '$events' : '$tasks';