Skip to content

Commit

Permalink
initial version
Browse files Browse the repository at this point in the history
  • Loading branch information
barrydegraaff committed May 23, 2023
1 parent 4a41363 commit bfbc9e4
Show file tree
Hide file tree
Showing 11 changed files with 324 additions and 2 deletions.
8 changes: 6 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,2 +1,6 @@
# out-of-office-alert-zimlet
This Classic UI Zimlets displays an alert as soon as you enter an email address in to/cc/bcc of someone with an active out of office reply.
# Out Of Office Alert Zimlet

This Classic UI Zimlets displays an alert as soon as you enter an email address in to/cc/bcc of someone with an active out of office reply. Avoid putting time in writing an email just to be greeted with an out-of-office message after sending an email.

![](screenshots/out-of-office-alert.png)

80 changes: 80 additions & 0 deletions com_zimbra_outofoffice_alert/com_zimbra_outofoffice_alert.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
function com_zimbra_outofoffice_alert_HandlerObject() {
com_zimbra_outofoffice_alert_HandlerObject.settings = {};
};

com_zimbra_outofoffice_alert_HandlerObject.prototype = new ZmZimletBase();
com_zimbra_outofoffice_alert_HandlerObject.prototype.constructor = com_zimbra_outofoffice_alert_HandlerObject;
var outOfOfficeAlertZimlet = com_zimbra_outofoffice_alert_HandlerObject;

outOfOfficeAlertZimlet.prototype.init = function () {
AjxDispatcher.require(["MailCore"]);
try {
ZmRecipients.prototype._bubblesChangedCallback =
function() {
if (this._resetContainerSize) {
this._resetContainerSize(); // body size might change due to change in size of address field (due to new bubbles).
}
outOfOfficeAlertZimlet.prototype.bubblesChanged(this);
};
} catch (e) {console.log(e);}
};

/* status method show a Zimbra status message
* */
outOfOfficeAlertZimlet.prototype.status = function(text, type) {
var transitions = [ ZmToast.FADE_IN, ZmToast.PAUSE, ZmToast.FADE_OUT ];
appCtxt.getAppController().setStatusMsg(text, type, null, transitions);
};

outOfOfficeAlertZimlet.prototype.bubblesChanged = function(ZmRecipients) {
var zimletInstance = appCtxt._zimletMgr.getZimletByName('com_zimbra_outofoffice_alert').handlerObject;
var rawAddresses = ZmRecipients.getRawAddrFields();
var allAddresses = rawAddresses.TO + " " + rawAddresses.CC;
if(rawAddresses.BCC)
{
allAddresses = allAddresses + " " + rawAddresses.BCC;
}
allAddresses = allAddresses.replaceAll('<', ' ');
var regex = /\S+[a-z0-9]@[a-z0-9\.]+/img;
var recipients = allAddresses.match(regex);

var requestData = {}
requestData.accounts = recipients;
var request = new XMLHttpRequest();
var url = '/service/extension/outofofficebanner';
var formData = new FormData();
formData.append("jsondata", JSON.stringify(requestData));
request.open('POST', url);
request.onreadystatechange = function (e) {
if (request.readyState == 4) {
if (request.status == 200) {
var response = JSON.parse(request.responseText);
var hasOoo = false;
var recipientsWithOoo = "";
for (var key in response) {
if (response.hasOwnProperty(key)) {
if(response[key] == true)
{
hasOoo = true;
recipientsWithOoo = recipientsWithOoo + key;
}
}
}
if(hasOoo)
{
var zimletInstance = appCtxt._zimletMgr.getZimletByName('com_zimbra_outofoffice_alert').handlerObject;
zimletInstance.status(zimletInstance.getMessage('outOfOfficeAlertZimlet_message') + " " + recipientsWithOoo);
}
}
}
}
request.send(formData);
};

/* status method show a Zimbra status message
* */
outOfOfficeAlertZimlet.prototype.status = function(text, type) {
var transitions = [ ZmToast.FADE_IN, ZmToast.PAUSE, ZmToast.PAUSE, ZmToast.PAUSE, ZmToast.FADE_OUT ];
appCtxt.getAppController().setStatusMsg(text, type, null, transitions);
};

Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# Unicode conversion see: http://itpro.cz/juniconv/

outOfOfficeAlertZimlet_message = The following recipient(s) are out of office:
4 changes: 4 additions & 0 deletions com_zimbra_outofoffice_alert/com_zimbra_outofoffice_alert.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
<zimlet name="com_zimbra_outofoffice_alert" version="0.0.1" description="Display an alert when composing an email when someone in the To field has an active out of office reply." target="main view-window">
<include>com_zimbra_outofoffice_alert.js</include>
<handlerObject>com_zimbra_outofoffice_alert_HandlerObject</handlerObject>
</zimlet>
8 changes: 8 additions & 0 deletions extension/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
build
lib
outofofficebanner.jar
.idea
*.iml
openjdk*
java*
out
3 changes: 3 additions & 0 deletions extension/META-INF/MANIFEST.MF
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
Manifest-Version: 1.0
Zimbra-Extension-Class: com.zimbra.outofofficebanner.OutOfOfficeBannerExtension

50 changes: 50 additions & 0 deletions extension/build.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
<project name="outofofficebanner" default="jar" basedir=".">
<description>
Build file for the Zimbra Extension.
</description>

<property name="lib" value="lib/"/>
<property name="build" value="build/"/>
<property name="src" value="src/"/>
<property name="jar" value="outofofficebanner.jar"/>

<path id="libraries">
<fileset dir="${lib}" >
<include name="*.jar"/>
</fileset>
</path>

<target name="clean">
<delete dir="${build}"/>
<delete file="mytest.jar"/>
<mkdir dir="${build}"/>
</target>

<target name="compile"
depends="clean">
<javac srcdir="${src}"
destdir="${build}"
includeantruntime="false"
debug="true"
debuglevel="lines,vars,source"
target="17"
source="17">
<classpath>
<path refid="libraries" />
</classpath>
</javac>
</target>

<target name="jar"
depends="compile">
<jar jarfile="${jar}"
compress="false"
basedir="${build}">
<manifest>
<attribute name="Zimbra-Extension-Class" value="com.zimbra.outofofficebanner.OutOfOfficeBannerExtension" />
</manifest>
<fileset dir="${build}" />
</jar>
</target>

</project>
9 changes: 9 additions & 0 deletions extension/makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
all: extension

extension:
ant jar

clean:
rm -f outofofficebanner.jar

.PHONY: all extension
123 changes: 123 additions & 0 deletions extension/src/com/zimbra/outofofficebanner/OutOfOfficeBanner.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
package com.zimbra.outofofficebanner;

import com.zimbra.cs.account.Account;
import com.zimbra.cs.account.Provisioning;
import com.zimbra.cs.extension.ExtensionHttpHandler;
import com.zimbra.cs.account.AuthToken;
import com.zimbra.cs.servlet.util.AuthUtil;

import java.io.*;
import java.nio.charset.StandardCharsets;
import java.util.*;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;


import org.apache.commons.io.IOUtils;
import org.json.JSONArray;
import org.json.JSONObject;

public class OutOfOfficeBanner extends ExtensionHttpHandler {

/**
* @return path
*/
@Override
public String getPath() {
return "/outofofficebanner";
}

/**
* Processes HTTP GET requests.
*
* @param req request message
* @param resp response message
*/
@Override
public void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException {
resp.getOutputStream().print("com.zimbra.outofofficebanner is installed.");
}

/**
* Processes HTTP POST requests.
*
* @param req request message
* @param resp response message
*/

/* This extension works with aliasses as well. */
/*
let testData = {}
testData.accounts = ["[email protected]", "[email protected]"];
var request = new XMLHttpRequest();
var url = '/service/extension/outofofficebanner';
var formData = new FormData();
formData.append("jsondata", JSON.stringify(testData));
request.open('POST', url);
request.onreadystatechange = function (e) {
if (request.readyState == 4) {
if (request.status == 200) {
const Response = JSON.parse(request.responseText);
console.log(Response);
}
if (request.status == 400) {
const Response = JSON.parse(request.responseText);
console.log(Response);
}
}
}
request.send(formData);
* */
@Override
public void doPost(HttpServletRequest req, HttpServletResponse resp) throws IOException {
//all authentication is done by AuthUtil.getAuthTokenFromHttpReq, returns null if unauthorized
final AuthToken authToken = AuthUtil.getAuthTokenFromHttpReq(req, resp, false, true);
if (authToken != null) {
try {
//Get the jsondata field from the multipart request send to the server and parse it to JSON Object.
JSONObject receivedJSON = new JSONObject(IOUtils.toString(req.getPart("jsondata").getInputStream(), StandardCharsets.UTF_8));
JSONArray reqAccounts = receivedJSON.getJSONArray("accounts");
JSONObject responseJSON = new JSONObject();

//loop to get all json objects from data json array
for (int i = 0; i < reqAccounts.length(); i++) {
//use of toLowerCase to prevent people from using this endpoint to determine if accounts exist on server (privacy).
String reqAccount = reqAccounts.get(i).toString();

boolean replyInEffect = false;
try {
Account account = Provisioning.getInstance().getAccountByName(reqAccount);
boolean replyEnabled = account.isPrefOutOfOfficeReplyEnabled();

// Check if we are in any configured out of office interval
Date now = new Date();
Date fromDate = account.getGeneralizedTimeAttr(Provisioning.A_zimbraPrefOutOfOfficeFromDate, null);
Date untilDate = account.getGeneralizedTimeAttr(Provisioning.A_zimbraPrefOutOfOfficeUntilDate, null);

if (replyEnabled && (fromDate != null && !now.before(fromDate))) {
if (untilDate == null) {
replyInEffect = true;
} else if (untilDate != null && !now.after(untilDate)) {
replyInEffect = true;
} else {
replyInEffect = false;
}
}
responseJSON.put(reqAccount, replyInEffect);

} catch (Exception e) {
responseJSON.put(reqAccount, false);
}

}
resp.setContentType("application/json");
resp.setCharacterEncoding("UTF-8");
resp.getOutputStream().print(responseJSON.toString());

} catch (Exception e) {
e.printStackTrace();
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package com.zimbra.outofofficebanner;

import com.zimbra.common.service.ServiceException;
import com.zimbra.cs.extension.ExtensionDispatcherServlet;
import com.zimbra.cs.extension.ZimbraExtension;

/**
* This extension registers a custom HTTP handler with <code>ExtensionDispatcherServlet<code>
*
* @author vmahajan
*/
public class OutOfOfficeBannerExtension implements ZimbraExtension {

/**
* Defines a name for the extension. It must be an identifier.
*
* @return extension name
*/
public String getName() {
return "OutOfOfficeBannerExtension";
}

/**
* Initializes the extension. Called when the extension is loaded.
*
* @throws com.zimbra.common.service.ServiceException
*/
public void init() throws ServiceException {
ExtensionDispatcherServlet.register(this, new OutOfOfficeBanner());
}

/**
* Terminates the extension. Called when the server is shut down.
*/
public void destroy() {
ExtensionDispatcherServlet.unregister(this);
}
}
Binary file added screenshots/out-of-office-alert.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.

0 comments on commit bfbc9e4

Please sign in to comment.