1 /* 2 * ***** BEGIN LICENSE BLOCK ***** 3 * Zimbra Collaboration Suite Web Client 4 * Copyright (C) 2009, 2010, 2011, 2012, 2013, 2014, 2016 Synacor, Inc. 5 * 6 * The contents of this file are subject to the Common Public Attribution License Version 1.0 (the "License"); 7 * you may not use this file except in compliance with the License. 8 * You may obtain a copy of the License at: https://www.zimbra.com/license 9 * The License is based on the Mozilla Public License Version 1.1 but Sections 14 and 15 10 * have been added to cover use of software over a computer network and provide for limited attribution 11 * for the Original Developer. In addition, Exhibit A has been modified to be consistent with Exhibit B. 12 * 13 * Software distributed under the License is distributed on an "AS IS" basis, 14 * WITHOUT WARRANTY OF ANY KIND, either express or implied. 15 * See the License for the specific language governing rights and limitations under the License. 16 * The Original Code is Zimbra Open Source Web Client. 17 * The Initial Developer of the Original Code is Zimbra, Inc. All rights to the Original Code were 18 * transferred by Zimbra, Inc. to Synacor, Inc. on September 14, 2015. 19 * 20 * All portions of the code are Copyright (C) 2009, 2010, 2011, 2012, 2013, 2014, 2016 Synacor, Inc. All Rights Reserved. 21 * ***** END LICENSE BLOCK ***** 22 */ 23 24 /** 25 * show history of the status window 26 * @param parent the element that created this view 27 * @private 28 */ 29 ZmResourceConflictDialog = function(parent) { 30 var selectId = Dwt.getNextId(); 31 32 var saveButton = new DwtDialog_ButtonDescriptor(ZmResourceConflictDialog.SAVE_BUTTON, ZmMsg.save, DwtDialog.ALIGN_RIGHT, null); 33 var cancelButton = new DwtDialog_ButtonDescriptor(ZmResourceConflictDialog.CANCEL_BUTTON, ZmMsg.cancel, DwtDialog.ALIGN_RIGHT, null); 34 35 //Bug fix # 80490 - Added an ID handler to the dialog 36 DwtDialog.call(this, {parent:parent, id:"RESC_CONFLICT_DLG", standardButtons: DwtDialog.NO_BUTTONS, extraButtons: [saveButton, cancelButton]}); 37 38 this.setContent(this._contentHtml(selectId)); 39 this.setTitle(ZmMsg.resourceConflictLabel); 40 41 this._freeBusyStatusMap = { 42 "F" : "free", 43 "B" : "busy", 44 "T" : "tentative", 45 "O" : "outOfOffice", 46 "U" : "unknown" 47 }; 48 49 this.registerCallback(ZmResourceConflictDialog.SAVE_BUTTON, this._handleSaveButton, this); 50 this.registerCallback(ZmResourceConflictDialog.CANCEL_BUTTON, this._handleCancelButton, this); 51 }; 52 53 ZmResourceConflictDialog.prototype = new DwtDialog; 54 ZmResourceConflictDialog.prototype.constructor = ZmResourceConflictDialog; 55 56 ZmResourceConflictDialog.HEIGHT = 150; 57 ZmResourceConflictDialog.MAX_HEIGHT = 300; 58 59 60 ZmResourceConflictDialog.SAVE_BUTTON = ++DwtDialog.LAST_BUTTON; 61 ZmResourceConflictDialog.CANCEL_BUTTON = ++DwtDialog.LAST_BUTTON; 62 ZmResourceConflictDialog.IGNORE_SAVE_BUTTON = ++DwtDialog.LAST_BUTTON; 63 64 // Public methods 65 66 ZmResourceConflictDialog.prototype.toString = 67 function() { 68 return "ZmResourceConflictDialog"; 69 }; 70 71 ZmResourceConflictDialog.prototype._contentHtml = 72 function(selectId) { 73 this._listId = Dwt.getNextId(); 74 return [ "<div class='ResourceConflictMsg'>", ZmMsg.resourceConflictInfo, "</div>", 75 "<div class='ZmResourceConflictDialog' id='", this._listId, "' style='overflow:auto;height:", ZmResourceConflictDialog.HEIGHT ,"px;'></div>"].join(""); 76 }; 77 78 ZmResourceConflictDialog.prototype._addAttr = 79 function(html, title, value, data) { 80 if (value) { 81 html.append("<tr width=100% id='", this._rowId(data), "'>"); 82 html.append("<td align=right style='Zwidth:60px;' class='ZmReminderField'>", title, ": </td>"); 83 html.append("<td>",AjxStringUtil.htmlEncode(value), "</td>"); 84 html.append("</tr>"); 85 } 86 }; 87 88 ZmResourceConflictDialog.prototype._rowId = 89 function(data) { 90 var id = Dwt.getNextId(); 91 data.rowIds.push(id); 92 return id; 93 }; 94 95 ZmResourceConflictDialog.prototype._addConflictInst = 96 function(html, inst, data, attendeeMap, needSep) { 97 98 data.buttonId = Dwt.getNextId(); 99 data.deltaId = Dwt.getNextId(); 100 data.cancelButtonId = Dwt.getNextId(); 101 data.rowIds = []; 102 103 if (needSep) html.append("<tr id='", inst.ridZ, "_sep'><td colspan=4><div class=horizSep></div></td></tr>"); 104 105 html.append("<tr width=100% id='conflict_row_", inst.ridZ, "'>"); 106 html.append("<td colspan=2 valign='top'>"); 107 html.append("<table id='", inst.ridZ, "_conflictInstTxt' cellpadding=1 width='95%' cellspacing=0 border=0><tr>"); 108 html.append("<td width=25px>", AjxImg.getImageHtml("Appointment"), "</td>"); 109 html.append("<td><b>", this.getDurationText(inst), "</b></td>"); 110 html.append("</tr><tr>"); 111 html.append("<td align='left' colspan='2'>"); 112 html.append("<div class='ResourceConflictResolver'>"); 113 html.append("<span id='" + data.cancelButtonId + "'></span> <span id='" + data.deltaId + "'></span>"); 114 html.append("</div>"); 115 html.append("</td>"); 116 html.append("</tr></table>"); 117 html.append("</td>"); 118 //html.append("<td align=right valign='top' id='", data.cancelButtonId, "'>"); 119 //html.append("</td>"); 120 html.append("<td align=right valign='top' id='", data.buttonId, "'>"); 121 html.append("<table cellpadding=1 cellspacing=0 border=0>"); 122 123 var usr = inst.usr; 124 if(usr) { 125 if(!(usr instanceof Array)) { 126 usr = [usr]; 127 } 128 for(var i = 0; i < usr.length; i++) { 129 var fbStatusStr = ""; 130 131 var name = usr[i].name; 132 if(attendeeMap[name]) { 133 var at = attendeeMap[name]; 134 name = at.getFullName() || at.getEmail(); 135 } 136 var fbStatus = ZmMsg[this.getFreeBusyStatus(usr[i])]; 137 html.append("<tr>"); 138 html.append("<td>" + this.getAttendeeImgHtml(at) + "</td>"); 139 html.append("<td>" + name + "</td>"); 140 html.append("<td>(" + fbStatus + ")</td>"); 141 html.append("</tr>"); 142 } 143 } 144 145 html.append("</table>"); 146 html.append("</td>"); 147 html.append("</tr>"); 148 //this._addAttr(html, ZmMsg.location, appt.getReminderLocation(), data); 149 }; 150 151 ZmResourceConflictDialog.prototype.getAttendeeImgHtml = 152 function(at) { 153 var img = "Person"; 154 if(at.resType) { 155 img = (at.resType == ZmCalBaseItem.LOCATION) ? "Location" : "Resource"; 156 } 157 return AjxImg.getImageSpanHtml(img); 158 }; 159 160 ZmResourceConflictDialog.prototype.getFreeBusyStatus = 161 function(usr) { 162 return this._freeBusyStatusMap[usr.fb] ? this._freeBusyStatusMap[usr.fb] : "free"; 163 }; 164 165 166 ZmResourceConflictDialog.prototype.getDurationText = 167 function(inst) { 168 var hourMinOffset = 0, 169 start = new Date(), 170 userTimeZone = appCtxt.get(ZmSetting.DEFAULT_TIMEZONE), 171 currentTimeZone = AjxTimezone.getServerId(AjxTimezone.DEFAULT); 172 if(this._appt.isAllDayEvent() && userTimeZone != currentTimeZone) { 173 var offset1 = AjxTimezone.getOffset(AjxTimezone.getClientId(currentTimeZone), start); 174 var offset2 = AjxTimezone.getOffset(AjxTimezone.getClientId(userTimeZone), start); 175 hourMinOffset = (offset2 - offset1) * 60 * 1000; //offset is in minutes convert to miliseconds 176 } 177 start = new Date(inst.s + hourMinOffset); 178 var endTime = start.getTime() + inst.dur; 179 var end = new Date(endTime); 180 181 var pattern = ZmMsg.apptTimeInstance; 182 if(this._appt.isAllDayEvent()) { 183 pattern = ZmMsg.apptTimeAllDay; 184 if(end.getDate() != start.getDate()) { 185 pattern = ZmMsg.apptTimeAllDayMulti; 186 } 187 } 188 189 return AjxMessageFormat.format(pattern, [start, end, ""]); 190 }; 191 192 ZmResourceConflictDialog.prototype.initialize = 193 function(list, appt, callback, cancelCallback) { 194 this._list = list; 195 this._appt = appt; 196 this._instData = {}; 197 this._callback = callback; 198 this._cancelCallback = cancelCallback; 199 this._canceledInstanceCount = 0; 200 201 var attendeeMap = {}; 202 var types = [ZmCalBaseItem.PERSON, ZmCalBaseItem.LOCATION, ZmCalBaseItem.EQUIPMENT]; 203 204 for(var i = 0; i < types.length; i++) { 205 var attendees = appt.getAttendees(types[i]); 206 for(var j = 0; j < attendees.length; j++) { 207 var at = attendees[j]; 208 var email = at ? at.getEmail() : null; 209 if(email) { 210 attendeeMap[email] = at; 211 } 212 } 213 } 214 215 216 var html = new AjxBuffer(); 217 218 var formatter = AjxDateFormat.getDateTimeInstance(AjxDateFormat.SHORT, AjxDateFormat.MEDIUM); 219 220 var size = this._conflictSize = list.length; 221 222 var dlgC = document.getElementById(this._listId); 223 Dwt.setSize(dlgC, Dwt.DEFAULT, size > 5 ? ZmResourceConflictDialog.MAX_HEIGHT : ZmResourceConflictDialog.HEIGHT); 224 225 html.append("<table cellpadding=2 cellspacing=0 border=0 width=100%>"); 226 for (var i=0; i < size; i++) { 227 var inst = list[i]; 228 var data = this._instData[i] = {inst: inst}; 229 this._addConflictInst(html, inst, data, attendeeMap, i > 0); 230 } 231 html.append("</table>"); 232 233 if (this._cancelButtons) { 234 for (var buttonId in this._cancelButtons) { 235 this._cancelButtons[buttonId].dispose(); 236 } 237 } 238 this._cancelButtons = {}; 239 240 var div = document.getElementById(this._listId); 241 div.innerHTML = html.toString(); 242 243 if(appt.getRecurType() == ZmRecurrence.NONE && size==1) { 244 return; 245 } 246 247 var recurrence = appt.getRecurrence(); 248 249 for (var i = 0; i < size; i++) { 250 var data = this._instData[i]; 251 var cancelButtonContainer = document.getElementById(data.cancelButtonId); 252 cancelButtonContainer.innerHTML = this.getCancelHTML(recurrence.isInstanceCanceled(data.inst.ridZ)); 253 Dwt.setHandler(cancelButtonContainer, DwtEvent.ONCLICK, AjxCallback.simpleClosure(this._handleCancelInstance, this, data.inst.ridZ, data.cancelButtonId, data.deltaId)); 254 } 255 }; 256 257 ZmResourceConflictDialog._onClick = 258 function(ev) { 259 ev = ev || window.event; 260 var el = DwtUiEvent.getTarget(ev); 261 var edv = AjxCore.objectWithId(el._editViewId); 262 if (edv) { 263 edv._handleOnClick(el); 264 } 265 }; 266 267 ZmResourceConflictDialog.prototype._handleCancelInstance = 268 function(ridZ, cancelButtonId, deltaId) { 269 var instEl = document.getElementById(ridZ + "_conflictInstTxt"); 270 var deltaEl = document.getElementById(deltaId); 271 var cancelEl = document.getElementById(cancelButtonId); 272 if(instEl) { 273 var appt = this._appt; 274 var recurrence = appt.getRecurrence(); 275 if(recurrence) { 276 var cancelInstance = !recurrence.isInstanceCanceled(ridZ); 277 if(cancelInstance) { 278 recurrence.addCancelRecurId(ridZ); 279 this._canceledInstanceCount++; 280 }else { 281 recurrence.removeCancelRecurId(ridZ); 282 this._canceledInstanceCount--; 283 } 284 if(cancelEl) { 285 cancelEl.innerHTML = this.getCancelHTML(cancelInstance); 286 } 287 } 288 } 289 }; 290 291 ZmResourceConflictDialog.prototype.getCancelHTML = 292 function(isCanceled) { 293 return isCanceled ? ZmMsg.cancelled + " - <span class='FakeAnchor'>" + ZmMsg.restorePage + "</span>" : "<span class='FakeAnchor'>" + ZmMsg.cancelInstance + "</span>"; 294 }; 295 296 ZmResourceConflictDialog.prototype.popup = 297 function() { 298 DwtDialog.prototype.popup.call(this); 299 var dblBookingAllowed = appCtxt.get(ZmSetting.CAL_RESOURCE_DBL_BOOKING_ALLOWED); 300 this._button[ZmResourceConflictDialog.SAVE_BUTTON].setEnabled(dblBookingAllowed); 301 }; 302 303 ZmResourceConflictDialog.prototype._handleSaveButton = 304 function() { 305 if(this._callback) this._callback.run(); 306 this.popdown(); 307 }; 308 309 ZmResourceConflictDialog.prototype._handleCancelButton = 310 function() { 311 if(this._cancelCallback) this._cancelCallback.run(); 312 if(this._appt) this._appt.getRecurrence().resetCancelRecurIds(); 313 this.popdown(); 314 }; 315 316 ZmResourceConflictDialog.prototype._handleIgnoreAllAndSaveButton = 317 function() { 318 var size = this._list ? this._list.length : 0; 319 var appt = this._appt; 320 for (var i = 0; i < size; i++) { 321 var data = this._instData[i]; 322 if(appt && data.inst) { 323 appt._recurrence.addCancelRecurId(data.inst.ridZ); 324 } 325 } 326 this._handleSaveButton(); 327 }; 328