diff --git a/plugins/libcalendaring/lib/libcalendaring_itip.php b/plugins/libcalendaring/lib/libcalendaring_itip.php index a6fca9ce..ece0e484 100644 --- a/plugins/libcalendaring/lib/libcalendaring_itip.php +++ b/plugins/libcalendaring/lib/libcalendaring_itip.php @@ -213,6 +213,14 @@ class libcalendaring_itip $event['attendees'] = $reply_attendees; } } + // set RSVP=TRUE for every attendee if not set + else if ($method == 'REQUEST') { + foreach ($event['attendees'] as $i => $attendee) { + if (!isset($attendee['rsvp'])) { + $event['attendees'][$i]['rsvp']= true; + } + } + } // compose multipart message using PEAR:Mail_Mime $message = new Mail_mime("\r\n"); @@ -532,15 +540,21 @@ class libcalendaring_itip } /** - * Render event details in a table + * Render event/task details in a table */ function itip_object_details_table($event, $title) { $table = new html_table(array('cols' => 2, 'border' => 0, 'class' => 'calendar-eventdetails')); $table->add('ititle', $title); $table->add('title', Q($event['title'])); - $table->add('label', $this->plugin->gettext('date'), $this->domain); - $table->add('date', Q($this->lib->event_date_text($event))); + if ($event['start'] && $event['end']) { + $table->add('label', $this->plugin->gettext('date'), $this->domain); + $table->add('date', Q($this->lib->event_date_text($event))); + } + else if ($event['due'] && $event['_type'] == 'task') { + $table->add('label', $this->plugin->gettext('date'), $this->domain); + $table->add('date', Q($this->lib->event_date_text($event))); + } if ($event['location']) { $table->add('label', $this->plugin->gettext('location'), $this->domain); $table->add('location', Q($event['location'])); diff --git a/plugins/libcalendaring/libcalendaring.php b/plugins/libcalendaring/libcalendaring.php index 5a1a8b0d..36fc287a 100644 --- a/plugins/libcalendaring/libcalendaring.php +++ b/plugins/libcalendaring/libcalendaring.php @@ -247,11 +247,25 @@ class libcalendaring extends rcube_plugin */ public function event_date_text($event, $tzinfo = false) { - $fromto = ''; + $fromto = '--'; + + // handle task objects + if ($event['_type'] == 'task' && is_object($event['due'])) { + $date_format = $event['due']->_dateonly ? self::to_php_date_format($this->rc->config->get('calendar_date_format', $this->defaults['calendar_date_format'])) : null; + $fromto = $this->rc->format_date($event['due'], $date_format, false); + + // add timezone information + if ($fromto && $tzinfo && ($tzname = $this->timezone->getName())) { + $fromto .= ' (' . strtr($tzname, '_', ' ') . ')'; + } + + return $fromto; + } // abort if no valid event dates are given - if (!is_object($event['start']) || !is_a($event['start'], 'DateTime') || !is_object($event['end']) || !is_a($event['end'], 'DateTime')) + if (!is_object($event['start']) || !is_a($event['start'], 'DateTime') || !is_object($event['end']) || !is_a($event['end'], 'DateTime')) { return $fromto; + } $duration = $event['start']->diff($event['end'])->format('s'); diff --git a/plugins/libcalendaring/libvcalendar.php b/plugins/libcalendaring/libvcalendar.php index 855e0749..a89cec2b 100644 --- a/plugins/libcalendaring/libvcalendar.php +++ b/plugins/libcalendaring/libvcalendar.php @@ -567,7 +567,7 @@ class libvcalendar implements Iterator } // make organizer part of the attendees list for compatibility reasons - if (!empty($event['organizer']) && is_array($event['attendees'])) { + if (!empty($event['organizer']) && is_array($event['attendees']) && $event['_type'] == 'event') { array_unshift($event['attendees'], $event['organizer']); } diff --git a/plugins/tasklist/drivers/kolab/tasklist_kolab_driver.php b/plugins/tasklist/drivers/kolab/tasklist_kolab_driver.php index 624bdd56..68840838 100644 --- a/plugins/tasklist/drivers/kolab/tasklist_kolab_driver.php +++ b/plugins/tasklist/drivers/kolab/tasklist_kolab_driver.php @@ -774,6 +774,8 @@ class tasklist_kolab_driver extends tasklist_driver 'parent_id' => $record['parent_id'], 'recurrence' => $record['recurrence'], 'attendees' => $record['attendees'], + 'organizer' => $record['organizer'], + 'sequence' => $record['sequence'], ); // convert from DateTime to internal date format @@ -817,8 +819,8 @@ class tasklist_kolab_driver extends tasklist_driver } /** - * Convert the given task record into a data structure that can be passed to kolab_storage backend for saving - * (opposite of self::_to_rcube_event()) + * Convert the given task record into a data structure that can be passed to kolab_storage backend for saving + * (opposite of self::_to_rcube_event()) */ private function _from_rcube_task($task, $old = array()) { @@ -826,14 +828,14 @@ class tasklist_kolab_driver extends tasklist_driver $object['categories'] = (array)$task['tags']; if (!empty($task['date'])) { - $object['due'] = new DateTime($task['date'].' '.$task['time'], $this->plugin->timezone); + $object['due'] = rcube_utils::anytodatetime($task['date'].' '.$task['time'], $this->plugin->timezone); if (empty($task['time'])) $object['due']->_dateonly = true; unset($object['date']); } if (!empty($task['startdate'])) { - $object['start'] = new DateTime($task['startdate'].' '.$task['starttime'], $this->plugin->timezone); + $object['start'] = rcube_utils::anytodatetime($task['startdate'].' '.$task['starttime'], $this->plugin->timezone); if (empty($task['starttime'])) $object['start']->_dateonly = true; unset($object['startdate']); @@ -900,12 +902,6 @@ class tasklist_kolab_driver extends tasklist_driver unset($object['attachments']); } - // set current user as ORGANIZER - $identity = $this->rc->user->get_identity(); - if (empty($object['attendees']) && $identity['email']) { - $object['attendees'] = array(array('role' => 'ORGANIZER', 'name' => $identity['name'], 'email' => $identity['email'])); - } - $object['_owner'] = $identity['email']; unset($object['tempid'], $object['raw'], $object['list'], $object['flagged'], $object['tags']); diff --git a/plugins/tasklist/localization/en_US.inc b/plugins/tasklist/localization/en_US.inc index f43fbb93..b37faa88 100644 --- a/plugins/tasklist/localization/en_US.inc +++ b/plugins/tasklist/localization/en_US.inc @@ -33,6 +33,7 @@ $labels['status-needs-action'] = 'Needs action'; $labels['status-in-process'] = 'In process'; $labels['status-completed'] = 'Completed'; $labels['status-cancelled'] = 'Cancelled'; +$labels['assignedto'] = 'Assigned to'; $labels['all'] = 'All'; $labels['flagged'] = 'Flagged'; @@ -98,35 +99,35 @@ $labels['arialabeltaskselector'] = 'List mode'; $labels['arialabeltasklisting'] = 'Tasks listing'; // attendees -$labels['attendee'] = 'Participant'; +$labels['attendee'] = 'Assignee'; $labels['role'] = 'Role'; $labels['availability'] = 'Avail.'; $labels['confirmstate'] = 'Status'; -$labels['addattendee'] = 'Add participant'; +$labels['addattendee'] = 'Add assignee'; $labels['roleorganizer'] = 'Organizer'; $labels['rolerequired'] = 'Required'; $labels['roleoptional'] = 'Optional'; $labels['rolechair'] = 'Chair'; -$labels['rolenonparticipant'] = 'Absent'; +$labels['rolenonparticipant'] = 'Observer'; $labels['sendinvitations'] = 'Send invitations'; -$labels['sendnotifications'] = 'Notify participants about modifications'; -$labels['sendcancellation'] = 'Notify participants about task cancellation'; -$labels['invitationsubject'] = 'You\'ve been invited to "$title"'; -$labels['invitationmailbody'] = "*\$title*\n\nWhen: \$date\n\nInvitees: \$attendees\n\nPlease find attached an iCalendar file with all the task details which you can import to your tasks application."; -$labels['invitationattendlinks'] = "In case your email client doesn't support iTip requests you can use the following link to either accept or decline this invitation:\n\$url"; -$labels['eventupdatesubject'] = '"$title" has been updated'; -$labels['eventupdatesubjectempty'] = 'A task that concerns you has been updated'; -$labels['eventupdatemailbody'] = "*\$title*\n\nWhen: \$date\n\nInvitees: \$attendees\n\nPlease find attached an iCalendar file with the updated task details which you can import to your tasks application."; -$labels['eventcancelsubject'] = '"$title" has been canceled'; -$labels['eventcancelmailbody'] = "*\$title*\n\nWhen: \$date\n\nInvitees: \$attendees\n\nThe task has been cancelled by \$organizer.\n\nPlease find attached an iCalendar file with the updated task details."; +$labels['sendnotifications'] = 'Notify assignees about modifications'; +$labels['sendcancellation'] = 'Notify assignees about task cancellation'; +$labels['invitationsubject'] = 'You\'ve been assigned to "$title"'; +$labels['invitationmailbody'] = "*\$title*\n\nDue: \$date\n\nAssignees: \$attendees\n\nPlease find attached an iCalendar file with all the task details which you can import to your tasks application."; +$labels['itipupdatesubject'] = '"$title" has been updated'; +$labels['itipupdatesubjectempty'] = 'A task that concerns you has been updated'; +$labels['itipupdatemailbody'] = "*\$title*\n\nDue: \$date\n\nAssignees: \$attendees\n\nPlease find attached an iCalendar file with the updated task details which you can import to your tasks application."; +$labels['itipcancelsubject'] = '"$title" has been canceled'; +$labels['itipcancelmailbody'] = "*\$title*\n\nDue: \$date\n\nAssignees: \$attendees\n\nThe task has been cancelled by \$organizer.\n\nPlease find attached an iCalendar file with the updated task details."; +$labels['saveintasklist'] = 'save in '; // invitation handling (overrides labels from libcalendaring) $labels['itipobjectnotfound'] = 'The task referred by this message was not found in your tasks list.'; -$labels['itipmailbodyaccepted'] = "\$sender has accepted the assignment to the following task:\n\n*\$title*\n\nWhen: \$date\n\nInvitees: \$attendees"; -$labels['itipmailbodytentative'] = "\$sender has tentatively accepted the assignment to the following task:\n\n*\$title*\n\nWhen: \$date\n\nInvitees: \$attendees"; -$labels['itipmailbodydeclined'] = "\$sender has declined the assignment to the following task:\n\n*\$title*\n\nWhen: \$date\n\nInvitees: \$attendees"; -$labels['itipmailbodycancel'] = "\$sender has rejected your assignment to the following task:\n\n*\$title*\n\nWhen: \$date"; +$labels['itipmailbodyaccepted'] = "\$sender has accepted the assignment to the following task:\n\n*\$title*\n\nDue: \$date\n\nInvitees: \$attendees"; +$labels['itipmailbodytentative'] = "\$sender has tentatively accepted the assignment to the following task:\n\n*\$title*\n\nDue: \$date\n\nInvitees: \$attendees"; +$labels['itipmailbodydeclined'] = "\$sender has declined the assignment to the following task:\n\n*\$title*\n\nDue: \$date\n\nInvitees: \$attendees"; +$labels['itipmailbodycancel'] = "\$sender has rejected your assignment to the following task:\n\n*\$title*\n\nDue: \$date"; $labels['itipdeclineevent'] = 'Do you want to decline your assignment to this task?'; $labels['declinedeleteconfirm'] = 'Do you also want to delete this declined task from your tasks list?'; diff --git a/plugins/tasklist/skins/larry/tasklist.css b/plugins/tasklist/skins/larry/tasklist.css index bcdf29aa..1d891ed8 100644 --- a/plugins/tasklist/skins/larry/tasklist.css +++ b/plugins/tasklist/skins/larry/tasklist.css @@ -855,6 +855,14 @@ a.morelink:hover { margin-top: 0.5em; } +.edit-attendees-table tbody td { + padding: 4px 7px; +} + +.edit-attendees-table tbody tr:last-child td { + border-bottom: 0; +} + .edit-attendees-table th.role, .edit-attendees-table td.role { width: 9em; @@ -864,18 +872,19 @@ a.morelink:hover { .edit-attendees-table td.availability, .edit-attendees-table th.confirmstate, .edit-attendees-table td.confirmstate { - width: 4em; + width: 6em; } .edit-attendees-table th.options, .edit-attendees-table td.options { - width: 16px; + width: 24px; padding: 2px 4px; + text-align: right; } .edit-attendees-table th.sendmail, .edit-attendees-table td.sendmail { - width: 44px; + width: 48px; padding: 2px; } @@ -955,7 +964,7 @@ a.morelink:hover { div.form-section { position: relative; margin-top: 0.2em; - margin-bottom: 0.8em; + margin-bottom: 0.5em; } .form-section label { @@ -970,6 +979,10 @@ label.block { margin-bottom: 0.3em; } +#task-description { + margin-bottom: 1em; +} + #taskedit-completeness-slider { display: inline-block; margin-left: 2em; @@ -1047,7 +1060,7 @@ label.block { } .task-attendees span.organizer { - background-position: right -80px; + background-position: right 100px; } #all-task-attendees span.attendee { diff --git a/plugins/tasklist/skins/larry/templates/mainview.html b/plugins/tasklist/skins/larry/templates/mainview.html index 1881c762..727d31fc 100644 --- a/plugins/tasklist/skins/larry/templates/mainview.html +++ b/plugins/tasklist/skins/larry/templates/mainview.html @@ -156,12 +156,16 @@ -