Index: npl/mailserver/zarafa_webaccess_muc/root/var/www/htdocs/syn3/zarafa/client/layout/css/multiusercalendar.css
===================================================================
--- npl/mailserver/zarafa_webaccess_muc/root/var/www/htdocs/syn3/zarafa/client/layout/css/multiusercalendar.css	(revision c5c522c7ca74a4c1725ca5e3fe0bdcadff80e49a)
+++ npl/mailserver/zarafa_webaccess_muc/root/var/www/htdocs/syn3/zarafa/client/layout/css/multiusercalendar.css	(revision c5c522c7ca74a4c1725ca5e3fe0bdcadff80e49a)
@@ -0,0 +1,253 @@
+div.multiusercalendar_master {
+	width: 100%;
+	height: 100%;
+	background-color: #EEEEFF;
+	position: relative;
+}
+div.multiusercalendar_container {
+	width: 100%;
+	height: 100%;
+	overflow: auto;
+	position: relative;
+}
+
+div.multiusercalendar_container  div.multiusercalendar_scrollheader {
+	position: absolute;
+	left: 0px;
+	top: 0px;
+	z-index: 11;
+}
+div.multiusercalendar_container  div.multiusercalendar_scrollnamedisplay {
+	position: absolute;
+	left: 0px;
+	top: 0px;
+	z-index: 10;
+}
+div.multiusercalendar_container  div.multiusercalendar_scrollnamedisplay div.scroll_label {
+	position: absolute;
+	left: 0px;
+	top: 0px;
+
+	padding: 1px 2px;
+	white-space: nowrap;
+	background-color: #FFF;
+	opacity: 0.80;
+	filter: alpha(opacity=80);
+}
+
+table.multiusercalendar {
+	border: 1px solid #000;
+	border-right: 1px;
+	border-bottom: 0px;
+}
+table.multiusercalendar td {
+	border: 1px solid #000;
+	border-left: 0px;
+	border-top: 0px;
+	background-color: #C5D5FC;
+}
+table.multiusercalendar tr.header_timeline_day_daterow td.header_display {
+	text-align: center;
+	padding: 3px;
+}
+table.multiusercalendar tr.header_timeline_day_daterow td.date_column {
+	text-align: center;
+}
+table.multiusercalendar tr.header_timeline_day_timerow td {
+	width: 25px;
+	border-right: 1px dashed;
+}
+table.multiusercalendar tr.user_timeline_timerow td{
+	border-left: 0px;
+	border-right: 1px dashed #999;
+}
+table.multiusercalendar tr.user_timeline_timerow td.user_timeline_userdisplay {
+	border-right: 1px solid #000;
+	height: 60px;
+	width: 150px;
+}
+table.multiusercalendar tr.header_timeline_day_timerow td.lasthourofday, 
+table.multiusercalendar tr.user_timeline_timerow td.lasthourofday {
+	border-right: 1px solid #000;
+}
+table.multiusercalendar tr.user_timeline_timerow td.timeblock_nonworkinghours {
+	background-color: #FFF4BC;
+}
+table.multiusercalendar tr.user_timeline_timerow td.timeblock_workinghours {
+	background-color: #FFFFD5;
+}
+table.multiusercalendar tr.user_timeline_timerow td div.mucalendar_timeline_noaccess_notification {
+	position: relative;
+	left: 5px;
+	top: -20px;
+}
+table.multiusercalendar tr.user_timeline_timerow td div.mucalendar_timeline_noaccess_notification div {
+	position: absolute;
+	padding: 5px;
+	display: inline;
+	white-space: nowrap;
+	background-color: #FFF;
+	opacity: 0.80;
+	filter: alpha(opacity=80);
+	background-image: url(../img/icon_priority_high.gif);
+	background-repeat: no-repeat;
+	background-position: 5px 5px;
+	padding-left: 15px;
+}
+
+div.mucalendar_appointment_block {
+	border-top: 1px solid #000;
+	border-right: 0px;
+}
+div.mucalendar_appointment_block_tooltip {
+	border-top: 1px solid #000;
+	border-right: 0px;
+	position: absolute;
+	z-index: 900;
+	background-color: #0FF;
+}
+div.mucalendar_appointment_block_tooltip_trigger {
+	z-index: 901;
+}
+
+div.mucalendar_appointment_block_data {
+	position: absolute;
+	left: 0px;
+	top: 0px;
+	padding: 2px;
+	display: inline;
+	font-weight: bold;
+	text-overflow: ellipsis;
+	white-space: nowrap;
+}
+div.mucalendar_appointment_block_data_bg {
+	position: absolute;
+	left: 0px;
+	top: 0px;
+	padding: 2px;
+	display: inline;
+	height: 45px;
+
+}
+
+div.mucalendar_appointment_block_tooltip div.mucalendar_appointment_block_data_bg {
+	width: 100px;
+	background-color: #FFF;
+	opacity: 0.80;
+	filter: alpha(opacity=80);
+}
+
+
+div.multiusercalendar_container div.mucalendar_appointment_block_data .private {
+	background-image:url(../img/icon_private.gif);
+	background-position:0px 1px;
+	background-repeat:no-repeat;
+	padding-left:12px;
+	padding-right:5px;
+}
+div.multiusercalendar_container div.mucalendar_appointment_block_data .recurring {
+	background-image:url(../img/icon_recurring.gif);
+	background-position:0px 1px;
+	background-repeat:no-repeat;
+	padding-left:12px;
+	padding-right:5px;
+}
+div.multiusercalendar_container div.mucalendar_appointment_block_data .meetingrequest {
+	background-image:url(../img/icon_group.gif);
+	background-position:0px 1px;
+	background-repeat:no-repeat;
+	padding-left:12px;
+	padding-right:5px;
+}
+
+div.mucalendar_allattendeesbar_block {
+	border: 0px;
+	position: absolute;
+}
+
+
+
+/** NO ACCESS **/
+table.multiusercalendar tr.user_no_access td.user_timeline_userdisplay {
+}
+table.multiusercalendar tr.user_no_access td.timeblock_nonworkinghours {
+	background-color: #FF9999;
+}
+table.multiusercalendar tr.user_no_access td.timeblock_workinghours {
+	background-color: #FFBB99;
+}
+
+
+/* Busy States */
+div.multiusercalendar_master .busystate_free {
+	border-left: 3px solid #BBB;
+	border-bottom: 2px solid #BBB;
+}
+div.multiusercalendar_master .busystate_tentative {
+	border-left: 3px solid #AAC6FF;
+	border-bottom: 2px solid #AAC6FF;
+}
+div.multiusercalendar_master .busystate_busy {
+	border-left: 3px solid #0402FC;
+	border-bottom: 2px solid #0402FC;
+}
+div.multiusercalendar_master .busystate_outofoffice {
+	border-left: 3px solid #800080;
+	border-bottom: 2px solid #800080;
+}
+
+
+/* All attendees bar */
+.mucalendar_allattendeesbar_block_busystate_free {
+	background-color: #FFF;
+}
+.mucalendar_allattendeesbar_block_busystate_tentative {
+	background-color: #AAC6FF;
+}
+.mucalendar_allattendeesbar_block_busystate_busy {
+	background-color: #0402FC;
+}
+.mucalendar_allattendeesbar_block_busystate_outofoffice {
+	background-color: #800080;
+}
+
+
+/* DragDrop */
+.mucalendar_appointment_dragdrop {
+	position: absolute;
+	left: 150px;
+	top: 150px;
+	width: 100px;
+	height: 100px;
+	background-color: #F00;
+
+	border-left: 3px solid #F00;
+	opacity: 0.75;
+	filter: alpha(opacity=75);
+	z-index: 999;
+}
+.mucalendar_appointment_dragdrop_tooltip {
+	position: absolute;
+	left: 0px;
+	top: 0px;
+	width: 70px;
+	background-color: #FFF;
+	font-weight: bold;
+	padding: 2px;
+	z-index: 1000;
+}
+
+.mucalendar_move_up {
+	background-image: url(../img/move_up.gif);
+	margin-left: 5px;
+	
+}
+.mucalendar_move_up_down{
+	background-position:2px center;
+	background-repeat:no-repeat;
+	padding-left:6px;
+	cursor: pointer;
+}
+.mucalendar_move_down {
+	background-image: url(../img/move_down.gif);
+}
Index: npl/mailserver/zarafa_webaccess_muc/root/var/www/htdocs/syn3/zarafa/client/modules/multiusercalendarappointmentlistmodule.js
===================================================================
--- npl/mailserver/zarafa_webaccess_muc/root/var/www/htdocs/syn3/zarafa/client/modules/multiusercalendarappointmentlistmodule.js	(revision c5c522c7ca74a4c1725ca5e3fe0bdcadff80e49a)
+++ npl/mailserver/zarafa_webaccess_muc/root/var/www/htdocs/syn3/zarafa/client/modules/multiusercalendarappointmentlistmodule.js	(revision c5c522c7ca74a4c1725ca5e3fe0bdcadff80e49a)
@@ -0,0 +1,67 @@
+multiusercalendarappointmentlistmodule.prototype = new ListModule;
+multiusercalendarappointmentlistmodule.prototype.constructor = multiusercalendarappointmentlistmodule;
+multiusercalendarappointmentlistmodule.superclass = ListModule.prototype;
+
+function multiusercalendarappointmentlistmodule(id, element, title, data)
+{
+	if(arguments.length > 0) {
+		this.init(id, element, title, data);
+	}
+
+}
+
+multiusercalendarappointmentlistmodule.prototype.init = function(id, element, title, data)
+{
+	this.element = element;
+	this.contentElement = element;
+
+	this.selectedView = "";
+	if(!data){
+		var data = new Array();
+	}
+	data["has_no_menu"] = true;
+	this.MultiUserCalendarParent = data["mucalendarParent"];
+	this.storeid = data["storeid"];
+	this.groupID = data["groupID"];
+	this.entryid = data["folderentryid"];
+
+	multiusercalendarappointmentlistmodule.superclass.init.call(this, id, element, title, data);
+	this.initializeView("multiusercalendarappointmentlist");
+
+}
+
+multiusercalendarappointmentlistmodule.prototype.getStoreID = function(view){
+	return this.storeid;
+}
+
+multiusercalendarappointmentlistmodule.prototype.initializeView = function(view){
+	if (view){
+		this.selectedview = view;
+	}
+	this.viewController.initView(this.id, this.selectedview, this.contentElement, this.events, {mucalendarParent: this.MultiUserCalendarParent, storeid: this.storeid, groupID: this.groupID});
+}
+
+multiusercalendarappointmentlistmodule.prototype.list = function(useTimeOut)
+{
+	if(this.storeid && this.folderentryid) {
+		var data = new Object();
+		data["store"] = this.storeid;
+		data["entryid"] = this.folderentryid;
+		data["restriction"] = new Object();
+		data["restriction"]["startdate"] = this.viewedDays[0];
+		data["restriction"]["duedate"] = Number(this.viewedDays[this.viewedDays.length-1])+(24*60*60);
+	
+		webclient.xmlrequest.addData(this, "list", data);
+		this.viewController.loadMessage();
+	}
+}
+
+multiusercalendarappointmentlistmodule.prototype.setTimePeriod = function(viewedDays)
+{
+	this.viewedDays = viewedDays;
+}
+
+multiusercalendarappointmentlistmodule.prototype.deleteItems = function(action)
+{
+	this.viewController.deleteItems(action, this.groupID);
+}
Index: npl/mailserver/zarafa_webaccess_muc/root/var/www/htdocs/syn3/zarafa/client/modules/multiusercalendarmodule.js
===================================================================
--- npl/mailserver/zarafa_webaccess_muc/root/var/www/htdocs/syn3/zarafa/client/modules/multiusercalendarmodule.js	(revision c5c522c7ca74a4c1725ca5e3fe0bdcadff80e49a)
+++ npl/mailserver/zarafa_webaccess_muc/root/var/www/htdocs/syn3/zarafa/client/modules/multiusercalendarmodule.js	(revision c5c522c7ca74a4c1725ca5e3fe0bdcadff80e49a)
@@ -0,0 +1,378 @@
+multiusercalendarmodule.prototype = new ListModule;
+multiusercalendarmodule.prototype.constructor = multiusercalendarmodule;
+multiusercalendarmodule.superclass = ListModule.prototype;
+
+
+function multiusercalendarmodule(id, element, title, data)
+{
+	if(arguments.length > 0) {
+		this.init(id, element, title, data);
+	}
+	
+}
+
+multiusercalendarmodule.prototype.init = function(id, element, title, data)
+{
+	this.element = element;
+	if(!data){
+		var data = new Array();
+	}
+	data["has_no_menu"] = false;
+	this.groupIDCounter = 1;
+
+	this.setTitle(_("Multi User Calendar"), NBSP, true);
+	document.title = _("Multi User Calendar") +" - "+ _("Zarafa Webaccess");
+
+	this.numOfDaysInViewport = webclient.settings.get("calendar/mucalendar/zoomlevel", 3);
+	this.numOfDays = webclient.settings.get("calendar/mucalendar/numofdaysloaded", 5);
+
+	// timeToZero will return the timestamp of the start of the day
+	var startDate = timeToZero(( (new Date()).getStartDateOfWeek().getTime()/1000 ));
+	this.viewedDays = this.calcViewedDays(startDate, this.numOfDays);
+
+	this.groups = Array();
+
+	this.startupUsers = webclient.settings.get("calendar/mucalendar/startupusers", "").split(";");
+
+	if(webclient.hierarchy) {
+		var folder = dhtml.getElementById(webclient.hierarchy.selectedFolder);
+		if(folder) {
+			var folderIcon = folder.getElementsByTagName("div")[1];
+			var folderName = folderIcon.getElementsByTagName("span")[0];
+			folderName.className = "folder_title";
+		}
+	}
+
+	multiusercalendarmodule.superclass.init.call(this, id, element, title, data);
+
+//	if (!webclient.datepicker){
+//		var data2 = new Object();
+//		webclient.datepicker = webclient.loadModule("datepickerlist", _("Date picker"), "left", data2, BOX_LAYOUT, INSERT_ELEMENT_AT_TOP);
+//	}
+
+	// Set Menu items
+	this.menuItems.push(webclient.menu.createMenuItem("seperator", ""));
+	this.menuItems.push(webclient.menu.createMenuItem("prevperiod", _("Previous Period"), _("Load previous period"), eventMultiUserCalendarViewClickPrevPeriod));
+	this.menuItems.push(webclient.menu.createMenuItem("prevday", _("Previous Day"), _("Load previous day"), eventMultiUserCalendarViewClickPrevDay));
+	this.menuItems.push(webclient.menu.createMenuItem("nextday ", _("Next Day"), _(""), eventMultiUserCalendarViewClickNextDay));
+	this.menuItems.push(webclient.menu.createMenuItem("nextperiod", _("Next Period"), _("Forward"), eventMultiUserCalendarViewClickNextPeriod));
+	this.menuItems.push(webclient.menu.createMenuItem("seperator", ""));
+	this.menuItems.push(webclient.menu.createMenuItem("loadgroup", _("Load Group")+"...", _("Load group"), function(){
+		webclient.openModalDialog(-1, 'usergroup', DIALOG_URL+'task=usergroup_modal', 600,450, mucalendarDialog_loadgroups_addCallBack, {moduleID:this.id});
+	}));
+	this.menuItems.push(webclient.menu.createMenuItem("adduser", _("Add User")+"...", _("Add new user"), function(){
+		var windowData = new Object();
+		windowData["hide_users"] = ["contact"];
+		windowData["hide_groups"] = ["dynamic", "normal", "everyone", "security"];
+		windowData["hide_companies"] = true;
+
+		webclient.openModalDialog(-1, 'addressbook', DIALOG_URL+'&type=username&source=gab&task=addressbook_modal&storeid=' + webclient.hierarchy.defaultstore["id"], 800, 500, mucalendarDialog_adduser_addCallBack, {moduleID: this.id}, windowData);
+	}));
+	this.menuItems.push(webclient.menu.createMenuItem("removeuser", _("Remove user"), _("Remove user"), eventMultiUserCalendarViewRemoveUsers));
+	// Build menu and put it on the screen
+	webclient.menu.buildTopMenu(this.id, "appointment", this.menuItems, eventListNewMessage);
+	webclient.menu.showMenu();
+
+
+	var items = new Array();
+	items.push(webclient.menu.createMenuItem("open", _("Open"), false, eventAppointmentListContextMenuOpenMessage));
+	items.push(webclient.menu.createMenuItem("print", _("Print"), false, eventListContextMenuPrintMessage));
+	items.push(webclient.menu.createMenuItem("seperator", ""));
+	items.push(webclient.menu.createMenuItem("categories", _("Categories"), false, eventListContextMenuCategoriesMessage));
+	items.push(webclient.menu.createMenuItem("seperator", ""));
+	items.push(webclient.menu.createMenuItem("delete", _("Delete"), false, eventAppointmentListContextMenuDeleteMessage));
+	this.contextmenu = items;
+
+	/**
+	 * Adding storeID to multiusercalendar to fix the createNewItem. This is not
+	 * supplied by the server, so we have to take it from the hierarchymodule. 
+	 * This also means that is has to happen AFTER the call to the super class, 
+	 * otherwise it will be overwritten with the boolean value false.
+	 */
+	if(webclient.hierarchy && webclient.hierarchy.defaultstore && webclient.hierarchy.defaultstore.id){
+		this.storeid = webclient.hierarchy.defaultstore.id;
+	}
+
+	this.keys["muc"] = KEYS["muc"];
+
+	this.initializeView();
+}
+
+multiusercalendarmodule.prototype.initializeView = function(view)
+{
+	if(!this.contentElement){
+		this.contentElement = dhtml.addElement(this.element, "div", false, "mucalendar_content["+this.id+"]");
+	}
+
+	this.viewController.initView(this.id, "multiusercalendar", this.contentElement, this.events, {numOfDays: this.numOfDays, numOfDaysInViewport: this.numOfDaysInViewport, vieweddays: this.viewedDays});
+
+	//webclient.xmlrequest.addData(this, "getgroups", {});
+	//webclient.xmlrequest.sendRequest();
+	if(this.groups.length > 0){
+		for(var i=0;i<this.groups.length;i++){
+			this.viewController.addGroup({
+				"groupID": this.groups[i].groupID, 
+				"name":    this.groups[i].display_name, 
+				"access":  this.groups[i].access, 
+				"storeid": this.groups[i].storeid, 
+				"folderentryid": this.groups[i].folderentryid
+			});
+			this.groups[i].setTimePeriod(this.viewedDays);
+			this.groups[i].list();
+		}
+	}else{
+		var userData = new Array();
+		for(var i in this.startupUsers){
+			userData.push({
+				entryid: "",
+				username: this.startupUsers[i]
+			});
+		}
+		this.getUsers(userData);
+	}
+
+	webclient.inputmanager.addObject(this, this.element);
+	webclient.inputmanager.bindKeyControlEvent(this, this.keys["refresh"], "keyup", eventListKeyCtrlRefreshFolder);
+}
+
+multiusercalendarmodule.prototype.getGroup = function(groupID){
+	for(var i=0;i<this.groups.length;i++){
+		if(this.groups[i].groupID == groupID){
+			return this.groups[i];
+		}
+	}
+	return false;
+}
+
+multiusercalendarmodule.prototype.addGroup = function(groupEntryID){
+	webclient.xmlrequest.addData(this, "getgroupusers", {group_entry_id: groupEntryID});
+	setTimeout(function(){
+		webclient.xmlrequest.sendRequest();
+	}, 0);
+	this.removeAllGroups();
+}
+
+multiusercalendarmodule.prototype.getUsers = function(userData){
+	var users = new Array();
+
+	for(var i=0;i<userData.length;i++){
+		if(userData[i].username){
+			users.push({user: {user_entry_id: userData[i].entryid, username: userData[i].username} });
+		}
+	}
+
+	if(users.length > 0){
+		webclient.xmlrequest.addData(this, "getusers", {users: users});
+	}
+
+	setTimeout(function(){
+		webclient.xmlrequest.sendRequest();
+	}, 0);
+}
+
+multiusercalendarmodule.prototype.addUser = function(groupData){
+	// Check if the user has already been added
+	var adduser = true;
+	if(groupData.storeid != null){
+		for(var i=0;i<this.groups.length;i++){
+			if(this.groups[i].getStoreID() == groupData.storeid){
+				adduser = false;
+			}
+		}
+	}
+
+	if(adduser){
+		// Load module
+		var module = webclient.dispatcher.loadModule("multiusercalendarappointmentlistmodule");
+		webclient.addModule(module);
+
+		if(module) {
+			// Add module
+			var moduleID = webclient.addModule(module);
+			var data = {
+				mucalendarParent: this, 
+				userentryid: groupData.userentryid,
+				storeid: groupData.storeid,
+				folderentryid: groupData.folderentryid,
+				/**
+				 * The entryid has to be named as such because is used to check whether updated 
+				 * items are meant for the multiusercalendarappointmentlistmodule module by checking
+				 * it against their parent entryid.
+				 */
+				entryid: groupData.folderentryid,
+				display_name: groupData.display_name,
+				username: groupData.username,
+				email_address: groupData.username,
+				access: groupData.access,
+				groupID: this.groupIDCounter++
+			};
+			// Initialize module
+			module.init(moduleID, this.contentElement, null, data);
+			module.setTimePeriod(this.viewedDays);
+			module.list();
+
+			this.viewController.addGroup({
+				"groupID":data.groupID,
+				"name":groupData.display_name,
+				"access": groupData.access,
+				"storeid": groupData.storeid, 
+				"folderentryid": groupData.folderentryid,
+				"username": data.username
+			});
+
+			this.groups.push(module);
+		}
+
+		return module;
+	}else{
+//TODO: notify user of failed attempt to add user
+		return false;
+	}
+}
+
+multiusercalendarmodule.prototype.execute = function(type, action) {
+    if(type == "error") {
+        var message = dhtml.getXMLValue(dhtml.getXMLNode(action, "error"), "message");
+        alert(message);
+    }
+    else {
+        multiusercalendarmodule.superclass.execute.call(this, type, action);
+    }
+}
+
+multiusercalendarmodule.prototype.removeAllGroups = function(){
+	while(this.groups.length){
+		var group = this.groups.shift();
+		if(!this.groups.length){	// Last item
+			this.viewController.deleteGroup(group.groupID, {noresize: false});
+		}else{
+			this.viewController.deleteGroup(group.groupID, {noresize: true});
+		}
+		// Delete group module from webclient
+		webclient.deleteModule(group);
+	}
+}
+multiusercalendarmodule.prototype.deleteGroup = function(groupID){
+	for(var i=0;i<this.groups.length;i++){
+		if(this.groups[i].groupID == groupID){
+			var group = this.groups.splice(i, 1);
+			this.viewController.deleteGroup(group[0].groupID);
+			// Delete group module from webclient
+			webclient.deleteModule(group);
+		}
+	}
+	this.setStartupUsersSetting();
+}
+
+// Received from mucalendarAppointmentListView to pass on to mucalendarListModule
+multiusercalendarmodule.prototype.executeAppView = function(items, properties, action, groupID){
+	return this.viewController.addItems(items, properties, action, groupID);
+}
+
+multiusercalendarmodule.prototype.updateItemAppView = function(items, properties, action, groupID){
+	return this.viewController.updateItem(items, properties, action, groupID);
+}
+
+multiusercalendarmodule.prototype.addItemAppView = function(items, properties, action, groupID){
+	/**
+	 * The item is sent as an array to make sure the positionAllAttendeesBarItems and vPositionItems
+	 * methods are called from the view itself.
+	 */
+	return this.viewController.addItems(Array(items), properties, action, groupID);
+}
+
+multiusercalendarmodule.prototype.deleteItemsAppView = function(items, groupID){
+	return this.viewController.deleteItems(items, groupID);
+}
+
+
+// Receives listed items from server module multiusercalendarmodule
+multiusercalendarmodule.prototype.messageList = function(action){
+	var items = action.getElementsByTagName("item");
+	for(var i=0;i<items.length;i++){
+		var item = items[i];
+
+		if(dhtml.getXMLValue(item, "type") == "usergroup"){
+			var data = {
+				groupEntryID: dhtml.getXMLValue(item, "entryid"),
+				display_name: dhtml.getXMLValue(item, "subject")
+			}
+		}else{
+			var data = {
+				userentryid: dhtml.getXMLValue(item, "entryid"),
+				display_name: dhtml.getXMLValue(item, "display_name"),
+				storeid: dhtml.getXMLValue(item, "storeid"),
+				folderentryid: dhtml.getXMLValue(item, "calentryid"),
+				username: dhtml.getXMLValue(item, "username"),
+				access: dhtml.getXMLValue(item, "access")
+			}
+			this.addUser(data);
+		}
+	}
+	this.setStartupUsersSetting();
+}
+
+multiusercalendarmodule.prototype.setStartupUsersSetting = function(){
+	var userlist = new Array();
+	for(var i=0;i<this.groups.length;i++){
+		userlist.push(this.groups[i].username);
+	}
+	webclient.settings.set("calendar/mucalendar/startupusers", userlist.join(";"));
+	return userlist;
+}
+
+// Shift view time periods
+multiusercalendarmodule.prototype.changeTimePeriod = function(type){
+	var datepickerlistmodule = webclient.getModulesByName("datepickerlistmodule")[0];
+	var timestamp = new Date(this.viewedDays[0]*1000);
+
+	switch(type){
+		case "prevperiod":
+			timestamp.addDays(-this.viewedDays.length);
+			break;
+		case "prevday":
+			timestamp.addDays(-1);
+			break;
+		case "nextday":
+			timestamp.addDays(+1);
+			break;
+		case "nextperiod":
+			timestamp.addDays(+this.viewedDays.length);
+			break;
+	}
+
+	//datepickerlistmodule.changeMonth(timestamp.getMonth() + 1, timestamp.getFullYear(), true);
+	//datepickerlistmodule.changeSelectedDate(timestamp.getTime(),true);
+
+	this.viewedDays = this.calcViewedDays((timestamp.getTime()/1000), this.viewedDays.length);
+	this.initializeView();
+}
+
+
+multiusercalendarmodule.prototype.calcViewedDays = function(startdate, numOfDays){
+	var days = new Array();
+	var date = new Date(startdate * 1000);
+	days.push(date.getTime()/1000);
+	// Start at 1, first is already added
+	for(var i=1;i<numOfDays;i++){
+		date.addDays(+1);
+		days.push(date.getTime()/1000);
+	}
+	//days[(days.length-1)] = days[(days.length-1)] + 24*60*60;
+	return days;
+}
+
+
+multiusercalendarmodule.prototype.destructor = function()
+{
+	while(this.groups.length){
+		// Delete group module from webclient
+		webclient.deleteModule(this.groups.shift());
+	}
+	multiusercalendarmodule.superclass.destructor(this);
+}
+
+multiusercalendarmodule.prototype.refresh = function()
+{
+	this.initializeView();
+}
Index: npl/mailserver/zarafa_webaccess_muc/root/var/www/htdocs/syn3/zarafa/client/views/multiusercalendar.view.js
===================================================================
--- npl/mailserver/zarafa_webaccess_muc/root/var/www/htdocs/syn3/zarafa/client/views/multiusercalendar.view.js	(revision c5c522c7ca74a4c1725ca5e3fe0bdcadff80e49a)
+++ npl/mailserver/zarafa_webaccess_muc/root/var/www/htdocs/syn3/zarafa/client/views/multiusercalendar.view.js	(revision c5c522c7ca74a4c1725ca5e3fe0bdcadff80e49a)
@@ -0,0 +1,1848 @@
+MultiUserCalendarView.prototype = new View;
+MultiUserCalendarView.prototype.constructor = MultiUserCalendarView;
+MultiUserCalendarView.superclass = View.prototype;
+
+
+function MultiUserCalendarView(moduleID, element, events, data)
+{
+	this.element = element;
+	dhtml.addClassName(this.element, "MultiUserCalendarView");
+	
+	this.moduleID = moduleID;
+	this.events = events;
+	this.data = data;
+	this.numOfDays = data['numOfDays'];
+	this.numOfDaysInViewport = data['numOfDaysInViewport'];
+	this.numOfDaysInViewportOptions = Array(2,3,4,5,6,7,10,14);
+	this.viewedDays = data["vieweddays"];
+	// Set the ranges of the timeblocks
+	this.rangeTimeblocks = [
+		{nr: 0, workinghours: false, start:  0*60*60, end:  9*60*60, header: "&nbsp;"},	// 0-9
+		{nr: 1, workinghours: true,  start:  9*60*60, end: 12*60*60, header: "9"},		// 9-12
+		{nr: 2, workinghours: true,  start: 12*60*60, end: 15*60*60, header: "12"},		// 12-15
+		{nr: 3, workinghours: true,  start: 15*60*60, end: 18*60*60, header: "15"},		// 15-18
+		{nr: 4, workinghours: false, start: 18*60*60, end: 21*60*60, header: "18"},		// 18-21
+		{nr: 5, workinghours: false, start: 21*60*60, end: 24*60*60, header: "21"}		// 21-24
+	];
+	this.moveScrollHeadersDelay = webclient.settings.get("calendar/mucalendar/movescrollheadersdelay", 100);
+	
+	this.groupData = new Array();
+	this.reccurringItemsLookupTbl = new Object();
+	this.initView();
+}
+
+/**
+ * Function which intializes the view
+ */ 
+MultiUserCalendarView.prototype.initView = function()
+{
+	this.reccurringItemsLookupTbl = new Object();
+//mucalendar_timeline_header_timeblock[modinstance][date][hour]
+//mucalendar_timeline_timeblock[modinstance][userID][date][hour]
+//mucalendar_timeline_userdisplay[modinstance][userID]
+//mucalendar_allattendeesbar[modinstance]
+
+	//dhtml.addElement(this.element, "span", false, "", "Hello World!");
+	var mucalendarMaster = dhtml.getElementById("multiusercalendar_master["+this.moduleID+"]");
+	if(mucalendarMaster){
+		while(mucalendarMaster.firstChild){
+			mucalendarMaster.removeChild(mucalendarMaster.firstChild);
+		}
+	}else{
+		mucalendarMaster = dhtml.addElement(this.element, "div", "multiusercalendar_master", "multiusercalendar_master["+this.moduleID+"]");
+	}
+	var mucalendarContainer = dhtml.addElement(mucalendarMaster, "div", "multiusercalendar_container", "multiusercalendar_container["+this.moduleID+"]");
+	this.mucalendarContainer = mucalendarContainer;
+
+	var widths = this.recalculateSizeValues();
+
+	var contentTable = '<table id="mucalendar['+this.moduleID+']" class="multiusercalendar" cellspacing="0" style="width: '+widths.table+'px;">';
+	var contentDays = '<tr class="header_timeline_day_daterow">';
+	var contentHours = '<tr class="header_timeline_day_timerow">';
+	contentDays += '<td class="header_display" rowspan="2"> <!--##HEADERDISPLAY##--> </td>';
+	var contentZoomComponent = _("Zoom")+' ';
+	contentZoomComponent += '<select id="mucalendar_zoom_controller_field['+this.moduleID+']">';
+
+	// This var is used to check whether or not the selected num of days is in the options list
+	var selectedNumDaysInOptionsList = false;
+	for(var i=0;i<this.numOfDaysInViewportOptions.length;i++){
+		if(this.numOfDaysInViewportOptions[i] == this.numOfDaysInViewport){
+			selectedNumDaysInOptionsList = true;
+			contentZoomComponent += '<option selected="selected" value="'+this.numOfDaysInViewportOptions[i]+'">' + this.numOfDaysInViewportOptions[i] + '</option>';
+		}else{
+			contentZoomComponent += '<option value="'+this.numOfDaysInViewportOptions[i]+'">' + this.numOfDaysInViewportOptions[i] + '</option>';
+		}
+	}
+	// Selected number of days not in options list so add at the bottom
+	if(!selectedNumDaysInOptionsList){
+		contentZoomComponent += '<option selected="selected" value="'+this.numOfDaysInViewport+'">' + this.numOfDaysInViewport + '</option>';
+	}
+
+	contentZoomComponent += '</select>';
+
+	for(var i=0;i<this.viewedDays.length;i++){
+		var date = new Date(this.viewedDays[i]*1000);
+		var headerTimeblockId = 'mucalendar_timeline_header_timeblock['+this.moduleID+']['+this.viewedDays[i]+']';
+		contentDays += '<td class="date_column" colspan="'+this.rangeTimeblocks.length+'">' + date.getDayShortTranslation() + ' ' + date.getDate() + ' ' + date.getMonthShortTranslation() + ' ' + date.getFullYear() + '</td>';
+
+		for(var j=0;j<this.rangeTimeblocks.length;j++){
+			contentHours += '<td style="width:'+widths.timeblock+'px" id="'+headerTimeblockId+'['+this.rangeTimeblocks[j].nr+']" '+((j == (this.rangeTimeblocks.length-1))?'class="lasthourofday"':"")+' >';
+			contentHours += this.rangeTimeblocks[j].header;
+			contentHours += '</td>';
+		}
+	}
+
+	var contentAllAttendeesBar = '<tr>';
+	contentAllAttendeesBar += '<td>'+_("All Attendees")+'</td>';
+	contentAllAttendeesBar += '<td colspan="' + (this.viewedDays.length*this.rangeTimeblocks.length) + '" id="mucalendar_attendeesbar['+this.moduleID+']">&nbsp;</td>';
+	contentAllAttendeesBar += '</tr>';
+
+	contentDays += '</tr>';
+	contentHours += '</tr>';
+
+	contentTable += contentDays + contentHours + contentAllAttendeesBar;
+	contentTable += '</table>';
+	contentTable = contentTable.replace('<!--##HEADERDISPLAY##-->', contentZoomComponent);
+	// Adding container for the scroll headers. These headers are displayed when the user scrolls down and the normal header is not visible anymore.
+	contentTable += '<div id="multiusercalendar_scrollheader['+this.moduleID+']" class="multiusercalendar_scrollheader"></div>';
+	contentTable += '<div id="multiusercalendar_scrollnamedisplay['+this.moduleID+']" class="multiusercalendar_scrollnamedisplay"></div>';
+
+	mucalendarContainer.innerHTML = contentTable;
+
+	dhtml.addEvent(this.moduleID, dhtml.getElementById("mucalendar_zoom_controller_field["+this.moduleID+"]", "select"), "change", eventMultiUserCalendarZoomControllerFieldUpdate);
+	/**
+	 * disable propagation of select field's mousedown event to document body which cancel's mousedown action
+	 * which is not correct for webkit browsers and menu for select field will not be shown
+	 */
+	dhtml.addEvent(this.moduleID, dhtml.getElementById("mucalendar_zoom_controller_field["+this.moduleID+"]", "select"), "mousedown", function(module,element,event){event.stopPropagation();});
+
+	// Setup the scroll header
+	this.scrollHeaderElem = dhtml.getElementById("multiusercalendar_scrollheader["+this.moduleID+"]", "div");
+	var scrollDateRow = '<table id="mucalendar_headers['+this.moduleID+']" class="multiusercalendar" cellspacing="0" style="width: '+widths.table+'px;">';
+	scrollDateRow += contentDays + contentHours + '</table>';
+	this.scrollHeaderElem.innerHTML = scrollDateRow;
+	this.scrollHeaderElem.style.display = "none";
+
+	// Setup the scroll name display
+	this.scrollNameDisplayElem = dhtml.getElementById("multiusercalendar_scrollnamedisplay["+this.moduleID+"]", "div");
+	this.scrollNameDisplayElem.style.display = "none";
+
+	// When the user scrolls the scroll headers must be moved or removed
+	dhtml.addEvent(this, mucalendarContainer, "scroll", function(view, element, event){
+		if(this.moveScrollHeaderTimeout) clearTimeout(this.moveScrollHeaderTimeout);
+		// Build in delay for performance reasons
+		this.moveScrollHeaderTimeout = setTimeout(function(){ view.moveScrollHeader.call(view) }, this.moveScrollHeadersDelay);
+	});
+
+	// Drag/Drop events
+	dhtml.addEvent(this.moduleID, document.body, "mouseup", eventMultiUserCalandarViewMouseUpAppointment);
+	dhtml.addEvent(this.moduleID, document.body, "mousemove", eventMultiUserCalandarViewMouseMoveAppointment);
+	
+	// add keycontrol events
+	var module = webclient.getModule(this.moduleID);
+	if (!webclient.inputmanager.hasKeyControlEvent(module, "keyup", eventMUCKeyCtrl)){
+		webclient.inputmanager.addObject(module, module.element);
+		webclient.inputmanager.bindKeyControlEvent(module, module.keys["muc"], "keyup", eventMUCKeyCtrl, false);
+	}
+}
+
+
+MultiUserCalendarView.prototype.addGroup = function(groupData){
+	dhtml.removeEvent(element, "mouseup", eventMultiUserCalandarViewMouseUpAppointment);
+	dhtml.removeEvent(element, "mousemove", eventMultiUserCalandarViewMouseMoveAppointment);
+}
+
+MultiUserCalendarView.prototype.addGroup = function(groupData)
+{
+	// Remove tooltip if it is still there
+	var currTooltip = dhtml.getElementById("multiusercalendar_appointment_tooltip[" + this.moduleID + "]");
+	if(currTooltip){
+		currTooltip.parentNode.removeChild(currTooltip);
+	}
+
+	// Add new user row
+	var newRow = dhtml.getElementById("mucalendar["+this.moduleID+"]").insertRow(dhtml.getElementById("mucalendar["+this.moduleID+"]").rows.length);
+	newRow.className = "user_timeline_timerow";
+	newRow.setAttribute("id", "mucalendar_row["+this.moduleID+"]["+groupData["groupID"]+"]");
+
+	var newCellDisplay = newRow.insertCell(0);
+	newCellDisplay.className = "user_timeline_userdisplay";
+	newCellDisplay.setAttribute("id", "mucalendar_timeline_userdisplay["+this.moduleID+"]["+groupData["groupID"]+"]");
+	newCellDisplay.innerHTML = '<input type="checkbox" id="multiusercalendar_group_selection['+this.moduleID+']['+groupData["groupID"]+']"/>';
+	newCellDisplay.innerHTML += '<label for="multiusercalendar_group_selection['+this.moduleID+']['+groupData["groupID"]+']">';
+	if((groupData.access & MAPI_ACCESS_READ) == 0){
+		newRow.className += " user_no_access";
+		//newCellDisplay.innerHTML += ' <span class="icon icon_no_access" title="'+_("You don't have permission to view this user's calendar.")+'">&nbsp;</span>';
+	}
+	if((groupData.access & MAPI_ACCESS_CREATE_CONTENTS) == 0){
+		newCellDisplay.innerHTML += '<span class="icon icon_no_creation_rights" title="'+_("You don't have permission to create an entry in this folder.")+'">&nbsp;</span> ';
+	}
+	dhtml.addTextNode(newCellDisplay, groupData["name"]);
+	newCellDisplay.innerHTML += '</label>';
+	
+	var upElemId = "upelem_"+groupData['groupID'];
+	var upElem = dhtml.addElement(newCellDisplay, 'span', 'mucalendar_move_up_down mucalendar_move_up', upElemId, NBSP);
+	upElem.setAttribute("title", _("Move user up"));
+	dhtml.addEvent(this.moduleID, upElem, "click", eventMultiUserCalandarViewUpElemClick);
+	var downElemId = "downelem_"+groupData['groupID'];
+	var downElem = dhtml.addElement(newCellDisplay, 'span', 'mucalendar_move_up_down mucalendar_move_down', downElemId, NBSP);
+	downElem.setAttribute("title", _("Move user down"));
+	dhtml.addEvent(this.moduleID, downElem, "click", eventMultiUserCalandarViewDownElemClick);
+
+	for(var j=0;j<this.viewedDays.length;j++){
+		var timestamp = this.viewedDays[j];
+		for(var k=0;k<this.rangeTimeblocks.length;k++){
+			var newDayHourCell = newRow.insertCell(newRow.cells.length);
+			newDayHourCell.setAttribute("id", "mucalendar_timeline_timeblock["+this.moduleID+"]["+groupData["groupID"]+"]["+timestamp+"]["+this.rangeTimeblocks[k].nr+"]");
+			if(this.rangeTimeblocks[k].workinghours){
+				dhtml.addClassName(newDayHourCell, "timeblock_workinghours");
+			}else{
+				dhtml.addClassName(newDayHourCell, "timeblock_nonworkinghours");
+			}
+			dhtml.addTextNode(newDayHourCell, NBSP);
+
+			if((groupData.access & MAPI_ACCESS_CREATE_CONTENTS) != 0){
+				newDayHourCell.storeid = groupData.storeid;
+				newDayHourCell.folderentryid = groupData.folderentryid;
+				newDayHourCell.startTimestamp = this.viewedDays[j] + this.rangeTimeblocks[k].start;
+				dhtml.addEvent(this.moduleID, newDayHourCell, "dblclick", eventMultiUserCalendarViewClickNewAppointment);
+			}
+
+			// Adding no access warning for user in the first timeline cell
+			if(j==0 && k==0 && (groupData.access & MAPI_ACCESS_READ) == 0){
+				var positioningContainer = dhtml.addElement(newDayHourCell, "div", "mucalendar_timeline_noaccess_notification");
+				dhtml.addElement(positioningContainer, "div", null, null, _("You don't have permission to view this user's calendar."));
+			}
+		}
+		if(newDayHourCell){
+			dhtml.addClassName(newDayHourCell, "lasthourofday");
+		}
+	}
+
+
+	var displayNameScrollElem = dhtml.addElement(this.scrollNameDisplayElem, "div", "scroll_label", "multiusercalendar_scrolldisplaynamegroup["+this.moduleID+"]["+groupData["groupID"]+"]", groupData["name"]);
+
+	this.groupData.push({
+		groupID: groupData["groupID"],
+		scrollDisplayElem: displayNameScrollElem
+	});
+	this.reccurringItemsLookupTbl[groupData["groupID"]] = new Object();
+
+	return groupData["groupID"];
+
+}
+
+MultiUserCalendarView.prototype.deleteGroup = function(groupID, data){
+	var row = dhtml.getElementById("mucalendar_row["+this.moduleID+"]["+groupID+"]");
+	if(row && row.parentNode){
+		row.parentNode.removeChild(row);
+	}
+
+	// Delete items
+	var mucalendarContainer = dhtml.getElementById("multiusercalendar_container[" + this.moduleID + "]");
+	if(mucalendarContainer){
+		for(var i=(mucalendarContainer.childNodes.length-1);i>=0;i--){
+			var containerContent = mucalendarContainer.childNodes.item(i);
+			// Check if node is DIV element
+			if(containerContent && containerContent.nodeType == 1 && containerContent.nodeName == "DIV"){
+				// Check if groupID matches
+				if(containerContent.groupID == groupID){
+					containerContent.parentNode.removeChild(containerContent);
+				}
+			}
+		}
+	}
+
+	// Delete group data
+	for(var i=0;i<this.groupData.length;i++){
+		if(this.groupData[i].groupID == groupID){
+			this.groupData.splice(i, 1);
+		}
+	}
+	delete this.reccurringItemsLookupTbl[groupID];
+
+	if(!data || !data.noresize){
+		this.resizeView();
+	}
+}
+
+
+/**
+ * Function which resizes the view.
+ */ 
+MultiUserCalendarView.prototype.resizeView = function()
+{
+	// Remove tooltip if it is still there
+	var currTooltip = dhtml.getElementById("multiusercalendar_appointment_tooltip[" + this.moduleID + "]");
+	if(currTooltip){
+		currTooltip.parentNode.removeChild(currTooltip);
+	}
+
+	var widths = this.recalculateSizeValues();
+
+	var l_oTable = dhtml.getElementById("mucalendar["+this.moduleID+"]");
+	l_oTable.style.width = widths.table + "px";
+
+	var scrollHeadersTable = dhtml.getElementById('mucalendar_headers['+this.moduleID+']');
+	scrollHeadersTable.style.width = widths.table + "px";
+
+	// Resize the font in the day header cells
+	var timelineTable = dhtml.getElementById("mucalendar["+this.moduleID+"]", "table");
+	for(var i=0;i<this.viewedDays.length;i++){
+		if(widths.timeblockFontSize){
+			timelineTable.rows[0].cells[ i+1 ].style.fontSize = widths.timeblockFontSize;
+		}else{
+			timelineTable.rows[0].cells[ i+1 ].style.fontSize = "";
+		}
+
+		var timestamp = this.viewedDays[i];
+		var headerTimeblockId = 'mucalendar_timeline_header_timeblock['+this.moduleID+']['+timestamp+']';
+
+		for(var j=0;j<this.rangeTimeblocks.length;j++){
+			var timeblock = dhtml.getElementById(headerTimeblockId+"["+this.rangeTimeblocks[j].nr+"]");
+			timeblock.style.width = widths.timeblock + "px";
+			if(widths.timeblockFontSize){
+				timeblock.style.fontSize = widths.timeblockFontSize;
+			}else{
+				timeblock.style.fontSize = "";
+			}
+
+			var cellIndex = ( i * this.rangeTimeblocks.length ) + j;
+			var cell = scrollHeadersTable.rows[ 1 ].cells[ cellIndex ]
+			cell.style.width = widths.timeblock + "px";
+			if(widths.timeblockFontSize){
+				cell.style.fontSize = widths.timeblockFontSize;
+			}else{
+				cell.style.fontSize = "";
+			}
+		}
+	}
+
+	// Reposition items
+	var mucalendarContainer = dhtml.getElementById("multiusercalendar_container[" + this.moduleID + "]");
+	if(mucalendarContainer){
+		for(var i=0;i<mucalendarContainer.childNodes.length;i++){
+			var containerContent = mucalendarContainer.childNodes.item(i);
+			// Check if node is DIV element
+			if(containerContent.nodeType == 1 && containerContent.nodeName == "DIV"){
+				// Check if groupID, startdate and enddate are present
+				if(containerContent.groupID && containerContent.startdate && containerContent.enddate){
+					this.positionItem(containerContent, containerContent.groupID, containerContent.startdate, containerContent.enddate);
+				}
+			}
+		}
+	}
+
+	this.positionAllAttendeesBarItems();
+	this.vPositionItems();
+	this.positionScrollDisplayNames();
+}
+
+/**
+ * Positions the scroll display names that appear when the user scrolls to the left. 
+ * This has to be done after the calendar items have been positioned.
+ */ 
+MultiUserCalendarView.prototype.positionScrollDisplayNames = function()
+{
+	for(var i=0;i<this.groupData.length;i++){
+		var groupID = this.groupData[ i ]["groupID"];
+		var scrollDisplayElem = this.groupData[ i ]["scrollDisplayElem"];
+		
+		var mucTable = dhtml.getElementById("mucalendar["+this.moduleID+"]", "table");
+		var mucTblPos = dhtml.getElementTopLeft(mucTable);
+
+		var displayElem = dhtml.getElementById("mucalendar_timeline_userdisplay["+this.moduleID+"]["+groupID+"]", "td");
+		var displayElemPos = dhtml.getElementTopLeft(displayElem);
+		var displayElemHeight = displayElem.offsetHeight;
+
+		/**
+		 * Calculate the offsetTop to get the scroll display name element displayed at the bottom of
+		 * each row. First calculate the offset to the top of the muc table. Next add the height of 
+		 * the row and extract the height of the label.
+		 */
+		var offsetTop = (displayElemPos[1] - mucTblPos[1]) + displayElemHeight - 17;
+
+		scrollDisplayElem.style.top = offsetTop + "px";
+	}
+
+}
+
+
+MultiUserCalendarView.prototype.recalculateSizeValues = function()
+{
+	// Get the numOfDaysInViewport setting from the module
+	this.numOfDaysInViewport = webclient.getModule(this.moduleID).numOfDaysInViewport;
+	/**
+	 * Calculate with of an individual timeblock
+	 * 
+	 * availableWidth = viewportWidth - userdisplay
+	 * availableWidth = availableWidth / numOfDaysInViewport
+	 * availableWidth = availableWidth -6;                   // extract internal borders and right border
+	 * availableWidth = availableWidth / 6;                  // Get width for individual timeblocks
+	 * availableWidth = availableWidth - 2;                  // Extract padding space
+	 * ================================================================================
+	 * availableWidth =  (((((viewportWidth - userdisplay) / numOfDaysInViewport ) - 6 ) / 6 ) - 2)
+	 */
+	var widths = {
+		userDisplayColumn: 150,
+		day: 180,
+		timeblock: Math.round(((((this.element.offsetWidth - 150) / this.numOfDaysInViewport ) - 6 ) / 6 ) - 2),
+		timeblockFontSize: false,
+		table: 0
+	};
+	/**
+	 * Calculate width of the entire table
+	 * 
+	 * widthTable = timeblockWidth + 2;             // Width of individual timeblock and add it's padding
+	 * widthTable = widthTable * 6;                 // Multiply by 6, because there are 6 timeblocks per day
+	 * widthTable = widthTable + 6;                 // Add borders for timeblocks (only right borders)
+	 * widthTable = widthTable * numOfDays;         // Multiply by number of days in timeline
+	 * widthTable = widthTable + userDisplayColumn; // Add user display
+	 * widthTable = widthTable + 2;                 // Add padding of user display
+	 * widthTable = widthTable + 2;                 // Add borders of user display (left & right border)
+	 * ================================================================================
+	 * widthTable = ((((timeblockWidth + 2) * 6) + 6) * numOfDays) + userDisplayColumn + 2 + 2;
+	 */
+	widths.table = ((((widths.timeblock + 2) * 6) + 6) * this.viewedDays.length) + widths.userDisplayColumn + 2 + 2;
+
+	// When the size of the timeblock cells become too small we have to size down the font
+	if(widths.timeblock < 8){
+		widths.timeblockFontSize = "0pt"
+	}else if(widths.timeblock < 12){
+		widths.timeblockFontSize = "5pt"
+	}
+	return widths;
+}
+
+/** 
+ * Move the headers and the display names to make sure they stay visible when the user scrolls 
+ * through the MUC.
+ */
+MultiUserCalendarView.prototype.moveScrollHeader = function(){
+	if(this.mucalendarContainer && this.scrollHeaderElem){
+		var scrollTop = this.mucalendarContainer.scrollTop;
+		var scrollLeft = this.mucalendarContainer.scrollLeft;
+		
+		if(scrollTop > 0){
+			this.scrollHeaderElem.style.top = scrollTop + "px";
+			this.scrollHeaderElem.style.display = "";
+		}else{
+			this.scrollHeaderElem.style.display = "none";
+		}
+		
+		
+		if(scrollLeft > 150){
+			this.scrollNameDisplayElem.style.left = scrollLeft + "px";
+			this.scrollNameDisplayElem.style.display = "";
+		}else{
+			this.scrollNameDisplayElem.style.display = "none";
+		}
+	}
+}
+
+
+MultiUserCalendarView.prototype.execute = function(items, properties, action, groupID){
+	var entryids = new Array();
+	for(var i = 0; i < items.length; i++){
+		var entry = this.addItem(items[i], properties, action, groupID);
+		if(entry) {
+			entryids[entry['id']] = entry['entryid'];
+		}
+	}
+
+	//this.resizeView(); 
+
+	// Trigger ALL ATTENDEES bar update
+	this.positionAllAttendeesBarItems();
+	this.vPositionItems();
+	this.positionScrollDisplayNames();
+
+	return entryids;
+
+}
+
+MultiUserCalendarView.prototype.updateItem = function(item, properties, action, groupID){
+}
+
+MultiUserCalendarView.prototype.deleteItem = function(entryID, groupID){
+
+	var mucalendarContainer = dhtml.getElementById("multiusercalendar_container[" + this.moduleID + "]");
+	var blockID = "mucalendar_appointment["+this.moduleID+"]["+groupID+"]["+entryID+"]";
+
+	// Check to see if this entryID is stored in our recurringItemsLookupTable.
+	if(typeof this.reccurringItemsLookupTbl[groupID][entryID] != "undefined"){
+		for(var basedate in this.reccurringItemsLookupTbl[groupID][entryID]){
+			var block = dhtml.getElementById(blockID + "["+basedate+"]");
+			if(block){
+				block.parentNode.removeChild(block);
+
+				// also remove tooltip message
+				var currTooltip = dhtml.getElementById("multiusercalendar_appointment_tooltip[" + this.moduleID + "]");
+				if(currTooltip){
+					currTooltip.parentNode.removeChild(currTooltip);
+				}
+			}
+		}
+		delete this.reccurringItemsLookupTbl[groupID][entryID];
+
+	// Delete non-recurring appointment
+	}else{
+		var block = dhtml.getElementById(blockID);
+		if(block){
+			block.parentNode.removeChild(block);
+
+			// also remove tooltip message
+			var currTooltip = dhtml.getElementById("multiusercalendar_appointment_tooltip[" + this.moduleID + "]");
+			if(currTooltip){
+				currTooltip.parentNode.removeChild(currTooltip);
+			}
+		}
+	}
+}
+MultiUserCalendarView.prototype.deleteItems = function(items, groupID){
+	var entryidNodes = items.getElementsByTagName("entryid");
+	for(var i=0;i<entryidNodes.length;i++){
+		this.deleteItem(entryidNodes[i].firstChild.nodeValue, groupID);
+	}
+	// Update positions
+	this.positionAllAttendeesBarItems();
+	this.vPositionItems();
+	this.positionScrollDisplayNames();
+}
+
+MultiUserCalendarView.prototype.addItem = function(item, properties, action, groupID){
+	var xmlStartdate = dhtml.getXMLNode(item, "startdate");
+	var xmlEnddate = dhtml.getXMLNode(item, "duedate");
+	var entryID = dhtml.getXMLValue(item, "entryid");
+	var parentEntryID = dhtml.getXMLValue(item, "parent_entryid");
+	var group = webclient.getModule(this.moduleID).getGroup(groupID);
+
+	basedateNode = dhtml.getXMLNode(item, "basedate");
+	// Transaction of item properties
+	var appData = {
+		subject: dhtml.getXMLValue(item, "subject"),
+		location: dhtml.getXMLValue(item, "location"),
+		recurring: dhtml.getXMLValue(item, "recurring"),
+		meeting: dhtml.getXMLValue(item, "meeting"),
+		busystatus: dhtml.getXMLValue(item, "busystatus"),
+		private: dhtml.getXMLValue(item, "private"),
+		sensitivity: dhtml.getXMLValue(item, "sensitivity"),
+		label: dhtml.getXMLValue(item, "label"),
+		alldayevent: dhtml.getXMLValue(item, "alldayevent"),
+		basedate: (basedateNode) ? basedateNode.getAttribute("unixtime") : false,
+		username: group.username
+	};
+
+	if(appData.meeting == null){
+		appData.meeting = 0;
+	}
+	var blockID = "mucalendar_appointment["+this.moduleID+"]["+groupID+"]["+entryID+"]";
+	/**
+	 * If the appointment has a basedate then we need to add an extra part to 
+	 * the HTMLElementID. If you have multiple occurrences of the same series 
+	 * (read: same entryID) the HTMLElementID will not be unique. The extra part
+	 * will make it unique again. 
+	 * Also we need to keep track of all the recurring appointments and their 
+	 * occurrences. This is to delete all occurrences easily when the entire 
+	 * series is deleted. In that case we will only get an entryID, so we will 
+	 * not know what basedates are added to the HTMLElementID.
+	 */
+	if(appData.recurring && appData.basedate){
+		blockID += "["+appData.basedate+"]";
+		// Create if this is the first entry for this entryID
+		if(typeof this.reccurringItemsLookupTbl[ groupID ][ entryID ] == "undefined"){
+			this.reccurringItemsLookupTbl[ groupID ][ entryID ] = new Object();
+		}
+		this.reccurringItemsLookupTbl[ groupID ][ entryID ][ appData.basedate ] = appData.basedate;
+	}
+	if(group){
+		var storeID = group.getStoreID();
+	}
+
+	if(xmlStartdate && xmlEnddate){
+		timestampStartdate = xmlStartdate.getAttribute("unixtime");
+		timestampEnddate = xmlEnddate.getAttribute("unixtime");
+
+		showAppointment = false;
+		// Start of appointment is before end of time period and the end of appointment is before start of time period
+		if(timestampStartdate < (Number(this.viewedDays[this.viewedDays.length-1]) + (24*60*60)) && timestampEnddate > this.viewedDays[0]){
+			// Appintment is at least visible in the first or last day of the time period
+			if(timestampStartdate <= this.viewedDays[0] || timestampEnddate >= (Number(this.viewedDays[this.viewedDays.length-1]) + (24*60*60)) ){
+				showAppointment = true
+			}else{
+				// Check appointment if it falls on any visible day in the time period
+				for(var i=0;i<this.viewedDays.length;i++){
+					// Start of appointment falls in visible day
+					if(timestampStartdate >= this.viewedDays[i] && timestampStartdate < (Number(this.viewedDays[i])+(24*60*60)) ){
+						showAppointment = true;
+						break;
+					}
+					// End of appointment falls in visible day
+					if(timestampEnddate > this.viewedDays[i] && timestampEnddate < (Number(this.viewedDays[i])+(24*60*60)) ){
+						showAppointment = true;
+						break;
+					}
+				}
+			}
+		}
+
+		if(showAppointment){
+
+			// Remove old version of appointment
+			var oldApp = dhtml.getElementById(blockID);
+			if(oldApp){
+				oldApp.parentNode.removeChild(oldApp);
+			}
+			// Remove old tooltip
+			var currTooltip = dhtml.getElementById("multiusercalendar_appointment_tooltip[" + this.moduleID + "]");
+			if(currTooltip){
+				currTooltip.parentNode.removeChild(currTooltip);
+			}
+
+			var block = document.createElement("div");
+			// Transaction of item properties
+			block.setAttribute("id", blockID);
+			block.className = "mucalendar_appointment_block";
+			block.groupID = groupID;
+			block.startdate = timestampStartdate;
+			block.enddate = timestampEnddate;
+			block.entryid = entryID;
+			block.parententryid = parentEntryID;
+			block.storeid = storeID;
+			block.basedate = appData.basedate;
+			block.recurring = appData.recurring;
+			block.busystatus = appData.busystatus;
+			block.private = appData.private;
+			block.sensitivity = appData.sensitivity;
+			block.username = appData.username;
+			block.meetingrequest = appData.meeting;
+			block.alldayevent = appData.alldayevent;
+			block.label = appData.label;
+			switch(Number(appData.label)){
+				case 1:
+					dhtml.addClassName(block, "label_important");
+					break;
+				case 2:
+					dhtml.addClassName(block, "label_work");
+					break;
+				case 3:
+					dhtml.addClassName(block, "label_personal");
+					break;
+				case 4:
+					dhtml.addClassName(block, "label_holiday");
+					break;
+				case 5:
+					dhtml.addClassName(block, "label_required");
+					break;
+				case 6:
+					dhtml.addClassName(block, "label_travel_required");
+					break;
+				case 7:
+					dhtml.addClassName(block, "label_prepare_required");
+					break;
+				case 8:
+					dhtml.addClassName(block, "label_birthday");
+					break;
+				case 9:
+					dhtml.addClassName(block, "label_special_date");
+					break;
+				case 10:
+					dhtml.addClassName(block, "label_phone_interview");
+					break;
+				default: 
+					dhtml.addClassName(block, "label_none");
+			}
+			switch(Number(appData.busystatus)){
+				case 0:
+					dhtml.addClassName(block, "busystate_free");
+					break;
+				case 1:
+					dhtml.addClassName(block, "busystate_tentative");
+					break;
+				case 3:
+					dhtml.addClassName(block, "busystate_outofoffice");
+					break;
+				default:
+					dhtml.addClassName(block, "busystate_busy");
+					break;
+			}
+
+			dhtml.addEvent(this.moduleID, block, "mouseover", eventMultiUserCalanderViewAppointmentMouseOver);
+			//dhtml.addEvent(this.moduleID, block, "mouseout", eventMultiUserCalanderViewAppointmentMouseOut);
+			dhtml.getElementById("multiusercalendar_container["+this.moduleID+"]").appendChild(block);
+
+			var appDataNode = document.createElement("div");
+			dhtml.addClassName(appDataNode, "mucalendar_appointment_block_data");
+
+			dhtml.addTextNode(appDataNode, ((appData.subject)?appData.subject:""));
+			appDataNode.innerHTML += "<br/>";
+			if(appData.meeting != "0"){
+				appDataNode.innerHTML += "<span class='meetingrequest'>&nbsp;</span>";
+			}
+			if(appData.recurring == "1"){
+				appDataNode.innerHTML += "<span class='recurring'>&nbsp;</span>";
+			}
+			if(appData.private == "1"){
+				appDataNode.innerHTML += "<span class='private'>&nbsp;</span>";
+			}
+			if(appData.location){
+				dhtml.addTextNode(appDataNode, "(" + appData.location + ")");
+			}
+			appDataNode.innerHTML += "<br/>";
+
+			if(Number(appData.alldayevent)==1){
+				var timeperiodInfo = _("All day event");
+			}else{
+				var timeperiodInfo = (new Date(timestampStartdate*1000)).toTime() + " - " + (new Date(timestampEnddate*1000)).toTime();
+			}
+			appDataNode.innerHTML += timeperiodInfo;
+
+			// Add transparent background for data div
+			var appBGNode = document.createElement("div");
+			dhtml.addClassName(appBGNode, "mucalendar_appointment_block_data_bg");
+			block.appendChild(appBGNode);
+
+			block.appendChild(appDataNode);
+
+			this.positionItem(block, block.groupID, block.startdate, block.enddate);
+
+			// To calculate the size of the text, the text div should not have an overflow set.
+			appDataNode.style.overflow = "";
+			// If the appointment block is bigger than the text width, use the width of the appointment block
+			if(block.offsetWidth > appDataNode.offsetWidth){
+				appDataNode.style.width = block.style.width;
+			// Set maximum width for text width if it exceeds 100px
+			}else if(appDataNode.offsetWidth > 100){
+				appDataNode.style.width = "100px";
+			// Set width of text div. If this is not done, the overflow will shrink the div to zero.
+			}else{
+				appDataNode.style.width = appDataNode.offsetWidth + "px";
+			}
+			appDataNode.style.overflow = "hidden";
+
+			/**
+			 * Set the width of the text of this block. This will be used when positioning the items vertically
+			 */
+			block.contentwidth = Math.max(block.offsetWidth, appDataNode.offsetWidth);
+
+		}else if(entryID && groupID){
+			this.deleteItem(entryID, groupID);
+			// Update positions
+			this.positionAllAttendeesBarItems();
+			this.vPositionItems();
+			this.positionScrollDisplayNames();
+		}
+	}
+
+	return true;
+}
+
+
+MultiUserCalendarView.prototype.positionItem = function(element, groupID, starttimestamp, endtimestamp){
+	var start = {
+		timestampApp: starttimestamp,	// Timestamp of the start of the appointment
+		timestampDay: 0,				// Timestamp of the start of the day
+		hour: 0,						// Timestamp of the start hour (for example: 02:31 -> ((2*60)+31)*60=9060
+		timeblockNr: 0,					// Timeblock number of timeblock on the focused day (Ranges from 0 to 5)
+		timeblockNodeID: "",			// HTML ID of timeblock
+		pos: [0,0]						// Array with position coordinates [x, y]
+	};
+	var end = { timestampApp: endtimestamp, timestampDay: 0, hour: 0, timeblockNr: 0, timeblockNodeID: "", pos: [0,0] };
+	// Set start of appointment to start of timeline when the appointment begins before the timeline starts
+	if(start.timestampApp < this.viewedDays[0]){
+		start.timestampApp = this.viewedDays[0];
+		start.timestampDay = this.viewedDays[0];
+	}
+	// Set end of appointment to end of timeline when the appointment finishes after the timeline ends
+	if(end.timestampApp >= this.viewedDays[this.viewedDays.length-1] + (24*60*60)){
+		/**
+		 * The -1 is a correction to make sure the appointment does not end on the start of the next
+		* day (which is outside the range of the timeline)
+		 */
+		end.timestampApp = this.viewedDays[this.viewedDays.length-1] + (24*60*60)-1;
+		end.timestampDay = this.viewedDays[this.viewedDays.length-1];
+	}
+
+	if(start.timestampDay == 0 || end.timestampDay == 0){
+		for(var i=0;i<this.viewedDays.length;i++){
+			// Check if start of appointment starts before start of period and after end of previous period
+			if(i > 0 && this.viewedDays[i] > start.timestampApp && this.viewedDays[i-1] + (24*60*60) <= start.timestampApp){
+				start.timestampDay = this.viewedDays[i];
+				start.timestampApp = this.viewedDays[i];
+			}
+			// Check if start of appointment falls within focussed period
+			if(this.viewedDays[i] <= start.timestampApp && this.viewedDays[i] + (24*60*60) > start.timestampApp){
+				start.timestampDay = this.viewedDays[i];
+			}
+			// Check if the end of appointment ends before start of period and after end of previous period
+			if(i > 0 && this.viewedDays[i] > end.timestampApp && this.viewedDays[i-1] + (24*60*60)-1 <= end.timestampApp){
+				end.timestampDay = this.viewedDays[i-1];
+				/**
+				 * The -1 is a correction to make sure the appointment does not end on the start of the next
+				* day (which might be an invisible day)
+				 */
+				end.timestampApp = this.viewedDays[i-1] + (24*60*60) - 1;
+			}
+			// Check if end of appointment falls within focussed period
+			if(this.viewedDays[i] <= end.timestampApp && this.viewedDays[i] + (24*60*60) > end.timestampApp){
+				end.timestampDay = this.viewedDays[i];
+			}
+		}
+	}
+
+	// Calculate the number of seconds in the day that have passed before reaching the timestamp
+	start.hour = (start.timestampApp - start.timestampDay);
+	end.hour = (end.timestampApp - end.timestampDay);
+	// Determine the timeblocks for positioning purposes
+	start.timeblockNr = 0;
+	end.timeblockNr = 0;
+	for(var i=0;i<this.rangeTimeblocks.length;i++){
+		if(start.hour >= this.rangeTimeblocks[i].start && start.hour < this.rangeTimeblocks[i].end){
+			start.timeblockNr = i;
+		}
+		if(end.hour >= this.rangeTimeblocks[i].start && end.hour < this.rangeTimeblocks[i].end){
+			end.timeblockNr = i;
+		}
+	}
+
+	// Set timeblock IDs
+	start.timeblockNodeID = "mucalendar_timeline_timeblock["+this.moduleID+"]["+groupID+"]["+start.timestampDay+"]["+start.timeblockNr+"]";
+	end.timeblockNodeID = "mucalendar_timeline_timeblock["+this.moduleID+"]["+groupID+"]["+end.timestampDay+"]["+end.timeblockNr+"]";
+
+	// Get position of block and position of root element of the module
+	var posTimeblockStart = dhtml.getElementTopLeft(dhtml.getElementById(start.timeblockNodeID));
+	var posTimeblockEnd = dhtml.getElementTopLeft(dhtml.getElementById(end.timeblockNodeID));
+	var posModuleElem = dhtml.getElementTopLeft(dhtml.getElementById("multiusercalendar_container["+this.moduleID+"]"));
+
+	// Define start & end position of appointment
+	start.pos = Array((posTimeblockStart[0]-posModuleElem[0]), (posTimeblockStart[1]-posModuleElem[1]));
+	end.pos = Array((posTimeblockEnd[0]-posModuleElem[0]), (posTimeblockEnd[1]-posModuleElem[1]));
+
+	// startTimeblockRange : Range of time the start of the appointment is located in
+	// endTimeblockRange : Range of time the end of the appointment is located in
+	// startAppPassTimeblock : seconds passed start the start of the timeblock (start appointment)
+	// endAppPassTimeblock : seconds passed start the start of the timeblock (end appointment)
+
+	var startTimeblockRange = this.rangeTimeblocks[start.timeblockNr].end - this.rangeTimeblocks[start.timeblockNr].start;
+	var endTimeblockRange = this.rangeTimeblocks[end.timeblockNr].end - this.rangeTimeblocks[end.timeblockNr].start;
+	// Appointment_timestamp - (timestamp_start_day + start_timeblock_number_of_seconds_passed_start_of_day) = seconds_passed_start_of_timeblock
+	var startAppPassTimeblock = start.timestampApp - (start.timestampDay + this.rangeTimeblocks[start.timeblockNr].start);
+	var endAppPassTimeblock = end.timestampApp - (end.timestampDay + this.rangeTimeblocks[end.timeblockNr].start);
+
+	// Calculate X coordinates for start and end of appointment.
+	start.pos[0] = start.pos[0] + Math.floor((startAppPassTimeblock / startTimeblockRange) * dhtml.getElementById(start.timeblockNodeID).offsetWidth);
+	end.pos[0] = end.pos[0] + Math.floor((endAppPassTimeblock / endTimeblockRange) * dhtml.getElementById(end.timeblockNodeID).offsetWidth);
+
+	element.style.position = "absolute";
+	element.style.left = start.pos[0] + "px";
+	element.style.top = start.pos[1] + "px";
+	element.coords = {
+		startLeft: start.pos[0],
+		startTop: start.pos[1],
+		endLeft: end.pos[0]
+	};
+
+	// Set the width of the element (The -3 is pixel correction, not yet checked in MSIE)
+	var elemWidth = ((end.pos[0] - start.pos[0])-3);
+	if(elemWidth <= 0){
+		elemWidth = 1;
+	}
+	element.style.width = elemWidth + "px";
+	element.style.height = "50px";
+
+}
+
+MultiUserCalendarView.prototype.positionAllAttendeesBarItems = function(){
+	var mucalendarContainer = dhtml.getElementById("multiusercalendar_container[" + this.moduleID + "]");
+	var attendeesBarNode = dhtml.getElementById("mucalendar_attendeesbar["+this.moduleID+"]");
+	if(mucalendarContainer && attendeesBarNode){
+
+		// Remove old entries
+		for(var i=(attendeesBarNode.childNodes.length-1);i>=0;i--){
+			var attendeesBarContent = attendeesBarNode.childNodes.item(i);
+			// Check if node is DIV element
+			if(attendeesBarContent && attendeesBarContent.nodeType == 1 && attendeesBarContent.nodeName == "DIV"){
+				attendeesBarNode.removeChild(attendeesBarContent);
+			}
+		}
+
+		// Position all attendees bar blocks
+		var timeItemList = new Array();
+		for(var i=0;i<mucalendarContainer.childNodes.length;i++){
+				var containerContent = mucalendarContainer.childNodes.item(i);
+				// Check if node is DIV element
+				if(containerContent.nodeType == 1 && containerContent.nodeName == "DIV"){
+						// Check if groupID, startdate and enddate are present
+						if(containerContent.groupID && containerContent.startdate && containerContent.enddate && !containerContent.tooltip){
+							if(containerContent.busystatus != 0){
+								if(!timeItemList[containerContent.busystatus]){
+									timeItemList[containerContent.busystatus] = new Array();
+								}
+								timeItemList[containerContent.busystatus][ timeItemList[containerContent.busystatus].length ] = {
+										type: "start",
+										time: mucalendarContainer.childNodes.item(i).startdate,
+										leftCoord: mucalendarContainer.childNodes.item(i).coords.startLeft,
+										busyState: containerContent.busystatus
+								}
+								timeItemList[containerContent.busystatus][ timeItemList[containerContent.busystatus].length ] = {
+										type: "end",
+										time: mucalendarContainer.childNodes.item(i).enddate,
+										leftCoord: mucalendarContainer.childNodes.item(i).coords.endLeft,
+										busyState: containerContent.busystatus
+								}
+							}
+						}
+				}
+		}
+
+
+		var level = 0;
+		timeblocks = new Array();
+		for(var busystate in timeItemList){
+
+			timeItemList[busystate].sort(function(a,b){
+				return a.time-b.time;
+			});
+			for(var i in timeItemList[busystate]){
+				switch(timeItemList[busystate][i].type){
+					case "start":
+						if(level == 0){
+							timeblocks[timeblocks.length] = {
+								start: timeItemList[busystate][i].time, 
+								startCoord: timeItemList[busystate][i].leftCoord,
+								busyState: timeItemList[busystate][i].busyState
+							};
+						}
+						level++;
+						break;
+					case "end":
+						level--;
+						if(level == 0){
+							timeblocks[(timeblocks.length-1)].end = timeItemList[busystate][i].time;
+							timeblocks[(timeblocks.length-1)].endCoord = timeItemList[busystate][i].leftCoord;
+						}
+						break;
+				}
+			}
+		}
+
+		var attendeesBarCoordTop= attendeesBarNode.offsetTop;
+		var attendeesBarHeight = attendeesBarNode.offsetHeight;
+
+		for(var i=0;i<timeblocks.length;i++){
+			var blockWidth = timeblocks[i].endCoord - timeblocks[i].startCoord;
+			if(blockWidth <= 0){
+				blockWidth = 1;
+			}
+			var block = document.createElement("div");
+			dhtml.addClassName(block, "mucalendar_allattendeesbar_block");
+			switch(Number(timeblocks[i].busyState)){
+				case 0:
+					dhtml.addClassName(block, "mucalendar_allattendeesbar_block_busystate_free");
+					block.style.zIndex = 0;
+					break;
+				case 1:
+					dhtml.addClassName(block, "mucalendar_allattendeesbar_block_busystate_tentative");
+					block.style.zIndex = 1;
+					break;
+				case 3:
+					dhtml.addClassName(block, "mucalendar_allattendeesbar_block_busystate_outofoffice");
+					block.style.zIndex = 3;
+					break;
+				default:
+					dhtml.addClassName(block, "mucalendar_allattendeesbar_block_busystate_busy");
+					block.style.zIndex = 2;
+			}
+			block.style.width = ((blockWidth > 0) ? blockWidth : 1) + "px";
+			// -1 is pixel correction
+			block.style.height = (attendeesBarHeight-1) + "px";
+			block.style.left = timeblocks[i].startCoord + "px";
+			block.style.top = attendeesBarCoordTop + "px";
+			attendeesBarNode.appendChild(block);
+		}
+	}
+}
+
+
+
+
+
+
+// Position all items (called after an addition/removal/change)
+MultiUserCalendarView.prototype.vPositionItems = function()
+{
+	var mucalendarContainer = dhtml.getElementById("multiusercalendar_container[" + this.moduleID + "]");
+	var appointments = {};
+	if(mucalendarContainer){
+		for(var i=(mucalendarContainer.childNodes.length-1);i>=0;i--){
+			var containerContent = mucalendarContainer.childNodes.item(i);
+			// Check if node is DIV element
+			if(containerContent.nodeType == 1 && containerContent.nodeName == "DIV"){
+				// Check if groupID, startdate and enddate are present
+				if(containerContent.groupID && containerContent.startdate && containerContent.enddate){
+					if(!appointments[containerContent.groupID]){
+						appointments[containerContent.groupID] = new Array();
+					}
+					appointments[containerContent.groupID].push(containerContent);
+				}
+			}
+		}
+	}
+
+	/**
+	 * Use order of the this.groupData array to make sure the top group is 
+	 * sorted first (for repositioning reasons).
+	 */
+	for(var i=0;i<this.groupData.length;i++){
+		if(appointments[this.groupData[i].groupID]){
+			this.vPositionItemsOnGroup(appointments[this.groupData[i].groupID]);
+		}else{
+			this.vPositionItemsOnGroup(new Array());
+		}
+	}
+}
+
+// Gets all overlapping items and returns them as an array
+MultiUserCalendarView.prototype.getOverLapping = function(slots, start, end)
+{
+	var overlapping = new Object();
+	
+	for(elementid in slots) {
+		var slot = slots[elementid];
+		
+		if(slot.start < end && slot.end > start) 
+			overlapping[elementid] = slot;
+	}
+	
+	return overlapping;
+}
+
+// Propagates the 'maxdepth' value of slot with id 'elementid' to all its overlappers and their overlappers
+MultiUserCalendarView.prototype.propagateMaxSlot = function(slots, elementid, maxslot)
+{
+	// Loop protection
+	if(slots[elementid].maxslot == maxslot)
+		return;
+		
+	slots[elementid].maxslot = maxslot;
+	
+	var overlapping = this.getOverLapping(slots, slots[elementid].start, slots[elementid].end);
+
+	for(elementid in overlapping) {
+		this.propagateMaxSlot(slots, elementid, maxslot);
+	}
+}
+
+/**
+ * @todo implement multi dayevents in this function
+ */ 
+MultiUserCalendarView.prototype.vPositionItemsOnGroup = function(appointments)
+{
+	var items = appointments;
+
+	// This is the array of all items, which will be increasingly built up
+	// by this algorithm to calculate in which slot the items must go
+	var slots = new Object();
+
+	/**
+	 * Loop through the items to calculate the start and end pixels for each one. This has to be 
+	 * done before they are sorted. After this they are used in the overlapping check.
+	 */
+	for(var i = 0; i < items.length; i++)
+	{
+		items[i].start = items[i].offsetLeft;
+		items[i].end = items[i].offsetLeft + Math.max(items[i].offsetWidth, items[i].contentwidth);
+	}
+
+	// Sort items by startdate, and length (longest first)
+	items.sort(this.sortItems);
+
+	/* Loop through all the items, doing the following:
+	 * For each item, get all overlapping slots alread placed and try to place the
+	 * new item in slot0 .. slotN (with slotN being the maxslot of overlapper 0)
+	 * If there are no slots, set slot to N+1, and propagate a new maxslot (N+1)
+	 * to all overlappers and their overlappers.
+	 */
+	 
+	for(var i = 0; i < items.length; i++)
+	{
+		var overlapping = this.getOverLapping(slots, items[i].start, items[i].end);
+		var maxdepth = 0;
+		var placed = false;
+		var elementid = items[i].entryid + "_" + ((items[i].basedate)?items[i].basedate:"");
+		var slot = new Object;
+			
+		// Remember this info so we can do overlap checking later
+		slot.start = items[i].start;
+		slot.end = items[i].end;
+		
+		for(var overlapid in overlapping) { // this is more like an if(overlapping.length > 0)
+			maxdepth = overlapping[overlapid].maxslot; // maxslot should be the same for all overlappers, so take the first
+			
+			// Try to put the item in the leftmost slot
+			for(var slotnr = 0; slotnr < maxdepth; slotnr++) {
+				var slotfree = true;
+				for(var overlapper in overlapping) {
+					if(overlapping[overlapper].slot == slotnr) {
+						slotfree = false;
+						break;
+					}
+				}
+				
+				// This slot is still free, so use that
+				if(slotfree) {
+					placed = true;
+					slot.slot = slotnr;
+					slot.maxslot = maxdepth;
+					slots[elementid] = slot;
+					break;
+				}
+			}
+			break; // Yep, only go through this once
+		}
+
+		if(!placed) {
+			// No free slots, add a new slot on the right
+			slot.slot = maxdepth;
+			slot.maxslot = 0; // will be updated by propagateMaxSlot 
+
+			slots[elementid] = slot;
+				
+			// Propagate new 'maxslot' to all other overlapping slots (and their overlappers etc)
+			this.propagateMaxSlot(slots, elementid, maxdepth + 1);
+		}
+	}		
+	 
+	/* After running through this algorithm for all items, the 'slots' array
+	 * will contain both the slot number and the max. slot number for each item
+	 * which we now use to position and resize the items
+	 */
+	if(items[0] && items[0].groupID){
+		var displayCell = dhtml.getElementById("mucalendar_timeline_userdisplay["+this.moduleID+"]["+items[0].groupID+"]");
+		var numberOfSlots = 0;
+		// Position items
+		for(i = 0; i < items.length; i++)
+		{
+			var item = items[i];
+			var slot = slots[ items[i].entryid + "_" + ((items[i].basedate)?items[i].basedate:"") ];	// This is the elementid var used in the code above
+
+			item.style.top = displayCell.offsetTop + (slot.slot * 55) + "px";
+			if(numberOfSlots < slot.slot + 1){
+				numberOfSlots = slot.slot + 1;
+			}
+		}
+
+		if(numberOfSlots > 0){
+			displayCell.style.height = ((numberOfSlots * 55) + 5) + "px";
+		}else{
+			displayCell.style.height = "60px";
+		}
+	}
+}
+
+// Sort on starttime, if time is equal, sort on duration (longest first)
+MultiUserCalendarView.prototype.sortItems = function(itemA, itemB)
+{
+	if(itemA.start == itemB.start)
+		return (itemB.end - itemB.start) - (itemA.end - itemA.start);
+	if(itemA.start > itemB.start) return 1;
+	if(itemA.start < itemB.start) return -1;
+	
+	return 0;
+}
+
+
+MultiUserCalendarView.prototype.getTimeByPixel = function(coordX)
+{
+	var returnVal = false;
+	var i = 0; var j = 0; var found = false;
+	while(i<this.viewedDays.length && j<this.rangeTimeblocks.length && !found){
+		//mucalendar_timeline_header_timeblock[moduleID][startTime][TimeblockID]
+		var timeblockNodeID = "mucalendar_timeline_header_timeblock";
+		timeblockNodeID += "["+this.moduleID+"]";
+		timeblockNodeID += "["+this.viewedDays[i]+"]";
+		timeblockNodeID += "["+this.rangeTimeblocks[j].nr+"]";
+		var timeblock = dhtml.getElementById(timeblockNodeID);
+		var gridData = {
+			startX: timeblock.offsetLeft,
+			width: timeblock.offsetWidth,
+			endX: (timeblock.offsetLeft + timeblock.offsetWidth),
+			startTime: (this.viewedDays[i] + this.rangeTimeblocks[j].start),
+			endTime: (this.viewedDays[i] + this.rangeTimeblocks[j].end),
+			diffTime: this.rangeTimeblocks[j].end - this.rangeTimeblocks[j].start
+		};
+
+		// X coordinate falls within the timeblock
+		if(coordX > gridData.startX && coordX <= gridData.endX){
+			found = true;
+
+			var distanceFromStartX = (coordX - gridData.startX);
+			var pixelRatio = distanceFromStartX / gridData.width;
+			var timeFromTimeblockStart = pixelRatio * gridData.diffTime;
+			returnVal = gridData.startTime + timeFromTimeblockStart;
+		}else{
+			j++;
+			if(j >= this.rangeTimeblocks.length){
+				i++;
+				j=0;
+			}
+		}
+	}
+	return returnVal;
+}
+MultiUserCalendarView.prototype.getGroupIDByPixel = function(coordY)
+{
+	var returnVal = false;
+	//mucalendar_timeline_userdisplay[moduleID][groupID]
+	for(var i=0;i<this.groupData.length;i++){
+		var groupDisplay = dhtml.getElementById("mucalendar_timeline_userdisplay["+this.moduleID+"]["+this.groupData[i].groupID+"]");
+		if(groupDisplay){
+			// Check if Y coordinate falls within this group's area
+			if(coordY > groupDisplay.offsetTop && coordY <= (groupDisplay.offsetTop + groupDisplay.offsetHeight)){
+				returnVal = this.groupData[i].groupID;
+				break;
+			}
+		}
+	}
+	return returnVal;
+}
+
+/**
+ * Overwrite the loading message since that removes the MUC table.
+ */
+MultiUserCalendarView.prototype.loadMessage = function(){}
+MultiUserCalendarView.prototype.deleteLoadMessage = function(){}
+
+
+
+function eventMultiUserCalendarZoomControllerFieldUpdate(moduleObject, element, event)
+{
+	if(parseInt(element.value) > 0){
+		moduleObject.numOfDaysInViewport = parseInt(element.value);
+		moduleObject.resize();
+	}
+}
+
+function eventMultiUserCalanderViewAppointmentMouseOver(moduleObject, element, event){
+	var currTooltip = dhtml.getElementById("multiusercalendar_appointment_tooltip[" + moduleObject.id + "]");
+	if(currTooltip){
+		currTooltip.parentNode.removeChild(currTooltip);
+	}
+
+	var tooltip = element.cloneNode(true);
+	dhtml.removeEvents(tooltip);
+	tooltip.setAttribute("id", "multiusercalendar_appointment_tooltip[" + moduleObject.id + "]");
+
+	var trigger = element.cloneNode(false);
+	dhtml.removeEvents(trigger);
+	trigger.setAttribute("id", "multiusercalendar_appointment_tooltip_trigger[" + moduleObject.id + "]");
+	trigger.className = "";
+	dhtml.addClassName(trigger, "mucalendar_appointment_block_tooltip_trigger");
+	trigger.style.position = "absolute";
+	trigger.style.left = "-3px";
+	trigger.style.top = "-1px";
+	trigger.style.width = "10px";
+	trigger.style.height = (Number(trigger.style.height.replace("px", "")) + 5) + "px";
+	trigger.style.zIndex = "999";
+	trigger.tooltipParent = tooltip;
+
+	tooltip.tooltip = true;
+	var mucalendarContainer = dhtml.getElementById("multiusercalendar_container[" + moduleObject.id + "]");
+
+	dhtml.removeClassName(tooltip, "mucalendar_appointment_block");
+	dhtml.addClassName(tooltip, "mucalendar_appointment_block_tooltip");
+
+	// Transaction of item properties
+	tooltip.data = new Object();
+	tooltip.data.storeid = element.storeid;
+	tooltip.data.parententryid = element.parententryid;
+	tooltip.data.entryid = element.entryid;
+	tooltip.data.basedate = element.basedate;
+	tooltip.data.startdate = element.startdate;
+	tooltip.data.enddate = element.enddate;
+	tooltip.data.groupID = element.groupID;
+	tooltip.data.meetingrequest = element.meetingrequest;
+	tooltip.data.alldayevent = element.alldayevent;
+	tooltip.data.appNodeID = element.getAttribute("id");
+	tooltip.appendChild(trigger);
+
+
+	// Get the data and data background DIVs.
+	var elementNodes = {
+		data_bg: dhtml.getElementsByClassNameInElement(element, "mucalendar_appointment_block_data_bg", "div")[0],
+		data: dhtml.getElementsByClassNameInElement(element, "mucalendar_appointment_block_data", "div")[0]
+	};
+	var tooltipNodes = {
+		data_bg: dhtml.getElementsByClassNameInElement(tooltip, "mucalendar_appointment_block_data_bg", "div")[0],
+		data: dhtml.getElementsByClassNameInElement(tooltip, "mucalendar_appointment_block_data", "div")[0]
+	};
+
+	// Make all text visible
+	var elemDataNodeWidth = element.childNodes.item(1).style.width;
+	elementNodes.data.style.width = "";
+	elementNodes.data.style.overflow = "";
+
+	tooltipNodes.data_bg.style.width = elementNodes.data.offsetWidth + "px";
+	tooltipNodes.data.style.width = elementNodes.data.offsetWidth + "px";
+	trigger.style.width = elementNodes.data.offsetWidth + "px";
+
+	switch(Number(element.label)){
+		case 1:
+			dhtml.addClassName(tooltipNodes.data_bg, "label_important");
+			break;
+		case 2:
+			dhtml.addClassName(tooltipNodes.data_bg, "label_work");
+			break;
+		case 3:
+			dhtml.addClassName(tooltipNodes.data_bg, "label_personal");
+			break;
+		case 4:
+			dhtml.addClassName(tooltipNodes.data_bg, "label_holiday");
+			break;
+		case 5:
+			dhtml.addClassName(tooltipNodes.data_bg, "label_required");
+			break;
+		case 6:
+			dhtml.addClassName(tooltipNodes.data_bg, "label_travel_required");
+			break;
+		case 7:
+			dhtml.addClassName(tooltipNodes.data_bg, "label_prepare_required");
+			break;
+		case 8:
+			dhtml.addClassName(tooltipNodes.data_bg, "label_birthday");
+			break;
+		case 9:
+			dhtml.addClassName(tooltipNodes.data_bg, "label_special_date");
+			break;
+		case 9:
+			dhtml.addClassName(tooltipNodes.data_bg, "label_phone_interview");
+			break;
+		default: 
+			dhtml.addClassName(tooltipNodes.data_bg, "label_none");
+	}
+
+
+
+	elementNodes.data.style.overflow = "hidden";
+	elementNodes.data.style.width = elemDataNodeWidth;
+
+	mucalendarContainer.appendChild(tooltip);
+	if((element.sensitivity != null && !Number(element.sensitivity)) || element.username == webclient.username){
+		dhtml.addEvent(moduleObject, tooltip, "dblclick", eventMultiUserCalandarViewClickEditAppointment);
+
+		// DragDrop functionality
+		dhtml.addEvent(moduleObject, trigger, "mousedown", eventMultiUserCalandarViewMouseDownAppointment);
+	}
+	dhtml.addEvent(moduleObject, trigger, "mouseout", eventMultiUserCalanderViewAppointmentMouseOut);
+
+	dhtml.addEvent(moduleObject, tooltipNodes.data_bg, "mouseout", eventMultiUserCalanderViewAppointmentMouseOut);
+	dhtml.addEvent(moduleObject, tooltipNodes.data, "mouseout", eventMultiUserCalanderViewAppointmentMouseOut);
+	dhtml.addEvent(moduleObject, tooltip, "mouseout", eventMultiUserCalanderViewAppointmentMouseOut);
+
+	if(tooltip.offsetWidth < 10){
+		trigger.style.width = "10px";
+	}
+}
+function eventMultiUserCalanderViewAppointmentMouseOut(moduleObject, element, event){
+	if(element && element.tooltipParent){
+		element.tooltipParent.parentNode.removeChild(element.tooltipParent);
+	}
+}
+
+function eventMultiUserCalendarViewRemoveUsers(moduleObject, element, event){
+	for(var i=(moduleObject.groups.length-1);i>=0;i--){
+		var groupSelect = dhtml.getElementById("multiusercalendar_group_selection["+moduleObject.id+"][" + moduleObject.groups[i].groupID + "]");
+		if(groupSelect && groupSelect.checked){
+			moduleObject.deleteGroup(moduleObject.groups[i].groupID);
+		}
+	}
+}
+
+function eventMultiUserCalendarViewClickPrevPeriod(moduleObject, element, event)
+{
+	moduleObject.changeTimePeriod("prevperiod");
+}
+function eventMultiUserCalendarViewClickPrevDay(moduleObject, element, event)
+{
+	moduleObject.changeTimePeriod("prevday");
+}
+function eventMultiUserCalendarViewClickNextDay(moduleObject, element, event)
+{
+	moduleObject.changeTimePeriod("nextday");
+}
+function eventMultiUserCalendarViewClickNextPeriod(moduleObject, element, event)
+{
+	moduleObject.changeTimePeriod("nextperiod");
+}
+
+
+// Callback functions for dialogs
+function mucalendarDialog_loadgroups_addCallBack(result, callbackData) {
+	if(result.groupEntryID && callbackData.moduleID){
+		var module = webclient.getModule(callbackData.moduleID);
+		if(module){
+			module.addGroup(result.groupEntryID);
+		}
+	}
+}
+
+function mucalendarDialog_adduser_addCallBack(result, callbackData) {
+	if(result && result.value && callbackData.moduleID){
+		var module = webclient.getModule(callbackData.moduleID);
+		if(module){
+			var users = new Array();;
+			if(result.multiple){
+				for(var key in result){
+					if(key != "multiple" && key != "value"){
+						users.push({
+							entryid: result[key].entryid,
+							username: result[key].fileas
+						});
+					}
+				}
+			}else{
+				users.push({
+					entryid: result.entryid,
+					username: result.fileas
+				});
+			}
+
+			module.getUsers(users);
+		}
+	}
+}
+
+
+
+function eventMultiUserCalendarViewClickNewAppointment(moduleObject, element, event)
+{
+	var startTime = element.startTimestamp;
+	var startDate = new Date(element.startTimestamp*1000);
+	startDate.addHours(0.5);
+	var endTime = startDate.getTime()/1000; // starttime + 30minutes
+	webclient.openWindow(moduleObject, "appointment", DIALOG_URL+"task=appointment_standard&storeid=" + element.storeid+"&parententryid="+element.folderentryid+"&startdate="+startTime+"&enddate="+endTime);
+}
+
+/**
+ * Moves user down in the list of MUC when downelement is clicked.
+ */
+function eventMultiUserCalandarViewUpElemClick(moduleObject, element, event)
+{
+	var splitId = element.id.split("_");
+
+	for(var i=1;i<(moduleObject.groups.length);i++){
+		if(splitId[1] == moduleObject.groups[i].groupID) {
+			var tmp = moduleObject.groups[i];
+			moduleObject.groups[i] = moduleObject.groups[i-1];
+			moduleObject.groups[i-1] = tmp;
+			moduleObject.initializeView();
+			break;
+		}
+	}
+	moduleObject.setStartupUsersSetting();
+}
+
+/**
+ * Moves user up in the list of MUC when upelement is clicked.
+ */
+function eventMultiUserCalandarViewDownElemClick(moduleObject, element, event)
+{
+	var splitId = element.id.split("_");
+
+	for(var i=0;i<(moduleObject.groups.length - 1);i++){
+		if(splitId[1] == moduleObject.groups[i].groupID) {
+			var tmp = moduleObject.groups[i];
+			moduleObject.groups[i] = moduleObject.groups[i+1];
+			moduleObject.groups[i+1] = tmp;
+			moduleObject.initializeView();
+			break;
+		}
+	}
+	moduleObject.setStartupUsersSetting();
+}
+
+function eventMultiUserCalandarViewClickEditAppointment(moduleObject, element, event)
+{
+	// An element with a basedate is a recurring item
+	if(element.data.basedate){
+		// please note that this url is also printed, so make it more "interesting" by first set the entryid
+		webclient.openModalDialog(moduleObject, "occurrence", DIALOG_URL+"entryid="+element.data.entryid+"&storeid="+element.data.storeid+"&task=occurrence_modal&basedate="+element.data.basedate+"&parententryid=" + element.data.parententryid, 300, 200);
+
+	}else{
+		var uri = DIALOG_URL+"task=appointment_standard&storeid=" + element.data.storeid + "&parententryid=" + element.data.parententryid + "&entryid=" + element.data.entryid;
+		webclient.openWindow(moduleObject, "appointment", uri);
+
+	}
+}
+
+function eventMultiUserCalandarViewMouseDownAppointment(moduleObject, element, event)
+{
+	moduleObject.dragdrop = new Object();
+	// Used to indicate that the mouse is down
+	moduleObject.dragdrop.dragging = true;
+	// Used to indicate the mouse has moved outside its dragdrop bounding box and triggers drag procedure
+	moduleObject.dragdrop.dragStarted = false;
+
+
+	// Remember where the client clicked when the drag started so we can see how far the object has been dragged
+	moduleObject.dragdrop.clickX = event.clientX;
+	moduleObject.dragdrop.clickY = event.clientY;
+
+	// Remember where the user clicked within the element
+	var topleft = dhtml.getElementTopLeft(dhtml.getElementById(element.tooltipParent.data.appNodeID));
+	// Hackish: have to get the scroll value here
+	moduleObject.dragdrop.relClickX = event.clientX + dhtml.getElementById("multiusercalendar_container["+moduleObject.id+"]").scrollLeft - topleft[0];
+	moduleObject.dragdrop.relClickY = event.clientY + dhtml.getElementById("multiusercalendar_container["+moduleObject.id+"]").scrollTop - topleft[1];
+
+	// Transaction of item properties
+	// Save original data of appointment to be used while dragging.
+	moduleObject.dragdrop.elementData = {
+		startdate: element.tooltipParent.data.startdate,
+		enddate: element.tooltipParent.data.enddate,
+		groupID: element.tooltipParent.data.groupID,
+		parententryid: element.tooltipParent.data.parententryid,
+		entryid: element.tooltipParent.data.entryid,
+		meetingrequest: element.tooltipParent.data.meetingrequest,
+		alldayevent: element.tooltipParent.data.alldayevent,
+		basedate: element.tooltipParent.data.basedate,
+		origOffsetTop: element.tooltipParent.offsetTop
+	};
+	moduleObject.dragdrop.elementData.duration = moduleObject.dragdrop.elementData.enddate - moduleObject.dragdrop.elementData.startdate;
+
+}
+
+function eventMultiUserCalandarViewMouseMoveAppointment(moduleObject, element, event){
+	if(moduleObject.dragdrop && moduleObject.dragdrop.dragging){
+		/** 
+		 * A timeout is used to scroll the viewport when the mouse is close to 
+		 * the edge. This timeout has to be set because no move event is 
+		 * triggered when the mouse does not move. The timeout has to be cleared
+		 * to prevent it from popping up when the mouse has been moved to another
+		 * position and triggered another move event.
+		*/
+		clearTimeout(moduleObject.dragdrop.timeout);
+
+		if(moduleObject.dragdrop.dragStarted){
+
+			var dragdrop = dhtml.getElementById("multiusercalendar_appointment_dragdrop[" + moduleObject.id + "]");
+			var dragdropTooltip = dhtml.getElementById("multiusercalendar_appointment_dragdrop_tooltip[" + moduleObject.id + "]");
+			if(dragdrop){
+
+				// Calculate mouse position relative to the mucalendar container element.
+				var mousePosX = event.clientX - moduleObject.dragdrop.deviationMouseAppointmentX
+				var mousePosY = event.clientY - moduleObject.dragdrop.deviationMouseAppointmentY;
+
+				var container = dhtml.getElementById("multiusercalendar_container["+moduleObject.id+"]");
+
+				// Calculate distance from each of the four borders of the container viewport
+				var toRightBorder = (container.scrollLeft + container.offsetWidth) - mousePosX;
+				var toLeftBorder = mousePosX - container.scrollLeft;
+				var toBottomBorder = (container.scrollTop + container.offsetHeight) - mousePosY;
+				var toTopBorder = mousePosY - container.scrollTop;
+
+				// Calculate whether or not the mosue is near any of the borders
+				var againstBorder = false;
+				var scrollTriggerAreaWidth = 50;
+				if(toRightBorder < scrollTriggerAreaWidth && toRightBorder >= 0){
+					container.scrollLeft = container.scrollLeft + 15;
+					againstBorder = true;
+				}
+				if(toLeftBorder < scrollTriggerAreaWidth && toLeftBorder >= 0 && container.scrollLeft > 0){
+					container.scrollLeft = container.scrollLeft - 15;
+					againstBorder = true;
+				}
+				if(toBottomBorder < scrollTriggerAreaWidth && toBottomBorder >= 0){
+					container.scrollTop = container.scrollTop + 15;
+					againstBorder = true;
+				}
+				if(toTopBorder < scrollTriggerAreaWidth && toTopBorder >= 0 && container.scrollTop > 0){
+					container.scrollTop = container.scrollTop - 15;
+					againstBorder = true;
+				}
+				/**
+				 * Deviation is recalculated by getting the absolute X coordinate of the 
+				 * container div and subtracting the scrolled distance of the container. The 
+				 * deviation is the distance of the start of the timeline has scrolled to the left 
+				 * of the viewport.
+				 * 
+				 * +------------------+
+				 * |A      +---------+|
+				 * |       |B        ||
+				 * |  +----|---------||----+
+				 * |  |C   |         ||    |
+				 * |  |    |         ||    |
+				 * |  +----|---------||----+
+				 * |       +---------+|
+				 * +------------------+
+				 * 
+				 * A: Window
+				 * B: Container with viewport
+				 * C: Table with timeline
+				 * Deviation: B - C
+				 * 
+				 * TODO: The deviation is calculated differently when the move event is started:
+				 * moduleObject.dragdrop.deviationMouseAppointmentX = 
+				 *      event.clientX - (dragdrop.offsetLeft + moduleObject.dragdrop.relClickX);
+				 * moduleObject.dragdrop.deviationMouseAppointmentY = 
+				 *      event.clientY - (moduleObject.dragdrop.elementData.origOffsetTop + moduleObject.dragdrop.relClickY);
+				 */
+				var containerTopLeft = dhtml.getElementTopLeft(container);  // Array(x, y)
+				moduleObject.dragdrop.deviationMouseAppointmentX = containerTopLeft[0] - container.scrollLeft;
+				moduleObject.dragdrop.deviationMouseAppointmentY = containerTopLeft[1] - container.scrollTop;
+
+				// Trigger a timeout when the mouse is near a border
+				if(againstBorder){
+					var eventClientX = event.clientX;
+					var eventClientY = event.clientY;
+					moduleObject.dragdrop.timeout = setTimeout(function(){
+						/**
+						 * NOTE: the event object is "recreated" to prevent it from being used 
+						 * when the event object has been deleted.
+						 */
+						eventMultiUserCalandarViewMouseMoveAppointment(moduleObject, element, {clientX: eventClientX, clientY: eventClientY})
+					}, 100);
+				}
+
+				// Calculate new start and end date according to mouse position
+				var date = moduleObject.viewController.viewObject.getTimeByPixel(mousePosX);
+				if(date){
+					if(Number(moduleObject.dragdrop.elementData.alldayevent) == 1){
+						// Set date object of mouse position back to the start of the day
+						var dateObj = new Date(date*1000);
+						dateObj.setTimeStamp(dateObj.getDate(), (dateObj.getMonth()+1), dateObj.getFullYear());
+						date = dateObj.getTime()/1000;
+						// Set new start date
+						var newStartDate = dateObj.getTime()/1000;
+						dateObj.addDays(Math.ceil(moduleObject.dragdrop.elementData.duration/24/60/60));
+						// Set new end date
+						var newEndDate = dateObj.getTime()/1000;
+					}else{
+						// Use mouse position date to calculate new start and end date
+						var newStartDate = date - moduleObject.dragdrop.timeDifferenceCursorAppointment;
+						var newEndDate = newStartDate + moduleObject.dragdrop.elementData.duration;
+					}
+
+					// Get ID of the group area the mouse is focussing on. If non, use groupID of owner of appointment
+					var newGroupID = moduleObject.viewController.viewObject.getGroupIDByPixel(mousePosY);
+					if(!newGroupID || (moduleObject.getGroup(newGroupID).access & MAPI_ACCESS_CREATE_CONTENTS) == 0){
+						newGroupID = moduleObject.dragdrop.elementData.groupID;
+					}
+
+					// Reposition item
+					moduleObject.viewController.viewObject.positionItem(dragdrop, newGroupID, newStartDate, newEndDate);
+
+					if(dragdropTooltip){
+						dragdropTooltip.style.left = (dragdrop.offsetLeft + 2) + "px";
+						dragdropTooltip.style.top = (dragdrop.offsetTop + 2) + "px";
+
+						// Display time of dragdrop indicator element
+						dragdropTooltip.innerHTML = new Date(new Date(newStartDate*1000).floorHalfhour()).toTime()
+						dragdropTooltip.innerHTML += " - ";
+						dragdropTooltip.innerHTML += new Date(new Date(newEndDate*1000).floorHalfhour()).toTime();
+					}
+
+					// Set height of dragdrop indicator element to match that of the group display
+					var groupDisplay = dhtml.getElementById("mucalendar_timeline_userdisplay["+moduleObject.id+"]["+newGroupID+"]");
+					dragdrop.style.height = (groupDisplay.offsetHeight-1) + "px";
+				}
+
+			}
+		}else{
+			// Check to see if the cursor has moved at least 5 pixels since mousedown
+			if(Math.abs(moduleObject.dragdrop.clickY - event.clientX) > 5 || Math.abs(moduleObject.dragdrop.clickY - event.clientY) > 5){
+				moduleObject.dragdrop.dragStarted = true;
+
+				// Remove old dragdrop indicator element
+				var dragdrop = dhtml.getElementById("multiusercalendar_appointment_dragdrop[" + moduleObject.id + "]");
+				if(dragdrop){
+					dragdrop.parentNode.removeChild(dragdrop);
+				}
+
+				// Create dragdrop indicator element
+				var dragdrop = document.createElement("div");
+				dhtml.addClassName(dragdrop, "mucalendar_appointment_dragdrop");
+				dragdrop.setAttribute("id", "multiusercalendar_appointment_dragdrop[" + moduleObject.id + "]");
+				dhtml.getElementById("multiusercalendar_container["+moduleObject.id+"]").appendChild(dragdrop);
+
+				var dragdropTooltip = document.createElement("div");
+				dhtml.addClassName(dragdropTooltip, "mucalendar_appointment_dragdrop_tooltip");
+				dragdropTooltip.setAttribute("id", "multiusercalendar_appointment_dragdrop_tooltip[" + moduleObject.id + "]");
+				dhtml.getElementById("multiusercalendar_container["+moduleObject.id+"]").appendChild(dragdropTooltip);
+				dragdropTooltip.innerHTML = "xx:xx - xx:xx";
+
+				// Position dragdrop indicator
+				moduleObject.viewController.viewObject.positionItem(dragdrop, moduleObject.dragdrop.elementData.groupID, moduleObject.dragdrop.elementData.startdate, moduleObject.dragdrop.elementData.enddate);
+				dragdropTooltip.style.left = (dragdrop.offsetLeft + 2) + "px";
+				dragdropTooltip.style.top = (dragdrop.offsetTop + 2) + "px";
+
+				/**
+				 * Calculate the deviation between the mouse coordinates and the coordinates of the 
+				 * appointment block that are relative to the mucalendar container element.
+				 */
+				moduleObject.dragdrop.deviationMouseAppointmentX = event.clientX - (dragdrop.offsetLeft + moduleObject.dragdrop.relClickX);
+				moduleObject.dragdrop.deviationMouseAppointmentY = event.clientY - (moduleObject.dragdrop.elementData.origOffsetTop + moduleObject.dragdrop.relClickY);
+
+				// Get difference in time between position of cursor and the start of the appointment
+				var mousePosX = (event.clientX - moduleObject.dragdrop.deviationMouseAppointmentX);
+				cursorTime = moduleObject.viewController.viewObject.getTimeByPixel(mousePosX);
+				if(cursorTime){
+					moduleObject.dragdrop.timeDifferenceCursorAppointment = cursorTime - moduleObject.dragdrop.elementData.startdate;
+
+					// Display time of dragdrop indicator element
+					dragdropTooltip.innerHTML = new Date(new Date(moduleObject.dragdrop.elementData.startdate*1000).floorHalfhour()).toTime();
+					dragdropTooltip.innerHTML += " - ";
+					dragdropTooltip.innerHTML += new Date(new Date(moduleObject.dragdrop.elementData.enddate*1000).floorHalfhour()).toTime();
+				}else{
+					// This shouldn't happen...
+					moduleObject.dragdrop.timeDifferenceCursorAppointment = 0;
+				}
+			}
+		}
+	}
+}
+
+function eventMultiUserCalandarViewMouseUpAppointment(moduleObject, element, event){
+	if(moduleObject.dragdrop && moduleObject.dragdrop.dragging){
+
+		var dragdrop = dhtml.getElementById("multiusercalendar_appointment_dragdrop[" + moduleObject.id + "]");
+		if(dragdrop){
+			// Calculate mouse position relative to the mucalendar container element.
+			var mousePosX = (event.clientX - moduleObject.dragdrop.deviationMouseAppointmentX);
+			var mousePosY = (event.clientY - moduleObject.dragdrop.deviationMouseAppointmentY);
+
+			// Calculate new start and end date according to mouse position
+			var date = moduleObject.viewController.viewObject.getTimeByPixel(mousePosX);
+			if(date){
+				if(Number(moduleObject.dragdrop.elementData.alldayevent) == 1){
+					// Set date object of mouse position back to the start of the day
+					var dateObj = new Date(date*1000);
+					dateObj.setTimeStamp(dateObj.getDate(), (dateObj.getMonth()+1), dateObj.getFullYear());
+					date = dateObj.getTime()/1000;
+					// Set new start date
+					var newStartDate = dateObj.getTime()/1000;
+					dateObj.addDays(Math.ceil(moduleObject.dragdrop.elementData.duration/24/60/60));
+					// Set new end date
+					var newEndDate = dateObj.getTime()/1000;
+				}else{
+					// Use mouse position date to calculate new start and end date
+					var newStartDate = date - moduleObject.dragdrop.timeDifferenceCursorAppointment;
+					var newEndDate = newStartDate + moduleObject.dragdrop.elementData.duration;
+				}
+
+				// Get ID of the group area the mouse is focussing on. If non, use groupID of owner of appointment
+				var newGroupID = moduleObject.viewController.viewObject.getGroupIDByPixel(mousePosY);
+				if(newGroupID && (moduleObject.getGroup(newGroupID).access & MAPI_ACCESS_CREATE_CONTENTS) == 0){
+					newGroupID = moduleObject.dragdrop.elementData.groupID;
+				}
+
+				//var group = moduleObject.getGroup(newGroupID);
+				var group = moduleObject.getGroup(moduleObject.dragdrop.elementData.groupID);
+				if(group && newGroupID){
+					var newGroup = moduleObject.getGroup(newGroupID);
+
+					var props = new Object();
+					props["store"] = group.storeid;
+					props["parent_entryid"] = group.entryid;
+
+					props["entryid"] = moduleObject.dragdrop.elementData.entryid;
+					props["startdate"] = Math.floor((new Date(newStartDate*1000)).floorHalfhour()/1000);
+					props["duedate"] = Math.floor((new Date(newEndDate*1000)).floorHalfhour()/1000);
+					props["commonstart"] = props["startdate"];
+					props["commonend"] = props["duedate"];
+					props["duration"] = (props["duedate"]-props["startdate"])/60;
+
+					if(moduleObject.dragdrop.elementData.basedate) {
+						props["basedate"] = moduleObject.dragdrop.elementData.basedate;
+					}
+					
+					var send = false;
+					// NOTE: meetingrequest=3 is also possible, but not taken into account here (Means meeting request has been accepted)
+					if (moduleObject.dragdrop.elementData.meetingrequest && moduleObject.dragdrop.elementData.meetingrequest==1){ // 1 = olResponseOrganized
+						if( (new Date(newEndDate).getTime())>(new Date().getTime())){
+							send = confirm(_("Would you like to send an update to the attendees regarding changes to this meeting?"));
+						}
+					}
+					// Move appointment within same store to another time
+					group.save(props, send);
+
+					// Move appointment to another store
+					if(group.storeid != newGroup.storeid){
+						group.copyMessages(newGroup.storeid, newGroup.entryid, "move", true, moduleObject.dragdrop.elementData.entryid);
+						newGroup.list();
+					}
+
+				}
+
+			}
+
+		}
+
+
+		// Remove dragdrop indicator elements
+		var dragdrop = dhtml.getElementById("multiusercalendar_appointment_dragdrop[" + moduleObject.id + "]");
+		if(dragdrop){
+			dragdrop.parentNode.removeChild(dragdrop);
+		}
+		var dragdropTooltip = dhtml.getElementById("multiusercalendar_appointment_dragdrop_tooltip[" + moduleObject.id + "]");
+		if(dragdropTooltip){
+			dragdropTooltip.parentNode.removeChild(dragdropTooltip);
+		}
+		moduleObject.dragdrop.dragging = false;
+		moduleObject.dragdrop.startDrag = false;
+
+	}
+}
+/**
+ * Function which handles keys for MUC options.
+ */
+function eventMUCKeyCtrl(moduleObject, element, event)
+{
+	var viewObject = moduleObject.viewController.viewObject;
+	var zoom_level = false;
+
+	switch(event.keyCombination)
+	{
+		case this.keys["muc"]["add_user"]:
+			var windowData = new Object();
+			windowData["hide_users"] = ["contact"];
+			windowData["hide_groups"] = ["dynamic", "normal", "everyone", "security"];
+			windowData["hide_companies"] = true;
+
+			webclient.openModalDialog(-1, 'addressbook', DIALOG_URL+'&type=username&source=gab&groups=no&task=addressbook_modal&storeid=' + webclient.hierarchy.defaultstore["id"], 800, 500, mucalendarDialog_adduser_addCallBack, {moduleID:moduleObject.id}, windowData);
+			break;
+		case this.keys["muc"]["remove_user"]:
+			eventMultiUserCalendarViewRemoveUsers(moduleObject, element, event);
+			break;
+		case this.keys["muc"]["load_group"]:
+			webclient.openModalDialog(-1, 'usergroup', DIALOG_URL+'task=usergroup_modal', 600, 450, mucalendarDialog_loadgroups_addCallBack, {moduleID:moduleObject.id});
+			break;
+		case this.keys["muc"]["previous_period"]:
+			eventMultiUserCalendarViewClickPrevPeriod(moduleObject);
+			break;
+		case this.keys["muc"]["previous_day"]:
+			eventMultiUserCalendarViewClickPrevDay(moduleObject);
+			break;
+		case this.keys["muc"]["next_day"]:
+			eventMultiUserCalendarViewClickNextDay(moduleObject);
+			break;
+		case this.keys["muc"]["next_period"]:
+			eventMultiUserCalendarViewClickNextPeriod(moduleObject);
+			break;
+		case this.keys["muc"]["zoom_in"]:
+			zoom_level = parseInt(array_indexOf(viewObject.numOfDaysInViewport, viewObject.numOfDaysInViewportOptions), 10) - 1;
+			break;
+		case this.keys["muc"]["zoom_out"]:
+			zoom_level = parseInt(array_indexOf(viewObject.numOfDaysInViewport, viewObject.numOfDaysInViewportOptions), 10) + 1;
+			break;
+	}
+
+	if (zoom_level !== false){
+		if (viewObject.numOfDaysInViewportOptions[zoom_level]){
+			moduleObject.numOfDaysInViewport = viewObject.numOfDaysInViewportOptions[zoom_level];
+
+			var select = dhtml.getElementById("mucalendar_zoom_controller_field["+ moduleObject.id +"]");
+			select.value = moduleObject.numOfDaysInViewport;
+			moduleObject.resize();
+		}
+	}
+}
Index: npl/mailserver/zarafa_webaccess_muc/root/var/www/htdocs/syn3/zarafa/client/views/multiusercalendarappointmentlist.view.js
===================================================================
--- npl/mailserver/zarafa_webaccess_muc/root/var/www/htdocs/syn3/zarafa/client/views/multiusercalendarappointmentlist.view.js	(revision c5c522c7ca74a4c1725ca5e3fe0bdcadff80e49a)
+++ npl/mailserver/zarafa_webaccess_muc/root/var/www/htdocs/syn3/zarafa/client/views/multiusercalendarappointmentlist.view.js	(revision c5c522c7ca74a4c1725ca5e3fe0bdcadff80e49a)
@@ -0,0 +1,49 @@
+MultiUserCalendarAppointmentListView.prototype = new View;
+MultiUserCalendarAppointmentListView.prototype.constructor = MultiUserCalendarAppointmentListView;
+MultiUserCalendarAppointmentListView.superclass = View.prototype;
+
+function MultiUserCalendarAppointmentListView(moduleID, element, events, data)
+{
+	this.element = element;
+	
+	this.moduleID = moduleID;
+	this.events = events;
+	this.data = data;
+	this.MultiUserCalendarParent = (data['mucalendarParent'])?data['mucalendarParent']:false;
+	this.storeid = data["storeid"];
+	this.groupID = data["groupID"];
+	
+	this.columns = false;
+	this.sortColumn = false;
+	this.sortDirection = false;
+	
+	this.initView();
+}
+
+/**
+ * Function which intializes the view
+ */ 
+MultiUserCalendarAppointmentListView.prototype.initView = function()
+{
+}
+
+
+MultiUserCalendarAppointmentListView.prototype.resizeView = function(){
+}
+
+MultiUserCalendarAppointmentListView.prototype.execute = function(items, properties, action)
+{
+	return this.MultiUserCalendarParent.executeAppView(items, properties, action, this.groupID);
+}
+
+MultiUserCalendarAppointmentListView.prototype.updateItem = function(items, properties, action){
+	return this.MultiUserCalendarParent.updateItemAppView(items, properties, action, this.groupID);
+}
+
+MultiUserCalendarAppointmentListView.prototype.addItem = function(items, properties, action){
+	return this.MultiUserCalendarParent.addItemAppView(items, properties, action, this.groupID);
+}
+
+MultiUserCalendarAppointmentListView.prototype.deleteItems = function(items, properties, action){
+	return this.MultiUserCalendarParent.deleteItemsAppView(items, properties, action, this.groupID);
+}
Index: npl/mailserver/zarafa_webaccess_muc/root/var/www/htdocs/syn3/zarafa/server/modules/class.multiusercalendarappointmentlistmodule.php
===================================================================
--- npl/mailserver/zarafa_webaccess_muc/root/var/www/htdocs/syn3/zarafa/server/modules/class.multiusercalendarappointmentlistmodule.php	(revision c5c522c7ca74a4c1725ca5e3fe0bdcadff80e49a)
+++ npl/mailserver/zarafa_webaccess_muc/root/var/www/htdocs/syn3/zarafa/server/modules/class.multiusercalendarappointmentlistmodule.php	(revision c5c522c7ca74a4c1725ca5e3fe0bdcadff80e49a)
@@ -0,0 +1,22 @@
+<?php
+	/**
+	 * MultiUserCalendar Appointment List Module
+	*/
+	
+	require_once("mapi/class.recurrence.php");
+	require_once("server/modules/class.appointmentlistmodule.php");
+
+	class MultiUserCalendarAppointmentListModule extends AppointmentListModule 
+	{
+		/**
+		 * Constructor
+		 * @param int $id unique id.
+		 * @param array $data list of all actions.
+		 */
+		function MultiUserCalendarAppointmentListModule($id, $data)
+		{
+			parent::AppointmentListModule($id, $data);
+		}
+	}
+	
+?>
Index: npl/mailserver/zarafa_webaccess_muc/root/var/www/htdocs/syn3/zarafa/server/modules/class.multiusercalendarmodule.php
===================================================================
--- npl/mailserver/zarafa_webaccess_muc/root/var/www/htdocs/syn3/zarafa/server/modules/class.multiusercalendarmodule.php	(revision c5c522c7ca74a4c1725ca5e3fe0bdcadff80e49a)
+++ npl/mailserver/zarafa_webaccess_muc/root/var/www/htdocs/syn3/zarafa/server/modules/class.multiusercalendarmodule.php	(revision c5c522c7ca74a4c1725ca5e3fe0bdcadff80e49a)
@@ -0,0 +1,269 @@
+<?php
+	/**
+	 * MultiUserCalendar Module
+	*/
+	
+	class MultiUserCalendarModule extends ListModule 
+	{
+		/**
+		 * Constructor
+		 * @param int $id unique id.
+		 * @param array $data list of all actions.
+		 */
+		function MultiUserCalendarModule($id, $data)
+		{
+			$this->properties = Array();
+			$this->startdate = false;
+			$this->enddate = false;
+
+			$this->sort = array();
+
+			parent::ListModule($id, $data, array(OBJECT_SAVE, TABLE_SAVE, TABLE_DELETE));
+		}
+
+		/**
+		 * Executes all the actions in the $data variable.
+		 * @return boolean true on success of false on fialure.
+		 */
+		function execute()
+		{
+			$result = false;
+			
+			if(!mapi_zarafa_check_license($GLOBALS['mapisession']->getDefaultMessageStore(), "WA-ADVANCED-CALENDAR")) {
+				// return error
+				$data = array();
+				$data["attributes"] = array("type" => "error");
+				$data["error"] = array();
+				$data["error"]["hresult"] = MAPI_E_NO_ACCESS;
+				$data["error"]["hresult_name"] = get_mapi_error_name(MAPI_E_NO_ACCESS);
+				$data["error"]["message"] = _("Multi-user calendar is not available with your license");
+				
+				array_push($this->responseData["action"], $data);
+				$GLOBALS["bus"]->addData($this->responseData);
+				return true;
+			}
+			
+			foreach($this->data as $action)
+			{
+				if(isset($action["attributes"]) && isset($action["attributes"]["type"])) {
+					$store = $this->getActionStore($action);
+					$parententryid = $this->getActionParentEntryID($action);
+					$entryid = $this->getActionEntryID($action);
+				
+					switch($action["attributes"]["type"])
+					{
+						case "getaddressbookusers":
+							$result = $this->GABUsers($action);
+							break;
+						case "getgroups":
+							$result = $this->getGroups($action);
+							break;
+						case "savegroup":
+							$result = $this->saveGroup($action);
+							break;
+						case "getgroupusers":
+							$result = $this->getGroupUsers($action);
+							break;
+						case "getusers":
+							$result = $this->getUsers($action);
+							break;
+					}
+				}
+			}
+
+			return $result;
+		}
+
+		function getGroups($action){
+			$restriction = Array(
+				RES_AND,
+				Array(
+					Array(	// Check for PR_MESSAGE_CLASS == IPM.appointment
+						RES_CONTENT,
+						Array(
+							FUZZYLEVEL => FL_FULLSTRING,
+							ULPROPTAG => PR_MESSAGE_CLASS,
+							VALUE => Array(
+								PR_MESSAGE_CLASS => 'IPM.Appointment'
+							)
+						)
+					),
+					Array(	// Check if subject starts with "{B911D251-1842-4720-A131-F164B6C99078} - "
+						RES_CONTENT,
+						Array(
+							FUZZYLEVEL => FL_PREFIX,
+							ULPROPTAG => PR_SUBJECT,
+							VALUE => Array(
+								PR_SUBJECT => '{B911D251-1842-4720-A131-F164B6C99078} - '
+							)
+						)
+					)
+				)
+			);
+
+			$root = mapi_msgstore_openentry($GLOBALS['mapisession']->getDefaultMessageStore(),  null);
+			$rootProps = mapi_getProps($root, Array(PR_IPM_APPOINTMENT_ENTRYID));
+
+			$folder = mapi_msgstore_openentry($GLOBALS['mapisession']->getDefaultMessageStore(), $rootProps[PR_IPM_APPOINTMENT_ENTRYID]);
+			$table = mapi_folder_getcontentstable($folder, MAPI_ASSOCIATED);
+			$calendaritems = mapi_table_queryallrows($table, Array(PR_ENTRYID, PR_SUBJECT), $restriction);
+
+			$items = Array();
+			for($i=0;$i<count($calendaritems);$i++){
+				$items[] = Array(
+					'type' => 'usergroup',
+					'entryid' => bin2hex($calendaritems[$i][PR_ENTRYID]),
+					'subject' => str_replace('{B911D251-1842-4720-A131-F164B6C99078} - ', '', $calendaritems[$i][PR_SUBJECT])
+				);
+			}
+
+
+			$data = array();
+			$data["attributes"] = array("type" => "list");
+			$data = array_merge($data, array("item"=>$items));
+
+			array_push($this->responseData["action"], $data);
+			$GLOBALS["bus"]->addData($this->responseData);
+			return true;
+		}
+
+		function getGroupUsers($action){
+			$recipientRestriction = Array(
+				// Check for PR_ADDRTYPE == ZARAFA
+				RES_CONTENT,
+				Array(
+					FUZZYLEVEL => FL_FULLSTRING,
+					ULPROPTAG => PR_ADDRTYPE,
+					VALUE => Array(
+						PR_ADDRTYPE => 'ZARAFA'
+					)
+				)
+			);
+
+			$items = Array();
+
+			$message = mapi_msgstore_openentry($GLOBALS['mapisession']->getDefaultMessageStore(), hex2bin($action['group_entry_id']));
+
+			$msgRecipientsTable = mapi_message_getrecipienttable($message);
+
+			$msgRecipientsTableItems = mapi_table_queryallrows($msgRecipientsTable, Array(PR_DISPLAY_NAME, PR_ADDRTYPE, PR_ENTRYID, PR_EMAIL_ADDRESS), $recipientRestriction);
+
+			for($i=0;$i<count($msgRecipientsTableItems);$i++){
+				$item = Array(
+					'type' => 'user',
+					'userentryid' => bin2hex($msgRecipientsTableItems[$i][PR_ENTRYID]),
+					'display_name' => $msgRecipientsTableItems[$i][PR_DISPLAY_NAME],
+					'username' => $msgRecipientsTableItems[$i][PR_EMAIL_ADDRESS]
+				);
+
+				// Get StoreEntryID by username  (this => $GLOBALS['mapisession'])   [check hresult==0]
+				$user_store_entryid = mapi_msgstore_createentryid($GLOBALS['mapisession']->getDefaultMessageStore(), $item['username']);
+
+				// Open store (this => $GLOBALS['mapisession'])
+				$userStore = $GLOBALS['mapisession']->openMessageStore($user_store_entryid);
+
+				// Open root folder
+				$userRoot = mapi_msgstore_openentry($userStore, null);
+
+				// Get calendar entryID
+				$userRootProps = mapi_getprops($userRoot, array(PR_STORE_ENTRYID, PR_IPM_APPOINTMENT_ENTRYID));
+
+				// Open Calendar folder   [check hresult==0]
+				$calFolder = mapi_msgstore_openentry($userStore, $userRootProps[PR_IPM_APPOINTMENT_ENTRYID]);
+
+				if($calFolder){
+					$calFolderProps = mapi_getProps($calFolder, Array(PR_ACCESS));
+					$item['access'] = $calFolderProps[PR_ACCESS];
+
+					$item["storeid"] = bin2hex($userRootProps[PR_STORE_ENTRYID]);
+					$item["calentryid"] = bin2hex($userRootProps[PR_IPM_APPOINTMENT_ENTRYID]);
+
+				}else{
+					$item['access'] = 0;
+				}
+
+				$items[] = $item;
+			}
+
+			$data = array();
+			$data["attributes"] = array("type" => "list");
+			$data = array_merge($data, array("item"=>$items));
+
+			array_push($this->responseData["action"], $data);
+			$GLOBALS["bus"]->addData($this->responseData);
+			return true;
+		}
+
+		function getUsers($action){
+			if(!$action["users"])
+				return false;
+
+			$items = Array();
+
+			foreach($action["users"] as $key => $user){
+				$item = Array();
+				// Handle the weird behaviour of the XML Reader
+				if(!isset($user["username"]) && isset($user["user"])){
+					$user = $user["user"];
+				}
+
+				if($user['username']){
+					// Get StoreEntryID by username  (this => $GLOBALS['mapisession'])   [check hresult==0]
+					$user_store_entryid = mapi_msgstore_createentryid($GLOBALS['mapisession']->getDefaultMessageStore(), $user['username']);
+
+					// Open store (this => $GLOBALS['mapisession'])
+					$userStore = $GLOBALS['mapisession']->openMessageStore($user_store_entryid);
+
+					// Open root folder
+					$userRoot = mapi_msgstore_openentry($userStore, null);
+
+					// Get calendar entryID
+					$userRootProps = mapi_getprops($userRoot, array(PR_STORE_ENTRYID, PR_IPM_APPOINTMENT_ENTRYID));
+
+					// Check to see if this user has an entryID for the calendar
+					if($userRootProps[PR_IPM_APPOINTMENT_ENTRYID]){
+						// Open Calendar folder   [check hresult==0]
+						$calFolder = mapi_msgstore_openentry($userStore, $userRootProps[PR_IPM_APPOINTMENT_ENTRYID]);
+						if($calFolder){
+							$calFolderProps = mapi_getProps($calFolder, Array(PR_ACCESS));
+							$item['access'] = $calFolderProps[PR_ACCESS];
+
+							$item["storeid"] = bin2hex($userRootProps[PR_STORE_ENTRYID]);
+							$item["calentryid"] = bin2hex($userRootProps[PR_IPM_APPOINTMENT_ENTRYID]);
+
+						// Could not open calendar
+						}else{
+							$item['access'] = 0;
+						}
+					// No Calendar entryID
+					}else{
+						$item['access'] = 0;
+					}
+
+					$ab = $GLOBALS["mapisession"]->getAddressbook();
+					$userData = array( array( PR_DISPLAY_NAME => u2w($user['username']) ) );
+					$userData = mapi_ab_resolvename($ab, $userData, EMS_AB_ADDRESS_LOOKUP);
+					if(is_array($userData) && count($userData) > 0){
+						$item["display_name"] = windows1252_to_utf8($userData[0][PR_DISPLAY_NAME]);
+						$item["username"] = $user['username'];
+					}else{
+						$item['unknown_user'] = 1;
+						$item["username"] = $user['username'];
+					}
+					$items[] = $item;
+				}
+
+			}
+
+			$data = array();
+			$data["attributes"] = array("type" => "list");
+			$data = array_merge($data, array("item"=>$items));
+
+			array_push($this->responseData["action"], $data);
+			$GLOBALS["bus"]->addData($this->responseData);
+			return true;
+		}
+
+	}
+	
+?>
Index: npl/mailserver/zarafa_webaccess_muc/zarafa_webaccess_muc.SlackBuild
===================================================================
--- npl/mailserver/zarafa_webaccess_muc/zarafa_webaccess_muc.SlackBuild	(revision c5c522c7ca74a4c1725ca5e3fe0bdcadff80e49a)
+++ npl/mailserver/zarafa_webaccess_muc/zarafa_webaccess_muc.SlackBuild	(revision c5c522c7ca74a4c1725ca5e3fe0bdcadff80e49a)
@@ -0,0 +1,26 @@
+#!/bin/bash
+
+#zarafa-webaccess-multiuser client - this is for the supported commercial version of zarafa
+
+#Use the debian 5 i386 package inside: https://download.zarafa.com/supported/final/6.40/6.40.12-29942/zcp-6.40.12-29942-debian-5.0-i386-supported.tar.gz
+#This contains: zarafa-webaccess-muc_6.40.12-29942_all.deb
+#Extract with: dpkg-deb -X zarafa-webaccess-muc_6.40.12-29942_all.deb tmp
+
+#Name of the Syn-3 package that we are going to create
+NAME=`basename $0|sed 's/.SlackBuild//'`
+
+
+VER=6.40.12
+
+ARCH=noarch
+
+
+mkdir /tmp/pkg
+cp -r root/* /tmp/pkg || exit 1
+find /tmp/pkg -name .svn -exec rm -r "{}" \; 
+
+
+#make main package
+syn3_makepkg /tmp/pkg $NAME $VER $ARCH || exit 1
+
+
Index: npl/mailserver/zarafa_webaccess_muc/zarafa_webaccess_muc.arch
===================================================================
--- npl/mailserver/zarafa_webaccess_muc/zarafa_webaccess_muc.arch	(revision c5c522c7ca74a4c1725ca5e3fe0bdcadff80e49a)
+++ npl/mailserver/zarafa_webaccess_muc/zarafa_webaccess_muc.arch	(revision c5c522c7ca74a4c1725ca5e3fe0bdcadff80e49a)
@@ -0,0 +1,1 @@
+noarch
Index: npl/mailserver/zarafa_webaccess_muc/zarafa_webaccess_muc.md5
===================================================================
--- npl/mailserver/zarafa_webaccess_muc/zarafa_webaccess_muc.md5	(revision c5c522c7ca74a4c1725ca5e3fe0bdcadff80e49a)
+++ npl/mailserver/zarafa_webaccess_muc/zarafa_webaccess_muc.md5	(revision c5c522c7ca74a4c1725ca5e3fe0bdcadff80e49a)
@@ -0,0 +1,21 @@
+e955257ccfc03cb8a77ba4b14da2bc28  ./root/var/www/htdocs/syn3/zarafa/client/layout/css/multiusercalendar.css
+eb317a3ed34948c2cddbe218c5def21b  ./root/var/www/htdocs/syn3/zarafa/client/modules/multiusercalendarappointmentlistmodule.js
+074a6aaf415d042cd3064a63483bff75  ./root/var/www/htdocs/syn3/zarafa/client/modules/multiusercalendarmodule.js
+eac64ee58b2ab1a5458f3a437eb054c1  ./root/var/www/htdocs/syn3/zarafa/client/views/multiusercalendarappointmentlist.view.js
+50475dafdafae4293630b9f7c3a128dd  ./root/var/www/htdocs/syn3/zarafa/client/views/multiusercalendar.view.js
+c89c7da01e01b82065cd862600dfc931  ./root/var/www/htdocs/syn3/zarafa/server/modules/class.multiusercalendarappointmentlistmodule.php
+c8c2f71e29f047ffb1e8c67c1436f0dd  ./root/var/www/htdocs/syn3/zarafa/server/modules/class.multiusercalendarmodule.php
+74824bbb128d7039130a3198d4144411  ./tmp/usr/share/doc/zarafa-webaccess-muc/changelog.Debian.gz
+1ac66c193ba1fc7a829ad738d4cd2237  ./tmp/usr/share/doc/zarafa-webaccess-muc/copyright
+e955257ccfc03cb8a77ba4b14da2bc28  ./tmp/usr/share/zarafa-webaccess/client/layout/css/multiusercalendar.css
+eb317a3ed34948c2cddbe218c5def21b  ./tmp/usr/share/zarafa-webaccess/client/modules/multiusercalendarappointmentlistmodule.js
+074a6aaf415d042cd3064a63483bff75  ./tmp/usr/share/zarafa-webaccess/client/modules/multiusercalendarmodule.js
+eac64ee58b2ab1a5458f3a437eb054c1  ./tmp/usr/share/zarafa-webaccess/client/views/multiusercalendarappointmentlist.view.js
+50475dafdafae4293630b9f7c3a128dd  ./tmp/usr/share/zarafa-webaccess/client/views/multiusercalendar.view.js
+c89c7da01e01b82065cd862600dfc931  ./tmp/usr/share/zarafa-webaccess/server/modules/class.multiusercalendarappointmentlistmodule.php
+c8c2f71e29f047ffb1e8c67c1436f0dd  ./tmp/usr/share/zarafa-webaccess/server/modules/class.multiusercalendarmodule.php
+a16a2707801da45bbe6127c4d5be2a89  ./zarafa-webaccess-muc_6.40.12-29942_all.deb
+43b5c5f446496aff4522b4c5b3de8a41  ./zarafa-webaccess-muc_7.1.4-41394_all.deb
+e025e41e3bc9c878114b7d173215cfce  ./zarafa_webaccess_muc.pkg
+0329eb049d6b92f03ca8ebccaf0bf7c1  ./zarafa_webaccess_muc.SlackBuild
+3a5e640e8b1f5cb1a5b7b49efde1975a  ./zarafa_webaccess_muc.SlackBuild.log.gz
Index: npl/mailserver/zarafa_webaccess_muc/zarafa_webaccess_muc.version
===================================================================
--- npl/mailserver/zarafa_webaccess_muc/zarafa_webaccess_muc.version	(revision c5c522c7ca74a4c1725ca5e3fe0bdcadff80e49a)
+++ npl/mailserver/zarafa_webaccess_muc/zarafa_webaccess_muc.version	(revision c5c522c7ca74a4c1725ca5e3fe0bdcadff80e49a)
@@ -0,0 +1,1 @@
+6.40.12
