grommunio /
grommunio-web
| 1 | <?php |
||
| 2 | require_once(BASE_PATH . 'server/includes/mapi/class.recurrence.php'); |
||
| 3 | |||
| 4 | /** |
||
| 5 | * Appointment ItemModule |
||
| 6 | * Module which openes, creates, saves and deletes an item. It |
||
| 7 | * extends the Module class. |
||
| 8 | */ |
||
| 9 | class AppointmentItemModule extends ItemModule |
||
| 10 | { |
||
| 11 | /** |
||
| 12 | * Constructor |
||
| 13 | * @param int $id unique id. |
||
| 14 | * @param array $data list of all actions. |
||
| 15 | */ |
||
| 16 | function __construct($id, $data) |
||
| 17 | { |
||
| 18 | parent::__construct($id, $data); |
||
| 19 | |||
| 20 | $this->properties = $GLOBALS['properties']->getAppointmentProperties(); |
||
| 21 | |||
| 22 | $this->plaintext = true; |
||
| 23 | $this->skipCopyProperties = array( |
||
| 24 | $this->properties['goid'], |
||
| 25 | $this->properties['goid2'], |
||
| 26 | $this->properties['request_sent'], |
||
| 27 | PR_OWNER_APPT_ID |
||
| 28 | ); |
||
| 29 | } |
||
| 30 | |||
| 31 | function open($store, $entryid, $action) |
||
| 32 | { |
||
| 33 | if($store && $entryid) { |
||
| 34 | $data = array(); |
||
| 35 | |||
| 36 | $message = $GLOBALS['operations']->openMessage($store, $entryid); |
||
| 37 | |||
| 38 | if(empty($message)) { |
||
| 39 | return; |
||
| 40 | } |
||
| 41 | |||
| 42 | // Open embedded message if requested |
||
| 43 | $attachNum = !empty($action['attach_num']) ? $action['attach_num'] : false; |
||
| 44 | if($attachNum) { |
||
| 45 | // get message props of sub message |
||
| 46 | $parentMessage = $message; |
||
| 47 | $message = $GLOBALS['operations']->openMessage($store, $entryid, $attachNum); |
||
| 48 | |||
| 49 | if(empty($message)) { |
||
| 50 | return; |
||
| 51 | } |
||
| 52 | |||
| 53 | $data['item'] = $GLOBALS['operations']->getEmbeddedMessageProps($store, $message, $this->properties, $parentMessage, $attachNum); |
||
| 54 | } else { |
||
| 55 | // add all standard properties from the series/normal message |
||
| 56 | $data['item'] = $GLOBALS['operations']->getMessageProps($store, $message, $this->properties, $this->plaintext); |
||
| 57 | } |
||
| 58 | |||
| 59 | // if appointment is recurring then only we should get properties of occurrence if basedate is supplied |
||
| 60 | if($data['item']['props']['recurring'] === true) { |
||
| 61 | if(!empty($action['basedate'])) { |
||
| 62 | // check for occurrence/exception |
||
| 63 | $basedate = $action['basedate']; |
||
| 64 | |||
| 65 | $recur = new Recurrence($store, $message); |
||
| 66 | |||
| 67 | $exceptionatt = $recur->getExceptionAttachment($basedate); |
||
| 68 | |||
| 69 | // Single occurrences are never recurring |
||
| 70 | $data['item']['props']['recurring'] = false; |
||
| 71 | |||
| 72 | if($exceptionatt) { |
||
| 73 | // Existing exception (open existing item, which includes basedate) |
||
| 74 | $exceptionattProps = mapi_getprops($exceptionatt, array(PR_ATTACH_NUM)); |
||
| 75 | $exception = mapi_attach_openobj($exceptionatt, 0); |
||
| 76 | |||
| 77 | // overwrite properties with the ones from the exception |
||
| 78 | $exceptionProps = $GLOBALS['operations']->getMessageProps($store, $exception, $this->properties, $this->plaintext); |
||
| 79 | |||
| 80 | /** |
||
| 81 | * If recurring item has set reminder to true then |
||
| 82 | * all occurrences before the 'flagdueby' value(of recurring item) |
||
| 83 | * should not show that reminder is set. |
||
| 84 | */ |
||
| 85 | if (isset($exceptionProps['props']['reminder']) && $data['item']['props']['reminder'] == true) { |
||
| 86 | $flagDueByDay = $recur->dayStartOf($data['item']['props']['flagdueby']); |
||
| 87 | |||
| 88 | if ($flagDueByDay > $basedate) { |
||
| 89 | $exceptionProps['props']['reminder'] = false; |
||
| 90 | } |
||
| 91 | } |
||
| 92 | |||
| 93 | // The properties must be merged, if the recipients or attachments are present in the exception |
||
| 94 | // then that list should be used. Otherwise the list from the series must be applied (this |
||
| 95 | // corresponds with OL2007). |
||
| 96 | // @FIXME getMessageProps should not return empty string if exception doesn't contain body |
||
| 97 | // by this change we can handle a situation where user has set empty string in the body explicitly |
||
| 98 | if (!empty($exceptionProps['props']['body']) || !empty($exceptionProps['props']['html_body'])) { |
||
| 99 | if(!empty($exceptionProps['props']['body'])) { |
||
| 100 | $data['item']['props']['body'] = $exceptionProps['props']['body']; |
||
| 101 | } |
||
| 102 | |||
| 103 | if(!empty($exceptionProps['props']['html_body'])) { |
||
| 104 | $data['item']['props']['html_body'] = $exceptionProps['props']['html_body']; |
||
| 105 | } |
||
| 106 | |||
| 107 | $data['item']['props']['isHTML'] = $exceptionProps['props']['isHTML']; |
||
| 108 | } |
||
| 109 | // remove properties from $exceptionProps so array_merge will not overwrite it |
||
| 110 | unset($exceptionProps['props']['html_body']); |
||
| 111 | unset($exceptionProps['props']['body']); |
||
| 112 | unset($exceptionProps['props']['isHTML']); |
||
| 113 | |||
| 114 | $data['item']['props'] = array_merge($data['item']['props'], $exceptionProps['props']); |
||
| 115 | if (isset($exceptionProps['recipients'])) { |
||
| 116 | $data['item']['recipients'] = $exceptionProps['recipients']; |
||
| 117 | } |
||
| 118 | |||
| 119 | if (isset($exceptionProps['attachments'])) { |
||
| 120 | $data['item']['attachments'] = $exceptionProps['attachments']; |
||
| 121 | } |
||
| 122 | |||
| 123 | // Make sure we are using the passed basedate and not something wrong in the opened item |
||
| 124 | $data['item']['props']['basedate'] = $basedate; |
||
| 125 | $data['item']['attach_num'] = array($exceptionattProps[PR_ATTACH_NUM]); |
||
| 126 | } else if($recur->isDeleteException($basedate)) { |
||
| 127 | // Exception is deleted, should not happen, but if it the case then give error |
||
| 128 | $this->sendFeedback(false, |
||
| 129 | array( |
||
| 130 | 'type' => ERROR_ZARAFA, |
||
| 131 | 'info' => array( |
||
| 132 | 'original_message' => _('Could not open occurrence.'), |
||
| 133 | 'display_message' => _('Could not open occurrence, specific occurrence is probably deleted.') |
||
| 134 | ) |
||
| 135 | ) |
||
| 136 | ); |
||
| 137 | return; |
||
| 138 | } else { |
||
| 139 | // opening an occurrence of a recurring series (same as normal open, but add basedate, startdate and enddate) |
||
| 140 | $data['item']['props']['basedate'] = $basedate; |
||
| 141 | $data['item']['props']['startdate'] = $recur->getOccurrenceStart($basedate); |
||
| 142 | $data['item']['props']['duedate'] = $recur->getOccurrenceEnd($basedate); |
||
| 143 | $data['item']['props']['commonstart'] = $data['item']['props']['startdate']; |
||
| 144 | $data['item']['props']['commonend'] = $data['item']['props']['duedate']; |
||
| 145 | unset($data['item']['props']['reminder_time']); |
||
| 146 | |||
| 147 | /** |
||
| 148 | * If recurring item has set reminder to true then |
||
| 149 | * all occurrences before the 'flagdueby' value(of recurring item) |
||
| 150 | * should not show that reminder is set. |
||
| 151 | */ |
||
| 152 | if (isset($exceptionProps['props']['reminder']) && $data['item']['props']['reminder'] == true) { |
||
| 153 | $flagDueByDay = $recur->dayStartOf($data['item']['props']['flagdueby']); |
||
| 154 | |||
| 155 | if ($flagDueByDay > $basedate) { |
||
| 156 | $exceptionProps['props']['reminder'] = false; |
||
| 157 | } |
||
| 158 | } |
||
| 159 | } |
||
| 160 | } else { |
||
| 161 | // Opening a recurring series, get the recurrence information |
||
| 162 | $recur = new Recurrence($store, $message); |
||
| 163 | $recurpattern = $recur->getRecurrence(); |
||
| 164 | $tz = $recur->tz; // no function to do this at the moment |
||
| 165 | |||
| 166 | // Add the recurrence pattern to the data |
||
| 167 | if(isset($recurpattern) && is_array($recurpattern)) { |
||
| 168 | $data['item']['props'] += $recurpattern; |
||
| 169 | } |
||
| 170 | |||
| 171 | // Add the timezone information to the data |
||
| 172 | if(isset($tz) && is_array($tz)) { |
||
| 173 | $data['item']['props'] += $tz; |
||
| 174 | } |
||
| 175 | } |
||
| 176 | } |
||
| 177 | |||
| 178 | // Send the data |
||
| 179 | $this->addActionData('item', $data); |
||
| 180 | $GLOBALS['bus']->addData($this->getResponseData()); |
||
| 181 | } |
||
| 182 | } |
||
| 183 | |||
| 184 | /** |
||
| 185 | * Function does customization of exception based on module data. |
||
| 186 | * like, here it will generate display message based on actionType |
||
| 187 | * for particular exception. |
||
| 188 | * |
||
| 189 | * @param object $e Exception object |
||
| 190 | * @param string $actionType the action type, sent by the client |
||
| 191 | * @param MAPIobject $store Store object of message. |
||
| 192 | * @param string $parententryid parent entryid of the message. |
||
| 193 | * @param string $entryid entryid of the message. |
||
| 194 | * @param array $action the action data, sent by the client |
||
| 195 | */ |
||
| 196 | function handleException(&$e, $actionType = null, $store = null, $parententryid = null, $entryid = null, $action = null) |
||
| 197 | { |
||
| 198 | if (is_null($e->displayMessage)) { |
||
| 199 | switch ($actionType) { |
||
| 200 | case "save": |
||
| 201 | if ($e->getCode() == MAPI_E_NO_ACCESS) { |
||
| 202 | $message = mapi_msgstore_openentry($store, $entryid); |
||
| 203 | $messageProps = mapi_getprops($message, array(PR_MESSAGE_CLASS, PR_ENTRYID, PR_PARENT_ENTRYID, PR_STORE_ENTRYID)); |
||
| 204 | $messageClass = $messageProps[PR_MESSAGE_CLASS]; |
||
| 205 | |||
| 206 | $text = $messageClass !== "IPM.Appointment" ? _('a meeting request') : _('an appointment'); |
||
| 207 | $msg = _('You have insufficient privileges to move ' . $text .' in this calendar. The calendar owner can set these using the \'permissions\'-tab of the folder properties (right click the calendar folder > properties > permissions)'); |
||
| 208 | |||
| 209 | $e->setDisplayMessage($msg); |
||
| 210 | $e->setTitle(_('Insufficient privileges')); |
||
| 211 | |||
| 212 | // Need this notification to refresh the calendar. |
||
| 213 | $GLOBALS['bus']->notify(bin2hex($parententryid), TABLE_DELETE, $messageProps); |
||
| 214 | } |
||
| 215 | break; |
||
| 216 | } |
||
| 217 | } |
||
| 218 | parent::handleException($e, $actionType, $store, $parententryid, $entryid, $action); |
||
| 219 | } |
||
| 220 | |||
| 221 | |||
| 222 | /** |
||
| 223 | * Save the give appointment or meeting request to the calendar. |
||
| 224 | * |
||
| 225 | * @param mapistore $store MAPI store of the message |
||
| 226 | * @param string $parententryid Parent entryid of the message (folder entryid, NOT message entryid) |
||
| 227 | * @param string $entryid entryid of the message |
||
| 228 | * @param array $action Action array containing json request |
||
| 229 | * @param string $actionType The action type which triggered this action |
||
| 230 | */ |
||
| 231 | function save($store, $parententryid, $entryid, $action, $actionType = 'save') |
||
| 232 | { |
||
| 233 | $result = false; |
||
| 234 | |||
| 235 | // Save appointment (saveAppointment takes care of creating/modifying exceptions to recurring |
||
| 236 | // items if necessary) |
||
| 237 | $messageProps = $GLOBALS['operations']->saveAppointment($store, $entryid, $parententryid, $action, $actionType, $this->directBookingMeetingRequest); |
||
| 238 | |||
| 239 | // Notify the bus if the save was OK |
||
| 240 | if($messageProps && !(is_array($messageProps) && isset($messageProps['error'])) && !isset($messageProps['remindertimeerror']) ){ |
||
| 241 | $GLOBALS['bus']->notify(bin2hex($parententryid), TABLE_SAVE, $messageProps); |
||
| 242 | $result = true; |
||
| 243 | } |
||
| 244 | |||
| 245 | $errorMsg = false; |
||
| 246 | if(!$result && isset($messageProps['remindertimeerror']) && !$messageProps['remindertimeerror']){ |
||
| 247 | $errorMsg = _('Cannot set a reminder to appear before the previous occurrence. Reset reminder to save the change'); |
||
| 248 | } else if (isset($messageProps['isexceptionallowed']) && $messageProps['isexceptionallowed'] === false){ |
||
| 249 | $errorMsg = _('Two occurrences cannot occur on the same day'); |
||
| 250 | } elseif(is_array($messageProps) && isset($messageProps['error'])){ |
||
| 251 | switch($messageProps['error']){ |
||
| 252 | case 1: |
||
| 253 | $errorMsg = sprintf(_('You marked \'%s\' as a resource. You cannot schedule a meeting with \'%s\' because you do not have the appropriate permissions for that account. Either enter the name as a required or optional attendee or talk to your administrator about giving you permission to schedule \'%s\'.'), $messageProps['displayname'], $messageProps['displayname'], $messageProps['displayname']); |
||
| 254 | break; |
||
| 255 | case 2: |
||
| 256 | $errorMsg = sprintf(_('\'%s\' has declined your meeting because \'%s\' does not automatically accept meeting requests.'), $messageProps['displayname'], $messageProps['displayname']); |
||
| 257 | break; |
||
| 258 | case 3: |
||
| 259 | $errorMsg = sprintf(_('\'%s\' has declined your meeting because it is recurring. You must book each meeting separately with this resource.'), $messageProps['displayname']); |
||
| 260 | break; |
||
| 261 | case 4: |
||
| 262 | $errorMsg = sprintf(_('\'%s\' is already booked for this specified time. You must use another time or find another resource.'), $messageProps['displayname']); |
||
| 263 | break; |
||
| 264 | default: |
||
| 265 | $errorMsg = _('Meeting was not scheduled.'); |
||
| 266 | break; |
||
| 267 | } |
||
| 268 | } else { |
||
| 269 | // Recurring but non-existing exception (same as normal open, but add basedate, startdate and enddate) |
||
| 270 | $data = array(); |
||
| 271 | if ($result) { |
||
| 272 | $data = Conversion::mapMAPI2XML($this->properties, $messageProps); |
||
| 273 | |||
| 274 | // Get recipient information from the saved appointment to update client side |
||
| 275 | // according to the latest recipient related changes only if changes requested from client. |
||
| 276 | $savedAppointment = $GLOBALS['operations']->openMessage($store, $messageProps[PR_ENTRYID]); |
||
| 277 | if(!empty($action['recipients'])) { |
||
| 278 | $recipients = $GLOBALS["operations"]->getRecipientsInfo($savedAppointment); |
||
| 279 | if(!empty($recipients)) { |
||
| 280 | $data["recipients"] = array( |
||
| 281 | "item" => $recipients |
||
| 282 | ); |
||
| 283 | } |
||
| 284 | } |
||
| 285 | |||
| 286 | // Get attachments information from the saved appointment to update client side |
||
| 287 | // according to the latest attachments related changes only if changes requested from client. |
||
| 288 | if (!empty($action['attachments'])) { |
||
| 289 | $attachments = $GLOBALS["operations"]->getAttachmentsInfo($savedAppointment); |
||
| 290 | if (!empty($attachments)) { |
||
| 291 | $data["attachments"] = array( |
||
| 292 | "item" => $attachments |
||
| 293 | ); |
||
| 294 | } |
||
| 295 | } |
||
| 296 | |||
| 297 | $data['action_response'] = Array( |
||
| 298 | 'resources_booked' => $this->directBookingMeetingRequest |
||
| 299 | ); |
||
| 300 | |||
| 301 | if(isset($action['message_action']) && isset($action['message_action']['paste'])) { |
||
| 302 | $data['action_response']['resources_pasted'] = true; |
||
| 303 | } |
||
| 304 | |||
| 305 | } else { |
||
| 306 | if(!empty($action['message_action']['send'])){ |
||
| 307 | $errorMsg = _('Meeting could not be sent.'); |
||
| 308 | } else { |
||
| 309 | $errorMsg = _('Meeting could not be saved.'); |
||
| 310 | } |
||
| 311 | } |
||
| 312 | } |
||
| 313 | |||
| 314 | if($errorMsg===false){ |
||
| 315 | |||
| 316 | $this->addActionData('update', array('item' => $data)); |
||
| 317 | $GLOBALS['bus']->addData($this->getResponseData()); |
||
| 318 | } else { |
||
| 319 | $this->sendFeedback(false, array( |
||
| 320 | 'type' => ERROR_ZARAFA, |
||
| 321 | 'info' => array( |
||
| 322 | 'display_message' => $errorMsg, |
||
| 323 | ) |
||
| 324 | )); |
||
| 325 | } |
||
| 326 | } |
||
| 327 | } |
||
| 328 | ?> |
||
|
0 ignored issues
–
show
|
|||
| 329 |
Using a closing tag in PHP files that only contain PHP code is not recommended as you might accidentally add whitespace after the closing tag which would then be output by PHP. This can cause severe problems, for example headers cannot be sent anymore.
A simple precaution is to leave off the closing tag as it is not required, and it also has no negative effects whatsoever.