diff options
author | raimund-schluessler <raimund.schluessler@googlemail.com> | 2014-05-06 22:12:03 +0400 |
---|---|---|
committer | raimund-schluessler <raimund.schluessler@googlemail.com> | 2014-05-06 22:12:03 +0400 |
commit | 6cdfb6cb6895d77fc08d62d2543c3c33a415281f (patch) | |
tree | 9cd42d795689b212c8b7f9e77aa97289f6dbf02a | |
parent | b4d84ffdb830691bea1452eb94c67fa29bcb8478 (diff) |
Set reminders of any type
-rw-r--r-- | css/style.css | 22 | ||||
-rw-r--r-- | css/style.less | 24 | ||||
-rw-r--r-- | img/sprites.svg | 74 | ||||
-rw-r--r-- | js/app/controllers/detailscontroller.coffee | 85 | ||||
-rw-r--r-- | js/app/filters/reminderDetails.coffee | 31 | ||||
-rw-r--r-- | js/app/services/businesslayer/tasksbusinesslayer.coffee | 159 | ||||
-rw-r--r-- | js/app/services/models/tasksmodel.coffee | 4 | ||||
-rw-r--r-- | js/app/services/persistence.coffee | 9 | ||||
-rw-r--r-- | js/public/app.js | 349 | ||||
-rw-r--r-- | l10n/de.php | 18 | ||||
-rw-r--r-- | lib/controller/taskscontroller.php | 51 | ||||
-rw-r--r-- | lib/helper.php | 55 | ||||
-rw-r--r-- | templates/main.php | 2 | ||||
-rw-r--r-- | templates/part.details.php | 6 |
14 files changed, 785 insertions, 104 deletions
diff --git a/css/style.css b/css/style.css index 109014ac..d7ad9958 100644 --- a/css/style.css +++ b/css/style.css @@ -816,6 +816,7 @@ padding: 9px 20px 9px 55px; position: relative; text-shadow: 0 1px 0 white; + border-top: none; } #task-details .body .section input { -moz-box-sizing: border-box; @@ -842,7 +843,8 @@ border-radius: 0px; border: 1px solid #CCC; padding: 0; - width: 100px; + width: 98px; + margin: 0; font-weight: normal; } #task-details .body .section .icon { @@ -867,6 +869,14 @@ #task-details .body .section .icon.detail-start { background-position: -220px -80px; } +#task-details .body .section .icon.detail-remindertype { + display: none; + left: 38px; + opacity: 0.4; +} +#task-details .body .section .icon.detail-remindertype:hover { + opacity: 0.8; +} #task-details .body .section .section-title { color: #9FA2A6; font-weight: bold; @@ -904,6 +914,12 @@ #task-details .body .section.date .section-title.repeat { margin-top: -2px; } +#task-details .body .section.editing .icon.detail-remindertype { + display: block; +} +#task-details .body .section .icon.detail-remindertype { + background-position: -260px -40px; +} #task-details .body .section.detail-reminder .section-description { display: none; font-size: 11px; @@ -943,6 +959,10 @@ input.datepicker-input { input.timepicker-input { width: 80px; } +input.duration-input { + margin: 2px 0 0 0.3em; + width: 35px; +} .ui-widget-content { background: none repeat scroll 0 0 #F9F9F9; border-radius: 4px 4px 4px 4px; diff --git a/css/style.less b/css/style.less index d34814c1..d672bb7f 100644 --- a/css/style.less +++ b/css/style.less @@ -851,6 +851,7 @@ padding: 9px 20px 9px 55px; position: relative; text-shadow: 0 1px 0 white; + border-top:none; input{ -moz-box-sizing: border-box; background: none repeat scroll 0 0 white; @@ -876,7 +877,8 @@ border-radius: 0px; border: 1px solid #CCC; padding:0; - width: 100px; + width: 98px; + margin:0; font-weight: normal; } .icon{ @@ -899,6 +901,14 @@ &.detail-date, &.detail-start { background-position: -220px -80px; } + &.detail-remindertype{ + display: none; + left: 38px; + opacity: 0.4; + &:hover{ + opacity: 0.8; + } + } } .section-title{ color: #9FA2A6; @@ -941,6 +951,14 @@ } } } + &.editing{ + .icon.detail-remindertype{ + display: block; + } + } + .icon.detail-remindertype{ + background-position: -260px -40px; + } &.detail-reminder{ .section-description{ display: none; @@ -992,6 +1010,10 @@ input.datepicker-input{ input.timepicker-input{ width: 80px; } +input.duration-input{ + margin: 2px 0 0 0.3em; + width: 35px; +} .ui-widget-content{ color: #423E3E; background: none repeat scroll 0 0 #F9F9F9; diff --git a/img/sprites.svg b/img/sprites.svg index 9251e93d..7cd220e4 100644 --- a/img/sprites.svg +++ b/img/sprites.svg @@ -1485,34 +1485,72 @@ <g> <g> <defs> - <rect id="SVGID_205_" x="220" y="80" width="20" height="20"/> + <rect id="SVGID_205_" x="260" y="40" width="20" height="20"/> </defs> <clipPath id="SVGID_206_"> <use xlink:href="#SVGID_205_" overflow="visible"/> </clipPath> - <rect x="224.555" y="90.047" clip-path="url(#SVGID_206_)" fill="#FFFFFF" width="10.892" height="1.406"/> - <rect x="224.555" y="92.485" clip-path="url(#SVGID_206_)" fill="#FFFFFF" width="10.892" height="1.406"/> + <polygon clip-path="url(#SVGID_206_)" fill="#737272" points="264.172,45.436 266.518,41.954 268.863,45.436 "/> <g clip-path="url(#SVGID_206_)"> <defs> - <rect id="SVGID_207_" x="220" y="80" width="20" height="20"/> + <rect id="SVGID_207_" x="260" y="40" width="20" height="20"/> </defs> <clipPath id="SVGID_208_"> <use xlink:href="#SVGID_207_" overflow="visible"/> </clipPath> - <path clip-path="url(#SVGID_208_)" fill="none" stroke="#FFFFFF" stroke-width="2" stroke-miterlimit="8" d="M222.875,96.831 - c0,0.11,0.09,0.2,0.201,0.2h13.85c0.109,0,0.199-0.09,0.199-0.2V86.419c0-0.111-0.09-0.2-0.199-0.2h-13.85 - c-0.111,0-0.201,0.089-0.201,0.2V96.831z"/> - <rect x="222.875" y="86.218" clip-path="url(#SVGID_208_)" fill="#FFFFFF" width="14.25" height="2.813"/> - <rect x="225.095" y="83.531" clip-path="url(#SVGID_208_)" fill="#FFFFFF" width="1.11" height="2.438"/> - <rect x="233.797" y="83.531" clip-path="url(#SVGID_208_)" fill="#FFFFFF" width="1.108" height="2.438"/> - <path clip-path="url(#SVGID_208_)" fill="none" stroke="#B0B0B0" stroke-width="2" stroke-miterlimit="8" d="M222.875,95.737 - c0,0.11,0.09,0.2,0.201,0.2h13.85c0.109,0,0.199-0.09,0.199-0.2V85.325c0-0.111-0.09-0.2-0.199-0.2h-13.85 - c-0.111,0-0.201,0.089-0.201,0.2V95.737z"/> - <rect x="222.875" y="85.124" clip-path="url(#SVGID_208_)" fill="#B0B0B0" width="14.25" height="2.813"/> - <rect x="225.095" y="82.437" clip-path="url(#SVGID_208_)" fill="#B0B0B0" width="1.11" height="2.438"/> - <rect x="233.797" y="82.437" clip-path="url(#SVGID_208_)" fill="#B0B0B0" width="1.108" height="2.438"/> - <rect x="224.555" y="89.344" clip-path="url(#SVGID_208_)" fill="#B0B0B0" width="10.892" height="1.406"/> - <rect x="224.555" y="91.781" clip-path="url(#SVGID_208_)" fill="#B0B0B0" width="10.892" height="1.406"/> + <polygon clip-path="url(#SVGID_208_)" fill="none" stroke="#727272" stroke-miterlimit="10" points="264.172,45.436 + 266.518,41.954 268.863,45.436 "/> + </g> + <rect x="265.553" y="45.436" clip-path="url(#SVGID_206_)" fill="#737272" width="1.93" height="8"/> + + <rect x="265.553" y="45.436" clip-path="url(#SVGID_206_)" fill="none" stroke="#727272" stroke-miterlimit="10" width="1.93" height="8"/> + <polygon clip-path="url(#SVGID_206_)" fill="#737272" points="275.828,54.564 273.482,58.046 271.137,54.564 "/> + <g clip-path="url(#SVGID_206_)"> + <defs> + <rect id="SVGID_209_" x="260" y="40" width="20" height="20"/> + </defs> + <clipPath id="SVGID_210_"> + <use xlink:href="#SVGID_209_" overflow="visible"/> + </clipPath> + <polygon clip-path="url(#SVGID_210_)" fill="none" stroke="#727272" stroke-miterlimit="10" points="275.828,54.564 + 273.482,58.046 271.137,54.564 "/> + </g> + <rect x="272.518" y="46.564" clip-path="url(#SVGID_206_)" fill="#737272" width="1.93" height="8"/> + + <rect x="272.518" y="46.564" clip-path="url(#SVGID_206_)" fill="none" stroke="#727272" stroke-miterlimit="10" width="1.93" height="8"/> + </g> +</g> +<g> + <g> + <defs> + <rect id="SVGID_211_" x="220" y="80" width="20" height="20"/> + </defs> + <clipPath id="SVGID_212_"> + <use xlink:href="#SVGID_211_" overflow="visible"/> + </clipPath> + <rect x="224.555" y="90.047" clip-path="url(#SVGID_212_)" fill="#FFFFFF" width="10.891" height="1.406"/> + <rect x="224.555" y="92.485" clip-path="url(#SVGID_212_)" fill="#FFFFFF" width="10.891" height="1.406"/> + <g clip-path="url(#SVGID_212_)"> + <defs> + <rect id="SVGID_213_" x="220" y="80" width="20" height="20"/> + </defs> + <clipPath id="SVGID_214_"> + <use xlink:href="#SVGID_213_" overflow="visible"/> + </clipPath> + <path clip-path="url(#SVGID_214_)" fill="none" stroke="#FFFFFF" stroke-width="2" stroke-miterlimit="8" d="M222.875,96.831 + c0,0.11,0.089,0.2,0.2,0.2h13.85c0.11,0,0.2-0.09,0.2-0.2V86.419c0-0.111-0.09-0.2-0.2-0.2h-13.85c-0.111,0-0.2,0.089-0.2,0.2 + V96.831z"/> + <rect x="222.875" y="86.218" clip-path="url(#SVGID_214_)" fill="#FFFFFF" width="14.25" height="2.813"/> + <rect x="225.094" y="83.531" clip-path="url(#SVGID_214_)" fill="#FFFFFF" width="1.11" height="2.438"/> + <rect x="233.796" y="83.531" clip-path="url(#SVGID_214_)" fill="#FFFFFF" width="1.108" height="2.438"/> + <path clip-path="url(#SVGID_214_)" fill="none" stroke="#B0B0B0" stroke-width="2" stroke-miterlimit="8" d="M222.875,95.737 + c0,0.11,0.089,0.2,0.2,0.2h13.85c0.11,0,0.2-0.09,0.2-0.2V85.325c0-0.111-0.09-0.2-0.2-0.2h-13.85c-0.111,0-0.2,0.089-0.2,0.2 + V95.737z"/> + <rect x="222.875" y="85.124" clip-path="url(#SVGID_214_)" fill="#B0B0B0" width="14.25" height="2.813"/> + <rect x="225.094" y="82.437" clip-path="url(#SVGID_214_)" fill="#B0B0B0" width="1.11" height="2.438"/> + <rect x="233.796" y="82.437" clip-path="url(#SVGID_214_)" fill="#B0B0B0" width="1.108" height="2.438"/> + <rect x="224.555" y="89.344" clip-path="url(#SVGID_214_)" fill="#B0B0B0" width="10.891" height="1.406"/> + <rect x="224.555" y="91.781" clip-path="url(#SVGID_214_)" fill="#B0B0B0" width="10.891" height="1.406"/> </g> </g> </g> diff --git a/js/app/controllers/detailscontroller.coffee b/js/app/controllers/detailscontroller.coffee index c8a4001d..c6ea4677 100644 --- a/js/app/controllers/detailscontroller.coffee +++ b/js/app/controllers/detailscontroller.coffee @@ -38,15 +38,57 @@ $timeout, $routeParams) -> ) @_$scope.durations = [ - {name: t('tasks_enhanced','years'), abbr: 'y'}, - {name: t('tasks_enhanced','months'), abbr: 'm'}, - {name: t('tasks_enhanced','days'), abbr: 'd'}, - {name: t('tasks_enhanced','hours'), abbr: 'h'}, - {name: t('tasks_enhanced','minutes'), abbr: 'i'}, - {name: t('tasks_enhanced','seconds'), abbr: 's'} + { + name: t('tasks_enhanced','week'), + names: t('tasks_enhanced','weeks'), + id: 'week'}, + { + name: t('tasks_enhanced','day'), + names: t('tasks_enhanced','days'), + id: 'day'}, + { + name: t('tasks_enhanced','hour'), + names: t('tasks_enhanced','hours'), + id: 'hour'}, + { + name: t('tasks_enhanced','minute'), + names: t('tasks_enhanced','minutes'), + id: 'minute'}, + { + name: t('tasks_enhanced','second'), + names: t('tasks_enhanced','seconds'), + id: 'second'} ] - @_$scope.duration = _$scope.durations[1] + @_$scope.params = (task) -> + params = [ + { + name: t('tasks_enhanced','before beginning'), + invert: true + related:'START', + id: "10"}, + { + name: t('tasks_enhanced','after beginning'), + invert: false + related:'START', + id: "00"}, + { + name: t('tasks_enhanced','before end'), + invert: true + related:'END', + id: "11"}, + { + name: t('tasks_enhanced','after end'), + invert: false + related:'END', + id: "01"} + ] + if task.due && task.start + return params + else if task.start + return params.slice(0,2) + else + return params.slice(2) @_$scope.closeDetails = () -> if _$scope.status.searchActive @@ -54,7 +96,6 @@ $timeout, $routeParams) -> else _$location.path('/lists/'+_$scope.route.listID) - @_$scope.deleteTask = (taskID) -> _$scope.closeDetails() _$timeout(() -> @@ -76,6 +117,7 @@ $timeout, $routeParams) -> else _$location.path('/lists/'+_$scope.route.listID + '/tasks/' + _$scope.route.taskID + '/edit/duedate') + _tasksbusinesslayer.initDueDate(_$scope.route.taskID) @_$scope.editStart = () -> if _$scope.status.searchActive @@ -84,6 +126,7 @@ $timeout, $routeParams) -> else _$location.path('/lists/'+_$scope.route.listID + '/tasks/' + _$scope.route.taskID + '/edit/startdate') + _tasksbusinesslayer.initStartDate(_$scope.route.taskID) @_$scope.editReminder = () -> if _$scope.status.searchActive @@ -92,6 +135,7 @@ $timeout, $routeParams) -> else _$location.path('/lists/'+_$scope.route.listID + '/tasks/' + _$scope.route.taskID + '/edit/reminder') + _tasksbusinesslayer.initReminder(_$scope.route.taskID) @_$scope.editNote = () -> if _$scope.status.searchActive @@ -182,23 +226,42 @@ $timeout, $routeParams) -> moment(date,'HH:mm'),'time') @_$scope.setreminderday = (date) -> - _tasksbusinesslayer.setReminder(_$scope.route.taskID, + _tasksbusinesslayer.setReminderDate(_$scope.route.taskID, moment(date,'MM/DD/YYYY'),'day') @_$scope.setremindertime = (date) -> - _tasksbusinesslayer.setReminder(_$scope.route.taskID, + _tasksbusinesslayer.setReminderDate(_$scope.route.taskID, moment(date,'HH:mm'),'time') @_$scope.reminderType = (task) -> if !angular.isUndefined(task) if task.reminder == null - if moment(task.start, "YYYYMMDDTHHmmss").isValid() + if moment(task.start, "YYYYMMDDTHHmmss").isValid() || + moment(task.due, "YYYYMMDDTHHmmss").isValid() return 'DURATION' else return 'DATE-TIME' else return task.reminder.type + @_$scope.changeReminderType = (task) -> + _tasksbusinesslayer.checkReminderDate(task.id) + if @reminderType(task) == 'DURATION' + if task.reminder + task.reminder.type = 'DATE-TIME' + else + task.reminder = {type:'DATE-TIME'} + else + if task.reminder + task.reminder.type = 'DURATION' + else + task.reminder = {type:'DURATION'} + _tasksbusinesslayer.setReminder(task.id) + + + @_$scope.setReminderDuration = (taskID) -> + _tasksbusinesslayer.setReminder(_$scope.route.taskID) + return new DetailsController($scope, $window, TasksModel, TasksBusinessLayer, $route, $location, $timeout, $routeParams) ]
\ No newline at end of file diff --git a/js/app/filters/reminderDetails.coffee b/js/app/filters/reminderDetails.coffee index 346374e7..f649e785 100644 --- a/js/app/filters/reminderDetails.coffee +++ b/js/app/filters/reminderDetails.coffee @@ -25,15 +25,34 @@ angular.module('Tasks').filter 'reminderDetails', () -> if reminder.type == 'DATE-TIME' && moment(reminder.date, "YYYYMMDDTHHmmss").isValid() return moment(reminder.date, "YYYYMMDDTHHmmss").lang('reminder').calendar() - else if reminder.type == 'DURATION' + else if reminder.type == 'DURATION' && reminder.duration ds = t('tasks_enhanced', 'Remind me') for token in scope.durations - if reminder.duration[token.abbr] - ds+=' '+reminder.duration[token.abbr]+' '+t('tasks_enhanced',token.name) - if reminder.duration.invert - ds+= ' '+t('tasks_enhanced','before') + if +reminder.duration[token.id] + time = 1 + ds+=' '+reminder.duration[token.id]+' ' + if +reminder.duration[token.id] == 1 + ds+=token.name + else + ds+=token.names + if !time + if reminder.duration.params.related == 'END' + ds+= ' '+t('tasks_enhanced','at the end') + else + ds+= ' '+t('tasks_enhanced','at the beginning') else - ds+= ' '+t('tasks_enhanced','after') + if reminder.duration.params.invert + if reminder.duration.params.related == 'END' + ds+= ' '+t('tasks_enhanced','before end') + else + ds+= ' '+t('tasks_enhanced','before beginning') + else + if reminder.duration.params.related == 'END' + ds+= ' '+t('tasks_enhanced','after end') + else + ds+= ' '+t('tasks_enhanced','after beginning') return ds + else + return t('tasks_enhanced', 'Remind me') else return t('tasks_enhanced', 'Remind me')
\ No newline at end of file diff --git a/js/app/services/businesslayer/tasksbusinesslayer.coffee b/js/app/services/businesslayer/tasksbusinesslayer.coffee index a472c4fd..84b998b5 100644 --- a/js/app/services/businesslayer/tasksbusinesslayer.coffee +++ b/js/app/services/businesslayer/tasksbusinesslayer.coffee @@ -62,6 +62,11 @@ angular.module('Tasks').factory 'TasksBusinessLayer', @_$tasksmodel.removeById(taskID) @_persistence.deleteTask(taskID) + initDueDate: (taskID) -> + due = moment(@_$tasksmodel.getById(taskID).due, "YYYYMMDDTHHmmss") + if !due.isValid() + @setDue(taskID, moment().startOf('hour').add('h',1),'time') + setDue: (taskID, date, type='day') -> due = moment(@_$tasksmodel.getById(taskID).due, "YYYYMMDDTHHmmss") if type=='day' @@ -77,13 +82,23 @@ angular.module('Tasks').factory 'TasksBusinessLayer', else return @_$tasksmodel.setDueDate(taskID,due.format('YYYYMMDDTHHmmss')) + @checkReminderDate(taskID) @_persistence.setDueDate(taskID, if due.isValid() then due.unix() else false) deleteDueDate: (taskID) -> + reminder = @_$tasksmodel.getById(taskID).reminder + if (reminder != null && reminder.type == 'DURATION' && + reminder.duration.params.related == 'END') + @deleteReminderDate(taskID) @_$tasksmodel.setDueDate(taskID, null) @_persistence.setDueDate(taskID, false) + initStartDate: (taskID) -> + start = moment(@_$tasksmodel.getById(taskID).start, "YYYYMMDDTHHmmss") + if !start.isValid() + @setStart(taskID, moment().startOf('hour').add('h',1),'time') + setStart: (taskID, date, type='day') -> start = moment(@_$tasksmodel.getById(taskID).start, "YYYYMMDDTHHmmss") if type == 'day' @@ -99,23 +114,59 @@ angular.module('Tasks').factory 'TasksBusinessLayer', else return @_$tasksmodel.setStartDate(taskID,start.format('YYYYMMDDTHHmmss')) + @checkReminderDate(taskID) @_persistence.setStartDate(taskID, if start.isValid() then start.unix() else false) deleteStartDate: (taskID) -> + reminder = @_$tasksmodel.getById(taskID).reminder + if (reminder != null && reminder.type == 'DURATION' && + reminder.duration.params.related == 'START') + @deleteReminderDate(taskID) @_$tasksmodel.setStartDate(taskID, null) @_persistence.setStartDate(taskID, false) - setReminder: (taskID, date, type='day') -> + initReminder: (taskID) -> + if !@checkReminderDate(taskID) + task = @_$tasksmodel.getById(taskID) + task.reminder = { + type: 'DURATION', + action: 'DISPLAY', + duration: { + token: 'week', + week: 0, + day: 0, + hour: 0, + minute: 0, + second: 0, + params: { + invert: true + } + } + } + if moment(task.start, "YYYYMMDDTHHmmss").isValid() + p = task.reminder.duration.params + p.related = 'START' + p.id = '10' + else if moment(task.due, "YYYYMMDDTHHmmss").isValid() + p = task.reminder.duration.params + p.related = 'END' + p.id = '11' + else + task.reminder.type = 'DATE-TIME' + task.reminder.date = moment().startOf('hour').add('h',1) + .format('YYYYMMDDTHHmmss') + @setReminder(taskID) + + setReminderDate: (taskID, date, type='day') -> reminder = @_$tasksmodel.getById(taskID).reminder newreminder = { type: 'DATE-TIME', action: 'DISPLAY', - duration: null, - trigger: null + duration: null } if type == 'day' - if !(angular.isUndefined(reminder) || reminder == null) + if (@checkReminderDate(taskID) || reminder == null) reminderdate = moment(reminder.date, "YYYYMMDDTHHmmss") newreminder.action = reminder.action if (reminderdate.isValid() && reminder.type == 'DATE-TIME') @@ -125,7 +176,7 @@ angular.module('Tasks').factory 'TasksBusinessLayer', else reminderdate = date.add('h',12) else if type == 'time' - if !(angular.isUndefined(reminder) || reminder == null) + if (@checkReminderDate(taskID) || reminder == null) reminderdate = moment(reminder.date, "YYYYMMDDTHHmmss") newreminder.action = reminder.action if (reminderdate.isValid() && reminder.type == 'DATE-TIME') @@ -137,11 +188,105 @@ angular.module('Tasks').factory 'TasksBusinessLayer', else return newreminder.date = reminderdate.format('YYYYMMDDTHHmmss') - @_$tasksmodel.setReminderDate(taskID,newreminder) + @_$tasksmodel.setReminder(taskID,newreminder) @_persistence.setReminder(taskID,newreminder) + setReminder: (taskID) -> + if @checkReminderDate(taskID) + reminder = @_$tasksmodel.getById(taskID).reminder + @_persistence.setReminder(taskID,reminder) + + checkReminderDate: (taskID) -> + task = @_$tasksmodel.getById(taskID) + reminder = task.reminder + if(reminder != null && reminder.type == 'DURATION') + if !reminder.duration + return false + else if reminder.duration.params.related == 'START' + token = 'start' + else if reminder.duration.params.related == 'END' + token = 'due' + else + return false + date = moment(task[token], "YYYYMMDDTHHmmss") + duration = reminder.duration + d = { + w: duration.week, + d: duration.day, + h: duration.hour, + m: duration.minute, + s: duration.second + } + if duration.params.invert + date = date.subtract(d) + else + date = date.add(d) + task.reminder.date = date.format('YYYYMMDDTHHmmss') + else if(reminder != null && reminder.type == 'DATE-TIME') + duration = reminder.duration + date = moment(reminder.date, "YYYYMMDDTHHmmss") + if !date.isValid() + return false + if duration + if duration.params.related == 'START' + related = moment(task.start, "YYYYMMDDTHHmmss") + else + related = moment(task.due, "YYYYMMDDTHHmmss") + seg = @secondsToSegments(date.diff(related, 'seconds')) + duration.params.invert = seg.invert + duration.token = 'week' + duration.week = seg.week + duration.day = seg.day + duration.hour = seg.hour + duration.minute = seg.minute + duration.second = seg.second + else + if task.start + related = moment(task.start, "YYYYMMDDTHHmmss") + rel = 'START' + d = 0 + else if task.due + related = moment(task.due, "YYYYMMDDTHHmmss") + rel = 'END' + d = 1 + else + return true + seg = @secondsToSegments(date.diff(related, 'seconds')) + reminder.duration = { + token: 'week' + params: { + related: rel + invert: seg.invert + id: +seg.invert+''+d + } + week: seg.week + day: seg.day + hour: seg.hour + minute: seg.minute + second: seg.second + } + else + return false + return true + + secondsToSegments: (s) -> + if s<0 + s *= -1 + i = true + else + i = false + w = Math.floor(s/604800) + s -= w*604800 + d = Math.floor(s/86400) + s -= d*86400 + h = Math.floor(s/3600) + s -= h*3600 + m = Math.floor(s/60) + s -= m*60 + return {week:w, day:d, hour:h, minute:m, second:s, invert: i} + deleteReminderDate: (taskID) -> - @_$tasksmodel.setReminderDate(taskID, null) + @_$tasksmodel.setReminder(taskID, null) @_persistence.setReminder(taskID,false) changeCalendarId: (taskID, calendarID) -> diff --git a/js/app/services/models/tasksmodel.coffee b/js/app/services/models/tasksmodel.coffee index a759c751..32b63284 100644 --- a/js/app/services/models/tasksmodel.coffee +++ b/js/app/services/models/tasksmodel.coffee @@ -127,7 +127,7 @@ angular.module('Tasks').factory 'TasksModel', setDueDate: (taskID,date) -> @update({id:taskID,due:date}) - setReminderDate: (taskID,reminder) -> + setReminder: (taskID,reminder) -> @update({id:taskID,reminder:reminder}) setStartDate: (taskID,date) -> @@ -136,7 +136,7 @@ angular.module('Tasks').factory 'TasksModel', overdue: (due) -> return (moment(due, "YYYYMMDDTHHmmss").isValid() && moment(due, "YYYYMMDDTHHmmss"). - diff(moment().startOf('day'), 'days', true) < 0) + diff(moment()) < 0) due: (due) -> return moment(due, 'YYYYMMDDTHHmmss').isValid() diff --git a/js/app/services/persistence.coffee b/js/app/services/persistence.coffee index ab07ed88..dbfbeba3 100644 --- a/js/app/services/persistence.coffee +++ b/js/app/services/persistence.coffee @@ -205,7 +205,14 @@ angular.module('Tasks').factory 'Persistence', data: type: reminder.type action: reminder.action - duration: reminder.duration + week: reminder.duration.week + day: reminder.duration.day + hour: reminder.duration.hour + minute: reminder.duration.minute + second: reminder.duration.second + invert: reminder.duration.params.invert + related: reminder.duration.params.related + else return @_request.post '/apps/tasks_enhanced/tasks/{taskID}/reminder', params diff --git a/js/public/app.js b/js/public/app.js index 63c4ad2e..328f04a8 100644 --- a/js/public/app.js +++ b/js/public/app.js @@ -349,26 +349,60 @@ }); this._$scope.durations = [ { - name: t('tasks_enhanced', 'years'), - abbr: 'y' + name: t('tasks_enhanced', 'week'), + names: t('tasks_enhanced', 'weeks'), + id: 'week' }, { - name: t('tasks_enhanced', 'months'), - abbr: 'm' + name: t('tasks_enhanced', 'day'), + names: t('tasks_enhanced', 'days'), + id: 'day' }, { - name: t('tasks_enhanced', 'days'), - abbr: 'd' + name: t('tasks_enhanced', 'hour'), + names: t('tasks_enhanced', 'hours'), + id: 'hour' }, { - name: t('tasks_enhanced', 'hours'), - abbr: 'h' + name: t('tasks_enhanced', 'minute'), + names: t('tasks_enhanced', 'minutes'), + id: 'minute' }, { - name: t('tasks_enhanced', 'minutes'), - abbr: 'i' - }, { - name: t('tasks_enhanced', 'seconds'), - abbr: 's' + name: t('tasks_enhanced', 'second'), + names: t('tasks_enhanced', 'seconds'), + id: 'second' } ]; - this._$scope.duration = _$scope.durations[1]; + this._$scope.params = function(task) { + var params; + params = [ + { + name: t('tasks_enhanced', 'before beginning'), + invert: true, + related: 'START', + id: "10" + }, { + name: t('tasks_enhanced', 'after beginning'), + invert: false, + related: 'START', + id: "00" + }, { + name: t('tasks_enhanced', 'before end'), + invert: true, + related: 'END', + id: "11" + }, { + name: t('tasks_enhanced', 'after end'), + invert: false, + related: 'END', + id: "01" + } + ]; + if (task.due && task.start) { + return params; + } else if (task.start) { + return params.slice(0, 2); + } else { + return params.slice(2); + } + }; this._$scope.closeDetails = function() { if (_$scope.status.searchActive) { return _$location.path('/search/' + _$scope.route.searchString); @@ -391,24 +425,27 @@ }; this._$scope.editDueDate = function() { if (_$scope.status.searchActive) { - return _$location.path('/search/' + _$scope.route.searchString + '/tasks/' + _$scope.route.taskID + '/edit/duedate'); + _$location.path('/search/' + _$scope.route.searchString + '/tasks/' + _$scope.route.taskID + '/edit/duedate'); } else { - return _$location.path('/lists/' + _$scope.route.listID + '/tasks/' + _$scope.route.taskID + '/edit/duedate'); + _$location.path('/lists/' + _$scope.route.listID + '/tasks/' + _$scope.route.taskID + '/edit/duedate'); } + return _tasksbusinesslayer.initDueDate(_$scope.route.taskID); }; this._$scope.editStart = function() { if (_$scope.status.searchActive) { - return _$location.path('/search/' + _$scope.route.searchString + '/tasks/' + _$scope.route.taskID + '/edit/startdate'); + _$location.path('/search/' + _$scope.route.searchString + '/tasks/' + _$scope.route.taskID + '/edit/startdate'); } else { - return _$location.path('/lists/' + _$scope.route.listID + '/tasks/' + _$scope.route.taskID + '/edit/startdate'); + _$location.path('/lists/' + _$scope.route.listID + '/tasks/' + _$scope.route.taskID + '/edit/startdate'); } + return _tasksbusinesslayer.initStartDate(_$scope.route.taskID); }; this._$scope.editReminder = function() { if (_$scope.status.searchActive) { - return _$location.path('/search/' + _$scope.route.searchString + '/tasks/' + _$scope.route.taskID + '/edit/reminder'); + _$location.path('/search/' + _$scope.route.searchString + '/tasks/' + _$scope.route.taskID + '/edit/reminder'); } else { - return _$location.path('/lists/' + _$scope.route.listID + '/tasks/' + _$scope.route.taskID + '/edit/reminder'); + _$location.path('/lists/' + _$scope.route.listID + '/tasks/' + _$scope.route.taskID + '/edit/reminder'); } + return _tasksbusinesslayer.initReminder(_$scope.route.taskID); }; this._$scope.editNote = function() { if (_$scope.status.searchActive) { @@ -500,15 +537,15 @@ return _tasksbusinesslayer.setDue(_$scope.route.taskID, moment(date, 'HH:mm'), 'time'); }; this._$scope.setreminderday = function(date) { - return _tasksbusinesslayer.setReminder(_$scope.route.taskID, moment(date, 'MM/DD/YYYY'), 'day'); + return _tasksbusinesslayer.setReminderDate(_$scope.route.taskID, moment(date, 'MM/DD/YYYY'), 'day'); }; this._$scope.setremindertime = function(date) { - return _tasksbusinesslayer.setReminder(_$scope.route.taskID, moment(date, 'HH:mm'), 'time'); + return _tasksbusinesslayer.setReminderDate(_$scope.route.taskID, moment(date, 'HH:mm'), 'time'); }; this._$scope.reminderType = function(task) { if (!angular.isUndefined(task)) { if (task.reminder === null) { - if (moment(task.start, "YYYYMMDDTHHmmss").isValid()) { + if (moment(task.start, "YYYYMMDDTHHmmss").isValid() || moment(task.due, "YYYYMMDDTHHmmss").isValid()) { return 'DURATION'; } else { return 'DATE-TIME'; @@ -518,6 +555,30 @@ } } }; + this._$scope.changeReminderType = function(task) { + _tasksbusinesslayer.checkReminderDate(task.id); + if (this.reminderType(task) === 'DURATION') { + if (task.reminder) { + task.reminder.type = 'DATE-TIME'; + } else { + task.reminder = { + type: 'DATE-TIME' + }; + } + } else { + if (task.reminder) { + task.reminder.type = 'DURATION'; + } else { + task.reminder = { + type: 'DURATION' + }; + } + } + return _tasksbusinesslayer.setReminder(task.id); + }; + this._$scope.setReminderDuration = function(taskID) { + return _tasksbusinesslayer.setReminder(_$scope.route.taskID); + }; } return DetailsController; @@ -1006,6 +1067,14 @@ return this._persistence.deleteTask(taskID); }; + TasksBusinessLayer.prototype.initDueDate = function(taskID) { + var due; + due = moment(this._$tasksmodel.getById(taskID).due, "YYYYMMDDTHHmmss"); + if (!due.isValid()) { + return this.setDue(taskID, moment().startOf('hour').add('h', 1), 'time'); + } + }; + TasksBusinessLayer.prototype.setDue = function(taskID, date, type) { var due; if (type == null) { @@ -1028,14 +1097,28 @@ return; } this._$tasksmodel.setDueDate(taskID, due.format('YYYYMMDDTHHmmss')); + this.checkReminderDate(taskID); return this._persistence.setDueDate(taskID, due.isValid() ? due.unix() : false); }; TasksBusinessLayer.prototype.deleteDueDate = function(taskID) { + var reminder; + reminder = this._$tasksmodel.getById(taskID).reminder; + if (reminder !== null && reminder.type === 'DURATION' && reminder.duration.params.related === 'END') { + this.deleteReminderDate(taskID); + } this._$tasksmodel.setDueDate(taskID, null); return this._persistence.setDueDate(taskID, false); }; + TasksBusinessLayer.prototype.initStartDate = function(taskID) { + var start; + start = moment(this._$tasksmodel.getById(taskID).start, "YYYYMMDDTHHmmss"); + if (!start.isValid()) { + return this.setStart(taskID, moment().startOf('hour').add('h', 1), 'time'); + } + }; + TasksBusinessLayer.prototype.setStart = function(taskID, date, type) { var start; if (type == null) { @@ -1058,15 +1141,56 @@ return; } this._$tasksmodel.setStartDate(taskID, start.format('YYYYMMDDTHHmmss')); + this.checkReminderDate(taskID); return this._persistence.setStartDate(taskID, start.isValid() ? start.unix() : false); }; TasksBusinessLayer.prototype.deleteStartDate = function(taskID) { + var reminder; + reminder = this._$tasksmodel.getById(taskID).reminder; + if (reminder !== null && reminder.type === 'DURATION' && reminder.duration.params.related === 'START') { + this.deleteReminderDate(taskID); + } this._$tasksmodel.setStartDate(taskID, null); return this._persistence.setStartDate(taskID, false); }; - TasksBusinessLayer.prototype.setReminder = function(taskID, date, type) { + TasksBusinessLayer.prototype.initReminder = function(taskID) { + var p, task; + if (!this.checkReminderDate(taskID)) { + task = this._$tasksmodel.getById(taskID); + task.reminder = { + type: 'DURATION', + action: 'DISPLAY', + duration: { + token: 'week', + week: 0, + day: 0, + hour: 0, + minute: 0, + second: 0, + params: { + invert: true + } + } + }; + if (moment(task.start, "YYYYMMDDTHHmmss").isValid()) { + p = task.reminder.duration.params; + p.related = 'START'; + p.id = '10'; + } else if (moment(task.due, "YYYYMMDDTHHmmss").isValid()) { + p = task.reminder.duration.params; + p.related = 'END'; + p.id = '11'; + } else { + task.reminder.type = 'DATE-TIME'; + task.reminder.date = moment().startOf('hour').add('h', 1).format('YYYYMMDDTHHmmss'); + } + } + return this.setReminder(taskID); + }; + + TasksBusinessLayer.prototype.setReminderDate = function(taskID, date, type) { var newreminder, reminder, reminderdate; if (type == null) { type = 'day'; @@ -1075,11 +1199,10 @@ newreminder = { type: 'DATE-TIME', action: 'DISPLAY', - duration: null, - trigger: null + duration: null }; if (type === 'day') { - if (!(angular.isUndefined(reminder) || reminder === null)) { + if (this.checkReminderDate(taskID) || reminder === null) { reminderdate = moment(reminder.date, "YYYYMMDDTHHmmss"); newreminder.action = reminder.action; if (reminderdate.isValid() && reminder.type === 'DATE-TIME') { @@ -1091,7 +1214,7 @@ reminderdate = date.add('h', 12); } } else if (type === 'time') { - if (!(angular.isUndefined(reminder) || reminder === null)) { + if (this.checkReminderDate(taskID) || reminder === null) { reminderdate = moment(reminder.date, "YYYYMMDDTHHmmss"); newreminder.action = reminder.action; if (reminderdate.isValid() && reminder.type === 'DATE-TIME') { @@ -1106,12 +1229,128 @@ return; } newreminder.date = reminderdate.format('YYYYMMDDTHHmmss'); - this._$tasksmodel.setReminderDate(taskID, newreminder); + this._$tasksmodel.setReminder(taskID, newreminder); return this._persistence.setReminder(taskID, newreminder); }; + TasksBusinessLayer.prototype.setReminder = function(taskID) { + var reminder; + if (this.checkReminderDate(taskID)) { + reminder = this._$tasksmodel.getById(taskID).reminder; + return this._persistence.setReminder(taskID, reminder); + } + }; + + TasksBusinessLayer.prototype.checkReminderDate = function(taskID) { + var d, date, duration, rel, related, reminder, seg, task, token; + task = this._$tasksmodel.getById(taskID); + reminder = task.reminder; + if (reminder !== null && reminder.type === 'DURATION') { + if (!reminder.duration) { + return false; + } else if (reminder.duration.params.related === 'START') { + token = 'start'; + } else if (reminder.duration.params.related === 'END') { + token = 'due'; + } else { + return false; + } + date = moment(task[token], "YYYYMMDDTHHmmss"); + duration = reminder.duration; + d = { + w: duration.week, + d: duration.day, + h: duration.hour, + m: duration.minute, + s: duration.second + }; + if (duration.params.invert) { + date = date.subtract(d); + } else { + date = date.add(d); + } + task.reminder.date = date.format('YYYYMMDDTHHmmss'); + } else if (reminder !== null && reminder.type === 'DATE-TIME') { + duration = reminder.duration; + date = moment(reminder.date, "YYYYMMDDTHHmmss"); + if (!date.isValid()) { + return false; + } + if (duration) { + if (duration.params.related === 'START') { + related = moment(task.start, "YYYYMMDDTHHmmss"); + } else { + related = moment(task.due, "YYYYMMDDTHHmmss"); + } + seg = this.secondsToSegments(date.diff(related, 'seconds')); + duration.params.invert = seg.invert; + duration.token = 'week'; + duration.week = seg.week; + duration.day = seg.day; + duration.hour = seg.hour; + duration.minute = seg.minute; + duration.second = seg.second; + } else { + if (task.start) { + related = moment(task.start, "YYYYMMDDTHHmmss"); + rel = 'START'; + d = 0; + } else if (task.due) { + related = moment(task.due, "YYYYMMDDTHHmmss"); + rel = 'END'; + d = 1; + } else { + return true; + } + seg = this.secondsToSegments(date.diff(related, 'seconds')); + reminder.duration = { + token: 'week', + params: { + related: rel, + invert: seg.invert, + id: +seg.invert + '' + d + }, + week: seg.week, + day: seg.day, + hour: seg.hour, + minute: seg.minute, + second: seg.second + }; + } + } else { + return false; + } + return true; + }; + + TasksBusinessLayer.prototype.secondsToSegments = function(s) { + var d, h, i, m, w; + if (s < 0) { + s *= -1; + i = true; + } else { + i = false; + } + w = Math.floor(s / 604800); + s -= w * 604800; + d = Math.floor(s / 86400); + s -= d * 86400; + h = Math.floor(s / 3600); + s -= h * 3600; + m = Math.floor(s / 60); + s -= m * 60; + return { + week: w, + day: d, + hour: h, + minute: m, + second: s, + invert: i + }; + }; + TasksBusinessLayer.prototype.deleteReminderDate = function(taskID) { - this._$tasksmodel.setReminderDate(taskID, null); + this._$tasksmodel.setReminder(taskID, null); return this._persistence.setReminder(taskID, false); }; @@ -1611,7 +1850,7 @@ }); }; - TasksModel.prototype.setReminderDate = function(taskID, reminder) { + TasksModel.prototype.setReminder = function(taskID, reminder) { return this.update({ id: taskID, reminder: reminder @@ -1626,7 +1865,7 @@ }; TasksModel.prototype.overdue = function(due) { - return moment(due, "YYYYMMDDTHHmmss").isValid() && moment(due, "YYYYMMDDTHHmmss").diff(moment().startOf('day'), 'days', true) < 0; + return moment(due, "YYYYMMDDTHHmmss").isValid() && moment(due, "YYYYMMDDTHHmmss").diff(moment()) < 0; }; TasksModel.prototype.due = function(due) { @@ -1930,7 +2169,13 @@ data: { type: reminder.type, action: reminder.action, - duration: reminder.duration + week: reminder.duration.week, + day: reminder.duration.day, + hour: reminder.duration.hour, + minute: reminder.duration.minute, + second: reminder.duration.second, + invert: reminder.duration.params.invert, + related: reminder.duration.params.related } }; } else { @@ -2112,25 +2357,49 @@ (function() { angular.module('Tasks').filter('reminderDetails', function() { return function(reminder, scope) { - var ds, token, _i, _len, _ref; + var ds, time, token, _i, _len, _ref; if (!(angular.isUndefined(reminder) || reminder === null)) { if (reminder.type === 'DATE-TIME' && moment(reminder.date, "YYYYMMDDTHHmmss").isValid()) { return moment(reminder.date, "YYYYMMDDTHHmmss").lang('reminder').calendar(); - } else if (reminder.type === 'DURATION') { + } else if (reminder.type === 'DURATION' && reminder.duration) { ds = t('tasks_enhanced', 'Remind me'); _ref = scope.durations; for (_i = 0, _len = _ref.length; _i < _len; _i++) { token = _ref[_i]; - if (reminder.duration[token.abbr]) { - ds += ' ' + reminder.duration[token.abbr] + ' ' + t('tasks_enhanced', token.name); + if (+reminder.duration[token.id]) { + time = 1; + ds += ' ' + reminder.duration[token.id] + ' '; + if (+reminder.duration[token.id] === 1) { + ds += token.name; + } else { + ds += token.names; + } } } - if (reminder.duration.invert) { - ds += ' ' + t('tasks_enhanced', 'before'); + if (!time) { + if (reminder.duration.params.related === 'END') { + ds += ' ' + t('tasks_enhanced', 'at the end'); + } else { + ds += ' ' + t('tasks_enhanced', 'at the beginning'); + } } else { - ds += ' ' + t('tasks_enhanced', 'after'); + if (reminder.duration.params.invert) { + if (reminder.duration.params.related === 'END') { + ds += ' ' + t('tasks_enhanced', 'before end'); + } else { + ds += ' ' + t('tasks_enhanced', 'before beginning'); + } + } else { + if (reminder.duration.params.related === 'END') { + ds += ' ' + t('tasks_enhanced', 'after end'); + } else { + ds += ' ' + t('tasks_enhanced', 'after beginning'); + } + } } return ds; + } else { + return t('tasks_enhanced', 'Remind me'); } } else { return t('tasks_enhanced', 'Remind me'); diff --git a/l10n/de.php b/l10n/de.php index 2e3439b7..2280dd6e 100644 --- a/l10n/de.php +++ b/l10n/de.php @@ -8,17 +8,33 @@ "All" => "Alle", "Current" => "Aktuell", "years" => "Jahre", +"year" => "Jahr", "months" => "Monate", +"month" => "Monat", "weeks" => "Wochen", +"week" => "Woche", +"Week" => "Woche", "days" => "Tage", +"day" => "Tag", "Hours" => "Stunden", +"Hour" => "Stunde", "hours" => "Stunden", +"hour" => "Stunde", "Minutes" => "Minuten", +"Minute" => "Minute", "minutes" => "Minuten", +"minute" => "Minute", "seconds" => "Sekunden", +"second" => "Sekunde", "before" => "vorher", "after" => "danach", -"Week" => "Woche", +"before beginning" => "vor Beginn", +"after beginning" => "nach Beginn", +"before end" => "vor Ende", +"after end" => "nach Ende", +"on time" => "pünktlich", +"at the end" => "am Ende", +"at the beginning" => "zu Beginn", "Add Task" => "Task hinzufügen", "Set due date" => "Fälligkeitsdatum auswählen", "Remind me" => "Erinnere mich", diff --git a/lib/controller/taskscontroller.php b/lib/controller/taskscontroller.php index c4b6f1c0..6d77aed6 100644 --- a/lib/controller/taskscontroller.php +++ b/lib/controller/taskscontroller.php @@ -356,6 +356,8 @@ class TasksController extends Controller { if ($type == false){ unset($vtodo->VALARM); + $vtodo->setDateTime('LAST-MODIFIED', 'now', \Sabre\VObject\Property\DateTime::UTC); + $vtodo->setDateTime('DTSTAMP', 'now', \Sabre\VObject\Property\DateTime::UTC); \OC_Calendar_Object::edit($taskId, $vcalendar->serialize()); } elseif (in_array($type,$types)) { @@ -369,21 +371,60 @@ class TasksController extends Controller { } else { unset($valarm->TRIGGER); } - $triggervalue = ''; + $tv = ''; + $related = null; if ($type == 'DATE-TIME') { $date = new \DateTime('@'.$this->params('date')); - $triggervalue = $date->format('Ymd\THis\Z'); + $tv = $date->format('Ymd\THis\Z'); } elseif ($type == 'DURATION') { - // TODO - $triggervalue = '-PT5M'; + $invert = $this->params('invert'); + $related= $this->params('related'); + $week = (int)$this->params('week'); + $day = (int)$this->params('day'); + $hour = (int)$this->params('hour'); + $minute = (int)$this->params('minute'); + $second = (int)$this->params('second'); + + // Create duration string + if($week || $day || $hour || $minute || $second) { + if ($invert){ + $tv.='-'; + } + $tv.='P'; + if ($week){ + $tv.=$week.'W'; + } + if ($day){ + $tv.=$day.'D'; + } + $tv.='T'; + if ($hour){ + $tv.=$hour.'H'; + } + if ($minute){ + $tv.=$minute.'M'; + } + if ($second){ + $tv.=$second.'S'; + } + }else{ + $tv = 'PT0S'; + } } - $valarm->addProperty('TRIGGER', $triggervalue, array('VALUE' => $type)); + if($related == 'END'){ + $valarm->addProperty('TRIGGER', $tv, array('VALUE' => $type, 'RELATED' => $related)); + } else { + $valarm->addProperty('TRIGGER', $tv, array('VALUE' => $type)); + } + $vtodo->setDateTime('LAST-MODIFIED', 'now', \Sabre\VObject\Property\DateTime::UTC); + $vtodo->setDateTime('DTSTAMP', 'now', \Sabre\VObject\Property\DateTime::UTC); \OC_Calendar_Object::edit($taskId, $vcalendar->serialize()); } catch (\Exception $e) { } } + return $response; } diff --git a/lib/helper.php b/lib/helper.php index 86165bf2..016d6bf0 100644 --- a/lib/helper.php +++ b/lib/helper.php @@ -36,6 +36,7 @@ Class helper { public static function arrayForJSON($id, $vtodo, $user_timezone){ $task = array( 'id' => $id ); $task['name'] = $vtodo->getAsString('SUMMARY'); + $task['created'] = $vtodo->getAsString('CREATED'); $task['note'] = $vtodo->getAsString('DESCRIPTION'); $task['location'] = $vtodo->getAsString('LOCATION'); $task['categories'] = $vtodo->getAsArray('CATEGORIES'); @@ -70,30 +71,68 @@ Class helper { try { $reminderType = $reminder->TRIGGER['VALUE']->value; - $reminderTrigger = $reminder->TRIGGER->value; $reminderAction = $reminder->ACTION->value; - $parsed1 = null; + if($reminderType == 'DATE-TIME'){ $reminderDate = $reminder->TRIGGER->getDateTime(); $reminderDate->setTimezone(new \DateTimeZone($user_timezone)); $reminderDate = $reminderDate->format('Ymd\THis'); - } elseif ($reminderType == 'DURATION' && $start) { - $parsed_complete = VObject\DateTimeParser::parseDuration($reminder->TRIGGER); + } elseif ($reminderType == 'DURATION' && ($start || $due)) { + $parsed = VObject\DateTimeParser::parseDuration($reminder->TRIGGER,true); // Calculate the reminder date from duration and start date - $reminderDate = $start->modify($parsed)->format('Ymd\THis'); + if($reminder->TRIGGER['RELATED']->value == 'END' && $due){ + $reminderDate = $due->modify($parsed)->format('Ymd\THis'); + } elseif ($start) { + $reminderDate = $start->modify($parsed)->format('Ymd\THis'); + } else{ + throw new \Exception('Reminder duration related to not available date.'); + } + $result = preg_match('/^(?P<plusminus>\+|-)?P((?P<week>\d+)W)?((?P<day>\d+)D)?(T((?P<hour>\d+)H)?((?P<minute>\d+)M)?((?P<second>\d+)S)?)?$/', $reminder->TRIGGER, $matches); + $invert = false; + if ($matches['plusminus']==='-') { + $invert = true; + } + + $parts = array( + 'week', + 'day', + 'hour', + 'minute', + 'second', + ); + + $reminderDuration = array( + 'token' => null + ); + foreach($parts as $part) { + $matches[$part] = isset($matches[$part])&&$matches[$part]?(int)$matches[$part]:0; + $reminderDuration[$part] = $matches[$part]; + if($matches[$part] && !$reminderDuration['token']){ + $reminderDuration['token'] = $part; + } + } + if($reminderDuration['token'] == null){ + $reminderDuration['token'] = $parts[0]; + } + + $reminderDuration['params'] = array( + 'id' => (int)$invert.(int)($reminder->TRIGGER['RELATED']->value == 'END'), + 'related'=> $reminder->TRIGGER['RELATED']->value?$reminder->TRIGGER['RELATED']->value:'START', + 'invert'=> $invert + ); + } else { $reminderDate = null; + $reminderDuration = null; } - $task['reminder'] = array( 'type' => $reminderType, - 'trigger' => $reminderTrigger, 'action' => $reminderAction, 'date' => $reminderDate, - 'duration' => $parsed_complete + 'duration' => $reminderDuration ); } catch(\Exception $e) { diff --git a/templates/main.php b/templates/main.php index 4e2ed9db..3ba5be46 100644 --- a/templates/main.php +++ b/templates/main.php @@ -5,7 +5,7 @@ <a id="search" ng-click="openSearch()" oc-click-focus="{selector: '#search-toolbar input', timeout: 0}"> <span class="icon search"></span> </a> - <a id="loading" ng-click="update()"> + <a id="loading" ng-click="update()" stop-event="click"> <span class="loading" ng-class="{'done':!isLoading()}"></span> </a> </div> diff --git a/templates/part.details.php b/templates/part.details.php index 08f1d642..ebe369ae 100644 --- a/templates/part.details.php +++ b/templates/part.details.php @@ -49,6 +49,7 @@ <div class="section detail-reminder" ng-class="{'date':isDue(task.reminder.date), 'editing':route.parameter=='reminder'}" ng-click="editReminder()" stop-event="click"> <!-- oc-click-focus="{selector: 'div.detail-reminder input.datepicker-input', timeout: 0}" --> <span class="icon detail-reminder" ng-class="{'overdue':isOverDue(task.reminder.date)}"></span> + <span class="icon detail-remindertype" ng-click="changeReminderType(task)" ng-show="task.due || task.start"></span> <div class="section-title" ng-class="{'overdue':isOverDue(task.reminder.date)}" ng-hide="route.parameter=='reminder'"> <text rel="">{{ task.reminder | reminderDetails:this }}</text> </div> @@ -63,8 +64,9 @@ <input class="timepicker-input medium focus" type="text" key-value="" placeholder="hh:mm" value="{{ task.reminder.date | timeTaskList }}" timepicker="reminder" stop-event="click"> </div> <div ng-switch-when="DURATION"> - <input class="datepicker-input medium focus" type="text" key-value="" placeholder="" value=""> - <select ng-model="duration" ng-options="duration.name for duration in durations"></select> + <input ng-change="setReminderDuration(task.id)" class="duration-input medium focus" type="number" key-value="" placeholder="" ng-model="task.reminder.duration[task.reminder.duration.token]"> + <select ng-model="task.reminder.duration.token" ng-options="duration.id as duration.names for duration in durations"></select> + <select ng-change="setReminderDuration(task.id)" ng-model="task.reminder.duration.params" ng-options="param as param.name for param in params(task) track by param.id"></select> </div> </div> </div> |