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