1 /*
  2  * ***** BEGIN LICENSE BLOCK *****
  3  * Zimbra Collaboration Suite Web Client
  4  * Copyright (C) 2011, 2012, 2013, 2014, 2016 Synacor, Inc.
  5  *
  6  * The contents of this file are subject to the Common Public Attribution License Version 1.0 (the "License");
  7  * you may not use this file except in compliance with the License.
  8  * You may obtain a copy of the License at: https://www.zimbra.com/license
  9  * The License is based on the Mozilla Public License Version 1.1 but Sections 14 and 15
 10  * have been added to cover use of software over a computer network and provide for limited attribution
 11  * for the Original Developer. In addition, Exhibit A has been modified to be consistent with Exhibit B.
 12  *
 13  * Software distributed under the License is distributed on an "AS IS" basis,
 14  * WITHOUT WARRANTY OF ANY KIND, either express or implied.
 15  * See the License for the specific language governing rights and limitations under the License.
 16  * The Original Code is Zimbra Open Source Web Client.
 17  * The Initial Developer of the Original Code is Zimbra, Inc.  All rights to the Original Code were
 18  * transferred by Zimbra, Inc. to Synacor, Inc. on September 14, 2015.
 19  *
 20  * All portions of the code are Copyright (C) 2011, 2012, 2013, 2014, 2016 Synacor, Inc. All Rights Reserved.
 21  * ***** END LICENSE BLOCK *****
 22  */
 23 
 24 ZmPriorityMessageFilterDialog = function() {
 25 
 26 	DwtDialog.call(this, {parent:appCtxt.getShell(), className:"ZmPriorityMessageFilterDialog", title:ZmMsg.activityStream});
 27 
 28 	// set content
 29 	this.setContent(this._contentHtml());
 30 	this._initialize();
 31 	var okButton = this.getButton(DwtDialog.OK_BUTTON);
 32 	okButton.setText(ZmMsg.save);
 33 	this.setButtonListener(DwtDialog.OK_BUTTON, new AjxListener(this, this._okButtonListener));
 34 	this._rules = AjxDispatcher.run("GetFilterRules");
 35 };
 36 
 37 ZmPriorityMessageFilterDialog.prototype = new DwtDialog;
 38 ZmPriorityMessageFilterDialog.prototype.constructor = ZmPriorityMessageFilterDialog;
 39 
 40 ZmPriorityMessageFilterDialog.prototype._contentHtml = 
 41 function() {   
 42 	var html = "<div id='PRIORITYMESSAGE_PROMPT_FORM'>";
 43 	return html;			
 44 };
 45 
 46 ZmPriorityMessageFilterDialog.prototype._initialize = 
 47 function() {
 48 	var streamListener = this._onMoveMsgIntoStream.bind(this);
 49 	var advancedListener = this._onAdvancedControls.bind(this);
 50 	var params = {};
 51 	params.parent = this;
 52 	params.template = "prefs.Pages#PriorityMessageFilterPrompt";
 53 	params.id = "PriorityInboxDialog";
 54 	params.form = {
 55 		items: [
 56 			{ id: "MOVE_MSG_STREAM", type: "DwtCheckbox", label: ZmMsg.enableActivityStream, checked: false, onclick: streamListener},
 57 			{ id: "NOT_TO_ME", type: "DwtCheckbox", label: ZmMsg.moveNotToMe, checked: false},
 58 			{ id: "SELECT_FIELD", type: "DwtSelect", items:[ZmMsg.to, ZmMsg.toOrCc]},
 59 			{ id: "NOT_IN_ADDR", type: "DwtCheckbox", label: ZmMsg.moveNotInAddrBook, checked: false},
 60 			{ id: "DL_SUBSCRIBED", type: "DwtCheckbox", label: ZmMsg.moveMessagesFromDL, checked: true},
 61 			{ id: "MASS_MARKETING", type: "DwtCheckbox", label: ZmMsg.massMarketingMessages, checked: true}
 62 		]
 63 	};
 64 	this._priorityMessageForm = new DwtForm(params);
 65 	this._priorityMessageForm.setScrollStyle(DwtControl.CLIP);
 66 	var div = document.getElementById("PRIORITYMESSAGE_PROMPT_FORM");
 67 	this._priorityMessageForm.appendElement(div);
 68 	
 69 	this._moveMsgIntoStream = this._priorityMessageForm.getControl("MOVE_MSG_STREAM");
 70 	this._notToMe = this._priorityMessageForm.getControl("NOT_TO_ME");
 71 	this._selectField = this._priorityMessageForm.getControl("SELECT_FIELD");
 72 	this._selectField.fixedButtonWidth();
 73 	this._notInMyAddrBk = this._priorityMessageForm.getControl("NOT_IN_ADDR");
 74 	this._dlSubscribedTo = this._priorityMessageForm.getControl("DL_SUBSCRIBED");
 75 	this._massMarketing = this._priorityMessageForm.getControl("MASS_MARKETING");
 76 	
 77 	this._streamHash = {};
 78 	this._streamHash[ZmFilterRule.TEST_BULK] = {control: this._massMarketing, negative: false};
 79 	this._streamHash[ZmFilterRule.TEST_LIST] = {control: this._dlSubscribedTo, negative: false};
 80 	this._streamHash[ZmFilterRule.TEST_ADDRBOOK] = {control: this._notInMyAddrBk, negative: true, headerValue: "from"};
 81 	this._streamHash[ZmFilterRule.TEST_ME] = {control: this._notToMe, negative: true, headerValue: "to"};
 82 	
 83     this._advancedControls = new DwtText({parent:this,className:"FakeAnchor"});
 84     this._advancedControls.setText(ZmMsg.advancedControls);
 85     this._advancedControls.getHtmlElement().onclick = advancedListener;
 86     this._advancedControls.replaceElement(document.getElementById("PriorityInboxAdvancedControls"));
 87 };
 88 
 89 ZmPriorityMessageFilterDialog.prototype.popup =
 90 function() {
 91 	var callback = new AjxCallback(this, this._handleResponseLoadRules);
 92 	this._rules.loadRules(true, callback); // make sure rules are loaded (for when we save)
 93 	
 94 	DwtDialog.prototype.popup.call(this);
 95 };
 96 
 97 ZmPriorityMessageFilterDialog.prototype._handleResponseLoadRules =
 98 function() {
 99 	this._activityStreamRule = this._rules.getRuleByName(ZmMsg.activityStreamsRule);
100 	this._setStreamSelections();
101 };
102 
103 ZmPriorityMessageFilterDialog.prototype._onMoveMsgIntoStream = 
104 function() {
105 	var enabled = this._moveMsgIntoStream.isSelected();
106 	this._notToMe.setEnabled(enabled);
107 	this._selectField.setEnabled(enabled);
108 	this._notInMyAddrBk.setEnabled(enabled);
109 	this._dlSubscribedTo.setEnabled(enabled);
110 	this._massMarketing.setEnabled(enabled);
111 };
112 
113 ZmPriorityMessageFilterDialog.prototype._onAdvancedControls = 
114 function(controlId) {
115 	var filterRuleDialog = appCtxt.getFilterRuleDialog();
116 	var isPriority = false;
117 	var rule = this._activityStreamRule;	
118 	
119 	if (rule) {
120 		filterRuleDialog.popup(rule, true);		
121 	}
122 	else {
123 		//create rule with default conditions
124 		var ruleName = isPriority ? ZmMsg.markAsPriorityRule : ZmMsg.activityStreamsRule;
125 		var rule = new ZmFilterRule(ruleName, true, {}, {});
126         rule.addAction(ZmFilterRule.A_FOLDER, ZmMsg.activityStreamsRule);
127         for (var id in this._streamHash) {
128             if (id == ZmFilterRule.TEST_ME) {
129 				var meTestValue = this._selectField.getValue() == ZmMsg.to ? ZmFilterRule.C_ADDRESS_VALUE[ZmFilterRule.C_TO] : ZmFilterRule.C_ADDRESS_VALUE[ZmFilterRule.C_TO_CC];
130                 rule.addCondition(id, ZmFilterRule.OP_NOT_IS, null, meTestValue);	
131             }
132             else if (id == ZmFilterRule.TEST_ADDRBOOK) {
133                 rule.addCondition(id, ZmFilterRule.OP_NOT_IN ,"contacts", this._streamHash[id].headerValue); //Address in From not in Contacts	
134             }
135             else {
136                 rule.addCondition(id);
137             }
138         }
139 		rule.setGroupOp(ZmFilterRule.GROUP_ANY);		
140 		filterRuleDialog.popup(rule, true);
141 	}
142 };
143 
144 ZmPriorityMessageFilterDialog.prototype._setStreamSelections = 
145 function() {
146 	if (this._activityStreamRule) {
147 		if (this._activityStreamRule.active) {
148 			this._moveMsgIntoStream.setEnabled(true);
149 			this._moveMsgIntoStream.setSelected(true);
150 		}
151 		else {
152 			this._moveMsgIntoStream.setSelected(false);
153 		}
154 		var conditions = this._activityStreamRule.conditions;
155 		//initialize checkboxes before loading them
156 		this._massMarketing.setSelected(false);
157 		this._dlSubscribedTo.setSelected(false);
158 		this._notInMyAddrBk.setSelected(false);
159 		this._notToMe.setSelected(false);
160 
161 		for (var c in conditions) {
162 			var length = AjxUtil.isArray(conditions[c]) ? conditions[c].length : -1;
163 			for (var i=0; i<length; i++) {
164 				var isNegative = AjxUtil.isArray(conditions[c]) && conditions[c][i].negative ? (conditions[c][i].negative == "1") : false;
165 				if (this._streamHash[c]) {
166 					if (isNegative && (c == ZmFilterRule.TEST_ADDRBOOK || c == ZmFilterRule.TEST_ME)) {
167 						var header = AjxUtil.isArray(conditions[c]) && conditions[c][i].header;
168 						if (c == ZmFilterRule.TEST_ADDRBOOK) {
169 							value = ZmFilterRule.C_FROM;
170 						}
171 						else if (c == ZmFilterRule.TEST_ME) {
172 							if (header &&  header.toUpperCase() == ZmFilterRule.C_ADDRESS_VALUE[ZmFilterRule.C_TO].toUpperCase()) {
173 								value = header;
174 								this._selectField.setSelected(0);
175 							}
176 							else if (header &&  header.toUpperCase() == ZmFilterRule.C_ADDRESS_VALUE[ZmFilterRule.C_TO_CC].toUpperCase()) {
177 								value = header;
178 								this._selectField.setSelected(1);
179 							}
180 						}
181 						//var value = (c == ZmFilterRule.TEST_ADDRBOOK) ? ZmFilterRule.C_FROM : ZmFilterRule.C_TO;
182 						if (header && header.toLowerCase() == value.toLowerCase()) {
183 							this._streamHash[c].control.setSelected(true);
184 							this._streamHash[c].control.setEnabled(true);
185 						}
186 					}
187 					else if (!isNegative && !(c == ZmFilterRule.TEST_ADDRBOOK || c == ZmFilterRule.TEST_ME)) {
188 						this._streamHash[c].control.setSelected(true);
189 						this._streamHash[c].control.setEnabled(true);
190 					}
191 				} 
192 			}
193 		}
194 	}
195 	else {
196 		this._moveMsgIntoStream.setSelected(false);
197 	}
198 	this._onMoveMsgIntoStream();	
199 };
200 
201 ZmPriorityMessageFilterDialog.prototype._okButtonListener = 
202 function() {
203 	//build filter
204 	var foundCondition = false;
205 	var needSave = false; 
206 	var condition = {};
207 	var activityRule = this._rules.getRuleByName(ZmMsg.activityStreamsRule);
208 	
209 	//handle activity streams
210 	foundCondition = false;
211 	if (this._moveMsgIntoStream.isSelected()) {
212 		var streamRule = new ZmFilterRule(ZmMsg.activityStreamsRule, true, {}, {});
213 		streamRule.addAction(ZmFilterRule.A_FOLDER, ZmMsg.activityStreamsRule); 
214 		streamRule.setGroupOp(ZmFilterRule.GROUP_ANY);
215 		
216 		for (var id in this._streamHash) {
217 			var control = this._streamHash[id].control;
218 			var negative = this._streamHash[id].negative;
219 			var headerValue = this._streamHash[id].headerValue;
220 			if (control.isSelected()) {
221 				if (id == ZmFilterRule.TEST_ME) {
222 					var meTestValue = this._selectField.getValue() == ZmMsg.to ? ZmFilterRule.C_ADDRESS_VALUE[ZmFilterRule.C_TO] : ZmFilterRule.C_ADDRESS_VALUE[ZmFilterRule.C_TO_CC];
223 					streamRule.addCondition(id, ZmFilterRule.OP_NOT_IS, null, meTestValue);	
224 				}
225 				else if (id == ZmFilterRule.TEST_ADDRBOOK) {
226 					streamRule.addCondition(id, ZmFilterRule.OP_NOT_IN ,"contacts", headerValue); //Address in From not in Contacts	
227 				}
228 				else {
229 					streamRule.addCondition(id);
230 				}
231 				foundCondition = true;
232 			}
233 			else if (activityRule) {
234 				if (id == ZmFilterRule.TEST_ME && this._activityStreamRule.conditions[ZmFilterRule.TEST_ME]) {
235 					//if we uncheck the me filter we need to know which headerValue we are removing ("to" or "to,cc")
236 					activityRule = this._removeCondition(activityRule, id, negative, this._activityStreamRule.conditions[ZmFilterRule.TEST_ME][0].headerValue);	
237 				}
238 				else {
239 					activityRule = this._removeCondition(activityRule, id, negative, headerValue);
240 				}
241 			}
242 		}
243 		
244 		if (foundCondition && activityRule) {
245 			for (var id in streamRule.conditions) {
246 				activityRule.conditions[id] = streamRule.conditions[id];
247 			}
248 	
249 			for (var id in streamRule.actions) {
250 				activityRule.actions[id] = streamRule.actions[id];
251 			}
252 	
253 			activityRule.active = true;
254 			needSave = true;
255 		}
256 		else if(foundCondition) {
257 			this._rules.insertRule(streamRule); //insert last
258 			needSave = true;
259 		}
260 		else if (activityRule) {
261 			return this._handleConditionsError(ZmMsg.ruleNoConditonActivityFilter);	
262 		}
263 	}
264 	else if (activityRule) {
265 		//set existing rule to be non-active
266 		activityRule.active = false;
267 		needSave = true;
268 	}
269 	
270 	if (needSave) {
271 		this._rules.saveRules(null, true);
272 		this._createActivityStreamsFolder();
273 	}
274 	
275 	this.popdown();
276 			
277 };
278 
279 ZmPriorityMessageFilterDialog.prototype._getButtonsContainerStartTemplate =
280 function() {
281 	var html = "<div style='width: 250px; float: left;'><span id='PriorityInboxAdvancedControls'></span></div><div style='float:right;'>";
282 	html += DwtDialog.prototype._getButtonsContainerStartTemplate.call(this);
283 	return html;
284 };
285 
286 ZmPriorityMessageFilterDialog.prototype._getButtonsContainerEndTemplate = 
287 function() {
288 	var html = "</div>";
289 	html += DwtDialog.prototype._getButtonsContainerEndTemplate.call(this);
290 	return html;
291 };
292 
293 /**
294  * checks condition and value to determine if it should be removed; comparators are not checked
295  * @param rule
296  * @param condition
297  * @param isNegative
298  * @param headerValue
299  */
300 ZmPriorityMessageFilterDialog.prototype._removeCondition = 
301 function(rule, condition, isNegative, headerValue) {
302 	var c = rule.conditions[condition];
303 	if (c) {
304 		for (var i=0; i<c.length; i++) {
305 			var negativeCheck = isNegative ? c[i].negative == "1" : !c[i].negative;
306 			var headerCheck = headerValue ? c[i].header == headerValue : true;
307 			if (condition == ZmFilterRule.TEST_CONVERSATIONS) {
308 				headerCheck = headerValue ? c[i].where == headerValue : true;
309 			}
310 			if (negativeCheck && headerCheck) {				
311 				c.splice(i, 1);
312 				rule.conditions[condition] = c;
313 			}
314 		} 			
315 	}
316 	return rule;
317 };
318 
319 ZmPriorityMessageFilterDialog.prototype._handleConditionsError =
320 function(msg) {  
321 	var msgDialog = appCtxt.getMsgDialog();
322 	msgDialog.setMessage(msg, DwtMessageDialog.CRITICAL_STYLE);
323 	msgDialog.popup();
324 };
325 
326 ZmPriorityMessageFilterDialog.prototype._createActivityStreamsFolder =
327 function() {
328 	var jsonObj = {CreateFolderRequest:{_jsns:"urn:zimbraMail"}};
329 	var folder = jsonObj.CreateFolderRequest.folder = {l: ZmOrganizer.ID_ROOT, name: ZmMsg.activityStreamFolder, fie: 1, view: "message"};
330 	return appCtxt.getAppController().sendRequest({
331 		jsonObj: jsonObj,
332 		asyncMode: true,
333 		callback:  new AjxCallback(this, this._handleActivityStreamsFolderCreate)
334 	});
335 };
336 
337 ZmPriorityMessageFilterDialog.prototype._handleActivityStreamsFolderCreate = 
338 function(result) {
339 	var resp = result && result._data && result._data.CreateFolderResponse;
340 	if (resp) {
341 		appCtxt.set(ZmSetting.MAIL_ACTIVITYSTREAM_FOLDER, resp.folder[0].id);
342 	}
343 };
344