1 /*
  2  * ***** BEGIN LICENSE BLOCK *****
  3  * Zimbra Collaboration Suite Web Client
  4  * Copyright (C) 2005, 2006, 2007, 2008, 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) 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2016 Synacor, Inc. All Rights Reserved.
 21  * ***** END LICENSE BLOCK *****
 22  */
 23 /****************** OLD VERSION OF SCHEDULE VIEW *********************/
 24 ZmCalScheduleView = function(parent, posStyle, controller, dropTgt) {
 25 	ZmCalColView.call(this, parent, posStyle, controller, dropTgt, null, 1, true);
 26 };
 27 
 28 ZmCalScheduleView.prototype = new ZmCalColView;
 29 ZmCalScheduleView.prototype.constructor = ZmCalScheduleView;
 30 
 31 ZmCalScheduleView.prototype.toString =
 32 function() {
 33 	return "ZmCalScheduleView";
 34 };
 35 
 36 ZmCalScheduleView.prototype._apptMouseDownAction =
 37 function(ev, apptEl) {
 38     appt = this.getItemFromElement(apptEl);
 39     if (appt.isAllDayEvent()) {
 40         return false;
 41     } else {
 42         return ZmCalBaseView.prototype._apptMouseDownAction.call(this, ev, apptEl, appt);
 43     }
 44 }
 45 
 46 
 47 
 48 
 49 
 50 /****************** NEW VERSION OF SCHEDULE VIEW *********************/
 51 ZmCalNewScheduleView = function(parent, posStyle, controller, dropTgt) {
 52 	ZmCalColView.call(this, parent, posStyle, controller, dropTgt, ZmId.VIEW_CAL_FB, 1, true);
 53     var app = appCtxt.getApp(ZmApp.CALENDAR);
 54     this._fbCache = new ZmFreeBusyCache(app);
 55 };
 56 
 57 ZmCalNewScheduleView.prototype = new ZmCalColView;
 58 ZmCalNewScheduleView.prototype.constructor = ZmCalNewScheduleView;
 59 
 60 ZmCalNewScheduleView.prototype.toString = 
 61 function() {
 62 	return "ZmCalNewScheduleView";
 63 };
 64 
 65 ZmCalNewScheduleView.ATTENDEES_METADATA = 'MD_SCHED_VIEW_ATTENDEES';
 66 
 67 ZmCalNewScheduleView.prototype.getFreeBusyCache =
 68 function() {
 69     return this._fbCache;
 70 }
 71 
 72 ZmCalNewScheduleView.prototype._createHtml =
 73 function(abook) {
 74 	DBG.println(AjxDebug.DBG2, "ZmCalNewScheduleView in _createHtml!");
 75     //TODO: Check and remove unnecessary instance vars
 76     this._days = {};
 77 	this._columns = [];
 78 	this._hours = {};
 79 	this._layouts = [];
 80 	this._allDayAppts = [];
 81     this._calendarOwners = {};
 82 
 83 	this._headerYearId = Dwt.getNextId();
 84 	this._yearHeadingDivId = Dwt.getNextId();
 85 	this._yearAllDayDivId = Dwt.getNextId();
 86 	this._leftAllDaySepDivId = Dwt.getNextId();
 87 	this._leftApptSepDivId = Dwt.getNextId();
 88 
 89 	this._allDayScrollDivId = Dwt.getNextId();
 90 	this._allDayHeadingDivId = Dwt.getNextId();
 91 	this._allDayApptScrollDivId = Dwt.getNextId();
 92 	this._allDayDivId = Dwt.getNextId();
 93 	this._hoursScrollDivId = Dwt.getNextId();
 94 	this._bodyHourDivId = Dwt.getNextId();
 95 	this._allDaySepDivId = Dwt.getNextId();
 96 	this._bodyDivId = Dwt.getNextId();
 97 	this._apptBodyDivId = Dwt.getNextId();
 98 	this._newApptDivId = Dwt.getNextId();
 99 	this._newAllDayApptDivId = Dwt.getNextId();
100 	this._timeSelectionDivId = Dwt.getNextId();
101 
102 
103     this._unionHeadingDivId = Dwt.getNextId();
104     this._unionAllDayDivId = Dwt.getNextId();
105     this._unionHeadingSepDivId = Dwt.getNextId();
106     this._unionGridScrollDivId = Dwt.getNextId();
107     this._unionGridDivId = Dwt.getNextId();
108     this._unionGridSepDivId = Dwt.getNextId();
109     this._workingHrsFirstDivId = Dwt.getNextId();
110     this._workingHrsSecondDivId = Dwt.getNextId();
111 	
112 
113 	this._allDayRows = [];
114     this._attendees = {};
115     this._attendees[ZmCalBaseItem.PERSON] = {};
116     this._attendees[ZmCalBaseItem.LOCATION] = {};
117     this._attendees[ZmCalBaseItem.EQUIPMENT] = {};
118 
119     var html = new AjxBuffer();
120     html.append("<div id='", this._bodyDivId, "' class=calendar_body style='position:absolute'>");
121     html.append("<div id='", this._apptBodyDivId, "' style='width:100%;position:absolute;'>","</div>");
122     html.append("</div>");
123     this.getHtmlElement().innerHTML = html.toString();
124     
125 };
126 
127 
128 ZmCalNewScheduleView.prototype._layout =
129 function(refreshApptLayout) {
130 	DBG.println(AjxDebug.DBG2, "ZmCalNewScheduleView in layout!");
131 
132     var sz = this.getSize();
133 	var width = sz.x;
134 	var height = sz.y;
135     if (width == 0 || height == 0) { return; }
136     this._setBounds(this._bodyDivId, 0, 0, width, height);
137     this._setBounds(this._apptBodyDivId, 0, 0, width-Dwt.SCROLLBAR_WIDTH, height);
138     //this._layoutAllDayAppts();
139 	
140 };
141 
142 ZmCalNewScheduleView.prototype.getCalendarAccount =
143 function() {
144 	return null;
145 };
146 
147 //mouse actions removed for now
148 ZmCalNewScheduleView.prototype._apptMouseDownAction =
149 function(ev, apptEl) {
150     DBG.println(AjxDebug.DBG2,  "mouse listeners");    
151 };
152 
153 ZmCalNewScheduleView.prototype._doubleClickAction =
154 function(ev, div) {
155     this._mouseDownAction(ev, div, true);
156 };
157 
158 ZmCalNewScheduleView.prototype._mouseDownAction =
159 function(ev, div, isDblClick) {
160     DBG.println(AjxDebug.DBG2,  "mouse down action");
161     var target = DwtUiEvent.getTarget(ev),
162         targetId,
163         tmp,
164         index = {},
165         apptDate,
166         duration = 30,
167         folderId = null,
168         isAllDay = false;
169     isDblClick = isDblClick || false;
170     if(target && target.className.indexOf("ZmSchedulerGridDiv") != -1) {
171         targetId = target.id;
172         tmp = targetId.split("__");
173         index.start = tmp[1] - 1;
174         index.end = tmp[1];
175 
176         if(this._scheduleView) {
177             this._scheduleView.setDateBorder(index);
178             this._scheduleView._outlineAppt();
179             if(!this._date) {
180                 this._date = new Date();
181             }
182             apptDate = new Date(this._date);
183             apptDate.setHours(0, index.end*30, 0);
184 
185             this._timeSelectionEvent(apptDate, duration, isDblClick, isAllDay, folderId, ev.shiftKey);
186         }
187     }
188 };
189 
190 ZmCalNewScheduleView.prototype.getOrganizer =
191 function() {
192     var organizer = new ZmContact(null);
193 	organizer.initFromEmail(appCtxt.getUsername(), true);
194     return organizer;
195 };
196 //overridden method - do not remove
197 ZmCalNewScheduleView.prototype.getRsvp =
198 function() {
199     return false;
200 };
201 //overridden method - do not remove
202 ZmCalNewScheduleView.prototype._scrollToTime =
203 function(hour) {
204 };
205 
206 ZmCalNewScheduleView.prototype.getDateInfo =
207 function(date) {
208     var dateInfo = {},
209         d = date || new Date();
210 	dateInfo.startDate = AjxDateUtil.simpleComputeDateStr(d);
211 	dateInfo.endDate = AjxDateUtil.simpleComputeDateStr(d);
212 	dateInfo.timezone = AjxDateFormat.format("z", d);
213     dateInfo.isAllDay = true;
214     return dateInfo;
215 };
216 
217 ZmCalNewScheduleView.prototype._navDateChangeListener =
218 function(date) {
219     this._date = date;
220     this._scheduleView.changeDate(this.getDateInfo(date));
221 };
222 //overridden method - do not remove
223 ZmCalNewScheduleView.prototype._dateUpdate =
224 function(rangeChanged) {
225 };
226 
227 ZmCalNewScheduleView.prototype.set =
228 function(list, skipMiniCalUpdate) {
229     this._preSet();
230     //Check added for sync issue - not sure if schedule view is ready by this time
231     if(!this._scheduleView) {
232         this._calNotRenderedList = list;
233         return;
234     }
235     this.resetListItems(list);
236     this.renderAppts(list);
237 };
238 
239 ZmCalNewScheduleView.prototype.resetListItems =
240 function(list) {
241     this._selectedItems.removeAll();
242     var newList = list;
243     if (list && (list == this._list)) {
244         newList = list.clone();
245     }
246     this._resetList();
247     this._list = newList;
248 };
249 
250 ZmCalNewScheduleView.prototype.renderAppts =
251 function(list) {
252     var timeRange = this.getTimeRange();
253 	if (list) {
254 		var size = list.size();
255 		DBG.println(AjxDebug.DBG2,"list.size:"+size);
256 		if (size != 0) {
257             var showDeclined = appCtxt.get(ZmSetting.CAL_SHOW_DECLINED_MEETINGS);
258             this._computeApptLayout();
259 			for (var i=0; i < size; i++) {
260 				var ao = list.get(i);
261 				if (ao && ao.isInRange(timeRange.start, timeRange.end) &&
262 				    (showDeclined || (ao.ptst != ZmCalBaseItem.PSTATUS_DECLINED))) {
263                     this.addAppt(ao);
264 				}
265 			}
266 		}
267 	}
268 };
269 
270 ZmCalNewScheduleView.prototype.addAppt =
271 function(appt) {
272     if(this._scheduleView) {
273         var item = this._createItemHtml(appt),
274             div = this._getDivForAppt(appt);
275 
276 	    if (div) {
277             div.appendChild(item);
278         }
279         this._scheduleView.colorAppt(appt, item);
280     }
281 };
282 
283 ZmCalNewScheduleView.prototype.removeAppt =
284 function(appt) {
285     if(this._scheduleView) {
286         var itemId = this._getItemId(appt),
287             item = document.getElementById(itemId),
288             div = this._getDivForAppt(appt);
289 
290 	    if (div && item) {
291             div.removeChild(item);
292         }
293     }
294 };
295 
296 ZmCalNewScheduleView.prototype.removeApptByEmail =
297 function(email) {
298     if(this._scheduleView) {
299         for (var i = 0; i<this._list.size(); i++) {
300             var appt = this._list.get(i);
301             if(appt && appt.getFolder().getOwner() == email) {
302                 var itemId = this._getItemId(appt),
303                     item = document.getElementById(itemId),
304                     div = this._getDivForAppt(appt);
305 
306                 if (div && item) {
307                     div.removeChild(item);
308                 }
309             }
310         }
311     }
312 };
313 
314 ZmCalNewScheduleView.prototype.refreshAppts =
315 function() {
316     this._selectedItems.removeAll();
317     var newList = this._list.clone();
318     this._resetList();
319     this._list = newList;
320     this.renderAppts(newList);
321 };
322 
323 //overridden method - do not remove
324 ZmCalNewScheduleView.prototype._layoutAllDayAppts =
325 function() {
326 };
327 
328 ZmCalNewScheduleView.prototype.getAtttendees =
329 function() {
330     return this._attendees[ZmCalBaseItem.PERSON].getArray();
331 };
332 
333 ZmCalNewScheduleView.prototype.getMetadataAttendees =
334 function(organizer) {
335     var md = new ZmMetaData(organizer.getAccount());
336     var callback = new AjxCallback(this, this.processMetadataAttendees);
337     md.get('MD_SCHED_VIEW_ATTENDEES', null, callback);
338 };
339 
340 ZmCalNewScheduleView.prototype.processMetadataAttendees =
341 function(metadataResponse) {
342     var objAttendees = metadataResponse.getResponse().BatchResponse.GetMailboxMetadataResponse[0].meta[0]._attrs,
343         emails = [],
344         email,
345         acct,
346         i;
347 
348     for (email in objAttendees) {
349         if(email && objAttendees[email]) {
350             emails.push(objAttendees[email]);
351         }
352     }
353     this._mdAttendees = AjxVector.fromArray(emails);
354     for (i=0; i<this._mdAttendees.size(); i++) {
355         acct = ZmApptViewHelper.getAttendeeFromItem(this._mdAttendees.get(i), ZmCalBaseItem.PERSON);
356         this._attendees[ZmCalBaseItem.PERSON].add(acct, null, true);
357     }
358 
359     AjxDispatcher.require(["MailCore", "CalendarCore", "Calendar", "CalendarAppt"]);
360     this._scheduleView = new ZmFreeBusySchedulerView(this, this._attendees, this._controller, this.getDateInfo());
361     this._scheduleView.setComposeMode(false);
362     this._scheduleView.setVisible(true);
363     this._scheduleView.showMe();
364     this._scheduleView.reparentHtmlElement(this._apptBodyDivId);
365 
366     //Called to handle the sync issue
367     this.resetListItems(this._calNotRenderedList);
368     this.renderAppts(this._calNotRenderedList);
369     delete this._calNotRenderedList;
370 };
371 
372 ZmCalNewScheduleView.prototype.setMetadataAttendees =
373 function(organizer, email) {
374     if(!organizer) {
375         organizer = this.getOrganizer();
376     }
377     if (email instanceof Array) {
378         email = email.join(',');
379     }
380     var md = new ZmMetaData(organizer.getAccount());
381     this._mdAttendees.add(email, null, true);
382     return md.set(ZmCalNewScheduleView.ATTENDEES_METADATA, this._mdAttendees.getArray());
383 };
384 
385 ZmCalNewScheduleView.prototype.removeMetadataAttendees =
386 function(organizer, email) {
387     if(!organizer) {
388         organizer = this.getOrganizer();
389     }
390     if (email instanceof Array) {
391         email = email.join(',');
392     }
393     var md = new ZmMetaData(organizer.getAccount());
394     this._mdAttendees.remove(email, null, true);
395     return md.set(ZmCalNewScheduleView.ATTENDEES_METADATA, this._mdAttendees.getArray());
396 };
397 
398 ZmCalNewScheduleView.prototype._resetCalendarData =
399 function() {
400     var i,
401         tb,
402         acct,
403         acctEmail,
404         strAttendees,
405         mdAttendees,
406         organizer = this.getOrganizer();
407 	this._calendars = this._controller.getCheckedCalendars();
408 	this._calendars.sort(ZmFolder.sortCompareNonMail);
409 	this._folderIdToColIndex = {};
410 	this._columns = [];
411 	this._numCalendars = this._calendars.length;
412     this._attendees[ZmCalBaseItem.PERSON] = new AjxVector();
413 
414     for (i=0; i<this._numCalendars; i++) {
415         acctEmail = this._calendars[i].getOwner();
416         if(organizer.getEmail() != acctEmail) {
417             //if not organizer add to the attendee list
418             acct = ZmApptViewHelper.getAttendeeFromItem(acctEmail, ZmCalBaseItem.PERSON);
419             this._attendees[ZmCalBaseItem.PERSON].add(acct, null, true);
420         }
421     }
422 
423     if(this._scheduleView) {
424         //this._attendees[ZmCalBaseItem.PERSON] = this._scheduleView.getAttendees();
425 
426         for (i=0; i<this._mdAttendees.size(); i++) {
427             acct = ZmApptViewHelper.getAttendeeFromItem(this._mdAttendees.get(i), ZmCalBaseItem.PERSON);
428             this._attendees[ZmCalBaseItem.PERSON].add(acct, null, true);
429         }
430 
431         this._scheduleView.cleanup();
432         this._scheduleView.setComposeMode(false);
433         this._scheduleView.set(this.getDate(), organizer, this._attendees);
434         this._scheduleView.enablePartcipantStatusColumn(true);
435     }
436     else {
437         this._mdAttendees = new AjxVector();
438         this.getMetadataAttendees(organizer);        
439     }    
440 };